mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 14:00:16 +02:00
Merge pull request #4926 from lioncash/enum
IR: Convert RegisterClassType to an enum class
This commit is contained in:
18 files changed
+294
-300
No files matched your search
@@ -219,11 +219,8 @@ def parse_ops(ops):
|
||||
OpArg.DefaultInitializer = DefaultInit[1][:-1]
|
||||
|
||||
# If SSA type then we can generate validation for this op
|
||||
if (OpArg.IsSSA and
|
||||
(OpArg.Type == "GPR" or
|
||||
OpArg.Type == "GPRPair" or
|
||||
OpArg.Type == "FPR")):
|
||||
OpDef.EmitValidation.append(f"GetOpRegClass({ArgName}) == InvalidClass || WalkFindRegClass({ArgName}) == {OpArg.Type}Class")
|
||||
if OpArg.IsSSA and OpArg.Type in {"GPR", "GPRPair", "FPR"}:
|
||||
OpDef.EmitValidation.append(f"GetOpRegClass({ArgName}) == RegClass::Invalid || WalkFindRegClass({ArgName}) == RegClass::{OpArg.Type}")
|
||||
|
||||
OpArg.Name = ArgName
|
||||
OpArg.NameWithPrefix = NameWithPrefix
|
||||
@@ -415,7 +412,7 @@ def print_ir_sizes():
|
||||
[[nodiscard, gnu::const]] std::string_view const& GetName(IROps Op);
|
||||
[[nodiscard, gnu::const]] uint8_t GetArgs(IROps Op);
|
||||
[[nodiscard, gnu::const]] uint8_t GetRAArgs(IROps Op);
|
||||
[[nodiscard, gnu::const]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);
|
||||
[[nodiscard, gnu::const]] FEXCore::IR::RegClass GetRegClass(IROps Op);
|
||||
[[nodiscard, gnu::const]] bool HasSideEffects(IROps Op);
|
||||
[[nodiscard, gnu::const]] bool ImplicitFlagClobber(IROps Op);
|
||||
[[nodiscard, gnu::const]] bool GetHasDest(IROps Op);
|
||||
@@ -429,30 +426,29 @@ def print_ir_sizes():
|
||||
def print_ir_reg_classes():
|
||||
output_file.write("#ifdef IROP_REG_CLASSES_IMPL\n")
|
||||
|
||||
output_file.write("constexpr std::array<FEXCore::IR::RegisterClassType, IROps::OP_LAST + 1> IRRegClasses = {\n")
|
||||
output_file.write("constexpr std::array<FEXCore::IR::RegClass, IROps::OP_LAST + 1> IRRegClasses = {\n")
|
||||
for op in IROps:
|
||||
if op.Name == "Last":
|
||||
output_file.write("\tFEXCore::IR::InvalidClass,\n")
|
||||
output_file.write("\tRegClass::Invalid,\n")
|
||||
else:
|
||||
Class = "Invalid"
|
||||
if op.HasDest and op.DestType == None:
|
||||
if op.HasDest and op.DestType is None:
|
||||
ExitError("IR op {} has destination with no destination class".format(op.Name))
|
||||
|
||||
if op.HasDest and op.DestType == "SSA": # Special case SSA type
|
||||
output_file.write("\tFEXCore::IR::ComplexClass,\n")
|
||||
output_file.write("\tRegClass::Complex,\n")
|
||||
elif op.HasDest:
|
||||
output_file.write("\tFEXCore::IR::{}Class,\n".format(op.DestType))
|
||||
output_file.write("\tRegClass::{},\n".format(op.DestType))
|
||||
else:
|
||||
# No destination so it has an invalid destination class
|
||||
output_file.write("\tFEXCore::IR::InvalidClass, // No destination\n")
|
||||
output_file.write("\tRegClass::Invalid, // No destination\n")
|
||||
|
||||
|
||||
output_file.write("};\n\n")
|
||||
|
||||
output_file.write("// Make sure our array maps directly to the IROps enum\n")
|
||||
output_file.write("static_assert(IRRegClasses[IROps::OP_LAST] == FEXCore::IR::InvalidClass);\n\n")
|
||||
output_file.write("static_assert(IRRegClasses[IROps::OP_LAST] == RegClass::Invalid);\n\n")
|
||||
|
||||
output_file.write("FEXCore::IR::RegisterClassType GetRegClass(IROps Op) { return IRRegClasses[Op]; }\n\n")
|
||||
output_file.write("FEXCore::IR::RegClass GetRegClass(IROps Op) { return IRRegClasses[Op]; }\n\n")
|
||||
|
||||
output_file.write("#undef IROP_REG_CLASSES_IMPL\n")
|
||||
output_file.write("#endif\n\n")
|
||||
@@ -678,7 +674,7 @@ def print_ir_allocator_helpers():
|
||||
output_file.write("\t\treturn HeaderOp->Op;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tFEXCore::IR::RegisterClassType GetOpRegClass(const OrderedNode *Op) const {\n")
|
||||
output_file.write("\tFEXCore::IR::RegClass GetOpRegClass(const OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\treturn GetRegClass(GetOpType(Op));\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
|
||||
@@ -947,7 +947,7 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu
|
||||
|
||||
if (GPRSize == IR::OpSize::i64Bit) {
|
||||
IR::Ref R = emit->_StoreRegister(emit->Constant(Entrypoint), GPRSize);
|
||||
R->Reg = IR::PhysicalRegister(IR::GPRFixedClass, X86State::REG_R11).Raw;
|
||||
R->Reg = IR::PhysicalRegister(IR::RegClass::GPRFixed, X86State::REG_R11).Raw;
|
||||
} else {
|
||||
emit->_StoreContextFPR(GPRSize, emit->_VCastFromGPR(IR::OpSize::i64Bit, IR::OpSize::i64Bit, emit->Constant(Entrypoint)),
|
||||
offsetof(Core::CPUState, mm[0][0]));
|
||||
|
||||
@@ -625,10 +625,10 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
|
||||
|
||||
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
|
||||
|
||||
RAPass->AddRegisters(FEXCore::IR::GPRClass, GeneralRegisters.size());
|
||||
RAPass->AddRegisters(FEXCore::IR::GPRFixedClass, StaticRegisters.size());
|
||||
RAPass->AddRegisters(FEXCore::IR::FPRClass, GeneralFPRegisters.size());
|
||||
RAPass->AddRegisters(FEXCore::IR::FPRFixedClass, StaticFPRegisters.size());
|
||||
RAPass->AddRegisters(IR::RegClass::GPR, GeneralRegisters.size());
|
||||
RAPass->AddRegisters(IR::RegClass::GPRFixed, StaticRegisters.size());
|
||||
RAPass->AddRegisters(IR::RegClass::FPR, GeneralFPRegisters.size());
|
||||
RAPass->AddRegisters(IR::RegClass::FPRFixed, StaticFPRegisters.size());
|
||||
RAPass->PairRegs = PairRegisters;
|
||||
|
||||
{
|
||||
|
||||
@@ -95,11 +95,13 @@ private:
|
||||
|
||||
[[nodiscard]]
|
||||
ARMEmitter::Register GetReg(IR::PhysicalRegister Reg) const {
|
||||
LOGMAN_THROW_A_FMT(Reg.Class == IR::GPRFixedClass.Val || Reg.Class == IR::GPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
const auto RegClass = Reg.AsRegClass();
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass.Val) {
|
||||
LOGMAN_THROW_A_FMT(RegClass == IR::RegClass::GPRFixed || RegClass == IR::RegClass::GPR, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (RegClass == IR::RegClass::GPRFixed) {
|
||||
return StaticRegisters[Reg.Reg];
|
||||
} else if (Reg.Class == IR::GPRClass.Val) {
|
||||
} else if (RegClass == IR::RegClass::GPR) {
|
||||
return GeneralRegisters[Reg.Reg];
|
||||
}
|
||||
|
||||
@@ -118,11 +120,13 @@ private:
|
||||
|
||||
[[nodiscard]]
|
||||
ARMEmitter::VRegister GetVReg(IR::PhysicalRegister Reg) const {
|
||||
LOGMAN_THROW_A_FMT(Reg.Class == IR::FPRFixedClass.Val || Reg.Class == IR::FPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
const auto RegClass = Reg.AsRegClass();
|
||||
|
||||
if (Reg.Class == IR::FPRFixedClass.Val) {
|
||||
LOGMAN_THROW_A_FMT(RegClass == IR::RegClass::FPRFixed || RegClass == IR::RegClass::FPR, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (RegClass == IR::RegClass::FPRFixed) {
|
||||
return StaticFPRegisters[Reg.Reg];
|
||||
} else if (Reg.Class == IR::FPRClass.Val) {
|
||||
} else if (RegClass == IR::RegClass::FPR) {
|
||||
return GeneralFPRegisters[Reg.Reg];
|
||||
}
|
||||
|
||||
@@ -140,8 +144,8 @@ private:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static IR::RegisterClassType GetRegClass(IR::Ref Node) {
|
||||
return IR::RegisterClassType {IR::PhysicalRegister(Node).Class};
|
||||
static IR::RegClass GetRegClass(IR::Ref Node) {
|
||||
return IR::PhysicalRegister(Node).AsRegClass();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@@ -266,13 +270,13 @@ private:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsFPR(IR::RegisterClassType Class) {
|
||||
return Class == IR::FPRClass || Class == IR::FPRFixedClass;
|
||||
static bool IsFPR(IR::RegClass Class) {
|
||||
return Class == IR::RegClass::FPR || Class == IR::RegClass::FPRFixed;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsGPR(IR::RegisterClassType Class) {
|
||||
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
|
||||
static bool IsGPR(IR::RegClass Class) {
|
||||
return Class == IR::RegClass::GPR || Class == IR::RegClass::GPRFixed;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@@ -287,12 +291,12 @@ private:
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsGPR(IR::OrderedNodeWrapper Wrap) {
|
||||
return IsGPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class});
|
||||
return IsGPR(IR::PhysicalRegister(Wrap).AsRegClass());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsFPR(IR::OrderedNodeWrapper Wrap) {
|
||||
return IsFPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class});
|
||||
return IsFPR(IR::PhysicalRegister(Wrap).AsRegClass());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
|
||||
@@ -21,7 +21,7 @@ DEF_OP(LoadContext) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadContext>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
auto Dst = GetReg(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
@@ -52,7 +52,7 @@ DEF_OP(LoadContext) {
|
||||
DEF_OP(LoadContextPair) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadContextPair>();
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst1 = GetReg(Op->OutValue1);
|
||||
const auto Dst2 = GetReg(Op->OutValue2);
|
||||
|
||||
@@ -78,7 +78,7 @@ DEF_OP(StoreContext) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreContext>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
auto Src = GetZeroableReg(Op->Value);
|
||||
|
||||
switch (OpSize) {
|
||||
@@ -110,7 +110,7 @@ DEF_OP(StoreContextPair) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreContextPair>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
auto Src1 = GetZeroableReg(Op->Value1);
|
||||
auto Src2 = GetZeroableReg(Op->Value2);
|
||||
|
||||
@@ -135,11 +135,11 @@ DEF_OP(StoreContextPair) {
|
||||
DEF_OP(LoadRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
|
||||
if (Op->Class == IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticRegisters.size(), "out of range reg");
|
||||
|
||||
mov(GetReg(Node).X(), StaticRegisters[Op->Reg].X());
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
} else if (Op->Class == IR::RegClass::FPR) {
|
||||
const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "out of range reg");
|
||||
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
|
||||
@@ -175,12 +175,13 @@ DEF_OP(LoadAF) {
|
||||
|
||||
DEF_OP(StoreRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
auto Reg = IR::PhysicalRegister(Node);
|
||||
const auto Reg = IR::PhysicalRegister(Node);
|
||||
const auto RegClass = Reg.AsRegClass();
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass) {
|
||||
if (RegClass == IR::RegClass::GPRFixed) {
|
||||
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
|
||||
mov(ARMEmitter::Size::i64Bit, GetReg(Reg), GetReg(Op->Value));
|
||||
} else if (Reg.Class == IR::FPRFixedClass) {
|
||||
} else if (RegClass == IR::RegClass::FPRFixed) {
|
||||
const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
|
||||
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
|
||||
|
||||
@@ -193,7 +194,7 @@ DEF_OP(StoreRegister) {
|
||||
mov(guest.Q(), host.Q());
|
||||
}
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Reg.Class);
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", RegClass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +226,7 @@ DEF_OP(LoadContextIndexed) {
|
||||
|
||||
const auto Index = GetReg(Op->Index);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
switch (Op->Stride) {
|
||||
case 1:
|
||||
case 2:
|
||||
@@ -288,7 +289,7 @@ DEF_OP(StoreContextIndexed) {
|
||||
|
||||
const auto Index = GetReg(Op->Index);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Value = GetReg(Op->Value);
|
||||
|
||||
switch (Op->Stride) {
|
||||
@@ -372,7 +373,7 @@ DEF_OP(SpillRegister) {
|
||||
const auto OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetReg(Op->Value);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit: {
|
||||
@@ -413,7 +414,7 @@ DEF_OP(SpillRegister) {
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize); break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
} else if (Op->Class == FEXCore::IR::RegClass::FPR) {
|
||||
const auto Src = GetVReg(Op->Value);
|
||||
|
||||
switch (OpSize) {
|
||||
@@ -452,7 +453,7 @@ DEF_OP(SpillRegister) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize); break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +462,7 @@ DEF_OP(FillRegister) {
|
||||
const auto OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit: {
|
||||
@@ -502,7 +503,7 @@ DEF_OP(FillRegister) {
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize); break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
} else if (Op->Class == FEXCore::IR::RegClass::FPR) {
|
||||
const auto Dst = GetVReg(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
@@ -541,7 +542,7 @@ DEF_OP(FillRegister) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize); break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,7 +690,7 @@ DEF_OP(LoadMem) {
|
||||
const auto MemReg = GetReg(Op->Addr);
|
||||
const auto MemSrc = GenerateMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
@@ -723,7 +724,7 @@ DEF_OP(LoadMemPair) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadMemPair>();
|
||||
const auto Addr = GetReg(Op->Addr);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst1 = GetReg(Op->OutValue1);
|
||||
const auto Dst2 = GetReg(Op->OutValue2);
|
||||
|
||||
@@ -751,13 +752,13 @@ DEF_OP(LoadMemTSO) {
|
||||
|
||||
const auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid() || CTX->HostFeatures.SupportsTSOImm9, "unexpected offset");
|
||||
LOGMAN_THROW_A_FMT(Op->OffsetScale == 1, "unexpected offset scale");
|
||||
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MemOffsetType::SXTX, "unexpected offset type");
|
||||
}
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
@@ -779,7 +780,7 @@ DEF_OP(LoadMemTSO) {
|
||||
// Half-barrier once back-patched.
|
||||
nop();
|
||||
}
|
||||
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
@@ -794,7 +795,7 @@ DEF_OP(LoadMemTSO) {
|
||||
// Half-barrier once back-patched.
|
||||
nop();
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
@@ -1624,7 +1625,7 @@ DEF_OP(StoreMem) {
|
||||
const auto MemReg = GetReg(Op->Addr);
|
||||
const auto MemSrc = GenerateMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit: strb(Src, MemSrc); break;
|
||||
@@ -1735,7 +1736,7 @@ DEF_OP(StoreMemPair) {
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Addr = GetReg(Op->Addr);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src1 = GetZeroableReg(Op->Value1);
|
||||
const auto Src2 = GetZeroableReg(Op->Value2);
|
||||
switch (OpSize) {
|
||||
@@ -1762,13 +1763,13 @@ DEF_OP(StoreMemTSO) {
|
||||
|
||||
const auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Class == IR::RegClass::GPR) {
|
||||
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid() || CTX->HostFeatures.SupportsTSOImm9, "unexpected offset");
|
||||
LOGMAN_THROW_A_FMT(Op->OffsetScale == 1, "unexpected offset scale");
|
||||
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MemOffsetType::SXTX, "unexpected offset type");
|
||||
}
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
@@ -1789,7 +1790,7 @@ DEF_OP(StoreMemTSO) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled StoreMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
@@ -2302,7 +2303,7 @@ DEF_OP(ParanoidLoadMemTSO) {
|
||||
|
||||
auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
@@ -2323,7 +2324,7 @@ DEF_OP(ParanoidLoadMemTSO) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
@@ -2337,7 +2338,7 @@ DEF_OP(ParanoidLoadMemTSO) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
switch (OpSize) {
|
||||
@@ -2390,7 +2391,7 @@ DEF_OP(ParanoidStoreMemTSO) {
|
||||
|
||||
auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
@@ -2410,7 +2411,7 @@ DEF_OP(ParanoidStoreMemTSO) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP1, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
switch (OpSize) {
|
||||
|
||||
@@ -4251,7 +4251,7 @@ AddressMode OpDispatchBuilder::DecodeAddress(const X86Tables::DecodedOp& Op, con
|
||||
}
|
||||
|
||||
|
||||
Ref OpDispatchBuilder::LoadSource_WithOpSize(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
|
||||
Ref OpDispatchBuilder::LoadSource_WithOpSize(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
|
||||
IR::OpSize OpSize, uint32_t Flags, const LoadSourceOptions& Options) {
|
||||
auto [Align, LoadData, ForceLoad, AccessType, AllowUpperGarbage] = Options;
|
||||
AddressMode A = DecodeAddress(Op, Operand, AccessType, true /* IsLoad */);
|
||||
@@ -4341,15 +4341,14 @@ void OpDispatchBuilder::StoreXMMRegister(uint32_t XMM, const Ref Src) {
|
||||
StoreRegister(XMM, true, Src);
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::LoadSource(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
|
||||
uint32_t Flags, const LoadSourceOptions& Options) {
|
||||
Ref OpDispatchBuilder::LoadSource(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
|
||||
const LoadSourceOptions& Options) {
|
||||
const auto OpSize = OpSizeFromSrc(Op);
|
||||
return LoadSource_WithOpSize(Class, Op, Operand, OpSize, Flags, Options);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op,
|
||||
const FEXCore::X86Tables::DecodedOperand& Operand, const Ref Src, IR::OpSize OpSize,
|
||||
IR::OpSize Align, MemoryAccessType AccessType) {
|
||||
void OpDispatchBuilder::StoreResult_WithOpSize(RegClass Class, FEXCore::X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand,
|
||||
Ref Src, IR::OpSize OpSize, IR::OpSize Align, MemoryAccessType AccessType) {
|
||||
if (Operand.IsGPR()) {
|
||||
// 8Bit and 16bit destination types store their result without effecting the upper bits
|
||||
// 32bit ops ZEXT the result to 64bit
|
||||
@@ -4358,7 +4357,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
const auto gpr = Operand.Data.GPR.GPR;
|
||||
if (gpr >= FEXCore::X86State::REG_MM_0) {
|
||||
LOGMAN_THROW_A_FMT(OpSize == OpSize::i64Bit, "full");
|
||||
LOGMAN_THROW_A_FMT(Class == FPRClass, "MMX is floaty");
|
||||
LOGMAN_THROW_A_FMT(Class == RegClass::FPR, "MMX is floaty");
|
||||
|
||||
if (MMXState != MMXState_MMX) {
|
||||
ChgStateX87_MMX();
|
||||
@@ -4376,7 +4375,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
// Partial writes can come from FPRs.
|
||||
// TODO: Fix the instructions doing partial writes rather than dealing with it here.
|
||||
|
||||
LOGMAN_THROW_A_FMT(Class != IR::GPRClass, "Partial writes from GPR not allowed. Instruction: {}", Op->TableInfo->Name);
|
||||
LOGMAN_THROW_A_FMT(Class != RegClass::GPR, "Partial writes from GPR not allowed. Instruction: {}", Op->TableInfo->Name);
|
||||
|
||||
// XMM-size is handled in implementations.
|
||||
if (VectorSize != OpSize::i256Bit || OpSize != OpSize::i128Bit) {
|
||||
@@ -4429,14 +4428,12 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
}
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op,
|
||||
const FEXCore::X86Tables::DecodedOperand& Operand, const Ref Src, IR::OpSize Align,
|
||||
MemoryAccessType AccessType) {
|
||||
void OpDispatchBuilder::StoreResult(RegClass Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src,
|
||||
IR::OpSize Align, MemoryAccessType AccessType) {
|
||||
StoreResult_WithOpSize(Class, Op, Operand, Src, OpSizeFromDst(Op), Align, AccessType);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, const Ref Src, IR::OpSize Align,
|
||||
MemoryAccessType AccessType) {
|
||||
void OpDispatchBuilder::StoreResult(RegClass Class, X86Tables::DecodedOp Op, Ref Src, IR::OpSize Align, MemoryAccessType AccessType) {
|
||||
StoreResult(Class, Op, Op->Dest, Src, Align, AccessType);
|
||||
}
|
||||
|
||||
|
||||
@@ -1110,8 +1110,8 @@ public:
|
||||
// End of AVX 128-bit implementation
|
||||
|
||||
// AVX 256-bit operations
|
||||
void StoreResult_WithAVXInsert(VectorOpType Type, FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, Ref Value,
|
||||
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
void StoreResult_WithAVXInsert(VectorOpType Type, RegClass Class, FEXCore::X86Tables::DecodedOp Op, Ref Value, IR::OpSize Align,
|
||||
MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
if (Op->Dest.IsGPR() && Op->Dest.Data.GPR.GPR >= X86State::REG_XMM_0 && Op->Dest.Data.GPR.GPR <= X86State::REG_XMM_15 &&
|
||||
GetGuestVectorLength() == OpSize::i256Bit && Type == VectorOpType::SSE) {
|
||||
const auto gpr = Op->Dest.Data.GPR.GPR;
|
||||
@@ -1162,7 +1162,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void StoreContextHelper(IR::OpSize Size, RegisterClassType Class, Ref Value, uint32_t Offset) {
|
||||
void StoreContextHelper(IR::OpSize Size, RegClass Class, Ref Value, uint32_t Offset) {
|
||||
// For i128Bit, we won't see a normal Constant to inline, but as a special
|
||||
// case we can replace with a 2x64-bit store which can use inline zeroes.
|
||||
if (Size == OpSize::i128Bit) {
|
||||
@@ -1174,7 +1174,7 @@ public:
|
||||
|
||||
if (Const->Constant == IR::NamedVectorConstant::NAMED_VECTOR_ZERO) {
|
||||
Ref Zero = _Constant(0);
|
||||
Ref STP = _StoreContextPair(IR::OpSize::i64Bit, GPRClass, Zero, Zero, Offset);
|
||||
Ref STP = _StoreContextPair(IR::OpSize::i64Bit, RegClass::GPR, Zero, Zero, Offset);
|
||||
|
||||
// XXX: This works around InlineConstant not having an associated
|
||||
// register class, else we'd just do InlineConstant above.
|
||||
@@ -1230,16 +1230,16 @@ public:
|
||||
|
||||
if (Index >= GPR0Index && Index <= GPR15Index) {
|
||||
Ref R = _StoreRegister(Value, GPRSize);
|
||||
R->Reg = PhysicalRegister(GPRFixedClass, Index - GPR0Index).Raw;
|
||||
R->Reg = PhysicalRegister(RegClass::GPRFixed, Index - GPR0Index).Raw;
|
||||
} else if (Index == PFIndex) {
|
||||
_StorePF(Value, GPRSize);
|
||||
} else if (Index == AFIndex) {
|
||||
_StoreAF(Value, GPRSize);
|
||||
} else if (Index >= FPR0Index && Index <= FPR15Index) {
|
||||
Ref R = _StoreRegister(Value, VectorSize);
|
||||
R->Reg = PhysicalRegister(FPRFixedClass, Index - FPR0Index).Raw;
|
||||
R->Reg = PhysicalRegister(RegClass::FPRFixed, Index - FPR0Index).Raw;
|
||||
} else if (Index == DFIndex) {
|
||||
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
|
||||
_StoreContextGPR(OpSize::i8Bit, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
|
||||
} else {
|
||||
bool Partial = RegCache.Partial & (1ull << Index);
|
||||
auto Size = Partial ? OpSize::i64Bit : CacheIndexToOpSize(Index);
|
||||
@@ -1264,7 +1264,7 @@ public:
|
||||
StoreContextHelper(Size, Class, Value, Offset);
|
||||
// If Partial and MMX register, then we need to store all 1s in bits 64-80
|
||||
if (Partial && Index >= MM0Index && Index <= MM7Index) {
|
||||
_StoreContext(OpSize::i16Bit, IR::GPRClass, Constant(0xFFFF), Offset + 8);
|
||||
_StoreContextGPR(OpSize::i16Bit, Constant(0xFFFF), Offset + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1555,63 +1555,63 @@ private:
|
||||
|
||||
AddressMode DecodeAddress(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, MemoryAccessType AccessType, bool IsLoad);
|
||||
|
||||
Ref LoadSource(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
|
||||
Ref LoadSource(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
|
||||
const LoadSourceOptions& Options = {});
|
||||
Ref LoadSourceGPR(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
|
||||
const LoadSourceOptions& Options = {}) {
|
||||
return LoadSource(GPRClass, Op, Operand, Flags, Options);
|
||||
return LoadSource(RegClass::GPR, Op, Operand, Flags, Options);
|
||||
}
|
||||
Ref LoadSourceFPR(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
|
||||
const LoadSourceOptions& Options = {}) {
|
||||
return LoadSource(FPRClass, Op, Operand, Flags, Options);
|
||||
return LoadSource(RegClass::FPR, Op, Operand, Flags, Options);
|
||||
}
|
||||
|
||||
Ref LoadSource_WithOpSize(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
|
||||
IR::OpSize OpSize, uint32_t Flags, const LoadSourceOptions& Options = {});
|
||||
Ref LoadSource_WithOpSize(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize,
|
||||
uint32_t Flags, const LoadSourceOptions& Options = {});
|
||||
Ref LoadSourceGPR_WithOpSize(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize, uint32_t Flags,
|
||||
const LoadSourceOptions& Options = {}) {
|
||||
return LoadSource_WithOpSize(GPRClass, Op, Operand, OpSize, Flags, Options);
|
||||
return LoadSource_WithOpSize(RegClass::GPR, Op, Operand, OpSize, Flags, Options);
|
||||
}
|
||||
Ref LoadSourceFPR_WithOpSize(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize, uint32_t Flags,
|
||||
const LoadSourceOptions& Options = {}) {
|
||||
return LoadSource_WithOpSize(FPRClass, Op, Operand, OpSize, Flags, Options);
|
||||
return LoadSource_WithOpSize(RegClass::FPR, Op, Operand, OpSize, Flags, Options);
|
||||
}
|
||||
|
||||
void StoreResult_WithOpSize(RegisterClassType Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src,
|
||||
IR::OpSize OpSize, IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
|
||||
void StoreResult_WithOpSize(RegClass Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
|
||||
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
|
||||
void StoreResultGPR_WithOpSize(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
|
||||
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult_WithOpSize(GPRClass, Op, Operand, Src, OpSize, Align, AccessType);
|
||||
StoreResult_WithOpSize(RegClass::GPR, Op, Operand, Src, OpSize, Align, AccessType);
|
||||
}
|
||||
void StoreResultFPR_WithOpSize(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
|
||||
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult_WithOpSize(FPRClass, Op, Operand, Src, OpSize, Align, AccessType);
|
||||
StoreResult_WithOpSize(RegClass::FPR, Op, Operand, Src, OpSize, Align, AccessType);
|
||||
}
|
||||
|
||||
void StoreResult(RegisterClassType Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align,
|
||||
void StoreResult(RegClass Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align,
|
||||
MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
|
||||
void StoreResultGPR(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align,
|
||||
MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult(GPRClass, Op, Operand, Src, Align, AccessType);
|
||||
StoreResult(RegClass::GPR, Op, Operand, Src, Align, AccessType);
|
||||
}
|
||||
void StoreResultFPR(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align,
|
||||
MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult(FPRClass, Op, Operand, Src, Align, AccessType);
|
||||
StoreResult(RegClass::FPR, Op, Operand, Src, Align, AccessType);
|
||||
}
|
||||
|
||||
void StoreResult(RegisterClassType Class, X86Tables::DecodedOp Op, Ref Src, OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
|
||||
void StoreResult(RegClass Class, X86Tables::DecodedOp Op, Ref Src, OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
|
||||
void StoreResultGPR(X86Tables::DecodedOp Op, Ref Src, OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult(GPRClass, Op, Src, Align, AccessType);
|
||||
StoreResult(RegClass::GPR, Op, Src, Align, AccessType);
|
||||
}
|
||||
void StoreResultFPR(X86Tables::DecodedOp Op, Ref Src, OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
|
||||
StoreResult(FPRClass, Op, Src, Align, AccessType);
|
||||
StoreResult(RegClass::FPR, Op, Src, Align, AccessType);
|
||||
}
|
||||
|
||||
// In several instances, it's desirable to get a base address with the segment offset
|
||||
// applied to it. This pulls all the common-case appending into a single set of functions.
|
||||
[[nodiscard]]
|
||||
Ref MakeSegmentAddress(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize) {
|
||||
Ref Mem = LoadSource_WithOpSize(GPRClass, Op, Operand, OpSize, Op->Flags, {.LoadData = false});
|
||||
Ref Mem = LoadSourceGPR_WithOpSize(Op, Operand, OpSize, Op->Flags, {.LoadData = false});
|
||||
return AppendSegmentOffset(Mem, Op->Flags);
|
||||
}
|
||||
[[nodiscard]]
|
||||
@@ -1847,14 +1847,14 @@ private:
|
||||
// For DF, we need to transform 0/1 into 1/-1
|
||||
StoreDF(_SubShift(OpSize::i64Bit, Constant(1), Value, ShiftType::LSL, 1));
|
||||
} else if (BitOffset == FEXCore::X86State::RFLAG_TF_RAW_LOC) {
|
||||
auto PackedTF = _LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
auto PackedTF = _LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
// An exception should still be raised after an instruction that unsets TF, leave the unblocked bit set but unset
|
||||
// the TF bit to cause such behaviour. The handling code at the start of the next block will then unset the
|
||||
// unblocked bit before raising the exception.
|
||||
auto NewPackedTF = _Select(CondClass::EQ, Value, Constant(0), _And(OpSize::i32Bit, PackedTF, Constant(~1)), Constant(1));
|
||||
_StoreContext(OpSize::i8Bit, GPRClass, NewPackedTF, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
_StoreContextGPR(OpSize::i8Bit, NewPackedTF, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
} else {
|
||||
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
_StoreContextGPR(OpSize::i8Bit, Value, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1916,11 +1916,11 @@ private:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static RegisterClassType CacheIndexClass(int Index) {
|
||||
static RegClass CacheIndexClass(int Index) {
|
||||
if ((Index >= MM0Index && Index <= MM7Index) || Index >= FPR0Index) {
|
||||
return FPRClass;
|
||||
return RegClass::FPR;
|
||||
} else {
|
||||
return GPRClass;
|
||||
return RegClass::GPR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1952,14 +1952,14 @@ private:
|
||||
RegCache.Written &= ~Bit;
|
||||
}
|
||||
|
||||
Ref LoadRegCache(uint64_t Offset, uint8_t Index, RegisterClassType RegClass, IR::OpSize Size) {
|
||||
Ref LoadRegCache(uint64_t Offset, uint8_t Index, RegClass Class, IR::OpSize Size) {
|
||||
LOGMAN_THROW_A_FMT(Index < 64, "valid index");
|
||||
uint64_t Bit = (1ull << (uint64_t)Index);
|
||||
|
||||
if (Size == OpSize::i128Bit && (RegCache.Partial & Bit)) {
|
||||
// We need to load the full register extend if we previously did a partial access.
|
||||
Ref Value = RegCache.Value[Index];
|
||||
Ref Full = _LoadContext(Size, RegClass, Offset);
|
||||
Ref Full = _LoadContext(Size, Class, Offset);
|
||||
|
||||
// If we did a partial store, we're inserting into the full register
|
||||
if (RegCache.Written & Bit) {
|
||||
@@ -1973,7 +1973,7 @@ private:
|
||||
if (Index == DFIndex) {
|
||||
RegCache.Value[Index] = _LoadDF();
|
||||
} else if ((Index >= MM0Index && Index <= MM7Index) || Index >= AVXHigh0Index) {
|
||||
RegCache.Value[Index] = _LoadContext(Size, RegClass, Offset);
|
||||
RegCache.Value[Index] = _LoadContext(Size, Class, Offset);
|
||||
|
||||
// We may have done a partial load, this requires special handling.
|
||||
if (Size == OpSize::i64Bit) {
|
||||
@@ -1984,7 +1984,7 @@ private:
|
||||
} else if (Index == AFIndex) {
|
||||
RegCache.Value[Index] = _LoadAF(Size);
|
||||
} else {
|
||||
RegCache.Value[Index] = _LoadRegister(Offset, RegClass, Size);
|
||||
RegCache.Value[Index] = _LoadRegister(Offset, Class, Size);
|
||||
}
|
||||
|
||||
RegCache.Cached |= Bit;
|
||||
@@ -1993,21 +1993,21 @@ private:
|
||||
return RegCache.Value[Index];
|
||||
}
|
||||
|
||||
RefPair AllocatePair(FEXCore::IR::RegisterClassType Class, IR::OpSize Size) {
|
||||
if (Class == FPRClass) {
|
||||
RefPair AllocatePair(RegClass Class, IR::OpSize Size) {
|
||||
if (Class == RegClass::FPR) {
|
||||
return {_AllocateFPR(Size, Size), _AllocateFPR(Size, Size)};
|
||||
} else {
|
||||
return {_AllocateGPR(false), _AllocateGPR(false)};
|
||||
}
|
||||
}
|
||||
|
||||
RefPair LoadContextPair_Uncached(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, unsigned Offset) {
|
||||
RefPair LoadContextPair_Uncached(RegClass Class, IR::OpSize Size, unsigned Offset) {
|
||||
RefPair Values = AllocatePair(Class, Size);
|
||||
_LoadContextPair(Size, Class, Offset, Values.Low, Values.High);
|
||||
return Values;
|
||||
}
|
||||
|
||||
RefPair LoadRegCachePair(uint64_t Offset, uint8_t Index, RegisterClassType RegClass, IR::OpSize Size) {
|
||||
RefPair LoadRegCachePair(uint64_t Offset, uint8_t Index, RegClass Class, IR::OpSize Size) {
|
||||
LOGMAN_THROW_A_FMT(Index != DFIndex, "must be pairable");
|
||||
LOGMAN_THROW_A_FMT(Size != IR::OpSize::iUnsized, "Invalid size!");
|
||||
|
||||
@@ -2015,7 +2015,7 @@ private:
|
||||
uint64_t Bits = (3ull << (uint64_t)Index);
|
||||
const auto SizeInt = IR::OpSizeToSize(Size);
|
||||
if (((RegCache.Partial | RegCache.Cached) & Bits) == 0 && ((Offset / SizeInt) < 64)) {
|
||||
auto Values = LoadContextPair_Uncached(RegClass, Size, Offset);
|
||||
auto Values = LoadContextPair_Uncached(Class, Size, Offset);
|
||||
RegCache.Value[Index] = Values.Low;
|
||||
RegCache.Value[Index + 1] = Values.High;
|
||||
RegCache.Cached |= Bits;
|
||||
@@ -2027,13 +2027,13 @@ private:
|
||||
|
||||
// Fallback on a pair of loads
|
||||
return {
|
||||
.Low = LoadRegCache(Offset, Index, RegClass, Size),
|
||||
.High = LoadRegCache(Offset + SizeInt, Index + 1, RegClass, Size),
|
||||
.Low = LoadRegCache(Offset, Index, Class, Size),
|
||||
.High = LoadRegCache(Offset + SizeInt, Index + 1, Class, Size),
|
||||
};
|
||||
}
|
||||
|
||||
Ref LoadGPR(uint8_t Reg) {
|
||||
return LoadRegCache(Reg, GPR0Index + Reg, GPRClass, GetGPROpSize());
|
||||
return LoadRegCache(Reg, GPR0Index + Reg, RegClass::GPR, GetGPROpSize());
|
||||
}
|
||||
|
||||
Ref LoadContext(IR::OpSize Size, uint8_t Index) {
|
||||
@@ -2049,7 +2049,7 @@ private:
|
||||
}
|
||||
|
||||
Ref LoadXMMRegister(uint8_t Reg) {
|
||||
return LoadRegCache(Reg, FPR0Index + Reg, FPRClass, GetGuestVectorLength());
|
||||
return LoadRegCache(Reg, FPR0Index + Reg, RegClass::FPR, GetGuestVectorLength());
|
||||
}
|
||||
|
||||
Ref LoadDF() {
|
||||
@@ -2104,7 +2104,7 @@ private:
|
||||
// Recover the sign bit, it is the logical DF value
|
||||
return _Lshr(OpSize::i64Bit, LoadDF(), Constant(63));
|
||||
} else {
|
||||
return _LoadContext(OpSize::i8Bit, GPRClass, offsetof(Core::CPUState, flags[BitOffset]));
|
||||
return _LoadContextGPR(OpSize::i8Bit, offsetof(Core::CPUState, flags[BitOffset]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2221,7 +2221,7 @@ private:
|
||||
|
||||
HandleNZCV_RMW();
|
||||
CalculatePF(_ShiftFlags(OpSizeFromSrc(Op), Result, Dest, Shift, Src, OldPF, CFInverted));
|
||||
StoreResult(GPRClass, Op, Result, OpSize::iInvalid);
|
||||
StoreResultGPR(Op, Result, OpSize::iInvalid);
|
||||
}
|
||||
|
||||
// Helper to derive Dest by a given builder-using Expression with the opcode
|
||||
@@ -2309,8 +2309,8 @@ private:
|
||||
if (Size != OpSize::i32Bit) {
|
||||
return;
|
||||
}
|
||||
auto Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
|
||||
StoreResult(GPRClass, Op, Dest, OpSize::iInvalid);
|
||||
auto Dest = LoadSourceGPR(Op, Op->Dest, Op->Flags);
|
||||
StoreResultGPR(Op, Dest, OpSize::iInvalid);
|
||||
}
|
||||
|
||||
using ZeroShiftFunctionPtr = void (OpDispatchBuilder::*)(FEXCore::X86Tables::DecodedOp Op);
|
||||
@@ -2407,7 +2407,7 @@ private:
|
||||
LOGMAN_THROW_A_FMT(MMXState == MMXState_X87, "Expected state to be x87");
|
||||
_StackForceSlow();
|
||||
SetX87Top(Constant(0)); // top reset to zero
|
||||
_StoreContext(OpSize::i8Bit, GPRClass, Constant(0xFFFFUL), offsetof(FEXCore::Core::CPUState, AbridgedFTW));
|
||||
_StoreContextGPR(OpSize::i8Bit, Constant(0xFFFFUL), offsetof(FEXCore::Core::CPUState, AbridgedFTW));
|
||||
MMXState = MMXState_MMX;
|
||||
}
|
||||
|
||||
@@ -2444,19 +2444,19 @@ private:
|
||||
IROp_IRHeader* CurrentHeader {};
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsTSOEnabled(FEXCore::IR::RegisterClassType Class) const {
|
||||
bool IsTSOEnabled(RegClass Class) const {
|
||||
if (ForceTSO == ForceTSOMode::ForceEnabled) {
|
||||
return true;
|
||||
} else if (ForceTSO == ForceTSOMode::ForceDisabled) {
|
||||
return false;
|
||||
} else if (Class == FPRClass) {
|
||||
} else if (Class == RegClass::FPR) {
|
||||
return CTX->IsVectorAtomicTSOEnabled();
|
||||
} else {
|
||||
return CTX->IsAtomicTSOEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
Ref _StoreMemAutoTSO(RegisterClassType Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
Ref _StoreMemAutoTSO(RegClass Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
if (IsTSOEnabled(Class)) {
|
||||
return _StoreMemTSO(Class, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
} else {
|
||||
@@ -2464,13 +2464,13 @@ private:
|
||||
}
|
||||
}
|
||||
Ref _StoreMemGPRAutoTSO(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMemAutoTSO(GPRClass, Size, Addr, Value, Align);
|
||||
return _StoreMemAutoTSO(RegClass::GPR, Size, Addr, Value, Align);
|
||||
}
|
||||
Ref _StoreMemFPRAutoTSO(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMemAutoTSO(FPRClass, Size, Addr, Value, Align);
|
||||
return _StoreMemAutoTSO(RegClass::FPR, Size, Addr, Value, Align);
|
||||
}
|
||||
|
||||
Ref _LoadMemAutoTSO(RegisterClassType Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
Ref _LoadMemAutoTSO(RegClass Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
if (IsTSOEnabled(Class)) {
|
||||
return _LoadMemTSO(Class, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
} else {
|
||||
@@ -2478,15 +2478,15 @@ private:
|
||||
}
|
||||
}
|
||||
Ref _LoadMemGPRAutoTSO(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMemAutoTSO(GPRClass, Size, ssa0, Align);
|
||||
return _LoadMemAutoTSO(RegClass::GPR, Size, ssa0, Align);
|
||||
}
|
||||
Ref _LoadMemFPRAutoTSO(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMemAutoTSO(GPRClass, Size, ssa0, Align);
|
||||
return _LoadMemAutoTSO(RegClass::FPR, Size, ssa0, Align);
|
||||
}
|
||||
|
||||
Ref _LoadMemAutoTSO(RegisterClassType Class, OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
Ref _LoadMemAutoTSO(RegClass Class, OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != GPRClass, Size);
|
||||
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != RegClass::GPR, Size);
|
||||
|
||||
if (AtomicTSO) {
|
||||
return _LoadMemTSO(Class, Size, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
|
||||
@@ -2495,10 +2495,10 @@ private:
|
||||
}
|
||||
}
|
||||
Ref _LoadMemGPRAutoTSO(OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMemAutoTSO(GPRClass, Size, A, Align);
|
||||
return _LoadMemAutoTSO(RegClass::GPR, Size, A, Align);
|
||||
}
|
||||
Ref _LoadMemFPRAutoTSO(OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMemAutoTSO(FPRClass, Size, A, Align);
|
||||
return _LoadMemAutoTSO(RegClass::FPR, Size, A, Align);
|
||||
}
|
||||
|
||||
AddressMode SelectPairAddressMode(AddressMode A, IR::OpSize Size) {
|
||||
@@ -2517,16 +2517,16 @@ private:
|
||||
}
|
||||
|
||||
|
||||
RefPair LoadMemPair(RegisterClassType Class, OpSize Size, Ref Base, uint32_t Offset) {
|
||||
RefPair LoadMemPair(RegClass Class, OpSize Size, Ref Base, uint32_t Offset) {
|
||||
RefPair Values = AllocatePair(Class, Size);
|
||||
_LoadMemPair(Class, Size, Base, Offset, Values.Low, Values.High);
|
||||
return Values;
|
||||
}
|
||||
RefPair LoadMemPairFPR(OpSize Size, Ref Base, uint32_t Offset) {
|
||||
return LoadMemPair(FPRClass, Size, Base, Offset);
|
||||
return LoadMemPair(RegClass::FPR, Size, Base, Offset);
|
||||
}
|
||||
|
||||
RefPair _LoadMemPairAutoTSO(RegisterClassType Class, OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
RefPair _LoadMemPairAutoTSO(RegClass Class, OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
|
||||
// Use ldp if possible, otherwise fallback on two loads.
|
||||
@@ -2544,12 +2544,12 @@ private:
|
||||
}
|
||||
}
|
||||
RefPair _LoadMemPairFPRAutoTSO(OpSize Size, AddressMode A, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMemPairAutoTSO(FPRClass, Size, A, Align);
|
||||
return _LoadMemPairAutoTSO(RegClass::FPR, Size, A, Align);
|
||||
}
|
||||
|
||||
Ref _StoreMemAutoTSO(RegisterClassType Class, OpSize Size, AddressMode A, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
Ref _StoreMemAutoTSO(RegClass Class, OpSize Size, AddressMode A, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != GPRClass, Size);
|
||||
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != RegClass::GPR, Size);
|
||||
|
||||
if (AtomicTSO) {
|
||||
return _StoreMemTSO(Class, Size, Value, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
|
||||
@@ -2558,13 +2558,13 @@ private:
|
||||
}
|
||||
}
|
||||
Ref _StoreMemGPRAutoTSO(OpSize Size, AddressMode A, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMemAutoTSO(GPRClass, Size, A, Value, Align);
|
||||
return _StoreMemAutoTSO(RegClass::GPR, Size, A, Value, Align);
|
||||
}
|
||||
Ref _StoreMemFPRAutoTSO(OpSize Size, AddressMode A, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMemAutoTSO(FPRClass, Size, A, Value, Align);
|
||||
return _StoreMemAutoTSO(RegClass::FPR, Size, A, Value, Align);
|
||||
}
|
||||
|
||||
void _StoreMemPairAutoTSO(RegisterClassType Class, OpSize Size, AddressMode A, Ref Value1, Ref Value2, OpSize Align = OpSize::i8Bit) {
|
||||
void _StoreMemPairAutoTSO(RegClass Class, OpSize Size, AddressMode A, Ref Value1, Ref Value2, OpSize Align = OpSize::i8Bit) {
|
||||
const auto SizeInt = IR::OpSizeToSize(Size);
|
||||
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
|
||||
@@ -2579,7 +2579,7 @@ private:
|
||||
}
|
||||
}
|
||||
void _StoreMemPairFPRAutoTSO(OpSize Size, AddressMode A, Ref Value1, Ref Value2, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMemPairAutoTSO(FPRClass, Size, A, Value1, Value2, Align);
|
||||
return _StoreMemPairAutoTSO(RegClass::FPR, Size, A, Value1, Value2, Align);
|
||||
}
|
||||
|
||||
Ref Pop(IR::OpSize Size, Ref SP_RMW) {
|
||||
|
||||
@@ -2406,7 +2406,7 @@ void OpDispatchBuilder::MOVBetweenGPR_FPR(OpcodeArgs, VectorOpType VectorType) {
|
||||
Result = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
|
||||
}
|
||||
|
||||
StoreResult_WithAVXInsert(VectorType, FPRClass, Op, Result, OpSize::iInvalid);
|
||||
StoreResult_WithAVXInsert(VectorType, RegClass::FPR, Op, Result, OpSize::iInvalid);
|
||||
} else {
|
||||
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
|
||||
|
||||
|
||||
@@ -431,17 +431,6 @@ static_assert(sizeof(OrderedNode) == (sizeof(OrderedNodeHeader) + 2 * sizeof(uin
|
||||
// };
|
||||
using Ref = OrderedNode*;
|
||||
|
||||
struct FEX_PACKED RegisterClassType final {
|
||||
using value_type = uint32_t;
|
||||
|
||||
value_type Val;
|
||||
[[nodiscard]] constexpr operator value_type() const {
|
||||
return Val;
|
||||
}
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator==(const RegisterClassType&, const RegisterClassType&) = default;
|
||||
};
|
||||
|
||||
/* This iterator can be used to step though nodes.
|
||||
* Due to how our IR is laid out, this can be used to either step
|
||||
* though the CodeBlocks or though the code within a single block.
|
||||
@@ -727,12 +716,12 @@ struct fmt::formatter<FEXCore::IR::NodeID> : fmt::formatter<FEXCore::IR::NodeID:
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<FEXCore::IR::RegisterClassType> : fmt::formatter<FEXCore::IR::RegisterClassType::value_type> {
|
||||
using Base = fmt::formatter<FEXCore::IR::RegisterClassType::value_type>;
|
||||
struct fmt::formatter<FEXCore::IR::RegClass> : fmt::formatter<std::underlying_type_t<FEXCore::IR::RegClass>> {
|
||||
using Base = fmt::formatter<std::underlying_type_t<FEXCore::IR::RegClass>>;
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const FEXCore::IR::RegisterClassType& Class, FormatContext& ctx) const {
|
||||
return Base::format(Class.Val, ctx);
|
||||
auto format(const FEXCore::IR::RegClass& Class, FormatContext& ctx) const {
|
||||
return Base::format(FEXCore::ToUnderlying(Class), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,14 @@
|
||||
"UXTW = 1,",
|
||||
"SXTW = 2,"
|
||||
],
|
||||
"class RegClass : uint32_t": [
|
||||
"Invalid = 0,",
|
||||
"GPR = 1,",
|
||||
"GPRFixed = 2,",
|
||||
"FPR = 3,",
|
||||
"FPRFixed = 4,",
|
||||
"Complex = 5,"
|
||||
],
|
||||
"class RoundMode : uint8_t": [
|
||||
"Nearest = 0,",
|
||||
"NegInfinity = 1,",
|
||||
@@ -100,14 +108,8 @@
|
||||
]
|
||||
},
|
||||
"Defines": [
|
||||
"constexpr FEXCore::IR::RegisterClassType InvalidClass {0}",
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRClass {1}",
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {2}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRClass {3}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {4}",
|
||||
"constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
|
||||
"constexpr uint8_t NumClasses {6}",
|
||||
"",
|
||||
|
||||
"constexpr uint8_t FCMP_FLAG_EQ = 0",
|
||||
"constexpr uint8_t FCMP_FLAG_LT = 1",
|
||||
"constexpr uint8_t FCMP_FLAG_UNORDERED = 2",
|
||||
@@ -134,7 +136,7 @@
|
||||
"GPR": "OrderedNode*",
|
||||
"FPR": "OrderedNode*",
|
||||
"FenceType": "FenceType",
|
||||
"RegisterClass": "RegisterClassType",
|
||||
"RegisterClass": "RegClass",
|
||||
"CondClass": "CondClass",
|
||||
"SyscallFlags": "FEXCore::IR::SyscallFlags",
|
||||
"SHA256Sum": "SHA256Sum",
|
||||
@@ -409,8 +411,8 @@
|
||||
],
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContext to GPR\"",
|
||||
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContext to XMM\""
|
||||
]
|
||||
@@ -423,8 +425,8 @@
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContext to GPR\"",
|
||||
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContext to XMM\""
|
||||
]
|
||||
@@ -440,8 +442,8 @@
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContext to GPR\"",
|
||||
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContext to XMM\""
|
||||
]
|
||||
@@ -458,8 +460,8 @@
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($Value1) == $Class",
|
||||
"WalkFindRegClass($Value2) == $Class",
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContext to GPR\"",
|
||||
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContext to XMM\""
|
||||
]
|
||||
@@ -471,8 +473,8 @@
|
||||
],
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($BaseOffset >= offsetof(Core::CPUState, gregs[0]) && $BaseOffset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContextIndexed to GPR\"",
|
||||
"!($BaseOffset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $BaseOffset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContextIndexed to XMM\""
|
||||
]
|
||||
@@ -485,8 +487,8 @@
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($Value) == $Class",
|
||||
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
|
||||
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
|
||||
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
|
||||
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
|
||||
"!($BaseOffset >= offsetof(Core::CPUState, gregs[0]) && $BaseOffset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContextIndexed to GPR\"",
|
||||
"!($BaseOffset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $BaseOffset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContextIndexed to XMM\""
|
||||
]
|
||||
@@ -2827,7 +2829,7 @@
|
||||
"Int: 64-bit, 32-bit, 16-bit"
|
||||
],
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($OriginalValue) == FPRClass || WalkFindRegClass($OriginalValue) == GPRClass"
|
||||
"WalkFindRegClass($OriginalValue) == RegClass::FPR || WalkFindRegClass($OriginalValue) == RegClass::GPR"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
|
||||
@@ -61,36 +61,36 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, MemOffsetType
|
||||
*out << Names[FEXCore::ToUnderlying(Arg)];
|
||||
}
|
||||
|
||||
static void PrintArg(fextl::stringstream* out, const IRListView*, RegisterClassType Arg) {
|
||||
if (Arg == GPRClass.Val) {
|
||||
*out << "GPR";
|
||||
} else if (Arg == GPRFixedClass.Val) {
|
||||
*out << "GPRFixed";
|
||||
} else if (Arg == FPRClass.Val) {
|
||||
*out << "FPR";
|
||||
} else if (Arg == FPRFixedClass.Val) {
|
||||
*out << "FPRFixed";
|
||||
} else {
|
||||
*out << "Unknown Registerclass " << Arg;
|
||||
}
|
||||
static void PrintArg(fextl::stringstream* out, const IRListView*, RegClass Arg) {
|
||||
*out << [Arg] {
|
||||
switch (Arg) {
|
||||
case RegClass::Invalid: return "Invalid";
|
||||
case RegClass::GPR: return "GPR";
|
||||
case RegClass::GPRFixed: return "GPRFixed";
|
||||
case RegClass::FPR: return "FPR";
|
||||
case RegClass::FPRFixed: return "FPRFixed";
|
||||
case RegClass::Complex: return "Complex";
|
||||
}
|
||||
return "<Unknown RegClass Type>";
|
||||
}();
|
||||
}
|
||||
|
||||
static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg) {
|
||||
if (Arg.IsImmediate()) {
|
||||
auto PhyReg = PhysicalRegister(Arg);
|
||||
|
||||
switch (PhyReg.Class) {
|
||||
case FEXCore::IR::GPRClass.Val: *out << "r"; break;
|
||||
case FEXCore::IR::GPRFixedClass.Val: *out << "R"; break;
|
||||
case FEXCore::IR::FPRClass.Val: *out << "v"; break;
|
||||
case FEXCore::IR::FPRFixedClass.Val: *out << "V"; break;
|
||||
case FEXCore::IR::ComplexClass.Val: *out << "c"; break;
|
||||
case FEXCore::IR::InvalidClass.Val: *out << "invalid"; break;
|
||||
switch (PhyReg.AsRegClass()) {
|
||||
case RegClass::GPR: *out << "r"; break;
|
||||
case RegClass::GPRFixed: *out << "R"; break;
|
||||
case RegClass::FPR: *out << "v"; break;
|
||||
case RegClass::FPRFixed: *out << "V"; break;
|
||||
case RegClass::Complex: *out << "c"; break;
|
||||
case RegClass::Invalid: *out << "invalid"; break;
|
||||
default: *out << "unknown"; break;
|
||||
}
|
||||
|
||||
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
|
||||
*out << std::dec << (uint32_t)PhyReg.Reg;
|
||||
if (PhyReg.AsRegClass() != RegClass::Invalid) {
|
||||
*out << std::dec << uint32_t(PhyReg.Reg);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -397,17 +397,17 @@ void Dump(fextl::stringstream* out, const IRListView* IR) {
|
||||
|
||||
auto PhyReg = PhysicalRegister(CodeNode);
|
||||
if (!PhyReg.IsInvalid()) {
|
||||
switch (PhyReg.Class) {
|
||||
case FEXCore::IR::GPRClass.Val: *out << "(r"; break;
|
||||
case FEXCore::IR::GPRFixedClass.Val: *out << "(R"; break;
|
||||
case FEXCore::IR::FPRClass.Val: *out << "(v"; break;
|
||||
case FEXCore::IR::FPRFixedClass.Val: *out << "(V"; break;
|
||||
case FEXCore::IR::ComplexClass.Val: *out << "(complex"; break;
|
||||
case FEXCore::IR::InvalidClass.Val: *out << "(invalid"; break;
|
||||
switch (PhyReg.AsRegClass()) {
|
||||
case RegClass::GPR: *out << "(r"; break;
|
||||
case RegClass::GPRFixed: *out << "(R"; break;
|
||||
case RegClass::FPR: *out << "(v"; break;
|
||||
case RegClass::FPRFixed: *out << "(V"; break;
|
||||
case RegClass::Complex: *out << "(complex"; break;
|
||||
case RegClass::Invalid: *out << "(invalid"; break;
|
||||
default: *out << "(unknown"; break;
|
||||
}
|
||||
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
|
||||
*out << std::dec << (uint32_t)PhyReg.Reg << ")";
|
||||
if (PhyReg.AsRegClass() != RegClass::Invalid) {
|
||||
*out << std::dec << uint32_t(PhyReg.Reg) << ")";
|
||||
} else {
|
||||
*out << ")";
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ bool IsBlockExit(FEXCore::IR::IROps Op) {
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(Ref Node) {
|
||||
RegClass IREmitter::WalkFindRegClass(Ref Node) {
|
||||
auto Class = GetOpRegClass(Node);
|
||||
switch (Class) {
|
||||
case GPRClass:
|
||||
case FPRClass:
|
||||
case GPRFixedClass:
|
||||
case FPRFixedClass:
|
||||
case InvalidClass: return Class;
|
||||
case RegClass::GPR:
|
||||
case RegClass::FPR:
|
||||
case RegClass::GPRFixed:
|
||||
case RegClass::FPRFixed:
|
||||
case RegClass::Invalid: return Class;
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(Ref Node) {
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled op type: {} {} in argument class validation", ToUnderlying(IROp->Op), GetOpName(Node)); break;
|
||||
}
|
||||
return InvalidClass;
|
||||
return RegClass::Invalid;
|
||||
}
|
||||
|
||||
void IREmitter::ResetWorkingList() {
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
*
|
||||
* @{ */
|
||||
|
||||
FEXCore::IR::RegisterClassType WalkFindRegClass(Ref Node);
|
||||
RegClass WalkFindRegClass(Ref Node);
|
||||
|
||||
// These inlining helpers are used by IRDefines.inc so define first.
|
||||
Ref InlineMem(OpSize Size, Ref Offset, MemOffsetType OffsetType, uint8_t& OffsetScale, bool TSO = false) {
|
||||
@@ -124,67 +124,67 @@ public:
|
||||
}
|
||||
|
||||
IRPair<IROp_LoadContext> _LoadContextGPR(OpSize ByteSize, uint32_t Offset) {
|
||||
return _LoadContext(ByteSize, GPRClass, Offset);
|
||||
return _LoadContext(ByteSize, RegClass::GPR, Offset);
|
||||
}
|
||||
IRPair<IROp_LoadContext> _LoadContextFPR(OpSize ByteSize, uint32_t Offset) {
|
||||
return _LoadContext(ByteSize, FPRClass, Offset);
|
||||
return _LoadContext(ByteSize, RegClass::FPR, Offset);
|
||||
}
|
||||
IRPair<IROp_StoreContext> _StoreContextGPR(OpSize ByteSize, Ref Value, uint32_t Offset) {
|
||||
return _StoreContext(ByteSize, GPRClass, Value, Offset);
|
||||
return _StoreContext(ByteSize, RegClass::GPR, Value, Offset);
|
||||
}
|
||||
IRPair<IROp_StoreContext> _StoreContextFPR(OpSize ByteSize, Ref Value, uint32_t Offset) {
|
||||
return _StoreContext(ByteSize, FPRClass, Value, Offset);
|
||||
return _StoreContext(ByteSize, RegClass::FPR, Value, Offset);
|
||||
}
|
||||
|
||||
IRPair<IROp_LoadContextIndexed> _LoadContextGPRIndexed(Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
|
||||
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, GPRClass);
|
||||
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, RegClass::GPR);
|
||||
}
|
||||
IRPair<IROp_LoadContextIndexed> _LoadContextFPRIndexed(Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
|
||||
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, FPRClass);
|
||||
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, RegClass::FPR);
|
||||
}
|
||||
IRPair<IROp_StoreContextIndexed> _StoreContextGPRIndexed(Ref Value, Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
|
||||
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, GPRClass);
|
||||
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, RegClass::GPR);
|
||||
}
|
||||
IRPair<IROp_StoreContextIndexed> _StoreContextFPRIndexed(Ref Value, Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
|
||||
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, FPRClass);
|
||||
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, RegClass::FPR);
|
||||
}
|
||||
|
||||
IRPair<IROp_LoadMem> _LoadMem(RegisterClassType Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
IRPair<IROp_LoadMem> _LoadMem(RegClass Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMem(Class, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMemGPR(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMem(GPRClass, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
return _LoadMem(RegClass::GPR, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMemGPR(OpSize Size, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
return _LoadMem(GPRClass, Size, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
return _LoadMem(RegClass::GPR, Size, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMemFPR(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
|
||||
return _LoadMem(FPRClass, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
return _LoadMem(RegClass::FPR, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMemFPR(OpSize Size, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
return _LoadMem(FPRClass, Size, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
return _LoadMem(RegClass::FPR, Size, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMem(RegisterClassType Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
IRPair<IROp_StoreMem> _StoreMem(RegClass Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMem(Class, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMemGPR(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMem(GPRClass, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
return _StoreMem(RegClass::GPR, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMemGPR(OpSize Size, Ref Value, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
return _StoreMem(GPRClass, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
return _StoreMem(RegClass::GPR, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMemFPR(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
|
||||
return _StoreMem(FPRClass, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
return _StoreMem(RegClass::FPR, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMemFPR(OpSize Size, Ref Value, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
return _StoreMem(FPRClass, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
return _StoreMem(RegClass::FPR, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
|
||||
}
|
||||
|
||||
IRPair<IROp_StoreMemPair> _StoreMemPairGPR(OpSize Size, Ref Value1, Ref Value2, Ref Addr, uint32_t Offset) {
|
||||
return _StoreMemPair(GPRClass, Size, Value1, Value2, Addr, Offset);
|
||||
return _StoreMemPair(RegClass::GPR, Size, Value1, Value2, Addr, Offset);
|
||||
}
|
||||
IRPair<IROp_StoreMemPair> _StoreMemPairFPR(OpSize Size, Ref Value1, Ref Value2, Ref Addr, uint32_t Offset) {
|
||||
return _StoreMemPair(FPRClass, Size, Value1, Value2, Addr, Offset);
|
||||
return _StoreMemPair(RegClass::FPR, Size, Value1, Value2, Addr, Offset);
|
||||
}
|
||||
|
||||
IRPair<IROp_Select> Select01(FEXCore::IR::OpSize CompareSize, CondClass Cond, OrderedNode* Cmp1, OrderedNode* Cmp2) {
|
||||
@@ -308,7 +308,7 @@ public:
|
||||
}
|
||||
|
||||
/** @} */
|
||||
FEXCore::IR::RegisterClassType WalkFindRegClass(OrderedNodeWrapper ssa) {
|
||||
RegClass WalkFindRegClass(OrderedNodeWrapper ssa) {
|
||||
Ref RealNode = ssa.GetNode(DualListData.ListBegin());
|
||||
return WalkFindRegClass(RealNode);
|
||||
}
|
||||
|
||||
@@ -93,11 +93,11 @@ void IRValidation::Run(IREmitter* IREmit) {
|
||||
// After RA, the destination needs to be assigned a register and class
|
||||
auto PhyReg = PhysicalRegister(CodeNode);
|
||||
|
||||
FEXCore::IR::RegisterClassType ExpectedClass = IR::GetRegClass(IROp->Op);
|
||||
FEXCore::IR::RegisterClassType AssignedClass = FEXCore::IR::RegisterClassType {PhyReg.Class};
|
||||
const auto ExpectedClass = IR::GetRegClass(IROp->Op);
|
||||
const auto AssignedClass = PhyReg.AsRegClass();
|
||||
|
||||
// If no register class was assigned
|
||||
if (AssignedClass == IR::InvalidClass) {
|
||||
if (AssignedClass == IR::RegClass::Invalid) {
|
||||
HadError |= true;
|
||||
Errors << "%" << ID << ": Had destination but with no register class assigned" << std::endl;
|
||||
}
|
||||
@@ -109,10 +109,10 @@ void IRValidation::Run(IREmitter* IREmit) {
|
||||
}
|
||||
|
||||
// Assigned class wasn't the expected class and it is a non-complex op
|
||||
if (AssignedClass != ExpectedClass && ExpectedClass != IR::ComplexClass) {
|
||||
if (AssignedClass != ExpectedClass && ExpectedClass != IR::RegClass::Complex) {
|
||||
HadWarning |= true;
|
||||
Warnings << "%" << ID << ": Destination had register class " << AssignedClass.Val << " When register class "
|
||||
<< ExpectedClass.Val << " Was expected" << std::endl;
|
||||
Warnings << "%" << ID << ": Destination had register class " << uint32_t(AssignedClass) << " When register class "
|
||||
<< uint32_t(ExpectedClass) << " Was expected" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ $end_info$
|
||||
#include "Interface/IR/Passes.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
#include <FEXCore/fextl/vector.h>
|
||||
@@ -22,7 +23,7 @@ using namespace FEXCore;
|
||||
|
||||
namespace FEXCore::IR {
|
||||
namespace {
|
||||
struct RegisterClass {
|
||||
struct RegisterClassData {
|
||||
uint32_t Available;
|
||||
uint32_t Count;
|
||||
|
||||
@@ -32,9 +33,9 @@ namespace {
|
||||
Ref RegToSSA[32];
|
||||
};
|
||||
|
||||
IR::RegisterClassType GetRegClassFromNode(IR::IRListView* IR, IR::IROp_Header* IROp) {
|
||||
IR::RegisterClassType Class = IR::GetRegClass(IROp->Op);
|
||||
if (Class != IR::ComplexClass) {
|
||||
IR::RegClass GetRegClassFromNode(IR::IRListView* IR, IR::IROp_Header* IROp) {
|
||||
const auto Class = IR::GetRegClass(IROp->Op);
|
||||
if (Class != IR::RegClass::Complex) {
|
||||
return Class;
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ namespace {
|
||||
case IR::OP_LOADMEM:
|
||||
case IR::OP_LOADMEMTSO: return IROp->C<IR::IROp_LoadMem>()->Class;
|
||||
case IR::OP_FILLREGISTER: return IROp->C<IR::IROp_FillRegister>()->Class;
|
||||
default: return IR::InvalidClass;
|
||||
default: return IR::RegClass::Invalid;
|
||||
}
|
||||
};
|
||||
} // Anonymous namespace
|
||||
@@ -56,11 +57,11 @@ public:
|
||||
explicit ConstrainedRAPass(const FEXCore::CPUIDEmu* CPUID)
|
||||
: CPUID {CPUID} {}
|
||||
void Run(IREmitter* IREmit) override;
|
||||
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
|
||||
void AddRegisters(IR::RegClass Class, uint32_t RegisterCount) override;
|
||||
bool TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp);
|
||||
|
||||
private:
|
||||
RegisterClass Classes[IR::NumClasses];
|
||||
RegisterClassData Classes[IR::NumClasses];
|
||||
|
||||
IREmitter* IREmit;
|
||||
IRListView* IR;
|
||||
@@ -101,7 +102,7 @@ private:
|
||||
uint32_t SlotPlusOne = SpillSlots[IR->GetID(Node).Value];
|
||||
LOGMAN_THROW_A_FMT(SlotPlusOne >= 1, "Node must have been spilled");
|
||||
|
||||
RegisterClassType RegClass = GetRegClassFromNode(IR, IROp);
|
||||
const auto RegClass = GetRegClassFromNode(IR, IROp);
|
||||
return IREmit->_FillRegister(IROp->Size, IROp->ElementSize, SlotPlusOne - 1, RegClass);
|
||||
};
|
||||
|
||||
@@ -120,7 +121,7 @@ private:
|
||||
return Op != OP_INLINECONSTANT && Op != OP_INLINEENTRYPOINTOFFSET;
|
||||
};
|
||||
|
||||
RegisterClass* GetClass(PhysicalRegister Reg) {
|
||||
RegisterClassData* GetClass(PhysicalRegister Reg) {
|
||||
return &Classes[Reg.Class];
|
||||
};
|
||||
|
||||
@@ -133,13 +134,13 @@ private:
|
||||
LOGMAN_THROW_A_FMT(ID < SSAToReg.size(), "Only old nodes looked up");
|
||||
|
||||
PhysicalRegister Reg = SSAToReg[ID];
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
|
||||
return (Class->Available & GetRegBits(Reg)) == 0 && Class->RegToSSA[Reg.Reg] == Node;
|
||||
};
|
||||
|
||||
void FreeReg(PhysicalRegister Reg) {
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
LOGMAN_THROW_A_FMT(!(Class->Available & RegBits), "Register double-free");
|
||||
@@ -187,22 +188,22 @@ private:
|
||||
};
|
||||
|
||||
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp, Ref Node) {
|
||||
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
|
||||
uint8_t FlagOffset = Classes[FEXCore::ToUnderlying(RegClass::GPRFixed)].Count - 2;
|
||||
|
||||
if (IROp->Op == OP_STOREREGISTER) {
|
||||
return PhysicalRegister(Node);
|
||||
} else if (IROp->Op == OP_LOADPF || IROp->Op == OP_STOREPF) {
|
||||
return PhysicalRegister {GPRFixedClass, FlagOffset};
|
||||
return PhysicalRegister {RegClass::GPRFixed, FlagOffset};
|
||||
} else if (IROp->Op == OP_LOADAF || IROp->Op == OP_STOREAF) {
|
||||
return PhysicalRegister {GPRFixedClass, (uint8_t)(FlagOffset + 1)};
|
||||
return PhysicalRegister {RegClass::GPRFixed, uint8_t(FlagOffset + 1)};
|
||||
} else {
|
||||
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Class == GPRClass || Op->Class == FPRClass, "SRA classes");
|
||||
if (Op->Class == FPRClass) {
|
||||
return PhysicalRegister {FPRFixedClass, (uint8_t)Op->Reg};
|
||||
LOGMAN_THROW_A_FMT(Op->Class == RegClass::GPR || Op->Class == RegClass::FPR, "SRA classes");
|
||||
if (Op->Class == RegClass::FPR) {
|
||||
return PhysicalRegister {RegClass::FPRFixed, uint8_t(Op->Reg)};
|
||||
} else {
|
||||
return PhysicalRegister {GPRFixedClass, (uint8_t)Op->Reg};
|
||||
return PhysicalRegister {RegClass::GPRFixed, uint8_t(Op->Reg)};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -267,7 +268,7 @@ private:
|
||||
SourceIndex = SourcesNextUses.size();
|
||||
}
|
||||
|
||||
void SpillReg(RegisterClass* Class, IROp_CodeBlock* Block, IROp_Header* Exclude) {
|
||||
void SpillReg(RegisterClassData* Class, IROp_CodeBlock* Block, IROp_Header* Exclude) {
|
||||
// We're about to use next-use information, so calculate it.
|
||||
if (!AnySpilled) {
|
||||
CalculateNextUses(Block, Exclude);
|
||||
@@ -318,7 +319,7 @@ private:
|
||||
// If we already spilled the Candidate, we don't need to spill again.
|
||||
// Similarly, if we can rematerialize the instruction, we don't spill it.
|
||||
if (!Spilled && Header->Op != OP_CONSTANT) {
|
||||
LOGMAN_THROW_A_FMT(Reg.Class == GetRegClassFromNode(IR, Header), "Consistent");
|
||||
LOGMAN_THROW_A_FMT(Reg.AsRegClass() == GetRegClassFromNode(IR, Header), "Consistent");
|
||||
|
||||
// SpillSlots allocation is deferred.
|
||||
if (SpillSlots.empty()) {
|
||||
@@ -329,7 +330,7 @@ private:
|
||||
uint32_t Slot = IR->GetHeader()->SpillSlots++;
|
||||
|
||||
// We must map here in case we're spilling something we shuffled.
|
||||
auto SpillOp = IREmit->_SpillRegister(OrderedNodeWrapper::FromImmediate(Reg.Raw), Slot, RegisterClassType {Reg.Class});
|
||||
auto SpillOp = IREmit->_SpillRegister(OrderedNodeWrapper::FromImmediate(Reg.Raw), Slot, Reg.AsRegClass());
|
||||
SpillOp.first->Header.Size = Header->Size;
|
||||
SpillOp.first->Header.ElementSize = Header->ElementSize;
|
||||
SpillSlots[Value] = Slot + 1;
|
||||
@@ -341,7 +342,7 @@ private:
|
||||
};
|
||||
|
||||
void RemapReg(Ref Node, PhysicalRegister Reg) {
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
Class->RegToSSA[Reg.Reg] = Node;
|
||||
|
||||
uint32_t Index = IR->GetID(Node).Value;
|
||||
@@ -352,7 +353,7 @@ private:
|
||||
|
||||
// Record a given assignment of register Reg to Node.
|
||||
void SetReg(Ref Node, PhysicalRegister Reg) {
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
LOGMAN_THROW_A_FMT((Class->Available & RegBits) == RegBits, "Precondition");
|
||||
@@ -370,7 +371,7 @@ private:
|
||||
// Prioritize preferred registers.
|
||||
if (Node < PreferredReg.size()) {
|
||||
if (PhysicalRegister Reg = PreferredReg[Node]; !Reg.IsInvalid()) {
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
if ((Class->Available & RegBits) == RegBits) {
|
||||
@@ -383,10 +384,10 @@ private:
|
||||
// Try to handle tied registers. This can fail, the JIT will insert moves.
|
||||
if (int TiedIdx = IR::TiedSource(IROp->Op); TiedIdx >= 0) {
|
||||
auto Reg = PhysicalRegister(IROp->Args[TiedIdx]);
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
RegisterClassData* Class = GetClass(Reg);
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
if (Reg.Class != GPRFixedClass && Reg.Class != FPRFixedClass && (Class->Available & RegBits) == RegBits) {
|
||||
if (Reg.AsRegClass() != RegClass::GPRFixed && Reg.AsRegClass() != RegClass::FPRFixed && (Class->Available & RegBits) == RegBits) {
|
||||
SetReg(CodeNode, Reg);
|
||||
return;
|
||||
}
|
||||
@@ -394,7 +395,7 @@ private:
|
||||
|
||||
// Try to coalesce reserved pairs. Just a heuristic to remove some moves.
|
||||
if (IROp->Op == OP_ALLOCATEGPR && IROp->C<IROp_AllocateGPR>()->ForPair) {
|
||||
uint32_t Available = Classes[GPRClass].Available;
|
||||
uint32_t Available = Classes[FEXCore::ToUnderlying(RegClass::GPR)].Available;
|
||||
|
||||
// Only choose base register R if R and R + 1 are both free
|
||||
Available &= (Available >> 1);
|
||||
@@ -405,20 +406,20 @@ private:
|
||||
|
||||
if (Available) {
|
||||
unsigned Reg = std::countr_zero(Available);
|
||||
SetReg(CodeNode, PhysicalRegister(GPRClass, Reg));
|
||||
SetReg(CodeNode, PhysicalRegister(RegClass::GPR, Reg));
|
||||
return;
|
||||
}
|
||||
} else if (IROp->Op == OP_ALLOCATEGPRAFTER) {
|
||||
uint32_t Available = Classes[GPRClass].Available;
|
||||
uint32_t Available = Classes[FEXCore::ToUnderlying(RegClass::GPR)].Available;
|
||||
auto After = PhysicalRegister(IROp->Args[0]);
|
||||
if ((After.Reg & 1) == 0 && Available & (1ull << (After.Reg + 1))) {
|
||||
SetReg(CodeNode, PhysicalRegister(GPRClass, After.Reg + 1));
|
||||
SetReg(CodeNode, PhysicalRegister(RegClass::GPR, After.Reg + 1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterClassType ClassType = GetRegClassFromNode(IR, IROp);
|
||||
RegisterClass* Class = &Classes[ClassType];
|
||||
RegClass ClassType = GetRegClassFromNode(IR, IROp);
|
||||
RegisterClassData* Class = &Classes[FEXCore::ToUnderlying(ClassType)];
|
||||
|
||||
// Spill to make room in the register file.
|
||||
if (!Class->Available) {
|
||||
@@ -433,10 +434,10 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) {
|
||||
void ConstrainedRAPass::AddRegisters(IR::RegClass Class, uint32_t RegisterCount) {
|
||||
LOGMAN_THROW_A_FMT(RegisterCount <= 31, "Up to 31 regs supported");
|
||||
|
||||
Classes[Class].Count = RegisterCount;
|
||||
Classes[FEXCore::ToUnderlying(Class)].Count = RegisterCount;
|
||||
}
|
||||
|
||||
inline bool KillMove(IROp_Header* LastOp, IROp_Header* IROp, Ref LastNode, Ref CodeNode) {
|
||||
@@ -663,7 +664,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
// Static registers must be consistent at SRA load/store. Evict to ensure.
|
||||
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
|
||||
auto Reg = DecodeSRAReg(IROp, CodeNode);
|
||||
RegisterClass* Class = &Classes[Reg.Class];
|
||||
RegisterClassData* Class = &Classes[Reg.Class];
|
||||
|
||||
if (!(Class->Available & (1u << Reg.Reg))) {
|
||||
Ref Old = Class->RegToSSA[Reg.Reg];
|
||||
@@ -678,7 +679,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
|
||||
Ref Copy;
|
||||
|
||||
if (Reg.Class == FPRFixedClass) {
|
||||
if (Reg.AsRegClass() == RegClass::FPRFixed) {
|
||||
IROp_Header* Header = IR->GetOp<IROp_Header>(Old);
|
||||
Copy = IREmit->_VMov(Header->Size, OrderedNodeWrapper::FromImmediate(Reg.Raw));
|
||||
} else {
|
||||
|
||||
@@ -12,11 +12,11 @@ $end_info$
|
||||
#include <stdint.h>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
struct RegisterClassType;
|
||||
enum class RegClass : uint32_t;
|
||||
|
||||
class RegisterAllocationPass : public FEXCore::IR::Pass {
|
||||
public:
|
||||
virtual void AddRegisters(FEXCore::IR::RegisterClassType Class, uint32_t RegisterCount) = 0;
|
||||
virtual void AddRegisters(RegClass Class, uint32_t RegisterCount) = 0;
|
||||
|
||||
// Number of GPRs usable for pairs at start of GPR set. Must be even.
|
||||
uint32_t PairRegs;
|
||||
|
||||
@@ -1014,8 +1014,8 @@ void X87StackOptimization::Run(IREmitter* Emit) {
|
||||
// or similar. As long as the source size and dest size are one and the same.
|
||||
// This will avoid any conversions between source and stack element size and conversion back.
|
||||
if (!SlowPath && Value->Source && Value->Source->Size == Op->StoreSize && Value->InterpretAsFloat) {
|
||||
IREmit->_StoreMem(Value->InterpretAsFloat ? FPRClass : GPRClass, Op->StoreSize, Value->Source->Node, AddrNode, Offset, Align,
|
||||
OffsetType, OffsetScale);
|
||||
const auto ClassType = Value->InterpretAsFloat ? RegClass::FPR : RegClass::GPR;
|
||||
IREmit->_StoreMem(ClassType, Op->StoreSize, Value->Source->Node, AddrNode, Offset, Align, OffsetType, OffsetScale);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ union PhysicalRegister {
|
||||
return Raw == Other.Raw;
|
||||
}
|
||||
|
||||
PhysicalRegister(RegisterClassType Class, uint8_t Reg)
|
||||
PhysicalRegister(RegClass Class, uint8_t Reg)
|
||||
: Reg(Reg)
|
||||
, Class(Class.Val) {}
|
||||
, Class(uint8_t(Class)) {}
|
||||
|
||||
PhysicalRegister(OrderedNodeWrapper Arg)
|
||||
: Raw(Arg.GetImmediate()) {}
|
||||
@@ -29,12 +29,16 @@ union PhysicalRegister {
|
||||
PhysicalRegister(Ref Node)
|
||||
: Raw(Node->Reg) {}
|
||||
|
||||
RegClass AsRegClass() const {
|
||||
return RegClass {Class};
|
||||
}
|
||||
|
||||
static const PhysicalRegister Invalid() {
|
||||
return PhysicalRegister(InvalidClass, 0);
|
||||
return PhysicalRegister(RegClass::Invalid, 0);
|
||||
}
|
||||
|
||||
bool IsInvalid() const {
|
||||
static_assert(InvalidClass == 0);
|
||||
static_assert(uint8_t(RegClass::Invalid) == 0);
|
||||
return Raw == 0;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user