IR: remove pairs

They're now unused. And won't be missed.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
This commit is contained in:
Alyssa Rosenzweig committed 2024-08-14 09:37:06 -04:00
1 parent cab02be637
commit 64a45c0d29
9 files changed
+11 -217

No files matched your search

@@ -41,21 +41,6 @@ DEF_BINOP_WITH_CONSTANT(Lshl, lslv, lsl)
DEF_BINOP_WITH_CONSTANT(Lshr, lsrv, lsr)
DEF_BINOP_WITH_CONSTANT(Ror, rorv, ror)
DEF_OP(TruncElementPair) {
auto Op = IROp->C<IR::IROp_TruncElementPair>();
switch (IROp->Size) {
case 4: {
auto Dst = GetRegPair(Node);
auto Src = GetRegPair(Op->Pair.ID());
mov(ARMEmitter::Size::i32Bit, Dst.first, Src.first);
mov(ARMEmitter::Size::i32Bit, Dst.second, Src.second);
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Truncation size: {}", IROp->Size); break;
}
}
DEF_OP(Constant) {
auto Op = IROp->C<IR::IROp_Constant>();
auto Dst = GetReg(Node);
@@ -1371,11 +1356,6 @@ DEF_OP(Select) {
const auto Src2 = GetReg(Op->Cmp2.ID());
cmp(CompareEmitSize, Src1, Src2);
}
} else if (IsGPRPair(Op->Cmp1.ID())) {
const auto Src1 = GetRegPair(Op->Cmp1.ID());
const auto Src2 = GetRegPair(Op->Cmp2.ID());
cmp(EmitSize, Src1.first, Src2.first);
ccmp(EmitSize, Src1.second, Src2.second, ARMEmitter::StatusFlags::None, cc);
} else if (IsFPR(Op->Cmp1.ID())) {
const auto Src1 = GetVReg(Op->Cmp1.ID());
const auto Src2 = GetVReg(Op->Cmp2.ID());
@@ -654,12 +654,6 @@ bool Arm64JITCore::IsGPR(IR::NodeID Node) const {
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
}
bool Arm64JITCore::IsGPRPair(IR::NodeID Node) const {
auto Class = GetRegClass(Node);
return Class == IR::GPRPairClass;
}
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, const FEXCore::IR::IRListView* IR, FEXCore::Core::DebugData* DebugData,
const FEXCore::IR::RegisterAllocationData* RAData) {
FEXCORE_PROFILE_SCOPED("Arm64::CompileCode");
@@ -114,15 +114,6 @@ private:
FEX_UNREACHABLE;
}
[[nodiscard]]
std::pair<ARMEmitter::Register, ARMEmitter::Register> GetRegPair(IR::NodeID Node) const {
const auto Reg = GetPhys(Node);
LOGMAN_THROW_AA_FMT(Reg.Class == IR::GPRPairClass.Val, "Unexpected Class: {}", Reg.Class);
return std::make_pair(GeneralRegisters[Reg.Reg], GeneralRegisters[Reg.Reg + 1]);
}
[[nodiscard]]
FEXCore::IR::RegisterClassType GetRegClass(IR::NodeID Node) const;
@@ -253,8 +244,6 @@ private:
bool IsFPR(IR::NodeID Node) const;
[[nodiscard]]
bool IsGPR(IR::NodeID Node) const;
[[nodiscard]]
bool IsGPRPair(IR::NodeID Node) const;
[[nodiscard]]
ARMEmitter::ExtendedMemOperand GenerateMemOperand(uint8_t AccessSize, ARMEmitter::Register Base, IR::OrderedNodeWrapper Offset,
@@ -367,30 +367,6 @@ DEF_OP(SpillRegister) {
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize); break;
}
} else if (Op->Class == FEXCore::IR::GPRPairClass) {
const auto Src = GetRegPair(Op->Value.ID());
switch (OpSize) {
case 8: {
if (SlotOffset <= 252 && (SlotOffset & 0b11) == 0) {
stp<ARMEmitter::IndexType::OFFSET>(Src.first.W(), Src.second.W(), ARMEmitter::Reg::rsp, SlotOffset);
} else {
add(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Reg::rsp, SlotOffset);
stp<ARMEmitter::IndexType::OFFSET>(Src.first.W(), Src.second.W(), TMP1, 0);
}
break;
}
case 16: {
if (SlotOffset <= 504 && (SlotOffset & 0b111) == 0) {
stp<ARMEmitter::IndexType::OFFSET>(Src.first.X(), Src.second.X(), ARMEmitter::Reg::rsp, SlotOffset);
} else {
add(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Reg::rsp, SlotOffset);
stp<ARMEmitter::IndexType::OFFSET>(Src.first.X(), Src.second.X(), TMP1, 0);
}
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister(GPRPair) size: {}", OpSize); break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
}
@@ -480,30 +456,6 @@ DEF_OP(FillRegister) {
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize); break;
}
} else if (Op->Class == FEXCore::IR::GPRPairClass) {
const auto Src = GetRegPair(Node);
switch (OpSize) {
case 8: {
if (SlotOffset <= 252 && (SlotOffset & 0b11) == 0) {
ldp<ARMEmitter::IndexType::OFFSET>(Src.first.W(), Src.second.W(), ARMEmitter::Reg::rsp, SlotOffset);
} else {
add(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Reg::rsp, SlotOffset);
ldp<ARMEmitter::IndexType::OFFSET>(Src.first.W(), Src.second.W(), TMP1, 0);
}
break;
}
case 16: {
if (SlotOffset <= 504 && (SlotOffset & 0b111) == 0) {
ldp<ARMEmitter::IndexType::OFFSET>(Src.first.X(), Src.second.X(), ARMEmitter::Reg::rsp, SlotOffset);
} else {
add(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Reg::rsp, SlotOffset);
ldp<ARMEmitter::IndexType::OFFSET>(Src.first.X(), Src.second.X(), TMP1, 0);
}
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister(GPRPair) size: {}", OpSize); break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
}
+2 -17
View File
@@ -44,7 +44,7 @@
" * Textual class to group IR ops by type",
"* DestClass",
" * SSA class of the return when the return type is `SSA`",
" * Not used if the destination type is one of {GPR, GPRPair, FPR}",
" * Not used if the destination type is one of {GPR, FPR}",
"* DestSize",
" * The size of the destination type",
"* EmitValidation",
@@ -81,7 +81,6 @@
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {1}",
"constexpr FEXCore::IR::RegisterClassType FPRClass {2}",
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {3}",
"constexpr FEXCore::IR::RegisterClassType GPRPairClass {4}",
"constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
"constexpr FEXCore::IR::RegisterClassType InvalidClass {7}",
"",
@@ -146,7 +145,6 @@
"OpSize": "FEXCore::IR::OpSize",
"SSA": "OrderedNode*",
"GPR": "OrderedNode*",
"GPRPair": "OrderedNode*",
"FPR": "OrderedNode*",
"FenceType": "FenceType",
"RegisterClass": "RegisterClassType",
@@ -245,12 +243,7 @@
"Print SSA:$Value": {
"HasSideEffects": true,
"Desc": ["Debug operation that prints an SSA value to the console",
"May only print 64bits of the value",
"Depending on backend, may only support GPR printing"
],
"EmitValidation": [
"WalkFindRegClass($Value) != GPRPairClass"
]
"May only print 64bits of the value"]
},
"GPR = AllocateGPR i1:$ForPair": {
"Desc": ["Silly pseudo-instruction to allocate a register for a future destination",
@@ -920,14 +913,6 @@
"DestSize": "8"
},
"GPRPair = TruncElementPair GPRPair:$Pair, u8:#ByteSize": {
"Desc": [
"Truncates each element of a pair to the destination size",
"TODO: This IR op should get removed"
],
"DestSize": "ByteSize * 2",
"NumElements": "2"
},
"GPR = CycleCounter": {
"Desc": ["Returns the host 64bit cycle counter",
"Useful when emulating rdtsc",
-4
View File
@@ -77,8 +77,6 @@ static void PrintArg(fextl::stringstream* out, [[maybe_unused]] const IRListView
*out << "FPR";
} else if (Arg == FPRFixedClass.Val) {
*out << "FPRFixed";
} else if (Arg == GPRPairClass.Val) {
*out << "GPRPair";
} else {
*out << "Unknown Registerclass " << Arg;
}
@@ -100,7 +98,6 @@ static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNode
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
case FEXCore::IR::FPRClass.Val: *out << "(FPR"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "(FPRFixed"; break;
case FEXCore::IR::GPRPairClass.Val: *out << "(GPRPair"; break;
case FEXCore::IR::ComplexClass.Val: *out << "(Complex"; break;
case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break;
default: *out << "(Unknown"; break;
@@ -316,7 +313,6 @@ void Dump(fextl::stringstream* out, const IRListView* IR, IR::RegisterAllocation
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
case FEXCore::IR::FPRClass.Val: *out << "(FPR"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "(FPRFixed"; break;
case FEXCore::IR::GPRPairClass.Val: *out << "(GPRPair"; break;
case FEXCore::IR::ComplexClass.Val: *out << "(Complex"; break;
case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break;
default: *out << "(Unknown"; break;
@@ -38,7 +38,6 @@ FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(Ref Node) {
auto Class = GetOpRegClass(Node);
switch (Class) {
case GPRClass:
case GPRPairClass:
case FPRClass:
case GPRFixedClass:
case FPRFixedClass:
@@ -22,13 +22,11 @@ namespace FEXCore::IR::Validation {
struct RegState {
static constexpr IR::NodeID UninitializedValue {0};
static constexpr IR::NodeID InvalidReg {0xffff'ffff};
static constexpr IR::NodeID CorruptedPair {0xffff'fffe};
// This class makes some assumptions about how the host registers are arranged and mapped to virtual registers:
// 1. There will be less than 32 GPRs and 32 FPRs
// 2. If the GPRFixed class is used, there will be 16 GPRs and 16 FixedGPRs max
// 3. Same with FPRFixed
// 4. If the GPRPairClass is used, it is assumed each GPRPair N will map onto GPRs N and N + 1
// These assumptions were all true for the state of the arm64 and x86 jits at the time this was written
@@ -49,11 +47,6 @@ struct RegState {
// On arm64, there are 16 Fixed and 12 normal
FPRsFixed[Reg.Reg] = ssa;
return true;
case GPRPairClass:
// Alias paired registers onto both
GPRs[Reg.Reg] = ssa;
GPRs[Reg.Reg + 1] = ssa;
return true;
}
return false;
}
@@ -66,12 +59,6 @@ struct RegState {
case GPRFixedClass: return GPRsFixed[Reg.Reg];
case FPRClass: return FPRs[Reg.Reg];
case FPRFixedClass: return FPRsFixed[Reg.Reg];
case GPRPairClass:
// Make sure both halves of the Pair contain the same SSA
if (GPRs[Reg.Reg] == GPRs[Reg.Reg + 1]) {
return GPRs[Reg.Reg];
}
return CorruptedPair;
}
return InvalidReg;
}
@@ -139,14 +126,6 @@ void RAValidation::Run(IREmitter* IREmit) {
if (CurrentSSAAtReg == RegState::InvalidReg) {
HadError |= true;
Errors << fextl::fmt::format("%{}: Arg[{}] unknown Reg: {}, class: {}\n", ID, i, PhyReg.Reg, PhyReg.Class);
} else if (CurrentSSAAtReg == RegState::CorruptedPair) {
HadError |= true;
auto Lower = BlockRegState.Get(PhysicalRegister(GPRClass, uint8_t(PhyReg.Reg * 2) + 1));
auto Upper = BlockRegState.Get(PhysicalRegister(GPRClass, PhyReg.Reg * 2 + 1));
Errors << fextl::fmt::format("%{}: Arg[{}] expects paired reg{} to contain %{}, but it actually contains {{%{}, %{}}}\n", ID, i,
PhyReg.Reg, ArgID, Lower, Upper);
} else if (CurrentSSAAtReg == RegState::UninitializedValue) {
HadError |= true;
@@ -185,11 +185,11 @@ private:
};
RegisterClass* GetClass(PhysicalRegister Reg) {
return &Classes[(Reg.Class == GPRPairClass) ? GPRClass : Reg.Class];
return &Classes[Reg.Class];
};
uint32_t GetRegBits(PhysicalRegister Reg) {
return ((Reg.Class == GPRPairClass) ? 0b11 : 0b1) << Reg.Reg;
return 1 << Reg.Reg;
};
bool IsInRegisterFile(Ref Old) {
@@ -273,7 +273,7 @@ private:
// the next set bit and then clearing on each iteration.
#define foreach_bit(b, x) for (uint32_t __x = (x), b; ((b) = __builtin_ffs(__x) - 1, __x); __x &= ~(1 << (b)))
void SpillReg(RegisterClass* Class, IROp_Header* Exclude, bool Pair) {
void SpillReg(RegisterClass* Class, IROp_Header* Exclude) {
// Find the best node to spill according to the "furthest-first" heuristic.
// Since we defined IPs relative to the end of the block, the furthest
// next-use has the /smallest/ unsigned IP.
@@ -282,12 +282,6 @@ private:
uint8_t BestReg = ~0;
foreach_bit(i, Class->Allocated) {
// We have to prioritize the pair region if we're allocating for a Pair.
// See the comment at the call site in AssignReg.
if (Pair && Candidate != nullptr && i >= PairRegs) {
break;
}
Ref Old = Class->RegToSSA[i];
LOGMAN_THROW_AA_FMT(Old != nullptr, "Invariant3");
@@ -431,94 +425,20 @@ private:
}
RegisterClassType OrigClassType = GetRegClassFromNode(IR, IROp);
bool Pair = OrigClassType == GPRPairClass;
RegisterClassType ClassType = Pair ? GPRClass : OrigClassType;
RegisterClassType ClassType = OrigClassType;
RegisterClass* Class = &Classes[ClassType];
// Spill to make room in the register file. Free registers need not be
// contiguous, we'll shuffle later.
//
// There is one subtlety: when allocating a pair, we need at least 1 free
// register in the pair region. Else, we could end up trying to allocate a
// pair when the only free 2 regs are outside the pair region, and the pair
// region is made of all pairs (so nothing to shuffle). With 1 free
// register in the pair region, we'll be able to shuffle.
//
// When spilling for pairs, SpillReg prioritizes spilling the pair region
// which ensures this loop is well-behaved.
while (std::popcount(Class->Available) < (Pair ? 2 : 1) || (Pair && !(Class->Available & ((1u << PairRegs) - 1)))) {
if (!Class->Available) {
IREmit->SetWriteCursorBefore(CodeNode);
SpillReg(Class, Pivot, Pair);
SpillReg(Class, Pivot);
}
// There are now enough free registers, but they may be fragmented.
// Pick a scalar blocking a pair and shuffle to make room.
uint32_t Available = AvailableMask(Class, Pair);
if (!Available) {
LOGMAN_THROW_A_FMT(OrigClassType == GPRPairClass, "Already spilled");
// Find the first free scalar. There are at least 2.
unsigned Hole = std::countr_zero(Class->Available);
LOGMAN_THROW_AA_FMT(Class->Available & (1u << Hole), "Definition");
// Its neighbour is blocking the pair.
unsigned Blocked = Hole ^ 1;
LOGMAN_THROW_AA_FMT(!(Class->Available & (1u << Blocked)), "Invariant7");
LOGMAN_THROW_AA_FMT(Hole < PairRegs, "Pairable register");
// Find another free scalar to evict the neighbour
unsigned NewReg = std::countr_zero(Class->Available & ~(1u << Hole));
LOGMAN_THROW_AA_FMT(Class->Available & (1u << NewReg), "Ensured space");
IREmit->SetWriteCursorBefore(CodeNode);
Ref Old = Class->RegToSSA[Blocked];
LOGMAN_THROW_A_FMT(GetRegClassFromNode(IR, IR->GetOp<IROp_Header>(Old)) == GPRClass, "Only scalars have free neighbours");
FreeReg(PhysicalRegister(GPRClass, Blocked));
Ref Clobber = nullptr;
// If that scalar is free because it is killed by this instruction, it
// needs to be shuffled too, since the copy would clobber it.
for (auto s = 0; s < IR::GetRAArgs(Pivot->Op); ++s) {
// It is possible that the argument is to be remapped, but the actual
// remapping in the IR only happens later in the pass so we need to
// Map() explicitly. This can be hit with SRA shuffles.
Ref New = Map(IR->GetNode(Pivot->Args[s]));
const PhysicalRegister ClobberReg = SSAToReg[IR->GetID(New).Value];
if (ClobberReg.Class == GPRClass && ClobberReg.Reg == NewReg) {
Clobber = IR->GetNode(Pivot->Args[s]);
break;
}
}
if (Clobber) {
// Swap the registers.
LOGMAN_THROW_A_FMT(IsOld(Clobber), "Not yet mapped");
auto ClobberNew = IREmit->_Swap1(Map(Clobber), Map(Old));
Remap(Clobber, ClobberNew);
auto New = IREmit->_Swap2();
Remap(Old, New);
SetReg(New, PhysicalRegister(GPRClass, NewReg));
SetReg(ClobberNew, PhysicalRegister(GPRClass, Blocked));
FreeReg(PhysicalRegister(GPRClass, Blocked));
} else {
// Otherwise, simply copy.
auto Copy = IREmit->_Copy(Map(Old));
Remap(Old, Copy);
SetReg(Copy, PhysicalRegister(GPRClass, NewReg));
}
Available = AvailableMask(Class, Pair);
}
LOGMAN_THROW_AA_FMT(Available != 0, "Post-condition of spill and shuffle");
// Assign a free register in the appropriate class.
uint32_t Available = AvailableMask(Class, false);
LOGMAN_THROW_AA_FMT(Available != 0, "Post-condition of spilling");
unsigned Reg = std::countr_zero(Available);
SetReg(CodeNode, PhysicalRegister(OrigClassType, Reg));
};