From 49f8332c5baf6d6e610acc84f651cd0e41ffdfb5 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Fri, 16 May 2025 14:54:38 -0400 Subject: [PATCH] JIT: use registers directly from the IR This is the flag day change from the series, using all the new shiny infrastructre we added. Signed-off-by: Alyssa Rosenzweig --- FEXCore/Source/Interface/Core/JIT/JIT.cpp | 35 ++++------- FEXCore/Source/Interface/Core/JIT/JITClass.h | 60 ++++++++++++------- FEXCore/Source/Interface/IR/IRDumper.cpp | 57 +++++++++--------- .../Interface/IR/Passes/IRValidation.cpp | 23 ++++--- .../IR/Passes/RegisterAllocationPass.cpp | 19 ++++++ 5 files changed, 113 insertions(+), 81 deletions(-) diff --git a/FEXCore/Source/Interface/Core/JIT/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/JIT.cpp index d0886a0cf..6ef2a6677 100644 --- a/FEXCore/Source/Interface/Core/JIT/JIT.cpp +++ b/FEXCore/Source/Interface/Core/JIT/JIT.cpp @@ -80,7 +80,7 @@ static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) { namespace FEXCore::CPU { -void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::NodeID Node) { +void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) { FallbackInfo Info; if (!InterpreterOps::GetFallbackHandler(IROp, &Info)) { #if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED @@ -493,7 +493,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame* Fram return HostCode; } -void Arm64JITCore::Op_NoOp(const IR::IROp_Header* IROp, IR::NodeID Node) {} +void Arm64JITCore::Op_NoOp(const IR::IROp_Header* IROp, IR::Ref Node) {} Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread) : CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE) @@ -580,6 +580,10 @@ void Arm64JITCore::ClearCache() { Arm64JITCore::~Arm64JITCore() {} bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const { + if (WNode.IsImmediate()) { + return false; + } + auto OpHeader = IR->GetOp(WNode); if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) { @@ -594,6 +598,10 @@ bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_ } bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const { + if (WNode.IsImmediate()) { + return false; + } + auto OpHeader = IR->GetOp(WNode); if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) { @@ -612,22 +620,6 @@ bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, } } -FEXCore::IR::RegisterClassType Arm64JITCore::GetRegClass(IR::NodeID Node) const { - return FEXCore::IR::RegisterClassType {GetPhys(Node).Class}; -} - -bool Arm64JITCore::IsFPR(IR::NodeID Node) const { - auto Class = GetRegClass(Node); - - return Class == IR::FPRClass || Class == IR::FPRFixedClass; -} - -bool Arm64JITCore::IsGPR(IR::NodeID Node) const { - auto Class = GetRegClass(Node); - - return Class == IR::GPRClass || Class == IR::GPRFixedClass; -} - void Arm64JITCore::EmitInterruptChecks(bool CheckTF) { if (CheckTF) { ARMEmitter::ForwardLabel l_TFUnset; @@ -785,18 +777,17 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size } for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { - const auto ID = IR->GetID(CodeNode); switch (IROp->Op) { #define REGISTER_OP_RT(op, x) \ - case FEXCore::IR::IROps::OP_##op: std::invoke(RT_##x, this, IROp, ID); break + case FEXCore::IR::IROps::OP_##op: std::invoke(RT_##x, this, IROp, CodeNode); break #define REGISTER_OP(op, x) \ - case FEXCore::IR::IROps::OP_##op: Op_##x(IROp, ID); break + case FEXCore::IR::IROps::OP_##op: Op_##x(IROp, CodeNode); break #define IROP_DISPATCH_DISPATCH #include #undef REGISTER_OP - default: Op_Unhandled(IROp, ID); break; + default: Op_Unhandled(IROp, CodeNode); break; } } diff --git a/FEXCore/Source/Interface/Core/JIT/JITClass.h b/FEXCore/Source/Interface/Core/JIT/JITClass.h index 8bf86c984..5c2b96d4e 100644 --- a/FEXCore/Source/Interface/Core/JIT/JITClass.h +++ b/FEXCore/Source/Interface/Core/JIT/JITClass.h @@ -66,9 +66,7 @@ private: fextl::map JumpTargets; [[nodiscard]] - ARMEmitter::Register GetReg(IR::NodeID Node) const { - const auto Reg = GetPhys(Node); - + 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); if (Reg.Class == IR::GPRFixedClass.Val) { @@ -81,14 +79,17 @@ private: } [[nodiscard]] - ARMEmitter::Register GetReg(IR::OrderedNodeWrapper Wrap) const { - return GetReg(Wrap.ID()); + ARMEmitter::Register GetReg(IR::Ref Node) const { + return GetReg(IR::PhysicalRegister(Node)); } [[nodiscard]] - ARMEmitter::VRegister GetVReg(IR::NodeID Node) const { - const auto Reg = GetPhys(Node); + ARMEmitter::Register GetReg(IR::OrderedNodeWrapper Wrap) const { + return GetReg(IR::PhysicalRegister(Wrap)); + } + [[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); if (Reg.Class == IR::FPRFixedClass.Val) { @@ -101,20 +102,18 @@ private: } [[nodiscard]] - ARMEmitter::VRegister GetVReg(IR::OrderedNodeWrapper Wrap) const { - return GetVReg(Wrap.ID()); + ARMEmitter::VRegister GetVReg(IR::Ref Node) const { + return GetVReg(IR::PhysicalRegister(Node)); } [[nodiscard]] - FEXCore::IR::RegisterClassType GetRegClass(IR::NodeID Node) const; + ARMEmitter::VRegister GetVReg(IR::OrderedNodeWrapper Wrap) const { + return GetVReg(IR::PhysicalRegister(Wrap)); + } [[nodiscard]] - IR::PhysicalRegister GetPhys(IR::NodeID Node) const { - auto PhyReg = RAData->GetNodeRegister(Node); - - LOGMAN_THROW_A_FMT(!PhyReg.IsInvalid(), "Couldn't Allocate register for node: ssa{}. Class: {}", Node, PhyReg.Class); - - return PhyReg; + FEXCore::IR::RegisterClassType GetRegClass(IR::Ref Node) const { + return FEXCore::IR::RegisterClassType {IR::PhysicalRegister(Node).Class}; } [[nodiscard]] @@ -234,18 +233,33 @@ private: } [[nodiscard]] - bool IsFPR(IR::NodeID Node) const; + bool IsFPR(IR::RegisterClassType Class) const { + return Class == IR::FPRClass || Class == IR::FPRFixedClass; + } + [[nodiscard]] - bool IsGPR(IR::NodeID Node) const; + bool IsGPR(IR::RegisterClassType Class) const { + return Class == IR::GPRClass || Class == IR::GPRFixedClass; + } + + [[nodiscard]] + bool IsGPR(IR::Ref Node) { + return IsGPR(GetRegClass(Node)); + } + + [[nodiscard]] + bool IsFPR(IR::Ref Node) { + return IsFPR(GetRegClass(Node)); + } [[nodiscard]] bool IsGPR(IR::OrderedNodeWrapper Wrap) { - return IsGPR(Wrap.ID()); + return IsGPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class}); } [[nodiscard]] bool IsFPR(IR::OrderedNodeWrapper Wrap) { - return IsFPR(Wrap.ID()); + return IsFPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class}); } [[nodiscard]] @@ -339,7 +353,7 @@ private: /** @} */ uint32_t SpillSlots {}; - using OpType = void (Arm64JITCore::*)(const IR::IROp_Header* IROp, IR::NodeID Node); + using OpType = void (Arm64JITCore::*)(const IR::IROp_Header* IROp, IR::Ref Node); using ScalarFMAOpCaller = std::function; @@ -366,7 +380,7 @@ private: OpType RT_LoadMemTSO; OpType RT_StoreMemTSO; -#define DEF_OP(x) void Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node) +#define DEF_OP(x) void Op_##x(IR::IROp_Header const* IROp, IR::Ref Node) // Dynamic Dispatcher supporting operations DEF_OP(ParanoidLoadMemTSO); @@ -383,7 +397,7 @@ private: #undef DEF_OP }; -#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node) +#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::Ref Node) [[nodiscard]] fextl::unique_ptr CreateArm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread); diff --git a/FEXCore/Source/Interface/IR/IRDumper.cpp b/FEXCore/Source/Interface/IR/IRDumper.cpp index 7e55159a8..cc11a860b 100644 --- a/FEXCore/Source/Interface/IR/IRDumper.cpp +++ b/FEXCore/Source/Interface/IR/IRDumper.cpp @@ -83,6 +83,26 @@ static void PrintArg(fextl::stringstream* out, [[maybe_unused]] const IRListView } static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg, const IR::RegisterAllocationData* RAData) { + 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; + default: *out << "unknown"; break; + } + + if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) { + *out << std::dec << (uint32_t)PhyReg.Reg; + } + + return; + } + auto [CodeNode, IROp] = IR->at(Arg)(); const auto ArgID = Arg.ID(); @@ -90,25 +110,6 @@ static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNode *out << "%Invalid"; } else { *out << "%" << std::dec << ArgID; - if (RAData) { - auto PhyReg = RAData->GetNodeRegister(ArgID); - - switch (PhyReg.Class) { - case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break; - 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::ComplexClass.Val: *out << "(Complex"; break; - case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break; - default: *out << "(Unknown"; break; - } - - if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) { - *out << std::dec << (uint32_t)PhyReg.Reg << ")"; - } else { - *out << ")"; - } - } } if (GetHasDest(IROp->Op)) { @@ -323,16 +324,16 @@ void Dump(fextl::stringstream* out, const IRListView* IR, const IR::RegisterAllo *out << "%" << std::dec << ID; - if (RAData) { - auto PhyReg = RAData->GetNodeRegister(ID); + auto PhyReg = PhysicalRegister(CodeNode); + if (!PhyReg.IsInvalid()) { switch (PhyReg.Class) { - case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break; - 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::ComplexClass.Val: *out << "(Complex"; break; - case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break; - default: *out << "(Unknown"; break; + 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; + default: *out << "(unknown"; break; } if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) { *out << std::dec << (uint32_t)PhyReg.Reg << ")"; diff --git a/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp b/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp index dfdfc87e6..3879b98ab 100644 --- a/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp +++ b/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp @@ -94,9 +94,9 @@ void IRValidation::Run(IREmitter* IREmit) { Warnings << "%" << ID << ": Destination created but had no uses" << std::endl; } - if (RAData) { - // If we have a register allocator then the destination needs to be assigned a register and class - auto PhyReg = RAData->GetNodeRegister(ID); + if (CurrentIR.PostRA()) { + // 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}; @@ -127,6 +127,10 @@ void IRValidation::Run(IREmitter* IREmit) { for (uint32_t i = 0; i < NumArgs; ++i) { OrderedNodeWrapper Arg = IROp->Args[i]; const auto ArgID = Arg.ID(); + if (Arg.IsImmediate()) { + continue; + } + IROps Op = CurrentIR.GetOp(Arg)->Op; if (ArgID.IsValid()) { @@ -239,11 +243,14 @@ void IRValidation::Run(IREmitter* IREmit) { } } - for (uint32_t i = 0; i < CurrentIR.GetSSACount(); i++) { - auto [Node, IROp] = CurrentIR.at(IR::NodeID {i})(); - if (Node->NumUses != Uses[i] && IROp->Op != OP_CODEBLOCK && IROp->Op != OP_IRHEADER) { - HadError |= true; - Errors << "%" << i << " Has " << Uses[i] << " Uses, but reports " << Node->NumUses << std::endl; + // Use counts are only relevant pre-RA. + if (!CurrentIR.PostRA()) { + for (uint32_t i = 0; i < CurrentIR.GetSSACount(); i++) { + auto [Node, IROp] = CurrentIR.at(IR::NodeID {i})(); + if (Node->NumUses != Uses[i] && IROp->Op != OP_CODEBLOCK && IROp->Op != OP_IRHEADER) { + HadError |= true; + Errors << "%" << i << " Has " << Uses[i] << " Uses, but reports " << Node->NumUses << std::endl; + } } } diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index d3b770548..4d22ac820 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -652,6 +652,25 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) { } LOGMAN_THROW_A_FMT(SourceIndex == 0, "Consistent source count in block"); + + // Finalize results for the block. This will go away. + for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { + for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) { + if (IROp->Args[s].IsInvalid()) { + continue; + } + + auto Reg = SSAToReg[IROp->Args[s].ID().Value]; + + if (!Reg.IsInvalid()) { + IROp->Args[s].SetImmediate(Reg.Raw); + } + } + + if (GetHasDest(IROp->Op)) { + CodeNode->Reg = SSAToReg[IR->GetID(CodeNode).Value].Raw; + } + } } /* Now that we're done growing things, we can finalize our results.