OpcodeDispatcher: add cache

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
This commit is contained in:
Alyssa Rosenzweig committed 2024-06-26 14:49:05 -04:00
1 parent 7d939a3b3d
commit 46676ca376
7 files changed
+211 -52

No files matched your search

+14
View File
@@ -614,6 +614,20 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
DecodedInfo = &Block.DecodedInstructions[i];
bool IsLocked = DecodedInfo->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK;
// Do a partial register cache flush before every instruction. This
// prevents cross-instruction static register caching, while allowing
// context load/stores to be optimized within a block. Theoretically,
// this flush is not required for correctness, all mandatory flushes are
// included in instruction-specific handlers. Instead, this is a blunt
// heuristic to make the register cache less aggressive, as the current
// RA generates bad code in common cases with tied registers otherwise.
//
// However, it makes our exception handling behaviour more predictable.
// It is potentially correctness bearing in that sense, but that is a
// side effect here and (if that behaviour is required) we should handle
// that more explicitly later.
Thread->OpDispatcher->FlushRegisterCache(true);
if (ExtendedDebugInfo || Thread->OpDispatcher->CanHaveSideEffects(TableInfo, DecodedInfo)) {
Thread->OpDispatcher->_GuestOpcode(Block.Entry + BlockInstructionsLength - GuestRIP);
}
@@ -3366,8 +3366,8 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) {
auto Src2 = _LoadMem(GPRClass, Size, Dest_RSI, Size);
// We'll calculate PF/AF after the loop, so use them as temporaries here.
_StoreRegister(Src1, Core::CPUState::PF_AS_GREG, GPRClass, CTX->GetGPRSize());
_StoreRegister(Src2, Core::CPUState::AF_AS_GREG, GPRClass, CTX->GetGPRSize());
StoreRegister(Core::CPUState::PF_AS_GREG, false, Src1);
StoreRegister(Core::CPUState::AF_AS_GREG, false, Src2);
Ref TailCounter = LoadGPRRegister(X86State::REG_RCX);
@@ -3406,8 +3406,8 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) {
// Make sure to start a new block after ending this one
{
// Grab the sources from the last iteration so we can set flags.
auto Src1 = _LoadRegister(Core::CPUState::PF_AS_GREG, GPRClass, CTX->GetGPRSize());
auto Src2 = _LoadRegister(Core::CPUState::AF_AS_GREG, GPRClass, CTX->GetGPRSize());
auto Src1 = LoadGPR(Core::CPUState::PF_AS_GREG);
auto Src2 = LoadGPR(Core::CPUState::AF_AS_GREG);
GenerateFlags_SUB(Op, Src2, Src1);
}
auto Jump_ = Jump();
@@ -4362,7 +4362,7 @@ Ref OpDispatchBuilder::LoadSource_WithOpSize(RegisterClassType Class, const X86T
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
if (gpr >= FEXCore::X86State::REG_MM_0) {
A.Base = _LoadContext(OpSize, FPRClass, offsetof(FEXCore::Core::CPUState, mm[gpr - FEXCore::X86State::REG_MM_0]));
A.Base = LoadContext(MM0Index + gpr - FEXCore::X86State::REG_MM_0);
} else if (gpr >= FEXCore::X86State::REG_XMM_0) {
const auto gprIndex = gpr - X86State::REG_XMM_0;
@@ -4399,7 +4399,7 @@ Ref OpDispatchBuilder::LoadGPRRegister(uint32_t GPR, int8_t Size, uint8_t Offset
if (Size == -1) {
Size = GPRSize;
}
Ref Reg = _LoadRegister(GPR, GPRClass, GPRSize);
Ref Reg = LoadGPR(GPR);
if ((!AllowUpperGarbage && (Size != GPRSize)) || Offset != 0) {
// Extract the subregister if requested.
@@ -4413,11 +4413,6 @@ Ref OpDispatchBuilder::LoadGPRRegister(uint32_t GPR, int8_t Size, uint8_t Offset
return Reg;
}
Ref OpDispatchBuilder::LoadXMMRegister(uint32_t XMM) {
const auto VectorSize = CTX->HostFeatures.SupportsSVE256 ? 32 : 16;
return _LoadRegister(XMM, FPRClass, VectorSize);
}
void OpDispatchBuilder::StoreGPRRegister(uint32_t GPR, const Ref Src, int8_t Size, uint8_t Offset) {
const uint8_t GPRSize = CTX->GetGPRSize();
if (Size == -1) {
@@ -4427,16 +4422,14 @@ void OpDispatchBuilder::StoreGPRRegister(uint32_t GPR, const Ref Src, int8_t Siz
Ref Reg = Src;
if (Size != GPRSize || Offset != 0) {
// Need to do an insert if not automatic size or zero offset.
Reg = LoadGPRRegister(GPR);
Reg = _Bfi(IR::SizeToOpSize(GPRSize), Size * 8, Offset, Reg, Src);
Reg = _Bfi(IR::SizeToOpSize(GPRSize), Size * 8, Offset, LoadGPRRegister(GPR), Src);
}
_StoreRegister(Reg, GPR, GPRClass, GPRSize);
StoreRegister(GPR, false, Reg);
}
void OpDispatchBuilder::StoreXMMRegister(uint32_t XMM, const Ref Src) {
const auto VectorSize = CTX->HostFeatures.SupportsSVE256 ? 32 : 16;
_StoreRegister(Src, XMM, FPRClass, VectorSize);
StoreRegister(XMM, true, Src);
}
Ref OpDispatchBuilder::LoadSource(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
@@ -4455,7 +4448,8 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
const auto gpr = Operand.Data.GPR.GPR;
if (gpr >= FEXCore::X86State::REG_MM_0) {
_StoreContext(OpSize, Class, Src, offsetof(FEXCore::Core::CPUState, mm[gpr - FEXCore::X86State::REG_MM_0]));
LOGMAN_THROW_A_FMT(OpSize == 8, "full");
StoreContext(MM0Index + gpr - FEXCore::X86State::REG_MM_0, Src);
} else if (gpr >= FEXCore::X86State::REG_XMM_0) {
const auto gprIndex = gpr - X86State::REG_XMM_0;
const auto VectorSize = CTX->HostFeatures.SupportsSVE256 ? 32 : 16;
@@ -4543,6 +4537,8 @@ void OpDispatchBuilder::ResetWorkingList() {
DecodeFailure = false;
ShouldDump = false;
CurrentCodeBlock = nullptr;
RegCache.Written = 0;
RegCache.Cached = 0;
}
void OpDispatchBuilder::UnhandledOp(OpcodeArgs) {
+166 -13
View File
@@ -16,6 +16,7 @@
#include <FEXCore/fextl/map.h>
#include <FEXCore/fextl/vector.h>
#include <bit>
#include <cstdint>
#include <fmt/format.h>
#include <stddef.h>
@@ -1264,6 +1265,50 @@ public:
}
}
void FlushRegisterCache(bool SRAOnly = false) {
CalculateDeferredFlags();
const uint8_t GPRSize = CTX->GetGPRSize();
const auto VectorSize = CTX->HostFeatures.SupportsSVE256 ? 32 : 16;
// Write backwards. This is a heuristic to improve coalescing, since we
// often copy from (low) fixed GPRs to (high) PF/AF for celebrity
// instructions like "add rax, 1". This hack will go away with clauses.
uint64_t Bits = RegCache.Written;
// We have an SRA only mode that exists as a hack to make register caching
// less aggressive. We should get rid of this once RA can take it.
uint64_t Mask = ~0ULL;
if (SRAOnly) {
const uint64_t GPRMask = ((1ull << (AFIndex - GPR0Index + 1)) - 1) << GPR0Index;
const uint64_t FPRMask = ((1ull << (FPR15Index - FPR0Index + 1)) - 1) << FPR0Index;
Mask &= (GPRMask | FPRMask);
Bits &= Mask;
}
while (Bits != 0) {
uint32_t Index = 63 - std::countl_zero(Bits);
Ref Value = RegCache.Value[Index];
if (Index >= GPR0Index && Index <= AFIndex) {
_StoreRegister(Value, Index - GPR0Index, GPRClass, GPRSize);
} else if (Index >= FPR0Index && Index <= FPR15Index) {
_StoreRegister(Value, Index - FPR0Index, FPRClass, VectorSize);
} else if (Index == DFIndex) {
_StoreFlag(Value, X86State::RFLAG_DF_RAW_LOC);
} else {
_StoreContext(CacheIndexToSize(Index), CacheIndexClass(Index), Value, CacheIndexToContextOffset(Index));
}
Bits &= ~(1ull << Index);
}
RegCache.Written &= ~Mask;
RegCache.Cached &= ~Mask;
}
protected:
void SaveNZCV(IROps Op = OP_DUMMY) override {
/* Some opcodes are conservatively marked as clobbering flags, but in fact
@@ -1481,7 +1526,6 @@ private:
void UpdatePrefixFromSegment(Ref Segment, uint32_t SegmentReg);
Ref LoadGPRRegister(uint32_t GPR, int8_t Size = -1, uint8_t Offset = 0, bool AllowUpperGarbage = false);
Ref LoadXMMRegister(uint32_t XMM);
void StoreGPRRegister(uint32_t GPR, const Ref Src, int8_t Size = -1, uint8_t Offset = 0);
void StoreXMMRegister(uint32_t XMM, const Ref Src);
@@ -1697,9 +1741,9 @@ private:
if (IsNZCV(BitOffset)) {
InsertNZCV(BitOffset, Value, ValueOffset, MustMask);
} else if (BitOffset == FEXCore::X86State::RFLAG_PF_RAW_LOC) {
_StoreRegister(Value, Core::CPUState::PF_AS_GREG, GPRClass, CTX->GetGPRSize());
StoreRegister(Core::CPUState::PF_AS_GREG, false, Value);
} else if (BitOffset == FEXCore::X86State::RFLAG_AF_RAW_LOC) {
_StoreRegister(Value, Core::CPUState::AF_AS_GREG, GPRClass, CTX->GetGPRSize());
StoreRegister(Core::CPUState::AF_AS_GREG, false, Value);
} else {
if (ValueOffset || MustMask) {
Value = _Bfe(OpSize::i32Bit, 1, ValueOffset, Value);
@@ -1707,10 +1751,10 @@ private:
// For DF, we need to transform 0/1 into 1/-1
if (BitOffset == FEXCore::X86State::RFLAG_DF_RAW_LOC) {
Value = _SubShift(OpSize::i64Bit, _Constant(1), Value, ShiftType::LSL, 1);
StoreDF(_SubShift(OpSize::i64Bit, _Constant(1), Value, ShiftType::LSL, 1));
} else {
_StoreFlag(Value, BitOffset);
}
_StoreFlag(Value, BitOffset);
}
}
@@ -1726,10 +1770,13 @@ private:
void InvalidateAF() {
_InvalidateFlags((1u << X86State::RFLAG_AF_RAW_LOC));
InvalidateReg(Core::CPUState::AF_AS_GREG);
}
void InvalidatePF_AF() {
_InvalidateFlags((1u << X86State::RFLAG_PF_RAW_LOC) | (1u << X86State::RFLAG_AF_RAW_LOC));
InvalidateReg(Core::CPUState::PF_AS_GREG);
InvalidateReg(Core::CPUState::AF_AS_GREG);
}
CondClassType CondForNZCVBit(unsigned BitOffset, bool Invert) {
@@ -1746,8 +1793,114 @@ private:
}
}
void FlushRegisterCache() {
CalculateDeferredFlags();
/* Layout of cache indices. We use a single 64-bit bitmask for the cache */
static const int GPR0Index = 0;
static const int GPR15Index = 15;
static const int PFIndex = 16;
static const int AFIndex = 17;
/* Gap 18..19 */
static const int MM0Index = 20;
static const int MM7Index = 27;
static const int AbridgedFTWIndex = 28;
/* Gap 29..30 */
static const int DFIndex = 31;
static const int FPR0Index = 32;
static const int FPR15Index = 47;
static const int AVXHigh0Index = 48;
static const int AVXHigh15Index = 63;
int CacheIndexToContextOffset(int Index) {
switch (Index) {
case MM0Index ... MM7Index: return offsetof(FEXCore::Core::CPUState, mm[Index - MM0Index]);
case AVXHigh0Index ... AVXHigh15Index: return offsetof(FEXCore::Core::CPUState, avx_high[Index - AVXHigh0Index][0]);
case AbridgedFTWIndex: return offsetof(FEXCore::Core::CPUState, AbridgedFTW);
default: return -1;
}
}
RegisterClassType CacheIndexClass(int Index) {
if ((Index >= MM0Index && Index <= MM7Index) || Index >= FPR0Index) {
return FPRClass;
} else {
return GPRClass;
}
}
unsigned CacheIndexToSize(int Index) {
if (Index >= AVXHigh0Index) {
return 16;
} else if (Index <= MM7Index) {
return 8;
} else {
return 1;
}
}
struct {
uint64_t Cached;
uint64_t Written;
Ref Value[64];
} RegCache {};
void InvalidateReg(uint8_t Index) {
uint64_t Bit = (1ull << (uint64_t)Index);
RegCache.Cached &= ~Bit;
RegCache.Written &= ~Bit;
}
Ref LoadRegCache(uint64_t Offset, uint8_t Index, RegisterClassType RegClass, uint8_t Size) {
LOGMAN_THROW_AA_FMT(Index < 64, "valid index");
uint64_t Bit = (1ull << (uint64_t)Index);
if (!(RegCache.Cached & Bit)) {
if (Index == DFIndex) {
RegCache.Value[Index] = _LoadDF();
} else if ((Index >= MM0Index && Index <= AbridgedFTWIndex) || Index >= AVXHigh0Index) {
RegCache.Value[Index] = _LoadContext(Size, RegClass, Offset);
} else {
RegCache.Value[Index] = _LoadRegister(Offset, RegClass, Size);
}
RegCache.Cached |= Bit;
}
return RegCache.Value[Index];
}
Ref LoadGPR(uint8_t Reg) {
return LoadRegCache(Reg, GPR0Index + Reg, GPRClass, CTX->GetGPRSize());
}
Ref LoadContext(uint8_t Index) {
return LoadRegCache(CacheIndexToContextOffset(Index), Index, CacheIndexClass(Index), CacheIndexToSize(Index));
}
Ref LoadXMMRegister(uint8_t Reg) {
const auto Size = CTX->HostFeatures.SupportsSVE256 ? 32 : 16;
return LoadRegCache(Reg, FPR0Index + Reg, FPRClass, Size);
}
Ref LoadDF() {
return LoadGPR(DFIndex);
}
void StoreContext(uint8_t Index, Ref Value) {
LOGMAN_THROW_AA_FMT(Index < 64, "valid index");
LOGMAN_THROW_AA_FMT(Value != InvalidNode, "storing valid");
uint64_t Bit = (1ull << (uint64_t)Index);
RegCache.Value[Index] = Value;
RegCache.Cached |= Bit;
RegCache.Written |= Bit;
}
void StoreRegister(uint8_t Reg, bool FPR, Ref Value) {
StoreContext(Reg + (FPR ? FPR0Index : GPR0Index), Value);
}
void StoreDF(Ref Value) {
StoreContext(DFIndex, Value);
}
Ref GetRFLAG(unsigned BitOffset, bool Invert = false) {
@@ -1766,12 +1919,12 @@ private:
return _NZCVSelect(OpSize::i32Bit, CondForNZCVBit(BitOffset, Invert), _Constant(1), _Constant(0));
}
} else if (BitOffset == FEXCore::X86State::RFLAG_PF_RAW_LOC) {
return _LoadRegister(Core::CPUState::PF_AS_GREG, GPRClass, CTX->GetGPRSize());
return LoadGPR(Core::CPUState::PF_AS_GREG);
} else if (BitOffset == FEXCore::X86State::RFLAG_AF_RAW_LOC) {
return _LoadRegister(Core::CPUState::AF_AS_GREG, GPRClass, CTX->GetGPRSize());
return LoadGPR(Core::CPUState::AF_AS_GREG);
} else if (BitOffset == FEXCore::X86State::RFLAG_DF_RAW_LOC) {
// Recover the sign bit, it is the logical DF value
return _Lshr(OpSize::i64Bit, _LoadDF(), _Constant(63));
return _Lshr(OpSize::i64Bit, LoadDF(), _Constant(63));
} else {
return _LoadFlag(BitOffset);
}
@@ -1779,7 +1932,7 @@ private:
// Returns (DF ? -Size : Size)
Ref LoadDir(const unsigned Size) {
auto Dir = _LoadDF();
auto Dir = LoadDF();
auto Shift = FEXCore::ilog2(Size);
if (Shift) {
@@ -1793,7 +1946,7 @@ private:
Ref OffsetByDir(Ref X, const unsigned Size) {
auto Shift = FEXCore::ilog2(Size);
return _AddShift(OpSize::i64Bit, X, _LoadDF(), ShiftType::LSL, Shift);
return _AddShift(OpSize::i64Bit, X, LoadDF(), ShiftType::LSL, Shift);
}
// Compares two floats and sets flags for a COMISS instruction
@@ -530,17 +530,17 @@ void OpDispatchBuilder::AVX128_StoreResult_WithOpSize(FEXCore::X86Tables::Decode
Ref OpDispatchBuilder::AVX128_LoadXMMRegister(uint32_t XMM, bool High) {
if (High) {
return _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, avx_high[XMM][0]));
return LoadContext(AVXHigh0Index + XMM);
} else {
return _LoadRegister(XMM, FPRClass, 16);
return LoadXMMRegister(XMM);
}
}
void OpDispatchBuilder::AVX128_StoreXMMRegister(uint32_t XMM, const Ref Src, bool High) {
if (High) {
_StoreContext(16, FPRClass, Src, offsetof(FEXCore::Core::CPUState, avx_high[XMM][0]));
StoreContext(AVXHigh0Index + XMM, Src);
} else {
_StoreRegister(Src, XMM, FPRClass, 16);
StoreXMMRegister(XMM, Src);
}
}
@@ -2644,8 +2644,7 @@ void OpDispatchBuilder::SaveX87State(OpcodeArgs, Ref MemBase) {
{
// Abridged FTW
auto AbridgedFTW = _LoadContext(1, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
_StoreMem(GPRClass, 1, AbridgedFTW, MemBase, _Constant(4), 2, MEM_OFFSET_SXTX, 1);
_StoreMem(GPRClass, 1, LoadContext(AbridgedFTWIndex), MemBase, _Constant(4), 2, MEM_OFFSET_SXTX, 1);
}
// BYTE | 0 1 | 2 3 | 4 | 5 | 6 7 | 8 9 | a b | c d | e f |
@@ -2693,7 +2692,7 @@ void OpDispatchBuilder::SaveX87State(OpcodeArgs, Ref MemBase) {
// If OSFXSR bit in CR4 is not set than FXSAVE /may/ not save the XMM registers
// This is implementation dependent
for (uint32_t i = 0; i < Core::CPUState::NUM_MMS; ++i) {
Ref MMReg = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, mm[i]));
Ref MMReg = LoadContext(MM0Index + i);
_StoreMem(FPRClass, 16, MMReg, MemBase, _Constant(i * 16 + 32), 16, MEM_OFFSET_SXTX, 1);
}
@@ -2820,13 +2819,12 @@ void OpDispatchBuilder::RestoreX87State(Ref MemBase) {
{
// Abridged FTW
auto NewAbridgedFTW = _LoadMem(GPRClass, 1, MemBase, _Constant(4), 2, MEM_OFFSET_SXTX, 1);
_StoreContext(1, GPRClass, NewAbridgedFTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, _LoadMem(GPRClass, 1, MemBase, _Constant(4), 2, MEM_OFFSET_SXTX, 1));
}
for (uint32_t i = 0; i < Core::CPUState::NUM_MMS; ++i) {
auto MMReg = _LoadMem(FPRClass, 16, MemBase, _Constant(i * 16 + 32), 16, MEM_OFFSET_SXTX, 1);
_StoreContext(16, FPRClass, MMReg, offsetof(FEXCore::Core::CPUState, mm[i]));
StoreContext(MM0Index + i, MMReg);
}
}
@@ -2865,7 +2863,7 @@ void OpDispatchBuilder::DefaultX87State(OpcodeArgs) {
// all of the ST0-7/MM0-7 registers to zero.
Ref ZeroVector = LoadZeroVector(Core::CPUState::MM_REG_SIZE);
for (uint32_t i = 0; i < Core::CPUState::NUM_MMS; ++i) {
_StoreContext(16, FPRClass, ZeroVector, offsetof(FEXCore::Core::CPUState, mm[i]));
StoreContext(MM0Index + i, ZeroVector);
}
}
@@ -31,14 +31,14 @@ Ref OpDispatchBuilder::GetX87Top() {
void OpDispatchBuilder::SetX87ValidTag(Ref Value, bool Valid) {
// if we are popping then we must first mark this location as empty
Ref AbridgedFTW = _LoadContext(1, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
Ref AbridgedFTW = LoadContext(AbridgedFTWIndex);
Ref RegMask = _Lshl(OpSize::i32Bit, _Constant(1), Value);
Ref NewAbridgedFTW = Valid ? _Or(OpSize::i32Bit, AbridgedFTW, RegMask) : _Andn(OpSize::i32Bit, AbridgedFTW, RegMask);
_StoreContext(1, GPRClass, NewAbridgedFTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, NewAbridgedFTW);
}
Ref OpDispatchBuilder::GetX87ValidTag(Ref Value) {
Ref AbridgedFTW = _LoadContext(1, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
Ref AbridgedFTW = LoadContext(AbridgedFTWIndex);
return _And(OpSize::i32Bit, _Lshr(OpSize::i32Bit, AbridgedFTW, Value), _Constant(1));
}
@@ -51,8 +51,7 @@ Ref OpDispatchBuilder::GetX87Tag(Ref Value, Ref AbridgedFTW) {
}
Ref OpDispatchBuilder::GetX87Tag(Ref Value) {
Ref AbridgedFTW = _LoadContext(1, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
return GetX87Tag(Value, AbridgedFTW);
return GetX87Tag(Value, LoadContext(AbridgedFTWIndex));
}
void OpDispatchBuilder::SetX87FTW(Ref FTW) {
@@ -70,11 +69,11 @@ void OpDispatchBuilder::SetX87FTW(Ref FTW) {
}
}
_StoreContext(1, GPRClass, NewAbridgedFTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, NewAbridgedFTW);
}
Ref OpDispatchBuilder::GetX87FTW() {
Ref AbridgedFTW = _LoadContext(1, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
Ref AbridgedFTW = LoadContext(AbridgedFTWIndex);
Ref FTW = _Constant(0);
for (int i = 0; i < 8; i++) {
@@ -154,7 +153,6 @@ void OpDispatchBuilder::FLD(OpcodeArgs) {
SetX87Top(top);
// Write to ST[TOP]
_StoreContextIndexed(converted, top, 16, MMBaseOffset(), 16, FPRClass);
//_StoreContext(converted, 16, offsetof(FEXCore::Core::CPUState, mm[7][0]));
}
template void OpDispatchBuilder::FLD<32>(OpcodeArgs);
@@ -644,7 +642,7 @@ void OpDispatchBuilder::FNINIT(OpcodeArgs) {
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(Zero);
// Tags all get marked as invalid
_StoreContext(1, GPRClass, Zero, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, Zero);
}
template<size_t width, bool Integer, OpDispatchBuilder::FCOMIFlags whichflags, bool poptwice>
@@ -1215,7 +1213,7 @@ void OpDispatchBuilder::X87FCMOV(OpcodeArgs) {
void OpDispatchBuilder::X87EMMS(OpcodeArgs) {
// Tags all get set to 0b11
_StoreContext(1, GPRClass, _Constant(0), offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, _Constant(0));
}
void OpDispatchBuilder::X87FFREE(OpcodeArgs) {
@@ -60,7 +60,7 @@ void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(Zero);
// Tags all get marked as invalid
_StoreContext(1, GPRClass, Zero, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
StoreContext(AbridgedFTWIndex, Zero);
}
void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {