From 9db0ebd4b6d195410aac622bc9b27da7155babbc Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 10 Jun 2021 19:54:07 +0200 Subject: [PATCH] PowerPC: Set host CPU rounding mode on init and savestate Not doing this can cause desyncs when TASing. (I don't know how common such desyncs would be, though. For games that don't change rounding modes, they shouldn't be a problem.) --- Source/Core/Common/ArmFPURoundMode.cpp | 4 ---- Source/Core/Common/FPURoundMode.h | 2 -- Source/Core/Common/GenericFPURoundMode.cpp | 3 --- Source/Core/Common/x64FPURoundMode.cpp | 5 ----- Source/Core/Core/Core.cpp | 2 ++ Source/Core/Core/HW/CPU.cpp | 4 ++++ .../Interpreter_SystemRegisters.cpp | 19 ++++++------------- Source/Core/Core/PowerPC/PowerPC.cpp | 16 ++++++++++++---- Source/Core/Core/PowerPC/PowerPC.h | 2 ++ 9 files changed, 26 insertions(+), 31 deletions(-) diff --git a/Source/Core/Common/ArmFPURoundMode.cpp b/Source/Core/Common/ArmFPURoundMode.cpp index 94ed16d279..81daf68d2f 100644 --- a/Source/Core/Common/ArmFPURoundMode.cpp +++ b/Source/Core/Common/ArmFPURoundMode.cpp @@ -41,10 +41,6 @@ void SetRoundMode(int mode) // We don't need to do anything here since SetSIMDMode is always called after calling this } -void SetPrecisionMode(PrecisionMode mode) -{ -} - void SetSIMDMode(int rounding_mode, bool non_ieee_mode) { // When AH is disabled, FZ controls flush-to-zero for both inputs and outputs. When AH is enabled, diff --git a/Source/Core/Common/FPURoundMode.h b/Source/Core/Common/FPURoundMode.h index ac99478982..5fba5c9fda 100644 --- a/Source/Core/Common/FPURoundMode.h +++ b/Source/Core/Common/FPURoundMode.h @@ -29,8 +29,6 @@ enum PrecisionMode void SetRoundMode(int mode); -void SetPrecisionMode(PrecisionMode mode); - void SetSIMDMode(int rounding_mode, bool non_ieee_mode); /* diff --git a/Source/Core/Common/GenericFPURoundMode.cpp b/Source/Core/Common/GenericFPURoundMode.cpp index e8bbbb7e48..81b59e6d1e 100644 --- a/Source/Core/Common/GenericFPURoundMode.cpp +++ b/Source/Core/Common/GenericFPURoundMode.cpp @@ -11,9 +11,6 @@ namespace FPURoundMode void SetRoundMode(int mode) { } -void SetPrecisionMode(PrecisionMode mode) -{ -} void SetSIMDMode(int rounding_mode, bool non_ieee_mode) { } diff --git a/Source/Core/Common/x64FPURoundMode.cpp b/Source/Core/Common/x64FPURoundMode.cpp index a12f99efd2..9af3ec4691 100644 --- a/Source/Core/Common/x64FPURoundMode.cpp +++ b/Source/Core/Common/x64FPURoundMode.cpp @@ -22,11 +22,6 @@ void SetRoundMode(int mode) fesetround(rounding_mode_lut[mode]); } -void SetPrecisionMode(PrecisionMode /* mode */) -{ - // x64 doesn't need this - fpu is done with SSE -} - void SetSIMDMode(int rounding_mode, bool non_ieee_mode) { // OR-mask for disabling FPU exceptions (bits 7-12 in the MXCSR register) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 1ec847cecc..ddda19036f 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -25,6 +25,7 @@ #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/Event.h" +#include "Common/FPURoundMode.h" #include "Common/FileUtil.h" #include "Common/Flag.h" #include "Common/Logging/Log.h" @@ -625,6 +626,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); UndeclareAsCPUThread(); + FPURoundMode::LoadDefaultSIMDState(); // Spawn the CPU thread. The CPU thread will signal the event that boot is complete. s_cpu_thread = std::thread(cpuThreadFunc, savestate_path, delete_savestate); diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index b708263bfc..e12db40dde 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -87,6 +87,10 @@ static void ExecutePendingJobs(std::unique_lock& state_lock) void Run() { + // Updating the host CPU's rounding mode must be done on the CPU thread. + // We can't rely on PowerPC::Init doing it, since it's called from EmuThread. + PowerPC::RoundingModeUpdated(); + std::unique_lock state_lock(s_state_change_lock); while (s_state != State::PowerDown) { diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp index c9d23577b8..ae0ff724c3 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp @@ -6,7 +6,6 @@ #include "Common/Assert.h" #include "Common/CommonTypes.h" -#include "Common/FPURoundMode.h" #include "Common/Logging/Log.h" #include "Core/HW/GPFifo.h" #include "Core/HW/SystemTimers.h" @@ -26,13 +25,10 @@ mffsx: 80036608 mffsx: 80036650 (huh?) */ -// TODO(ector): More proper handling of SSE state. -// That is, set rounding mode etc when entering jit code or the interpreter loop -// Restore rounding mode when calling anything external -static void FPSCRtoFPUSettings(UReg_FPSCR fp) +static void FPSCRUpdated(UReg_FPSCR fp) { - FPURoundMode::SetRoundMode(fp.RN); + PowerPC::RoundingModeUpdated(); if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE) { @@ -40,9 +36,6 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp) // fp.VE, fp.OE, fp.UE, fp.ZE, fp.XE); // Pokemon Colosseum does this. Gah. } - - // Set SSE rounding mode and denormal handling - FPURoundMode::SetSIMDMode(fp.RN, fp.NI); } static void UpdateFPSCR(UReg_FPSCR* fpscr) @@ -57,7 +50,7 @@ void Interpreter::mtfsb0x(UGeckoInstruction inst) u32 b = 0x80000000 >> inst.CRBD; FPSCR.Hex &= ~b; - FPSCRtoFPUSettings(FPSCR); + FPSCRUpdated(FPSCR); if (inst.Rc) PowerPC::ppcState.UpdateCR1(); @@ -74,7 +67,7 @@ void Interpreter::mtfsb1x(UGeckoInstruction inst) else FPSCR |= b; - FPSCRtoFPUSettings(FPSCR); + FPSCRUpdated(FPSCR); if (inst.Rc) PowerPC::ppcState.UpdateCR1(); @@ -89,7 +82,7 @@ void Interpreter::mtfsfix(UGeckoInstruction inst) FPSCR = (FPSCR.Hex & ~mask) | (imm >> (4 * field)); - FPSCRtoFPUSettings(FPSCR); + FPSCRUpdated(FPSCR); if (inst.Rc) PowerPC::ppcState.UpdateCR1(); @@ -106,7 +99,7 @@ void Interpreter::mtfsfx(UGeckoInstruction inst) } FPSCR = (FPSCR.Hex & ~m) | (static_cast(rPS(inst.FB).PS0AsU64()) & m); - FPSCRtoFPUSettings(FPSCR); + FPSCRUpdated(FPSCR); if (inst.Rc) PowerPC::ppcState.UpdateCR1(); diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp index 4388a6ce5f..73eac23a94 100644 --- a/Source/Core/Core/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/PowerPC/PowerPC.cpp @@ -20,6 +20,7 @@ #include "Common/Logging/Log.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/HW/CPU.h" #include "Core/HW/SystemTimers.h" @@ -129,6 +130,7 @@ void DoState(PointerWrap& p) if (p.GetMode() == PointerWrap::MODE_READ) { + RoundingModeUpdated(); IBATUpdated(); DBATUpdated(); } @@ -180,6 +182,7 @@ static void ResetRegisters() } SetXER({}); + RoundingModeUpdated(); DBATUpdated(); IBATUpdated(); @@ -246,10 +249,6 @@ CPUCore DefaultCPUCore() void Init(CPUCore cpu_core) { - // NOTE: This function runs on EmuThread, not the CPU Thread. - // Changing the rounding mode has a limited effect. - FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53); - s_invalidate_cache_thread_safe = CoreTiming::RegisterEvent("invalidateEmulatedCache", InvalidateCacheThreadSafe); @@ -632,4 +631,13 @@ void UpdateFPRF(double dvalue) FPSCR.FPRF = Common::ClassifyDouble(dvalue); } +void RoundingModeUpdated() +{ + // The rounding mode is separate for each thread, so this must run on the CPU thread + ASSERT(Core::IsCPUThread()); + + FPURoundMode::SetRoundMode(FPSCR.RN); + FPURoundMode::SetSIMDMode(FPSCR.RN, FPSCR.NI); +} + } // namespace PowerPC diff --git a/Source/Core/Core/PowerPC/PowerPC.h b/Source/Core/Core/PowerPC/PowerPC.h index 34804dfb12..73919161f2 100644 --- a/Source/Core/Core/PowerPC/PowerPC.h +++ b/Source/Core/Core/PowerPC/PowerPC.h @@ -306,4 +306,6 @@ inline void SetXER_OV(bool value) void UpdateFPRF(double dvalue); +void RoundingModeUpdated(); + } // namespace PowerPC