From b9d93b19d46ca06752080549518a0e828a7da1c2 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Fri, 5 Sep 2025 00:09:09 +0100 Subject: [PATCH] X87StackOptimization: Defer writeback of the x87 stack pointer --- .../IR/Passes/x87StackOptimizationPass.cpp | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp b/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp index 5c7d7b98c..605c828fd 100644 --- a/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp @@ -298,7 +298,7 @@ private: void SetTopWithCache_Slow(Ref Value); Ref GetX87ValidTag_Slow(uint8_t Offset); // Resets fields to initial values - void Reset(bool AlsoSlowPath = true); + void Reset(); struct StackMemberInfo { StackMemberInfo() {} @@ -345,7 +345,11 @@ private: // Cached value for Top // If slowpath is false, then TopCache is nullptr. + bool FlushTopPending = false; std::array TopOffsetCache {}; + + void FlushTop(); + // Are we on the slow path? // Once we enter the slow path, we never come out. // This just simplifies the code atm. If there's a need to return to the fast path in the future @@ -364,13 +368,12 @@ inline void X87StackOptimization::InvalidateCaches() { } inline void X87StackOptimization::InvalidateTopOffsetCache() { + FlushTop(); TopOffsetCache.fill(nullptr); } -inline void X87StackOptimization::Reset(bool AlsoSlowPath) { - if (AlsoSlowPath) { - SlowPath = false; - } +inline void X87StackOptimization::Reset() { + SlowPath = false; StackData.clear(); InvalidateCaches(); } @@ -390,7 +393,7 @@ inline Ref X87StackOptimization::GetConstant(ssize_t Offset) { inline void X87StackOptimization::MigrateToSlowPathIf(bool ShouldMigrate) { if (ShouldMigrate && !SlowPath) { SynchronizeStackValues(); - Reset(false); // Reset everything but no need to change slowpath + StackData.clear(); SlowPath = true; } } @@ -420,9 +423,9 @@ inline Ref X87StackOptimization::GetOffsetTopWithCache_Slow(uint8_t Offset) { inline void X87StackOptimization::SetTopWithCache_Slow(Ref Value) { - IREmit->_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC); InvalidateTopOffsetCache(); TopOffsetCache[0] = Value; + FlushTopPending = true; } inline void X87StackOptimization::SetX87ValidTag(Ref Value, bool Valid) { @@ -555,11 +558,19 @@ inline void X87StackOptimization::UpdateTopForPush_Slow() { SetTopWithCache_Slow(TopOffset); } +void X87StackOptimization::FlushTop() { + if (FlushTopPending) { + IREmit->_StoreContext(OpSize::i8Bit, GPRClass, TopOffsetCache[0], offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC); + FlushTopPending = false; + } +} + // We synchronize stack values in a few occasions but one of the most important of those, // is when we move from fast to a slow path and need to make sure that the context is properly // written. Ref X87StackOptimization::SynchronizeStackValues() { - if (SlowPath) { // Nothing to do here. + if (SlowPath) { + FlushTop(); return GetTopWithCache_Slow(); } @@ -626,6 +637,8 @@ Ref X87StackOptimization::SynchronizeStackValues() { } } } + + FlushTop(); return TopValue; } @@ -815,6 +828,7 @@ void X87StackOptimization::Run(IREmitter* Emit) { case OP_INITSTACK: { StackData.clear(); + InvalidateTopOffsetCache(); break; } @@ -1058,6 +1072,7 @@ void X87StackOptimization::Run(IREmitter* Emit) { case OP_STACKFORCESLOW: { MigrateToSlowPathIf(true); + InvalidateTopOffsetCache(); break; }