diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 8e7bdd61c..9fd0edaf5 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -441,14 +441,7 @@ void OpDispatchBuilder::SecondaryALUOp(OpcodeArgs) { break; } case FEXCore::IR::IROps::OP_ANDWITHFLAGS: { - InvalidateDeferredFlags(); - - // SF/ZF/CF/OF - CachedNZCV = nullptr; - PossiblySetNZCVBits = (1u << 31) | (1u << 30); - NZCVDirty = false; - - // PF/AF + HandleNZ00Write(); CalculatePF(Result); _InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC); break; @@ -1298,16 +1291,8 @@ void OpDispatchBuilder::TESTOp(OpcodeArgs) { Src = Dest; } - InvalidateDeferredFlags(); - - // SF/ZF/CF/OF - OrderedNode *ALUOp = _AndWithFlags(IR::SizeToOpSize(Size), Dest, Src); - CachedNZCV = nullptr; - PossiblySetNZCVBits = (1u << 31) | (1u << 30); - NZCVDirty = false; - - // PF/AF - CalculatePF(ALUOp); + HandleNZ00Write(); + CalculatePF(_AndWithFlags(IR::SizeToOpSize(Size), Dest, Src)); _InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC); } @@ -5278,14 +5263,7 @@ void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCor break; } case FEXCore::IR::IROps::OP_ANDWITHFLAGS: { - InvalidateDeferredFlags(); - - // SF/ZF/CF/OF - CachedNZCV = nullptr; - PossiblySetNZCVBits = (1u << 31) | (1u << 30); - NZCVDirty = false; - - // PF/AF + HandleNZ00Write(); CalculatePF(Result); _InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC); break; diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 607a4f406..7e0df0530 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -1263,6 +1263,30 @@ private: return NZCVMask; } + // Set flag tracking to prepare for an operation that directly writes NZCV. If + // some bits are known to be zeroed, the PossiblySetNZCVBits mask can be + // passed. Otherwise, it defaults to assuming all bits may be set after + // (this is conservative). + void HandleNZCVWrite(uint32_t _PossiblySetNZCVBits = ~0) { + InvalidateDeferredFlags(); + CachedNZCV = nullptr; + PossiblySetNZCVBits = _PossiblySetNZCVBits; + NZCVDirty = false; + } + + // Set flag tracking to prepare for a read-modify-write operation on NZCV. + void HandleNZCV_RMW(uint32_t _PossiblySetNZCVBits = ~0) { + if (NZCVDirty && CachedNZCV) + _StoreNZCV(CachedNZCV); + + HandleNZCVWrite(_PossiblySetNZCVBits); + } + + // Special case of the above where we are known to zero C/V + void HandleNZ00Write() { + HandleNZCVWrite((1u << 31) | (1u << 30)); + } + OrderedNode *GetNZCV() { if (!CachedNZCV) { CachedNZCV = _LoadNZCV(); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/Flags.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/Flags.cpp index 8a99bf22b..4c45e8b13 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/Flags.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/Flags.cpp @@ -495,12 +495,8 @@ void OpDispatchBuilder::CalculateFlags_ADC(uint8_t SrcSize, OrderedNode *Res, Or CalculatePF(Res); if (SrcSize >= 4) { - if (NZCVDirty && CachedNZCV) - _StoreNZCV(CachedNZCV); - CachedNZCV = nullptr; - + HandleNZCV_RMW(); _AdcNZCV(OpSize, Src1, Src2); - PossiblySetNZCVBits = ~0; } else { // SF/ZF SetNZ_ZeroCV(SrcSize, Res); @@ -531,13 +527,8 @@ void OpDispatchBuilder::CalculateFlags_SBB(uint8_t SrcSize, OrderedNode *Res, Or // Rectify input carry CarryInvert(); - if (NZCVDirty && CachedNZCV) - _StoreNZCV(CachedNZCV); - CachedNZCV = nullptr; - NZCVDirty = false; - + HandleNZCV_RMW(); _SbbNZCV(OpSize, Src1, Src2); - PossiblySetNZCVBits = ~0; // Rectify output carry CarryInvert(); @@ -568,10 +559,8 @@ void OpDispatchBuilder::CalculateFlags_SUB(uint8_t SrcSize, OrderedNode *Res, Or // Stash CF before stomping over it auto OldCF = UpdateCF ? nullptr : GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC); + HandleNZCVWrite(); _SubNZCV(IR::SizeToOpSize(SrcSize), Src1, Src2); - CachedNZCV = nullptr; - NZCVDirty = false; - PossiblySetNZCVBits = ~0; // If we're updating CF, we need to invert it for correctness. If we're not // updating CF, we need to restore the CF since we stomped over it. @@ -590,10 +579,8 @@ void OpDispatchBuilder::CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, Or // Stash CF before stomping over it auto OldCF = UpdateCF ? nullptr : GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC); + HandleNZCVWrite(); _AddNZCV(IR::SizeToOpSize(SrcSize), Src1, Src2); - CachedNZCV = nullptr; - NZCVDirty = false; - PossiblySetNZCVBits = ~0; // We stomped over CF while calculation flags, restore it. if (!UpdateCF) @@ -601,6 +588,8 @@ void OpDispatchBuilder::CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, Or } void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, OrderedNode *Res, OrderedNode *High) { + HandleNZCVWrite(); + // PF/AF/ZF/SF // Undefined { @@ -619,13 +608,12 @@ void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, OrderedNode *Res, Or // undefined, this does what we need. auto Zero = _Constant(0); _CondAddNZCV(OpSize::i64Bit, Zero, Zero, CondClassType{COND_EQ}, 0x3 /* nzCV */); - CachedNZCV = nullptr; - NZCVDirty = false; - PossiblySetNZCVBits = ~0; } } void OpDispatchBuilder::CalculateFlags_UMUL(OrderedNode *High) { + HandleNZCVWrite(); + auto Zero = _Constant(0); OpSize Size = IR::SizeToOpSize(GetOpSize(High)); @@ -645,9 +633,6 @@ void OpDispatchBuilder::CalculateFlags_UMUL(OrderedNode *High) { // If High = 0, then sets to nZcv. Else sets to nzCV. Since SF/ZF undefined, // this does what we need. _CondAddNZCV(Size, Zero, Zero, CondClassType{COND_EQ}, 0x3 /* nzCV */); - CachedNZCV = nullptr; - NZCVDirty = false; - PossiblySetNZCVBits = ~0; } } diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/Vector.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/Vector.cpp index a23cb0f78..6532e4d9a 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/Vector.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/Vector.cpp @@ -3422,15 +3422,12 @@ void OpDispatchBuilder::VPALIGNROp(OpcodeArgs) { template void OpDispatchBuilder::UCOMISxOp(OpcodeArgs) { - InvalidateDeferredFlags(); - const auto SrcSize = Op->Src[0].IsGPR() ? GetGuestVectorLength() : GetSrcSize(Op); OrderedNode *Src1 = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, GetGuestVectorLength(), Op->Flags); OrderedNode *Src2 = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], SrcSize, Op->Flags); - CachedNZCV = nullptr; + HandleNZCVWrite(); _FCmp(ElementSize, Src1, Src2); - PossiblySetNZCVBits = ~0; ConvertNZCVToSSE(); // Zero AF. Note that the comparison sets the raw PF to 0/1 above, so PF[4] is