diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 938377f32..f8fa936e3 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -354,6 +354,15 @@ "Can potentially introduce more stutters, more likely the higher the number.", "Don't have this number larger than the increase count!" ] + }, + "UndefinedFlagAcidTest": { + "Type": "bool", + "Default": "false", + "AffectsCodeGen": "true", + "Desc": [ + "Ensures that all undefined flags generate consistent results", + "This is a useful test to determine if undefined flags are causing problems" + ] } }, "Debug": { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 8dc605ca7..cae9c6cdd 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -1004,6 +1004,7 @@ void OpDispatchBuilder::TESTOp(OpcodeArgs, uint32_t SrcIndex, bool DestRAX) { } InvalidateAF(); + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::ARPLOp(OpcodeArgs) { @@ -1410,6 +1411,7 @@ void OpDispatchBuilder::SHLOp(OpcodeArgs) { Ref Result = _Lshl(Size == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, Dest, Src); HandleShift(Op, Result, Dest, ShiftType::LSL, Src); + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHLImmediateOp(OpcodeArgs, bool SHL1Bit) { @@ -1423,6 +1425,7 @@ void OpDispatchBuilder::SHLImmediateOp(OpcodeArgs, bool SHL1Bit) { CalculateFlags_ShiftLeftImmediate(OpSizeFromSrc(Op), Result, Dest, Shift); CalculateDeferredFlags(); StoreResultGPR(Op, Result); + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHROp(OpcodeArgs) { @@ -1432,6 +1435,8 @@ void OpDispatchBuilder::SHROp(OpcodeArgs) { auto ALUOp = _Lshr(std::max(OpSize::i32Bit, Size), Dest, Src); HandleShift(Op, ALUOp, Dest, ShiftType::LSR, Src); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHRImmediateOp(OpcodeArgs, bool SHR1Bit) { @@ -1444,6 +1449,8 @@ void OpDispatchBuilder::SHRImmediateOp(OpcodeArgs, bool SHR1Bit) { CalculateFlags_ShiftRightImmediate(OpSizeFromSrc(Op), ALUOp, Dest, Shift); CalculateDeferredFlags(); StoreResultGPR(Op, ALUOp); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHLDOp(OpcodeArgs) { @@ -1487,6 +1494,8 @@ void OpDispatchBuilder::SHLDOp(OpcodeArgs) { Res = _Select(OpSize::i64Bit, OpSize::i64Bit, CondClass::EQ, Shift, Constant(0), Dest, Res); HandleShift(Op, Res, Dest, ShiftType::LSL, Shift); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHLDImmediateOp(OpcodeArgs) { @@ -1518,6 +1527,8 @@ void OpDispatchBuilder::SHLDImmediateOp(OpcodeArgs) { // Ensure Zext still occurs StoreResultGPR(Op, Dest); } + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHRDOp(OpcodeArgs) { @@ -1552,6 +1563,8 @@ void OpDispatchBuilder::SHRDOp(OpcodeArgs) { Res = _Select(OpSize::i64Bit, OpSize::i64Bit, CondClass::EQ, Shift, Constant(0), Dest, Res); HandleShift(Op, Res, Dest, ShiftType::LSR, Shift); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::SHRDImmediateOp(OpcodeArgs) { @@ -1582,6 +1595,8 @@ void OpDispatchBuilder::SHRDImmediateOp(OpcodeArgs) { // Ensure Zext still occurs StoreResultGPR(Op, Dest); } + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::ASHROp(OpcodeArgs, bool Immediate, bool SHR1Bit) { @@ -1611,6 +1626,8 @@ void OpDispatchBuilder::ASHROp(OpcodeArgs, bool Immediate, bool SHR1Bit) { HandleShift(Op, Result, Dest, ShiftType::ASR, Src); } + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); } void OpDispatchBuilder::RotateOp(OpcodeArgs, bool Left, bool IsImmediate, bool Is1Bit) { @@ -1681,6 +1698,8 @@ void OpDispatchBuilder::ANDNBMIOp(OpcodeArgs) { StoreResultGPR(Op, Dest); CalculateFlags_Logical(OpSizeFromSrc(Op), Dest); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::BEXTRBMIOp(OpcodeArgs) { @@ -1724,6 +1743,8 @@ void OpDispatchBuilder::BEXTRBMIOp(OpcodeArgs) { // AF are undefined. SetNZ_ZeroCV(GetOpSize(Dest), Dest); InvalidatePF_AF(); + + AcidTestFlags(X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::BLSIBMIOp(OpcodeArgs) { @@ -1745,6 +1766,8 @@ void OpDispatchBuilder::BLSIBMIOp(OpcodeArgs) { SetNZ_ZeroCV(Size, Result); InvalidatePF_AF(); SetCFInverted(GetRFLAG(X86State::RFLAG_ZF_RAW_LOC)); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::BLSMSKBMIOp(OpcodeArgs) { @@ -1765,6 +1788,8 @@ void OpDispatchBuilder::BLSMSKBMIOp(OpcodeArgs) { // and O) while setting S. SetNZ_ZeroCV(Size, Result); SetCFInverted(CFInv); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::BLSRBMIOp(OpcodeArgs) { @@ -1782,6 +1807,8 @@ void OpDispatchBuilder::BLSRBMIOp(OpcodeArgs) { SetNZ_ZeroCV(Size, Result); SetCFInverted(CFInv); InvalidatePF_AF(); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } // Handles SARX, SHLX, and SHRX @@ -1841,6 +1868,8 @@ void OpDispatchBuilder::BZHI(OpcodeArgs) { InvalidatePF_AF(); SetNZ_ZeroCV(Size, Result); SetCFInverted(CFInv); + + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::RORX(OpcodeArgs) { @@ -2565,6 +2594,9 @@ void OpDispatchBuilder::BTOp(OpcodeArgs, uint32_t SrcIndex, BTAction Action) { // to insert into CF directly. We could optimize perhaps. SetCFDirect(Value, 0, true); } + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, + X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::IMUL1SrcOp(OpcodeArgs) { @@ -2604,6 +2636,8 @@ void OpDispatchBuilder::IMUL1SrcOp(OpcodeArgs) { StoreResultGPR(Op, Dest); CalculateFlags_MUL(Size, Dest, ResultHigh); + + AcidTestFlags(X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::IMUL2SrcOp(OpcodeArgs) { @@ -2643,6 +2677,8 @@ void OpDispatchBuilder::IMUL2SrcOp(OpcodeArgs) { StoreResultGPR(Op, Dest); CalculateFlags_MUL(Size, Dest, ResultHigh); + + AcidTestFlags(X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::IMULOp(OpcodeArgs) { @@ -2698,6 +2734,8 @@ void OpDispatchBuilder::IMULOp(OpcodeArgs) { } CalculateFlags_MUL(Size, Result, ResultHigh); + + AcidTestFlags(X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::MULOp(OpcodeArgs) { @@ -2868,6 +2906,8 @@ void OpDispatchBuilder::DAAOp(OpcodeArgs) { SetCFInverted(CFInv); CalculatePF(AL); SetAFAndFixup(AF); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC); } void OpDispatchBuilder::DASOp(OpcodeArgs) { @@ -2894,6 +2934,8 @@ void OpDispatchBuilder::DASOp(OpcodeArgs) { SetCFDirect(NewCF); CalculatePF(AL); SetAFAndFixup(AF); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC); } void OpDispatchBuilder::AAAOp(OpcodeArgs) { @@ -2911,6 +2953,7 @@ void OpDispatchBuilder::AAAOp(OpcodeArgs) { // AL = AL & 0x0F A = _And(OpSize::i32Bit, A, Constant(0xFF0F)); StoreGPRRegister(X86State::REG_RAX, A, OpSize::i16Bit); + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::AASOp(OpcodeArgs) { @@ -2928,6 +2971,8 @@ void OpDispatchBuilder::AASOp(OpcodeArgs) { // AL = AL & 0x0F A = _And(OpSize::i32Bit, A, Constant(0xFF0F)); StoreGPRRegister(X86State::REG_RAX, A, OpSize::i16Bit); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::AAMOp(OpcodeArgs) { @@ -2942,6 +2987,8 @@ void OpDispatchBuilder::AAMOp(OpcodeArgs) { SetNZ_ZeroCV(OpSize::i8Bit, Res); CalculatePF(Res); InvalidateAF(); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::AADOp(OpcodeArgs) { @@ -2955,6 +3002,8 @@ void OpDispatchBuilder::AADOp(OpcodeArgs) { SetNZ_ZeroCV(OpSize::i8Bit, Result); CalculatePF(Result); InvalidateAF(); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::XLATOp(OpcodeArgs) { @@ -3754,6 +3803,10 @@ void OpDispatchBuilder::DIVOp(OpcodeArgs) { StoreGPRRegister(X86State::REG_RAX, Quotient, Size); StoreGPRRegister(X86State::REG_RDX, Remainder, Size); } + + // All flags are undefined. + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, + X86State::RFLAG_PF_RAW_LOC, X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::IDIVOp(OpcodeArgs) { @@ -3797,6 +3850,10 @@ void OpDispatchBuilder::IDIVOp(OpcodeArgs) { StoreGPRRegister(X86State::REG_RAX, Quotient, Size); StoreGPRRegister(X86State::REG_RDX, Remainder, Size); } + + // All flags are undefined. + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_ZF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, + X86State::RFLAG_PF_RAW_LOC, X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::BSFOp(OpcodeArgs) { @@ -3819,6 +3876,9 @@ void OpDispatchBuilder::BSFOp(OpcodeArgs) { // applications might rely on that in the wild. auto SelectOp = NZCVSelect(GPRSize, CondClass::EQ, Dest, Result); StoreResultGPR_WithOpSize(Op, Op->Dest, SelectOp, DstSize); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC, + X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::BSROp(OpcodeArgs) { @@ -3837,6 +3897,9 @@ void OpDispatchBuilder::BSROp(OpcodeArgs) { // If Src was zero then the destination doesn't get modified auto SelectOp = NZCVSelect(GPRSize, CondClass::EQ, Dest, Result); StoreResultGPR_WithOpSize(Op, Op->Dest, SelectOp, DstSize); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC, + X86State::RFLAG_CF_RAW_LOC); } void OpDispatchBuilder::CMPXCHGOp(OpcodeArgs) { @@ -4782,12 +4845,14 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::I case FEXCore::IR::IROps::OP_AND: case FEXCore::IR::IROps::OP_OR: { CalculateFlags_Logical(Size, Result); + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); break; } case FEXCore::IR::IROps::OP_ANDWITHFLAGS: { HandleNZ00Write(); CalculatePF(Result); InvalidateAF(); + AcidTestFlags(X86State::RFLAG_AF_RAW_LOC); break; } default: break; @@ -4938,6 +5003,8 @@ void OpDispatchBuilder::TZCNT(OpcodeArgs) { StoreResultGPR(Op, Src); CalculateFlags_ZCNT(OpSizeFromSrc(Op), Src); + + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::LZCNT(OpcodeArgs) { @@ -4947,6 +5014,7 @@ void OpDispatchBuilder::LZCNT(OpcodeArgs) { auto Res = _CountLeadingZeroes(OpSizeFromSrc(Op), Src); StoreResultGPR(Op, Res); CalculateFlags_ZCNT(OpSizeFromSrc(Op), Res); + AcidTestFlags(X86State::RFLAG_OF_RAW_LOC, X86State::RFLAG_SF_RAW_LOC, X86State::RFLAG_AF_RAW_LOC, X86State::RFLAG_PF_RAW_LOC); } void OpDispatchBuilder::MOVBEOp(OpcodeArgs) { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 71766e010..d410fdf5e 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -1321,6 +1321,7 @@ protected: private: FEX_CONFIG_OPT(ReducedPrecisionMode, X87REDUCEDPRECISION); + FEX_CONFIG_OPT(UndefinedFlagAcidTest, UNDEFINEDFLAGACIDTEST); struct JumpTargetInfo { Ref BlockEntry; @@ -1904,6 +1905,55 @@ private: InvalidateReg(Core::CPUState::AF_AS_GREG); } + // Sets the desired flags to zero when acid test is enabled. + // Not meant to be fast, just meant to produce consistent results. + void AcidTestFlags(unsigned BitOffset = 0, unsigned BitOffset2 = 0, unsigned BitOffset3 = 0, unsigned BitOffset4 = 0, + unsigned BitOffset5 = 0, unsigned BitOffset6 = 0) { + if (!UndefinedFlagAcidTest) [[likely]] { + return; + } + + unsigned BitFlags {}; + if (BitOffset) { + BitFlags |= 1U << BitOffset; + } + if (BitOffset2) { + BitFlags |= 1U << BitOffset2; + } + if (BitOffset3) { + BitFlags |= 1U << BitOffset3; + } + if (BitOffset4) { + BitFlags |= 1U << BitOffset4; + } + if (BitOffset5) { + BitFlags |= 1U << BitOffset5; + } + if (BitOffset6) { + BitFlags |= 1U << BitOffset6; + } + + auto Flag = Constant(0); + if (BitFlags & (1U << X86State::RFLAG_SF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_SF_RAW_LOC); + } + if (BitFlags & (1U << X86State::RFLAG_ZF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_ZF_RAW_LOC); + } + if (BitFlags & (1U << X86State::RFLAG_CF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_CF_RAW_LOC); + } + if (BitFlags & (1U << X86State::RFLAG_OF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_OF_RAW_LOC); + } + if (BitFlags & (1U << X86State::RFLAG_AF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_AF_RAW_LOC); + } + if (BitFlags & (1U << X86State::RFLAG_AF_RAW_LOC)) { + SetRFLAG(Flag, FEXCore::X86State::RFLAG_AF_RAW_LOC); + } + } + [[nodiscard]] static CondClass CondForNZCVBit(unsigned BitOffset, bool Invert) { switch (BitOffset) { @@ -2207,8 +2257,7 @@ private: Ref V_inv = GetRFLAG(FEXCore::X86State::RFLAG_OF_RAW_LOC, true); SetRFLAG(V_inv); - // Intel: OF, SF, and AF set to zero - // AMD: no mention of OF, SF and AF but actual hardware seems to always zero + // Both Intel and AMD manual state that OF, SF, and AF are set to zero. // // Zero AF. Note that the comparison sets the raw PF to 0/1 above, so // PF[4] is 0 so the XOR with PF will have no effect, so setting the AF