aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMerry <[email protected]>2024-02-13 02:19:38 +0000
committerMerry <[email protected]>2024-02-13 02:19:55 +0000
commit4ae4750b5a960871c10325e0864fd8646d53f2ef (patch)
tree2a2d24b2a08cf116b7364d307318a9ec3b0ecc99
parentba8192d89078af51ae6f97c9352e3683612cdff1 (diff)
downloaddynarmic-4ae4750b5a960871c10325e0864fd8646d53f2ef.tar.gz
dynarmic-4ae4750b5a960871c10325e0864fd8646d53f2ef.zip
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.
-rw-r--r--src/dynarmic/backend/arm64/emit_arm64_a64.cpp5
-rw-r--r--src/dynarmic/backend/arm64/fpsr_manager.cpp9
-rw-r--r--src/dynarmic/backend/arm64/fpsr_manager.h3
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<IR::Opcode::A64GetFPCR>(oaknut::CodeGenerator& code, EmitContext& ct
}
template<>
-void EmitIR<IR::Opcode::A64GetFPSR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
+void EmitIR<IR::Opcode::A64GetFPSR>(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;