OpcodeDispatcher: Add undefined flags acid test

Useful debug feature to make sure FEX returns consistent flag data for
instructions that have undefined flags. Just sets everything that is
undefined to zero always.

Manually walked through all the instructions in the architecture manual
to check for ones doing undefined flag setting, then glued the handler
on to the end.

Also fixes a comment that has incorrect information about undefined
flags.
This commit is contained in:
Ryan Houdek committed 2026-10-19 13:50:34 -07:00
1 parent 741e146fdf
commit 651b94f435
3 files changed
+128 -2

No files matched your search

@@ -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) {