diff --git a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp index 97a7214bb..d0c802b75 100644 --- a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp +++ b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp @@ -677,7 +677,7 @@ void Arm64Emitter::FillSpecialRegs(ARMEmitter::Register TmpReg, ARMEmitter::Regi } } -void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint32_t GPRSpillMask, uint32_t FPRSpillMask) { +void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint32_t GPRSpillMask, uint32_t FPRSpillMask, bool NZCV) { #ifndef VIXL_SIMULATOR if (EmitterCTX->HostFeatures.SupportsAFP) { // Disable AFP features when spilling registers. @@ -698,13 +698,15 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3 } #endif - // Regardless of what GPRs/FPRs we're spilling, we need to spill NZCV since it - // is always static and almost certainly clobbered by the subsequent code. - // - // TODO: Can we prove that NZCV is not used across a call in some cases and - // omit this? Might help x87 perf? Future idea. - mrs(TmpReg, ARMEmitter::SystemRegister::NZCV); - str(TmpReg.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + if (NZCV) { + // Regardless of what GPRs/FPRs we're spilling, we need to spill NZCV since it + // is always static and almost certainly clobbered by the subsequent code. + // + // TODO: Can we prove that NZCV is not used across a call in some cases and + // omit this? Might help x87 perf? Future idea. + mrs(TmpReg, ARMEmitter::SystemRegister::NZCV); + str(TmpReg.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + } // PF/AF are special, remove them from the mask uint32_t PFAFMask = ((1u << REG_PF.Idx()) | ((1u << REG_AF.Idx()))); @@ -726,7 +728,7 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3 } // Now handle PF/AF - if (PFAFSpillMask) { + if (NZCV && PFAFSpillMask) { auto PFOffset = offsetof(FEXCore::Core::CpuStateFrame, State.pf_raw); auto AFOffset = offsetof(FEXCore::Core::CpuStateFrame, State.af_raw); LOGMAN_THROW_A_FMT(PFAFSpillMask == PFAFMask, "PF/AF not spilled together"); @@ -776,7 +778,7 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3 } void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRFillMask, std::optional OptionalReg, - std::optional OptionalReg2) { + std::optional OptionalReg2, bool NZCV) { auto FindTempReg = [this](uint32_t* GPRFillMask) -> std::optional { for (auto Reg : StaticRegisters) { if (((1U << Reg.Idx()) & *GPRFillMask)) { @@ -810,13 +812,15 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF ldr(REG_CALLRET_SP, STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.callret_sp)); - // Regardless of what GPRs/FPRs we're filling, we need to fill NZCV since it - // is always static and was almost certainly clobbered. - // - // TODO: Can we prove that NZCV is not used across a call in some cases and - // omit this? Might help x87 perf? Future idea. - ldr(TmpReg.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); - msr(ARMEmitter::SystemRegister::NZCV, TmpReg); + if (NZCV) { + // Regardless of what GPRs/FPRs we're filling, we need to fill NZCV since it + // is always static and was almost certainly clobbered. + // + // TODO: Can we prove that NZCV is not used across a call in some cases and + // omit this? Might help x87 perf? Future idea. + ldr(TmpReg.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + msr(ARMEmitter::SystemRegister::NZCV, TmpReg); + } FillSpecialRegs(TmpReg, TmpReg2, true, FPRs); @@ -877,7 +881,7 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF } // Now handle PF/AF - if (PFAFFillMask) { + if (NZCV && PFAFFillMask) { LOGMAN_THROW_A_FMT(PFAFFillMask == PFAFMask, "PF/AF not filled together"); ldp(REG_PF.W(), REG_AF.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.pf_raw)); diff --git a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.h b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.h index 302ac7592..89eac7fd6 100644 --- a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.h +++ b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.h @@ -135,10 +135,10 @@ protected: // Returning REG_INVALID if there was no mapping. FEXCore::X86State::X86Reg GetX86RegRelationToARMReg(ARMEmitter::Register Reg); - void SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U); + void SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U, bool NZCV = true); void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U, std::optional OptionalReg = std::nullopt, - std::optional OptionalReg2 = std::nullopt); + std::optional OptionalReg2 = std::nullopt, bool NZCV = true); // Register 0-18 + 29 + 30 are caller saved static constexpr uint32_t CALLER_GPR_MASK = 0b0110'0000'0000'0111'1111'1111'1111'1111U; diff --git a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp index d8afba697..a3db23cb2 100644 --- a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp @@ -322,7 +322,7 @@ DEF_OP(Thunk) { // X0: CTX // X1: Args (from guest stack) - SpillStaticRegs(TMP1); // spill to ctx before ra64 spill + SpillStaticRegs(TMP1, true, ~0U, ~0U, false); // spill to ctx before ra64 spill PushDynamicRegs(TMP1); @@ -337,7 +337,7 @@ DEF_OP(Thunk) { PopDynamicRegs(); - FillStaticRegs(); // load from ctx after ra64 refill + FillStaticRegs(true, ~0U, ~0U, std::nullopt, std::nullopt, false); // load from ctx after ra64 refill } DEF_OP(ValidateCode) {