From 6a314bc9cd800d5d960195e1529511050790a8a1 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sat, 15 Jun 2024 11:40:46 -0400 Subject: [PATCH 1/2] RegisterAllocationPass: prioritize remat over spilling No instcountci changes yet, since nothing currently spills in instcountci. This mitigates spilling later seen with #3703, and should help for certain pathological blocks even without those changes (maybe we should try to get some of those blocks in instcountci?). Signed-off-by: Alyssa Rosenzweig --- .../IR/Passes/RegisterAllocationPass.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index 93aa5a1b8..130b9beb9 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -135,12 +135,16 @@ private: // Maps Old defs to their assigned spill slot + 1, or 0 if not spilled. fextl::vector SpillSlots; + bool Rematerializable(IROp_Header* IROp) { + return IROp->Op == OP_CONSTANT; + } + Ref InsertFill(Ref Old) { LOGMAN_THROW_A_FMT(IsOld(Old), "Precondition"); IROp_Header* IROp = IR->GetOp(Old); // Remat if we can - if (IROp->Op == OP_CONSTANT) { + if (Rematerializable(IROp)) { uint64_t Const = IROp->C()->Constant; return IREmit->_Constant(Const); } @@ -269,8 +273,6 @@ private: // 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. - // - // TODO: Prioritize constants, as they are cheaper to rematerialize. Ref Candidate = nullptr; uint32_t BestDistance = UINT32_MAX; uint8_t BestReg = ~0; @@ -291,6 +293,13 @@ private: // Skip any source used by the current instruction, it is unspillable. if (!HasSource(Exclude, Old)) { uint32_t NextUse = NextUses[IR->GetID(Old).Value]; + + // Prioritize remat over spilling. It is typically cheaper to remat a + // constant multiple times than to spill a single value. + if (!Rematerializable(IR->GetOp(Old))) { + NextUse += 100000; + } + if (NextUse < BestDistance) { BestDistance = NextUse; BestReg = i; From 9443b180768fe3fce282e8ee31ff4f2664698b94 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sat, 15 Jun 2024 14:13:23 -0400 Subject: [PATCH 2/2] RegisterAllocationPass: optimize spill loop Signed-off-by: Alyssa Rosenzweig --- .../IR/Passes/RegisterAllocationPass.cpp | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index 130b9beb9..9413fe90f 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -269,6 +269,10 @@ private: } }; + // Helper macro to walk the set bits b in a 32-bit word x, using ffs to get + // 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) { // 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 @@ -277,34 +281,32 @@ private: uint32_t BestDistance = UINT32_MAX; uint8_t BestReg = ~0; - for (int i = 0; i < Class->Count; ++i) { + 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; } - if (Class->Allocated & (1u << i)) { - Ref Old = Class->RegToSSA[i]; + Ref Old = Class->RegToSSA[i]; - LOGMAN_THROW_AA_FMT(Old != nullptr, "Invariant3"); - LOGMAN_THROW_A_FMT(SSAToReg[IR->GetID(Map(Old)).Value].Reg == i, "Invariant4"); + LOGMAN_THROW_AA_FMT(Old != nullptr, "Invariant3"); + LOGMAN_THROW_A_FMT(SSAToReg[IR->GetID(Map(Old)).Value].Reg == i, "Invariant4"); - // Skip any source used by the current instruction, it is unspillable. - if (!HasSource(Exclude, Old)) { - uint32_t NextUse = NextUses[IR->GetID(Old).Value]; + // Skip any source used by the current instruction, it is unspillable. + if (!HasSource(Exclude, Old)) { + uint32_t NextUse = NextUses[IR->GetID(Old).Value]; - // Prioritize remat over spilling. It is typically cheaper to remat a - // constant multiple times than to spill a single value. - if (!Rematerializable(IR->GetOp(Old))) { - NextUse += 100000; - } + // Prioritize remat over spilling. It is typically cheaper to remat a + // constant multiple times than to spill a single value. + if (!Rematerializable(IR->GetOp(Old))) { + NextUse += 100000; + } - if (NextUse < BestDistance) { - BestDistance = NextUse; - BestReg = i; - Candidate = Old; - } + if (NextUse < BestDistance) { + BestDistance = NextUse; + BestReg = i; + Candidate = Old; } } }