From 4ae4750b5a960871c10325e0864fd8646d53f2ef Mon Sep 17 00:00:00 2001 From: Merry Date: Tue, 13 Feb 2024 02:19:38 +0000 Subject: [PATCH] emit_arm64_a64: Take into account currently loaded FPSR Previously we just retrieved the last stored FPSR and used that when the guest asks for the current FPSR. This is incorrect behaviour. We failed to take into account the current state of the host FPSR. Here we take this into account. This bug was discovered via #795. --- src/dynarmic/backend/arm64/emit_arm64_a64.cpp | 5 +++-- src/dynarmic/backend/arm64/fpsr_manager.cpp | 9 +++++++++ src/dynarmic/backend/arm64/fpsr_manager.h | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/dynarmic/backend/arm64/emit_arm64_a64.cpp b/src/dynarmic/backend/arm64/emit_arm64_a64.cpp index 67fd91f2..5152a54b 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_a64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_a64.cpp @@ -10,6 +10,7 @@ #include "dynarmic/backend/arm64/abi.h" #include "dynarmic/backend/arm64/emit_arm64.h" #include "dynarmic/backend/arm64/emit_context.h" +#include "dynarmic/backend/arm64/fpsr_manager.h" #include "dynarmic/backend/arm64/reg_alloc.h" #include "dynarmic/interface/halt_reason.h" #include "dynarmic/ir/basic_block.h" @@ -277,11 +278,11 @@ void EmitIR(oaknut::CodeGenerator& code, EmitContext& ct } template<> -void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { +void EmitIR(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) { auto Wresult = ctx.reg_alloc.WriteW(inst); RegAlloc::Realize(Wresult); - code.LDR(Wresult, Xstate, offsetof(A64JitState, fpsr)); + ctx.fpsr.GetFpsr(Wresult); } template<> diff --git a/src/dynarmic/backend/arm64/fpsr_manager.cpp b/src/dynarmic/backend/arm64/fpsr_manager.cpp index 1a1c323e..1c614745 100644 --- a/src/dynarmic/backend/arm64/fpsr_manager.cpp +++ b/src/dynarmic/backend/arm64/fpsr_manager.cpp @@ -37,4 +37,13 @@ void FpsrManager::Load() { fpsr_loaded = true; } +void FpsrManager::GetFpsr(oaknut::WReg dest) { + code.LDR(dest, Xstate, state_fpsr_offset); + + if (fpsr_loaded) { + code.MRS(Xscratch1, oaknut::SystemReg::FPSR); + code.ORR(dest, dest, Wscratch1); + } +} + } // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/fpsr_manager.h b/src/dynarmic/backend/arm64/fpsr_manager.h index e003522d..9e5d68bf 100644 --- a/src/dynarmic/backend/arm64/fpsr_manager.h +++ b/src/dynarmic/backend/arm64/fpsr_manager.h @@ -9,6 +9,7 @@ namespace oaknut { struct CodeGenerator; +struct WReg; } // namespace oaknut namespace Dynarmic::Backend::Arm64 { @@ -21,6 +22,8 @@ public: void Load(); void Overwrite() { fpsr_loaded = false; } + void GetFpsr(oaknut::WReg); + private: oaknut::CodeGenerator& code; size_t state_fpsr_offset;