diff --git a/External/FEXCore/Scripts/json_ir_generator.py b/External/FEXCore/Scripts/json_ir_generator.py index fedf36c15..3ef6fe3da 100644 --- a/External/FEXCore/Scripts/json_ir_generator.py +++ b/External/FEXCore/Scripts/json_ir_generator.py @@ -40,10 +40,6 @@ def print_ir_structs(ops, defines): output_file.write("};\n\n"); - output_file.write("struct __attribute__((packed)) IROp_Empty {\n") - output_file.write("\tIROp_Header Header;\n") - output_file.write("};\n\n") - output_file.write("// User defined IR Op structs\n") for op_key, op_vals in ops.items(): SSAArgs = 0 @@ -59,34 +55,33 @@ def print_ir_structs(ops, defines): if ("SSANames" in op_vals and len(op_vals["SSANames"]) != 0): HasSSANames = True - if (HasArgs or SSAArgs != 0): - output_file.write("struct __attribute__((packed)) IROp_%s {\n" % op_key) - output_file.write("\tIROp_Header Header;\n\n") + output_file.write("struct __attribute__((packed)) IROp_%s {\n" % op_key) + output_file.write("\tIROp_Header Header;\n\n") - # SSA arguments have a hard requirement to appear after the header - if (SSAArgs != 0): - if (HasSSANames): - for i in range(0, SSAArgs): - output_file.write("\tOrderedNodeWrapper %s;\n" % (op_vals["SSANames"][i])); + # SSA arguments have a hard requirement to appear after the header + if (SSAArgs != 0): + if (HasSSANames): + for i in range(0, SSAArgs): + output_file.write("\tOrderedNodeWrapper %s;\n" % (op_vals["SSANames"][i])); - else: - output_file.write("private:\n") - for i in range(0, SSAArgs): - output_file.write("\tuint64_t : (sizeof(OrderedNodeWrapper) * 8);\n"); - output_file.write("public:\n") + else: + output_file.write("private:\n") + for i in range(0, SSAArgs): + output_file.write("\tuint64_t : (sizeof(OrderedNodeWrapper) * 8);\n"); + output_file.write("public:\n") - if (HasArgs): - output_file.write("\t// User defined data\n") + if (HasArgs): + output_file.write("\t// User defined data\n") - # Print out arguments in IR Op - for i in range(0, len(op_vals["Args"]), 2): - data_type = op_vals["Args"][i] - data_name = op_vals["Args"][i+1] - output_file.write("\t%s %s;\n" % (data_type, data_name)) + # Print out arguments in IR Op + for i in range(0, len(op_vals["Args"]), 2): + data_type = op_vals["Args"][i] + data_name = op_vals["Args"][i+1] + output_file.write("\t%s %s;\n" % (data_type, data_name)) - output_file.write("};\n") - else: - output_file.write("using IROp_%s = IROp_Empty;\n" % op_key) + output_file.write("\tstatic constexpr IROps OPCODE = OP_%s;\n" % op_key.upper()) + + output_file.write("};\n") # Add a static assert that the IR ops must be pod output_file.write("static_assert(std::is_pod::value);\n\n" % op_key) @@ -307,7 +302,6 @@ def print_ir_allocator_helpers(ops, defines): output_file.write("\tuint8_t GetOpSize(OrderedNode *Op) const {\n") output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n") - output_file.write("\t\tLogMan::Throw::A(HeaderOp->HasDest, \"Op %s has no dest\\n\", GetName(HeaderOp->Op));\n") output_file.write("\t\treturn HeaderOp->Size;\n") output_file.write("\t}\n\n") diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index 3e2a51b5a..c9e3a1ea6 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -94,13 +94,8 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { static_assert(sizeof(FEXCore::IR::IROp_Header) == 4); static_assert(sizeof(FEXCore::IR::OrderedNode) == 16); - auto HeaderIterator = CurrentIR->begin(); - IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); - IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); - auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - - IR::OrderedNode const *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); + auto BlockIterator = CurrentIR->GetBlocks().begin(); + auto BlockEnd = CurrentIR->GetBlocks().end(); // Allocate 16 bytes per SSA void *SSAData = alloca(ListSize * 16); @@ -111,14 +106,14 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { #define GD *GetDest(SSAData, WrapperOp) #define GDP GetDest(SSAData, WrapperOp) auto GetOpSize = [&](IR::OrderedNodeWrapper Node) { - FEXCore::IR::OrderedNode const *RealNode = Node.GetNode(ListBegin); - FEXCore::IR::IROp_Header const *IROp = RealNode->Op(DataBegin); + auto IROp = CurrentIR->GetOp(Node); return IROp->Size; }; while (1) { using namespace FEXCore::IR; - auto BlockIROp = BlockNode->Op(DataBegin)->C(); + auto [BlockNode, BlockHeader] = BlockIterator(); + auto BlockIROp = BlockHeader->CW(); LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); // We grab these nodes this way so we can iterate easily @@ -129,13 +124,11 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { bool Redo; } BlockResults{}; - auto HandleBlock = [&]() { - while (1) { - OrderedNodeWrapper WrapperOp = *CodeBegin(); - OrderedNode *RealNode = WrapperOp.GetNode(ListBegin); - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + auto HandleBlock = [&](OrderedNode *BlockNode) { + for (auto [CodeNode, IROp] : CurrentIR->GetCode(BlockNode)) { + OrderedNodeWrapper WrapperOp = CodeNode->Wrapped(ListBegin); uint8_t OpSize = IROp->Size; - uint32_t Node = WrapperOp.ID(); + uint32_t Node = CurrentIR->GetID(CodeNode); switch (IROp->Op) { case IR::OP_VALIDATECODE: { @@ -157,12 +150,8 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { case IR::OP_DUMMY: case IR::OP_BEGINBLOCK: + case IR::OP_ENDBLOCK: break; - case IR::OP_ENDBLOCK: { - auto Op = IROp->C(); - Thread->State.State.rip += Op->RIPIncrement; - break; - } case IR::OP_FENCE: { auto Op = IROp->C(); switch (Op->Fence) { @@ -187,10 +176,10 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { auto Op = IROp->C(); uint64_t Arg = *GetSrc(SSAData, Op->Header.Args[0]); if (!!Arg) { - BlockNode = Op->Header.Args[1].GetNode(ListBegin); + BlockIterator = NodeIterator(ListBegin, DataBegin, Op->Header.Args[1]); } else { - BlockNode = Op->Header.Args[2].GetNode(ListBegin); + BlockIterator = NodeIterator(ListBegin, DataBegin, Op->Header.Args[2]); } BlockResults.Redo = true; return; @@ -198,7 +187,7 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { } case IR::OP_JUMP: { auto Op = IROp->C(); - BlockNode = Op->Header.Args[0].GetNode(ListBegin); + BlockIterator = NodeIterator(ListBegin, DataBegin, Op->Header.Args[0]); BlockResults.Redo = true; return; break; @@ -4272,16 +4261,14 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { } }; - HandleBlock(); + HandleBlock(BlockNode); if (BlockResults.Redo) { continue; } - if (BlockIROp->Next.ID() == 0 || BlockResults.Quit) { + if (BlockResults.Quit || ++BlockIterator == BlockEnd) { break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index 6d1bf701e..55fdd45aa 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -577,15 +577,8 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const JumpTargets.clear(); CurrentIR = IR; uint32_t SSACount = CurrentIR->GetSSACount(); - uintptr_t ListBegin = CurrentIR->GetListData(); - uintptr_t DataBegin = CurrentIR->GetData(); - - auto HeaderIterator = CurrentIR->begin(); - IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); - IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); - auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + auto HeaderOp = CurrentIR->GetHeader(); if (HeaderOp->ShouldInterpret) { return reinterpret_cast(InterpreterFallbackHelperAddress); } @@ -633,19 +626,13 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const stp(TMP1, lr, MemOperand(sp, -16, PreIndex)); } - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { + for (auto [BlockNode, BlockHeader] : CurrentIR->GetBlocks()) { using namespace FEXCore::IR; - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); + auto BlockIROp = BlockHeader->CW(); LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR->at(BlockIROp->Begin); - auto CodeLast = CurrentIR->at(BlockIROp->Last); - { - uint32_t Node = BlockNode->Wrapped(ListBegin).ID(); + uint32_t Node = CurrentIR->GetID(BlockNode); auto IsTarget = JumpTargets.find(Node); if (IsTarget == JumpTargets.end()) { IsTarget = JumpTargets.try_emplace(Node).first; @@ -654,26 +641,12 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const bind(&IsTarget->second); } - while (1) { - OrderedNodeWrapper *WrapperOp = CodeBegin(); - FEXCore::IR::IROp_Header *IROp = WrapperOp->GetNode(ListBegin)->Op(DataBegin); - uint32_t Node = WrapperOp->ID(); + for (auto [CodeNode, IROp] : CurrentIR->GetCode(BlockNode)) { + uint32_t ID = CurrentIR->GetID(CodeNode); // Execute handler OpHandler Handler = OpHandlers[IROp->Op]; - (this->*Handler)(IROp, Node); - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); + (this->*Handler)(IROp, ID); } } diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp index 25fd7a0dd..ae0022195 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp @@ -6,15 +6,6 @@ using namespace vixl; using namespace vixl::aarch64; #define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node) -DEF_OP(EndBlock) { - auto Op = IROp->C(); - if (Op->RIPIncrement) { - ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CPUState, rip))); - add(TMP1, TMP1, Operand(Op->RIPIncrement)); - str(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CPUState, rip))); - } -} - DEF_OP(Fence) { auto Op = IROp->C(); switch (Op->Fence) { @@ -68,7 +59,7 @@ void JITCore::RegisterMiscHandlers() { REGISTER_OP(IRHEADER, NoOp); REGISTER_OP(CODEBLOCK, NoOp); REGISTER_OP(BEGINBLOCK, NoOp); - REGISTER_OP(ENDBLOCK, EndBlock); + REGISTER_OP(ENDBLOCK, NoOp); REGISTER_OP(FENCE, Fence); REGISTER_OP(BREAK, Break); REGISTER_OP(PHI, NoOp); diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp index 4755b020c..0a2927066 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp @@ -419,15 +419,8 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const JumpTargets.clear(); CurrentIR = IR; uint32_t SSACount = CurrentIR->GetSSACount(); - uintptr_t ListBegin = CurrentIR->GetListData(); - uintptr_t DataBegin = CurrentIR->GetData(); - - auto HeaderIterator = CurrentIR->begin(); - IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); - IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); - auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + auto HeaderOp = CurrentIR->GetHeader(); if (HeaderOp->ShouldInterpret) { return InterpreterFallbackHelperAddress; } @@ -514,18 +507,14 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const ret(); }; - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - while (1) { + for (auto [BlockNode, BlockHeader] : CurrentIR->GetBlocks()) { using namespace FEXCore::IR; - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR->at(BlockIROp->Begin); - auto CodeLast = CurrentIR->at(BlockIROp->Last); { - uint32_t Node = BlockNode->Wrapped(ListBegin).ID(); + auto BlockIROp = BlockHeader->CW(); + LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); + + uint32_t Node = CurrentIR->GetID(BlockNode); auto IsTarget = JumpTargets.find(Node); if (IsTarget == JumpTargets.end()) { IsTarget = JumpTargets.try_emplace(Node).first; @@ -534,12 +523,9 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const L(IsTarget->second); } - while (1) { - OrderedNodeWrapper *WrapperOp = CodeBegin(); - OrderedNode *RealNode = WrapperOp->GetNode(ListBegin); - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + for (auto [CodeNode, IROp] : CurrentIR->GetCode(BlockNode)) { uint8_t OpSize = IROp->Size; - uint32_t Node = WrapperOp->ID(); + uint32_t Node = CurrentIR->GetID(CodeNode); #ifdef DEBUG_RA if (IROp->Op != IR::OP_BEGINBLOCK && @@ -578,24 +564,6 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const #endif switch (IROp->Op) { - case IR::OP_BEGINBLOCK: { - auto IsTarget = JumpTargets.find(Node); - if (IsTarget == JumpTargets.end()) { - IsTarget = JumpTargets.try_emplace(Node).first; - } - else { - } - - L(IsTarget->second); - break; - } - case IR::OP_ENDBLOCK: { - auto Op = IROp->C(); - if (Op->RIPIncrement) { - add(qword [STATE + offsetof(FEXCore::Core::CPUState, rip)], Op->RIPIncrement); - } - break; - } case IR::OP_EXITFUNCTION: { RegularExit(); break; @@ -4952,6 +4920,8 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const break; } case IR::OP_DUMMY: + case IR::OP_BEGINBLOCK: + case IR::OP_ENDBLOCK: case IR::OP_IRHEADER: case IR::OP_PHIVALUE: case IR::OP_PHI: @@ -4960,18 +4930,6 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data()); break; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 44bf35429..2c707414e 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -4027,12 +4027,6 @@ void OpDispatchBuilder::CreateJumpBlocks(std::vector } static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWrapper Arg, IR::RegisterAllocationPass *RAPass) { - uintptr_t Data = IR->GetData(); - uintptr_t ListBegin = IR->GetListData(); - - OrderedNode *RealNode = Arg.GetNode(ListBegin); - auto IROp = RealNode->Op(Data); + auto [CodeNode, IROp] = IR->at(Arg)(); *out << "%ssa" << std::to_string(Arg.ID()); if (RAPass) { @@ -112,17 +108,8 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView } void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationPass *RAPass) { - uintptr_t ListBegin = IR->GetListData(); - uintptr_t DataBegin = IR->GetData(); + auto HeaderOp = IR->GetHeader(); - auto Begin = IR->begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); int8_t CurrentIndent = 0; auto AddIndent = [&out, &CurrentIndent]() { for (uint8_t i = 0; i < CurrentIndent; ++i) { @@ -132,31 +119,25 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc ++CurrentIndent; AddIndent(); - *out << "(%ssa" << std::to_string(RealNode->Wrapped(ListBegin).ID()) << ") " << "IRHeader "; + *out << "(%ssa0) " << "IRHeader "; *out << "#0x" << std::hex << HeaderOp->Entry << ", "; *out << "%ssa" << HeaderOp->Blocks.ID() << ", "; *out << "#" << std::dec << HeaderOp->BlockCount << std::endl; - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) { + { + auto BlockIROp = BlockHeader->C(); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR->at(BlockIROp->Begin); - auto CodeLast = IR->at(BlockIROp->Last); + AddIndent(); + *out << "(%ssa" << std::to_string(IR->GetID(BlockNode)) << ") " << "CodeBlock "; - AddIndent(); - *out << "(%ssa" << std::to_string(BlockNode->Wrapped(ListBegin).ID()) << ") " << "CodeBlock "; - - *out << "%ssa" << std::to_string(BlockIROp->Begin.ID()) << ", "; - *out << "%ssa" << std::to_string(BlockIROp->Last.ID()) << ", "; - *out << "%ssa" << std::to_string(BlockIROp->Next.ID()) << std::endl; + *out << "%ssa" << std::to_string(BlockIROp->Begin.ID()) << ", "; + *out << "%ssa" << std::to_string(BlockIROp->Last.ID()) << std::endl; + } ++CurrentIndent; - while (1) { - OrderedNodeWrapper *CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { + uint32_t ID = IR->GetID(CodeNode); auto Name = FEXCore::IR::GetName(IROp->Op); bool Skip{}; @@ -177,10 +158,10 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc } uint32_t NumElements = IROp->Size / ElementSize; - *out << "%ssa" << std::to_string(CodeOp->ID()); + *out << "%ssa" << std::to_string(ID); if (RAPass) { - uint64_t RegClass = RAPass->GetNodeRegister(CodeOp->ID()); + uint64_t RegClass = RAPass->GetNodeRegister(ID); FEXCore::IR::RegisterClassType Class {uint32_t(RegClass >> 32)}; uint32_t Reg = RegClass; switch (Class) { @@ -211,7 +192,7 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc NumElements = IROp->Size / ElementSize; } - *out << "(%ssa" << std::to_string(CodeOp->ID()) << " "; + *out << "(%ssa" << std::to_string(ID) << " "; *out << "i" << std::dec << (ElementSize * 8); if (NumElements > 1) { *out << "v" << std::dec << NumElements; @@ -219,14 +200,6 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc *out << ") "; } *out << Name; - switch (IROp->Op) { - case IR::OP_BEGINBLOCK: - *out << " %ssa" << std::to_string(CodeOp->ID()); - break; - case IR::OP_ENDBLOCK: - break; - default: break; - } #define IROP_ARGPRINTER_HELPER #include @@ -236,19 +209,18 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc *out << " "; while (NodeBegin != NodeBegin.Invalid()) { - OrderedNodeWrapper *NodeOp = NodeBegin(); - OrderedNode *NodeNode = NodeOp->GetNode(ListBegin); - auto IRNodeOp = NodeNode->Op(DataBegin)->C(); + auto [NodeNode, IROp] = NodeBegin(); + auto PhiOp = IROp->C(); *out << "[ "; - PrintArg(out, IR, IRNodeOp->Value, RAPass); + PrintArg(out, IR, PhiOp->Value, RAPass); *out << ", "; - PrintArg(out, IR, IRNodeOp->Block, RAPass); + PrintArg(out, IR, PhiOp->Block, RAPass); *out << " ]"; - if (IRNodeOp->Next.ID()) + if (PhiOp->Next.ID()) *out << ", "; - NodeBegin = IR->at(IRNodeOp->Next); + NodeBegin = IR->at(PhiOp->Next); } break; } @@ -259,21 +231,9 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAlloc *out << "\n"; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; } CurrentIndent = std::max(0, CurrentIndent - 1); - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); - } } } diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index c615bd7df..00033f81d 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -65,24 +65,32 @@ "CodeBlock": { "OpClass": "Misc", "SwitchGen": false, - "SSAArgs": "3", + "SSAArgs": "2", "RAOverride": "0", "SSANames": [ "Begin", - "Last", - "Next" + "Last" ] }, "BeginBlock": { - "OpClass": "Misc" + "HasSideEffects": true, + "OpClass": "Misc", + "SwitchGen": false, + "SSAArgs": "1", + "RAOverride": "0", + "SSANames": [ + "BlockHeader" + ] }, "EndBlock": { "HasSideEffects": true, "OpClass": "Misc", "SwitchGen": false, - "Args": [ - "uint64_t", "RIPIncrement" + "SSAArgs": "1", + "RAOverride": "0", + "SSANames": [ + "BlockHeader" ] }, diff --git a/External/FEXCore/Source/Interface/IR/IREmitter.cpp b/External/FEXCore/Source/Interface/IR/IREmitter.cpp index 995b347ff..6dae5ee0a 100644 --- a/External/FEXCore/Source/Interface/IR/IREmitter.cpp +++ b/External/FEXCore/Source/Interface/IR/IREmitter.cpp @@ -11,18 +11,16 @@ void IREmitter::ResetWorkingList() { CurrentCodeBlock = nullptr; } -void IREmitter::ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End) { +void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator After, AllNodesIterator End) { uintptr_t ListBegin = ListData.Begin(); - uintptr_t DataBegin = Data.Begin(); + auto NodeId = Node->Wrapped(ListBegin).ID(); while (After != End) { - OrderedNodeWrapper *WrapperOp = After(); - OrderedNode *RealNode = WrapperOp->GetNode(ListBegin); - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + auto [RealNode, IROp] = After(); uint8_t NumArgs = IR::GetArgs(IROp->Op); for (uint8_t i = 0; i < NumArgs; ++i) { - if (IROp->Args[i].ID() == Node->Wrapped(ListBegin).ID()) { + if (IROp->Args[i].ID() == NodeId) { Node->RemoveUse(); NewNode->AddUse(); IROp->Args[i].NodeOffset = NewNode->Wrapped(ListBegin).NodeOffset; @@ -66,16 +64,9 @@ void IREmitter::Remove(OrderedNode *Node) { IREmitter::IRPair IREmitter::CreateNewCodeBlock() { auto OldCursor = GetWriteCursor(); - SetWriteCursor(CodeBlocks.back()); auto CodeNode = CreateCodeNode(); - auto NewNode = _Dummy(); - SetCodeNodeBegin(CodeNode, NewNode); - - auto EndBlock = _EndBlock(0); - SetCodeNodeLast(CodeNode, EndBlock); - if (CurrentCodeBlock) { LinkCodeBlocks(CurrentCodeBlock, CodeNode); } diff --git a/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index 4a55c3460..a5620af5b 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -17,239 +17,204 @@ static uint64_t getMask(T Op) { bool ConstProp::Run(IREmitter *IREmit) { bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); auto OriginalWriteCursor = IREmit->GetWriteCursor(); - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - switch (IROp->Op) { + switch (IROp->Op) { /* - case OP_UMUL: - case OP_DIV: - case OP_UDIV: - case OP_REM: - case OP_UREM: - case OP_MULH: - case OP_UMULH: - case OP_LSHR: - case OP_ASHR: - case OP_ROL: - case OP_ROR: - case OP_LDIV: - case OP_LUDIV: - case OP_LREM: - case OP_LUREM: - case OP_BFI: - { - uint64_t Constant1; - uint64_t Constant2; + case OP_UMUL: + case OP_DIV: + case OP_UDIV: + case OP_REM: + case OP_UREM: + case OP_MULH: + case OP_UMULH: + case OP_LSHR: + case OP_ASHR: + case OP_ROL: + case OP_ROR: + case OP_LDIV: + case OP_LUDIV: + case OP_LREM: + case OP_LUREM: + case OP_BFI: + { + uint64_t Constant1; + uint64_t Constant2; - if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && - IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { - LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str()); - } - break; + if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && + IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { + LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str()); } - - case OP_SEXT: - case OP_NEG: - case OP_POPCOUNT: - case OP_FINDLSB: - case OP_FINDMSB: - case OP_REV: - case OP_SBFE: - { - uint64_t Constant1; - - if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) { - LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str()); - } - break; - } -*/ - case OP_ADD: { - auto Op = IROp->C(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = (Constant1 + Constant2) & getMask(Op) ; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } - break; - } - case OP_SUB: { - auto Op = IROp->C(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = (Constant1 - Constant2) & getMask(Op) ; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } - break; - } - case OP_AND: { - auto Op = IROp->CW(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = (Constant1 & Constant2) & getMask(Op) ; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { - // AND with same value results in original value - IREmit->ReplaceAllUsesWithInclusive(CodeNode, Op->Header.Args[0].GetNode(ListBegin), CodeBegin, CodeLast); - Changed = true; - } - break; - } - case OP_OR: { - auto Op = IROp->CW(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = Constant1 | Constant2; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { - // OR with same value results in original value - IREmit->ReplaceAllUsesWithInclusive(CodeNode, Op->Header.Args[0].GetNode(ListBegin), CodeBegin, CodeLast); - Changed = true; - } - break; - } - case OP_XOR: { - auto Op = IROp->CW(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = Constant1 ^ Constant2; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { - // XOR with same value results to zero - IREmit->SetWriteCursor(CodeNode); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, IREmit->_Constant(0), CodeBegin, CodeLast); - Changed = true; - } - break; - } - case OP_LSHL: { - auto Op = IROp->CW(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = (Constant1 << Constant2) & getMask(Op); - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } - else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && - Constant2 == 0) { - IREmit->SetWriteCursor(CodeNode); - OrderedNode *Arg = Op->Header.Args[0].GetNode(ListBegin); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, Arg, CodeBegin, CodeLast); - Changed = true; - } - break; - } - - - case OP_BFE: { - auto Op = IROp->CW(); - uint64_t Constant; - if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Header.Args[0], &Constant)) { - uint64_t SourceMask = (1ULL << Op->Width) - 1; - if (Op->Width == 64) - SourceMask = ~0ULL; - SourceMask <<= Op->lsb; - - uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb; - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } else if (IROp->Size == Op->Header.Args[0].GetNode(ListBegin)->Op(DataBegin)->Size && Op->Width == (IROp->Size * 8) && Op->lsb == 0 ) { - // A BFE that extracts all bits results in original value - // XXX - This is broken for now - see https://github.com/FEX-Emu/FEX/issues/351 - // IREmit->ReplaceAllUsesWithInclusive(CodeNode, Op->Header.Args[0].GetNode(ListBegin), CodeBegin, CodeLast); - // Changed = true; - } - - break; - } - case OP_MUL: { - auto Op = IROp->C(); - uint64_t Constant1; - uint64_t Constant2; - - if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && - IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { - uint64_t NewConstant = (Constant1 * Constant2) & getMask(Op); - IREmit->SetWriteCursor(CodeNode); - auto ConstantVal = IREmit->_Constant(NewConstant); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast); - Changed = true; - } - } - default: break; - } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; + break; } - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); + case OP_SEXT: + case OP_NEG: + case OP_POPCOUNT: + case OP_FINDLSB: + case OP_FINDMSB: + case OP_REV: + case OP_SBFE: + { + uint64_t Constant1; + + if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) { + LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str()); + } + break; + } +*/ + case OP_ADD: { + auto Op = IROp->C(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = (Constant1 + Constant2) & getMask(Op) ; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } + break; + } + case OP_SUB: { + auto Op = IROp->C(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = (Constant1 - Constant2) & getMask(Op) ; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } + break; + } + case OP_AND: { + auto Op = IROp->CW(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = (Constant1 & Constant2) & getMask(Op) ; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { + // AND with same value results in original value + IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Header.Args[0])); + Changed = true; + } + break; + } + case OP_OR: { + auto Op = IROp->CW(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = Constant1 | Constant2; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { + // OR with same value results in original value + IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Header.Args[0])); + Changed = true; + } + break; + } + case OP_XOR: { + auto Op = IROp->C(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = Constant1 ^ Constant2; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } else if (Op->Header.Args[0].ID() == Op->Header.Args[1].ID()) { + // XOR with same value results to zero + IREmit->SetWriteCursor(CodeNode); + IREmit->ReplaceAllUsesWith(CodeNode, IREmit->_Constant(0)); + Changed = true; + } + break; + } + case OP_LSHL: { + auto Op = IROp->CW(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = (Constant1 << Constant2) & getMask(Op); + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } + else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && + Constant2 == 0) { + IREmit->SetWriteCursor(CodeNode); + OrderedNode *Arg = CurrentIR.GetNode(Op->Header.Args[0]); + IREmit->ReplaceAllUsesWith(CodeNode, Arg); + Changed = true; + } + break; + } + case OP_BFE: { + auto Op = IROp->C(); + uint64_t Constant; + if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Header.Args[0], &Constant)) { + uint64_t SourceMask = (1ULL << Op->Width) - 1; + if (Op->Width == 64) + SourceMask = ~0ULL; + SourceMask <<= Op->lsb; + + uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb; + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } else if (IROp->Size == CurrentIR.GetOp(Op->Header.Args[0])->Size && Op->Width == (IROp->Size * 8) && Op->lsb == 0 ) { + // A BFE that extracts all bits results in original value + // XXX - This is broken for now - see https://github.com/FEX-Emu/FEX/issues/351 + // IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Header.Args[0])); + // Changed = true; + } + + break; + } + case OP_MUL: { + auto Op = IROp->C(); + uint64_t Constant1; + uint64_t Constant2; + + if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) && + IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) { + uint64_t NewConstant = (Constant1 * Constant2) & getMask(Op); + IREmit->SetWriteCursor(CodeNode); + auto ConstantVal = IREmit->_Constant(NewConstant); + IREmit->ReplaceAllUsesWith(CodeNode, ConstantVal); + Changed = true; + } + } + default: break; } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp index da6393be2..1bb996fac 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp @@ -16,53 +16,17 @@ private: bool DeadCodeElimination::Run(IREmitter *IREmit) { auto CurrentIR = IREmit->ViewIR(); - - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - int NumRemoved = 0; - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - - while (1) { - auto CodeOp = CodeLast(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - - // Skip over anything that has side effects - // Use count tracking can't safely remove anything with side effects - if (!IR::HasSideEffects(IROp->Op)) { - if (CodeNode->GetUses() == 0) { - NumRemoved++; - IREmit->Remove(CodeNode); - } + // Skip over anything that has side effects + // Use count tracking can't safely remove anything with side effects + if (!IR::HasSideEffects(IROp->Op)) { + if (CodeNode->GetUses() == 0) { + NumRemoved++; + IREmit->Remove(CodeNode); } - - if (CodeLast == CodeBegin) { - break; - } - --CodeLast; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp index 7380565d5..d043019a4 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp @@ -314,39 +314,18 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) { OffsetToBlockMap.clear(); auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - auto Begin = CurrentIR.begin(); - auto Op = Begin(); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(CurrentIR.GetID(BlockNode)).first->second; - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - // Walk the list and calculate the control flow - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second; - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { switch (IROp->Op) { case IR::OP_CONDJUMP: { auto Op = IROp->CW(); - OrderedNode *TrueTargetNode = Op->Header.Args[1].GetNode(ListBegin); - OrderedNode *FalseTargetNode = Op->Header.Args[2].GetNode(ListBegin); + OrderedNode *TrueTargetNode = CurrentIR.GetNode(Op->Header.Args[1]); + OrderedNode *FalseTargetNode = CurrentIR.GetNode(Op->Header.Args[2]); CurrentBlock->Successors.emplace_back(TrueTargetNode); CurrentBlock->Successors.emplace_back(FalseTargetNode); @@ -365,7 +344,7 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) { } case IR::OP_JUMP: { auto Op = IROp->CW(); - OrderedNode *TargetNode = Op->Header.Args[0].GetNode(ListBegin); + OrderedNode *TargetNode = CurrentIR.GetNode(Op->Header.Args[0]); CurrentBlock->Successors.emplace_back(TargetNode); { @@ -376,18 +355,6 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) { } default: break; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -433,41 +400,17 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); auto OriginalWriteCursor = IREmit->GetWriteCursor(); - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - // Walk the list and calculate the control flow - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); + // XXX: Walk the list and calculate the control flow ContextInfo LocalInfo = ClassifiedStruct; - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { ResetClassificationAccesses(&LocalInfo); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - - auto OpSize = [&](OrderedNode *node) { - return node->Op(DataBegin)->Size; - }; - - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STORECONTEXT) { auto Op = IROp->CW(); auto Info = FindMemberInfo(&LocalInfo, Op->Offset, IROp->Size); @@ -476,7 +419,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { uint8_t LastSize = Info->AccessSize; LastAccessType LastAccess = Info->Accessed; OrderedNode *LastStoreNode = Info->StoreNode; - RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_WRITE, Op->Header.Args[0].GetNode(ListBegin), CodeNode); + RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_WRITE, CurrentIR.GetNode(Op->Header.Args[0]), CodeNode); if (LastAccess == ACCESS_WRITE && LastClass == Op->Class && @@ -508,17 +451,17 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { IREmit->SetWriteCursor(CodeNode); - uint8_t TruncateSize = OpSize(LastNode); + uint8_t TruncateSize = IREmit->GetOpSize(LastNode); // Did store context do an implicit truncation? - if (OpSize(LastStoreNode) < TruncateSize) - TruncateSize = OpSize(LastStoreNode); + if (IREmit->GetOpSize(LastStoreNode) < TruncateSize) + TruncateSize = IREmit->GetOpSize(LastStoreNode); // Or are we doing a partial read if (IROp->Size < TruncateSize) TruncateSize = IROp->Size; - if (TruncateSize != OpSize(LastNode)) { + if (TruncateSize != IREmit->GetOpSize(LastNode)) { // We need to insert an explict truncation if (LastClass == FPRClass) { LastNode = IREmit->_VMov(LastNode, TruncateSize); // Vmov truncates and zexts when register width is smaller than source @@ -533,7 +476,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { } } - IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast); + IREmit->ReplaceAllUsesWith(CodeNode, LastNode); RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_READ, LastNode); Changed = true; } @@ -543,7 +486,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { LastSize == IROp->Size && Info->Accessed == ACCESS_READ) { // Did we read and then read again? - IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast); + IREmit->ReplaceAllUsesWith(CodeNode, LastNode); RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_READ, LastNode); Changed = true; } @@ -552,7 +495,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { auto Op = IROp->CW(); auto Info = FindMemberInfo(&LocalInfo, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1); auto LastStoreNode = Info->StoreNode; - RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_WRITE, Op->Header.Args[0].GetNode(ListBegin), CodeNode); + RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_WRITE, CurrentIR.GetNode(Op->Header.Args[0]), CodeNode); // Flags don't alias, so we can take the simple route here. Kill any flags that have been overwritten if (LastStoreNode != nullptr) @@ -571,12 +514,12 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { // If the last store matches this load value then we can replace the loaded value with the previous valid one IREmit->SetWriteCursor(CodeNode); auto Res = IREmit->_Bfe(1, 0, LastNode); - IREmit->ReplaceAllUsesWithInclusive(CodeNode, Res, CodeBegin, CodeLast); + IREmit->ReplaceAllUsesWith(CodeNode, Res); RecordAccess(Info, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_READ, LastNode); Changed = true; } else if (LastAccess == ACCESS_READ) { - IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast); + IREmit->ReplaceAllUsesWith(CodeNode, LastNode); RecordAccess(Info, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_READ, LastNode); Changed = true; } @@ -588,18 +531,6 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { // We can't track through these ResetClassificationAccesses(&LocalInfo); } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp index 55eae62e3..54a233ea5 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadFlagStoreElimination.cpp @@ -18,10 +18,10 @@ struct FlagInfo { * @brief This is a temporary pass to detect simple multiblock dead flag stores * * First pass computes which flags are read and written per block - * + * * Second pass computes which flags are stored, but overwritten by the next block(s). * It also propagates this information a few times to catch dead flags across multiple blocks. - * + * * Third pass removes the dead stores. * */ @@ -30,33 +30,13 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); // Pass 1 // Compute flags read/writes per block // This is conservative and doesn't try to be smart about loads after writes { - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STOREFLAG) { auto Op = IROp->CW(); @@ -67,17 +47,6 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { FlagMap[BlockNode].reads |= 1 << Op->Flag; } - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -87,23 +56,12 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { // Propagate the information a few times to eliminate more for (int i = 0; i < 5; i++) { - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_JUMP) { auto Op = IROp->CW(); - OrderedNode *TargetNode = Op->Header.Args[0].GetNode(ListBegin); + OrderedNode *TargetNode = CurrentIR.GetNode(Op->Header.Args[0]); // stores to remove are written by the next block but not read FlagMap[BlockNode].kill = FlagMap[TargetNode].writes & ~(FlagMap[TargetNode].reads); @@ -114,8 +72,8 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { else if (IROp->Op == OP_CONDJUMP) { auto Op = IROp->CW(); - OrderedNode *TrueTargetNode = Op->Header.Args[1].GetNode(ListBegin); - OrderedNode *FalseTargetNode = Op->Header.Args[2].GetNode(ListBegin); + OrderedNode *TrueTargetNode = CurrentIR.GetNode(Op->Header.Args[1]); + OrderedNode *FalseTargetNode = CurrentIR.GetNode(Op->Header.Args[2]); // stores to remove are written by the next blocks but not read FlagMap[BlockNode].kill = FlagMap[TrueTargetNode].writes & ~(FlagMap[TrueTargetNode].reads); @@ -124,18 +82,6 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { // Flags that are written by the next blocks can be considered as written by this block, if not read FlagMap[BlockNode].writes |= FlagMap[BlockNode].kill & ~FlagMap[BlockNode].reads; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -143,19 +89,8 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { // Pass 3 // Remove the dead stores { - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STOREFLAG) { auto Op = IROp->CW(); @@ -166,17 +101,6 @@ bool DeadFlagStoreElimination::Run(IREmitter *IREmit) { } } - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp index 5b1d4b439..2b29d8733 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp @@ -48,10 +48,8 @@ bool IRCompaction::Run(IREmitter *IREmit) { uintptr_t ListBegin = CurrentIR.GetListData(); uintptr_t DataBegin = CurrentIR.GetData(); - auto HeaderIterator = CurrentIR.begin(); - OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); - OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); - auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); + auto HeaderNode = CurrentIR.GetHeaderNode(); + auto HeaderOp = CurrentIR.GetHeader(); LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); // This compaction pass is something that we need to ensure correct ordering and distances between IROps @@ -71,61 +69,39 @@ bool IRCompaction::Run(IREmitter *IREmit) { // Zero is always zero(invalid) OldToNewRemap[0] = 0; auto LocalHeaderOp = LocalBuilder._IRHeader(OrderedNodeWrapper::WrapOffset(0).GetNode(ListBegin), HeaderOp->Entry, HeaderOp->BlockCount, HeaderOp->ShouldInterpret); - OldToNewRemap[HeaderNode->Wrapped(ListBegin).ID()] = LocalHeaderOp.Node->Wrapped(LocalListBegin).ID(); + OldToNewRemap[CurrentIR.GetID(HeaderNode)] = LocalIR.GetID(LocalHeaderOp.Node); { // Generate our codeblocks and link them together - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - OrderedNode* PrevCodeBlock{}; - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + LogMan::Throw::A(BlockHeader->Op == OP_CODEBLOCK, "IR type failed to be a code block"); - auto LocalBlockIRNode = LocalBuilder.CreateCodeNode(); - OldToNewRemap[BlockNode->Wrapped(ListBegin).ID()] = LocalBlockIRNode.Node->Wrapped(LocalListBegin).ID(); + auto LocalBlockIRNode = LocalBuilder._CodeBlock(LocalHeaderOp, LocalHeaderOp); // Use LocalHeaderOp as a dummy arg for now + OldToNewRemap[CurrentIR.GetID(BlockNode)] = LocalIR.GetID(LocalBlockIRNode.Node); GeneratedCodeBlocks.emplace_back(CodeBlockData{BlockNode, LocalBlockIRNode}); - - if (PrevCodeBlock) { - auto PrevLocalBlockIROp = PrevCodeBlock->Op(LocalDataBegin)->CW(); - PrevLocalBlockIROp->Next = LocalBlockIRNode.Node->Wrapped(LocalListBegin); - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); - PrevCodeBlock = LocalBlockIRNode; - } } // Link the IRHeader to the first code block LocalHeaderOp.first->Blocks = GeneratedCodeBlocks[0].NewNode->Wrapped(LocalListBegin); } + + { // Copy all of our IR ops over to the new location for (auto &Block : GeneratedCodeBlocks) { - auto BlockIROp = Block.OldNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); + // Isolate block contents from any previous headers/blocks + LocalBuilder.SetWriteCursor(nullptr); CodeBlockData FirstNode{}; CodeBlockData LastNode{}; uint32_t i {}; - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - LogMan::Throw::A(IROp->Op != OP_IRHEADER, "%%ssa%d ended up being IRHeader. Shouldn't have hit this", CodeOp->ID()); - + for (auto [CodeNode, IROp] : CurrentIR.GetCode(Block.OldNode)) { size_t OpSize = FEXCore::IR::GetSize(IROp->Op); // Allocate the ops locally for our local dispatch auto LocalPair = LocalBuilder.AllocateRawOp(OpSize); - IR::OrderedNodeWrapper LocalNodeWrapper = LocalPair.Node->Wrapped(LocalListBegin); // Copy usage infomation LocalPair.Node->NumUses = CodeNode->GetUses(); @@ -136,20 +112,18 @@ bool IRCompaction::Run(IREmitter *IREmit) { // Set our map remapper to map the new location // Even nodes that don't have a destination need to be in this map // Need to be able to remap branch targets any other bits - OldToNewRemap[CodeOp->ID()] = LocalNodeWrapper.ID(); + OldToNewRemap[CurrentIR.GetID(CodeNode)] = LocalIR.GetID(LocalPair.Node); if (i == 0) { FirstNode.OldNode = CodeNode; FirstNode.NewNode = LocalPair.Node; } - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { + if (IROp->Op == OP_ENDBLOCK) { LastNode.OldNode = CodeNode; LastNode.NewNode = LocalPair.Node; - break; } - ++CodeBegin; + ++i; } @@ -163,20 +137,11 @@ bool IRCompaction::Run(IREmitter *IREmit) { { // Fixup the arguments of all the IROps for (auto &Block : GeneratedCodeBlocks) { - auto BlockIROp = Block.OldNode->Op(DataBegin)->CW(); + auto BlockIROp = CurrentIR.GetOp(Block.OldNode); LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - - OrderedNodeWrapper LocalNodeWrapper = OrderedNodeWrapper::WrapOffset(OldToNewRemap[CodeOp->ID()] * sizeof(OrderedNode)); - OrderedNode *LocalNode = LocalNodeWrapper.GetNode(LocalListBegin); - FEXCore::IR::IROp_Header *LocalIROp = LocalNode->Op(LocalDataBegin); + for (auto [CodeNode, IROp] : CurrentIR.GetCode(Block.OldNode)) { + auto [LocalNode, LocalIROp] = LocalIR.at(OldToNewRemap[CurrentIR.GetID(CodeNode)])(); // Now that we have the op copied over, we need to modify SSA values to point to the new correct locations // This doesn't use IR::GetArgs(Op) because we need to remap all SSA nodes @@ -187,12 +152,6 @@ bool IRCompaction::Run(IREmitter *IREmit) { LogMan::Throw::A(OldToNewRemap[OldArg] != ~0U, "Tried remapping unfound node %%ssa%d", OldArg); LocalIROp->Args[i].NodeOffset = OldToNewRemap[OldArg] * sizeof(OrderedNode); } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; } } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp index 6cc3607cc..b6066d5af 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp @@ -2,6 +2,7 @@ #include "Interface/IR/Passes/RegisterAllocationPass.h" #include "Interface/Context/Context.h" #include "Interface/Core/OpcodeDispatcher.h" +#include "Common/BitSet.h" #include @@ -27,40 +28,33 @@ bool IRValidation::Run(IREmitter *IREmit) { std::unordered_map OffsetToBlockMap; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); std::ostringstream Errors; std::ostringstream Warnings; - auto Begin = CurrentIR.begin(); - auto Op = Begin(); + BitSet NodeIsLive; + NodeIsLive.Allocate(CurrentIR.GetSSACount()); - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); + std::vector Uses(CurrentIR.GetSSACount(), 0); + + auto HeaderOp = CurrentIR.GetHeader(); LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); IR::RegisterAllocationPass * RAPass{}; if (Manager->HasRAPass() && !HeaderOp->ShouldInterpret) { RAPass = Manager->GetRAPass(); } - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + auto BlockIROp = BlockHeader->CW(); LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second; + uint32_t BlockID = CurrentIR.GetID(BlockNode); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); + BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(BlockID).first->second; - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - uint32_t Node = CodeOp->ID(); + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { + uint32_t ID = CurrentIR.GetID(CodeNode); uint8_t OpSize = IROp->Size; @@ -68,18 +62,18 @@ bool IRValidation::Run(IREmitter *IREmit) { HadError |= OpSize == 0; // Does the op have a destination of size 0? if (OpSize == 0) { - Errors << "%ssa" << CodeOp->ID() << ": Had destination but with no size" << std::endl; + Errors << "%ssa" << ID << ": Had destination but with no size" << std::endl; } // Does the node have zero uses? Should have been DCE'd - if (RealNode->GetUses() == 0) { + if (CodeNode->GetUses() == 0) { HadWarning |= true; - Warnings << "%ssa" << CodeOp->ID() << ": Destination created but had no uses" << std::endl; + Warnings << "%ssa" << ID << ": Destination created but had no uses" << std::endl; } if (RAPass) { // If we have a register allocator then the destination needs to be assigned a register and class - uint64_t Reg = RAPass->GetNodeRegister(Node); + uint64_t Reg = RAPass->GetNodeRegister(ID); FEXCore::IR::RegisterClassType ExpectedClass = IR::GetRegClass(IROp->Op); FEXCore::IR::RegisterClassType AssignedClass = FEXCore::IR::RegisterClassType{uint32_t(Reg >> 32)}; @@ -87,34 +81,61 @@ bool IRValidation::Run(IREmitter *IREmit) { // If no register class was assigned if (AssignedClass == IR::InvalidClass) { HadError |= true; - Errors << "%ssa" << CodeOp->ID() << ": Had destination but with no register class assigned" << std::endl; + Errors << "%ssa" << ID << ": Had destination but with no register class assigned" << std::endl; } // If no physical register was assigned if ((uint32_t)Reg == ~0U) { HadError |= true; - Errors << "%ssa" << CodeOp->ID() << ": Had destination but with no register assigned" << std::endl; + Errors << "%ssa" << ID << ": Had destination but with no register assigned" << std::endl; } // Assigned class wasn't the expected class and it is a non-complex op if (AssignedClass != ExpectedClass && ExpectedClass != IR::ComplexClass) { HadWarning |= true; - Warnings << "%ssa" << CodeOp->ID() << ": Destination had register class " << AssignedClass.Val << " When register class " << ExpectedClass.Val << " Was expected" << std::endl; + Warnings << "%ssa" << ID << ": Destination had register class " << AssignedClass.Val << " When register class " << ExpectedClass.Val << " Was expected" << std::endl; } } } uint8_t NumArgs = IR::GetArgs(IROp->Op); + + if (NumArgs != IROp->NumArgs) { + switch (IROp->Op) { + case OP_BEGINBLOCK: + case OP_ENDBLOCK: + case OP_PHI: + case OP_PHIVALUE: + case OP_CONDJUMP: + case OP_JUMP: + // These override the nubmer of args for RA, so ignore them. + break; + default: + HadError |= true; + Errors << "%ssa" << ID << ": Has wrong number of Args" << std::endl; + } + } for (uint32_t i = 0; i < NumArgs; ++i) { OrderedNodeWrapper Arg = IROp->Args[i]; // Was an argument defined after this node? - if (Arg.ID() >= CodeOp->ID()) { + if (Arg.ID() >= ID) { HadError |= true; - Errors << "%ssa" << CodeOp->ID() << ": Arg[" << i << "] has definition after use at %ssa" << Arg.ID() << std::endl; + Errors << "%ssa" << ID << ": Arg[" << i << "] has definition after use at %ssa" << Arg.ID() << std::endl; + } + + if (Arg.ID() != 0 && !NodeIsLive.Get(Arg.ID())) { + HadError |= true; + Errors << "%ssa" << ID << ": Arg[" << i << "] refrences dead %ssa" << Arg.ID() << std::endl; + } + + if (Arg.ID() != 0) { + Uses[Arg.ID()]++; } } + NodeIsLive.Set(ID); + switch (IROp->Op) { case IR::OP_EXITFUNCTION: { CurrentBlock->HasExit = true; @@ -123,18 +144,18 @@ bool IRValidation::Run(IREmitter *IREmit) { case IR::OP_CONDJUMP: { auto Op = IROp->C(); - OrderedNode const *TrueTargetNode = Op->Header.Args[1].GetNode(ListBegin); - OrderedNode const *FalseTargetNode = Op->Header.Args[2].GetNode(ListBegin); + OrderedNode *TrueTargetNode = CurrentIR.GetNode(Op->Header.Args[1]); + OrderedNode *FalseTargetNode = CurrentIR.GetNode(Op->Header.Args[2]); CurrentBlock->Successors.emplace_back(TrueTargetNode); CurrentBlock->Successors.emplace_back(FalseTargetNode); - FEXCore::IR::IROp_Header const *TrueTargetOp = TrueTargetNode->Op(DataBegin); - FEXCore::IR::IROp_Header const *FalseTargetOp = FalseTargetNode->Op(DataBegin); + FEXCore::IR::IROp_Header const *TrueTargetOp = CurrentIR.GetOp(TrueTargetNode); + FEXCore::IR::IROp_Header const *FalseTargetOp = CurrentIR.GetOp(FalseTargetNode); if (TrueTargetOp->Op != OP_CODEBLOCK) { HadError |= true; - Errors << "CondJump %ssa" << CodeOp->ID() << ": True Target Jumps to Op that isn't the begining of a block" << std::endl; + Errors << "CondJump %ssa" << ID << ": True Target Jumps to Op that isn't the begining of a block" << std::endl; } else { auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[1].ID()).first; @@ -143,7 +164,7 @@ bool IRValidation::Run(IREmitter *IREmit) { if (FalseTargetOp->Op != OP_CODEBLOCK) { HadError |= true; - Errors << "CondJump %ssa" << CodeOp->ID() << ": False Target Jumps to Op that isn't the begining of a block" << std::endl; + Errors << "CondJump %ssa" << ID << ": False Target Jumps to Op that isn't the begining of a block" << std::endl; } else { auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[2].ID()).first; @@ -154,13 +175,13 @@ bool IRValidation::Run(IREmitter *IREmit) { } case IR::OP_JUMP: { auto Op = IROp->C(); - OrderedNode const *TargetNode = Op->Header.Args[0].GetNode(ListBegin); + OrderedNode *TargetNode = CurrentIR.GetNode(Op->Header.Args[0]); CurrentBlock->Successors.emplace_back(TargetNode); - FEXCore::IR::IROp_Header const *TargetOp = TargetNode->Op(DataBegin); + FEXCore::IR::IROp_Header const *TargetOp = CurrentIR.GetOp(TargetNode); if (TargetOp->Op != OP_CODEBLOCK) { HadError |= true; - Errors << "Jump %ssa" << CodeOp->ID() << ": Jump to Op that isn't the begining of a block" << std::endl; + Errors << "Jump %ssa" << ID << ": Jump to Op that isn't the begining of a block" << std::endl; } else { auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[0].ID()).first; @@ -172,37 +193,29 @@ bool IRValidation::Run(IREmitter *IREmit) { // LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data()); break; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; } // Blocks can only have zero (Exit), 1 (Unconditional branch) or 2 (Conditional) successors size_t NumSuccessors = CurrentBlock->Successors.size(); if (NumSuccessors > 2) { HadError |= true; - Errors << "%ssa" << BlockNode->Wrapped(ListBegin).ID() << " Has " << NumSuccessors << " successors which is too many" << std::endl; + Errors << "%ssa" << BlockID << " Has " << NumSuccessors << " successors which is too many" << std::endl; } { - auto GetOp = [&ListBegin, &DataBegin](auto Code) { - auto CodeOp = Code(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + auto GetOp = [](auto Code) { + auto [CodeNode, IROp] = Code(); return IROp->Op; }; - auto CodeCurrent = CodeLast; + auto CodeCurrent = CurrentIR.at(BlockIROp->Last); // Last instruction in the block must be EndBlock { auto Op = GetOp(CodeCurrent); if (Op != IR::OP_ENDBLOCK) { HadError |= true; - Errors << "%ssa" << BlockNode->Wrapped(ListBegin).ID() << " Failed to end block with EndBlock" << std::endl; + Errors << "%ssa" << BlockID << " Failed to end block with EndBlock" << std::endl; } } @@ -219,15 +232,17 @@ bool IRValidation::Run(IREmitter *IREmit) { break; default: HadError |= true; - Errors << "%ssa" << BlockNode->Wrapped(ListBegin).ID() << " Didn't have an exit IR op as its last instruction" << std::endl; + Errors << "%ssa" << BlockID << " Didn't have an exit IR op as its last instruction" << std::endl; }; } } + } - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); + for (int i = 0; i < CurrentIR.GetSSACount(); i++) { + auto [Node, IROp] = CurrentIR.at(i)(); + if (Node->NumUses != Uses[i] && IROp->Op != OP_CODEBLOCK && IROp->Op != OP_IRHEADER) { + HadError |= true; + Errors << "%ssa" << i << " Has " << Uses[i] << " Uses, but reports " << Node->NumUses << std::endl; } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp index ccce4abed..8a0b3482a 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp @@ -13,45 +13,26 @@ public: bool PhiValidation::Run(IREmitter *IREmit) { bool HadError = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); std::ostringstream Errors; - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - // Walk the list and calculate the control flow - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { bool FoundNonPhi{}; - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { switch (IROp->Op) { - // DUMMY doesn't matter for us - case IR::OP_DUMMY: break; + // BEGINBLOCK doesn't matter for us + case IR::OP_BEGINBLOCK: break; case IR::OP_PHIVALUE: case IR::OP_PHI: { if (FoundNonPhi) { // If we have found a non-phi IR op and then had a Phi or PhiValue value then this is a programming mistake // PHI values MUST be defined at the top of the block only HadError |= true; - Errors << "Phi %ssa" << CodeOp->ID() << ": Was defined after non-phi operations. Which is invalid!" << std::endl; + Errors << "Phi %ssa" << CurrentIR.GetID(CodeNode) << ": Was defined after non-phi operations. Which is invalid!" << std::endl; } // Check all the phi values to ensure they have the same type @@ -61,18 +42,6 @@ bool PhiValidation::Run(IREmitter *IREmit) { FoundNonPhi = true; break; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp index 26883d5e2..1705c64a7 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -24,30 +24,9 @@ bool DeadFlagCalculationEliminination::Run(IREmitter *IREmit) { bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STOREFLAG) { auto Op = IROp->CW(); // Set this node as the last one valid for this flag @@ -57,18 +36,6 @@ bool DeadFlagCalculationEliminination::Run(IREmitter *IREmit) { auto Op = IROp->CW(); LastValidFlagStores[Op->Flag] = nullptr; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } // If any flags are stored but not loaded by the end of the block, then erase them diff --git a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index f8677c0f3..55c1ef93e 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -206,10 +206,8 @@ namespace { return false; } - FEXCore::IR::RegisterClassType GetRegClassFromNode(uintptr_t ListBegin, uintptr_t DataBegin, FEXCore::IR::OrderedNodeWrapper const WrapperOp) { + FEXCore::IR::RegisterClassType GetRegClassFromNode(FEXCore::IR::IRListView *IR, FEXCore::IR::IROp_Header *IROp) { using namespace FEXCore; - IR::OrderedNode const *RealNode = WrapperOp.GetNode(ListBegin); - IR::IROp_Header const *IROp = RealNode->Op(DataBegin); FEXCore::IR::RegisterClassType Class = IR::GetRegClass(IROp->Op); if (Class != FEXCore::IR::ComplexClass) @@ -241,13 +239,13 @@ namespace { case IR::OP_PHIVALUE: { // Unwrap the PHIValue to get the class auto Op = IROp->C(); - return GetRegClassFromNode(ListBegin, DataBegin, Op->Value); + return GetRegClassFromNode(IR, IR->GetOp(Op->Value)); } case IR::OP_PHI: { // Class is defined from the values passed in // All Phi nodes should have its class be the same (Validation should confirm this auto Op = IROp->C(); - return GetRegClassFromNode(ListBegin, DataBegin, Op->PhiBegin); + return GetRegClassFromNode(IR, IR->GetOp(Op->PhiBegin)); } default: break; } @@ -258,47 +256,10 @@ namespace { // Walk the IR and set the node classes void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView *IR) { - uintptr_t ListBegin = IR->GetListData(); - uintptr_t DataBegin = IR->GetData(); - - auto Begin = IR->begin(); - auto Op = Begin(); - - FEXCore::IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == FEXCore::IR::OP_IRHEADER, "First op wasn't IRHeader"); - - FEXCore::IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == FEXCore::IR::OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR->at(BlockIROp->Begin); - auto CodeLast = IR->at(BlockIROp->Last); - while (1) { - FEXCore::IR::OrderedNodeWrapper *CodeOp = CodeBegin(); - FEXCore::IR::OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - uint32_t Node = CodeOp->ID(); - - // If the destination hasn't yet been set then set it now - if (IROp->HasDest) { - SetNodeClass(Graph, Node, GetRegClassFromNode(ListBegin, DataBegin, *CodeOp)); - } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); + for (auto [CodeNode, IROp] : IR->GetAllCode()) { + // If the destination hasn't yet been set then set it now + if (IROp->HasDest) { + SetNodeClass(Graph, IR->GetID(CodeNode), GetRegClassFromNode(IR, IROp)); } } } @@ -344,8 +305,8 @@ namespace FEXCore::IR { void CalculateNodeInterference(FEXCore::IR::IRListView *IR); void AllocateVirtualRegisters(); - FEXCore::IR::NodeWrapperIterator FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End); - FEXCore::IR::NodeWrapperIterator FindLastUseBefore(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End); + FEXCore::IR::AllNodesIterator FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End); + FEXCore::IR::AllNodesIterator FindLastUseBefore(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End); uint32_t FindNodeToSpill(IREmitter *IREmit, RegisterNode *RegisterNode, uint32_t CurrentLocation, LiveRange const *OpLiveRange, int32_t RematCost = -1); uint32_t FindSpillSlot(uint32_t Node, FEXCore::IR::RegisterClassType RegisterClass); @@ -395,31 +356,10 @@ namespace FEXCore::IR { } LiveRanges.assign(Nodes * sizeof(LiveRange), {~0U, ~0U}); - uintptr_t ListBegin = IR->GetListData(); - uintptr_t DataBegin = IR->GetData(); - - auto Begin = IR->begin(); - auto Op = Begin(); - - IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - constexpr uint32_t DEFAULT_REMAT_COST = 1000; - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR->at(BlockIROp->Begin); - auto CodeLast = IR->at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - IR::OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - uint32_t Node = CodeOp->ID(); + for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) { + for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { + uint32_t Node = IR->GetID(CodeNode); // If the destination hasn't yet been set then set it now if (IROp->HasDest) { @@ -445,7 +385,7 @@ namespace FEXCore::IR { } // Set this node's block ID - Graph->Nodes[Node].Head.BlockID = BlockNode->Wrapped(ListBegin).ID(); + Graph->Nodes[Node].Head.BlockID = IR->GetID(BlockNode); uint8_t NumArgs = IR::GetArgs(IROp->Op); for (uint8_t i = 0; i < NumArgs; ++i) { @@ -464,60 +404,32 @@ namespace FEXCore::IR { uint32_t CurrentSourcePartner = Node; while (NodeBegin != NodeBegin.Invalid()) { - FEXCore::IR::OrderedNodeWrapper *NodeOp = NodeBegin(); - FEXCore::IR::OrderedNode *NodeNode = NodeOp->GetNode(ListBegin); - auto IRNodeOp = NodeNode->Op(DataBegin)->C(); + auto [ValueNode, ValueHeader] = NodeBegin(); + auto ValueOp = ValueHeader->CW(); // Set the node partner to the current one // This creates a singly linked list of node partners to follow - SetNodePartner(Graph, CurrentSourcePartner, IRNodeOp->Value.ID()); - CurrentSourcePartner = IRNodeOp->Value.ID(); - NodeBegin = IR->at(IRNodeOp->Next); + SetNodePartner(Graph, CurrentSourcePartner, ValueOp->Value.ID()); + CurrentSourcePartner = ValueOp->Value.ID(); + NodeBegin = IR->at(ValueOp->Next); } } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } void ConstrainedRAPass::CalculateBlockInterferences(FEXCore::IR::IRListView *IR) { using namespace FEXCore; - uintptr_t ListBegin = IR->GetListData(); - uintptr_t DataBegin = IR->GetData(); - auto Begin = IR->begin(); - auto Op = Begin(); - - IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); + for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) { + auto BlockIROp = BlockHeader->CW(); LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - BlockInterferences *BlockInterferenceVector = &LocalBlockInterferences.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second; + BlockInterferences *BlockInterferenceVector = &LocalBlockInterferences.try_emplace(IR->GetID(BlockNode)).first->second; BlockInterferenceVector->reserve(BlockIROp->Last.ID() - BlockIROp->Begin.ID()); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR->at(BlockIROp->Begin); - auto CodeLast = IR->at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - uint32_t Node = CodeOp->ID(); + for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { + uint32_t Node = IR->GetID(CodeNode); LiveRange *NodeLiveRange = &LiveRanges[Node]; if (NodeLiveRange->Begin >= BlockIROp->Begin.ID() && @@ -530,18 +442,6 @@ namespace FEXCore::IR { // If the live range is not fully inside the block then add it to the global interference list GlobalBlockInterferences.emplace_back(Node); } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -562,33 +462,15 @@ namespace FEXCore::IR { } }; using namespace FEXCore; - uintptr_t ListBegin = IR->GetListData(); - uintptr_t DataBegin = IR->GetData(); - auto Begin = IR->begin(); - auto Op = Begin(); - - IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - - BlockInterferences *BlockInterferenceVector = &LocalBlockInterferences.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second; + for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) { + BlockInterferences *BlockInterferenceVector = &LocalBlockInterferences.try_emplace(IR->GetID(BlockNode)).first->second; std::vector Interferences; Interferences.reserve(BlockInterferenceVector->size() + GlobalBlockInterferences.size()); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR->at(BlockIROp->Begin); - auto CodeLast = IR->at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - uint32_t Node = CodeOp->ID(); + for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { + uint32_t Node = IR->GetID(CodeNode); // Check for every interference with the local block's interference for (auto RHSNode : *BlockInterferenceVector) { @@ -617,18 +499,6 @@ namespace FEXCore::IR { } Interferences.clear(); - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -719,18 +589,12 @@ namespace FEXCore::IR { } } - FEXCore::IR::NodeWrapperIterator ConstrainedRAPass::FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End) { - auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); + FEXCore::IR::AllNodesIterator ConstrainedRAPass::FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End) { + using namespace FEXCore::IR; + uint32_t SearchID = IREmit->ViewIR().GetID(Node); - uint32_t SearchID = Node->Wrapped(ListBegin).ID(); - - while (1) { - using namespace FEXCore::IR; - OrderedNodeWrapper *WrapperOp = Begin(); - OrderedNode *RealNode = WrapperOp->GetNode(ListBegin); - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + while(1) { + auto [RealNode, IROp] = Begin(); uint8_t NumArgs = FEXCore::IR::GetArgs(IROp->Op); for (uint8_t i = 0; i < NumArgs; ++i) { @@ -748,21 +612,16 @@ namespace FEXCore::IR { ++Begin; } - return FEXCore::IR::NodeWrapperIterator::Invalid(); + return AllNodesIterator::Invalid(); } - FEXCore::IR::NodeWrapperIterator ConstrainedRAPass::FindLastUseBefore(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End) { + FEXCore::IR::AllNodesIterator ConstrainedRAPass::FindLastUseBefore(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End) { auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - - uint32_t SearchID = Node->Wrapped(ListBegin).ID(); + uint32_t SearchID = CurrentIR.GetID(Node); while (1) { using namespace FEXCore::IR; - OrderedNodeWrapper *WrapperOp = End(); - OrderedNode *RealNode = WrapperOp->GetNode(ListBegin); - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + auto [RealNode, IROp] = End(); if (Node == RealNode) { // We walked back all the way to the definition of the IR op @@ -785,12 +644,11 @@ namespace FEXCore::IR { --End; } - return FEXCore::IR::NodeWrapperIterator::Invalid(); + return FEXCore::IR::AllNodesIterator::Invalid(); } uint32_t ConstrainedRAPass::FindNodeToSpill(IREmitter *IREmit, RegisterNode *RegisterNode, uint32_t CurrentLocation, LiveRange const *OpLiveRange, int32_t RematCost) { auto IR = IREmit->ViewIR(); - uintptr_t ListBegin = IR.GetListData(); uint32_t InterferenceToSpill = ~0U; uint32_t InterferenceFarthestNextUse = 0; @@ -827,13 +685,9 @@ namespace FEXCore::IR { continue; } - FEXCore::IR::OrderedNodeWrapper InterferenceOp = IR::OrderedNodeWrapper::WrapOffset(InterferenceNode * sizeof(IR::OrderedNode)); - FEXCore::IR::OrderedNode *InterferenceOrderedNode = InterferenceOp.GetNode(ListBegin); - - IR::OrderedNodeWrapper InterferenceNodeOpBegin = IR::OrderedNodeWrapper::WrapOffset(InterferenceLiveRange->Begin * sizeof(IR::OrderedNode)); - IR::OrderedNodeWrapper InterferenceNodeOpEnd = IR::OrderedNodeWrapper::WrapOffset(InterferenceLiveRange->End * sizeof(IR::OrderedNode)); - auto InterferenceNodeOpBeginIter = IR.at(InterferenceNodeOpBegin); - auto InterferenceNodeOpEndIter = IR.at(InterferenceNodeOpEnd); + auto [InterferenceOrderedNode, _] = IR.at(InterferenceNode)(); + auto InterferenceNodeOpBeginIter = IR.at(InterferenceLiveRange->Begin); + auto InterferenceNodeOpEndIter = IR.at(InterferenceLiveRange->End); bool Found{}; @@ -857,7 +711,7 @@ namespace FEXCore::IR { // does NOT have a use inside of this this node's live range // Search only inside the source node's live range to see if there is a use auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpBeginIter, NodeOpEndIter); - if (FirstUseLocation == IR::NodeWrapperIterator::Invalid()) { + if (FirstUseLocation == IR::NodeIterator::Invalid()) { // Looks like there isn't a usage of this interference node inside our node's live range // This means it is safe to spill this node and it'll result in in lower RA // Proper calculation of cost to spill would be to calculate the two distances from @@ -865,11 +719,11 @@ namespace FEXCore::IR { // This would ensure something will spill earlier if its previous use and next use are farther away auto InterferenceNodeNextUse = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpBeginIter, InterferenceNodeOpEndIter); auto InterferenceNodePrevUse = FindLastUseBefore(IREmit, InterferenceOrderedNode, InterferenceNodeOpBeginIter, NodeOpBeginIter); - LogMan::Throw::A(InterferenceNodeNextUse != IR::NodeWrapperIterator::Invalid(), "Couldn't find next usage of op"); + LogMan::Throw::A(InterferenceNodeNextUse != IR::NodeIterator::Invalid(), "Couldn't find next usage of op"); // If there is no use of the interference op prior to our op then it only has initial definition - if (InterferenceNodePrevUse == IR::NodeWrapperIterator::Invalid()) InterferenceNodePrevUse = InterferenceNodeOpBeginIter; + if (InterferenceNodePrevUse == IR::NodeIterator::Invalid()) InterferenceNodePrevUse = InterferenceNodeOpBeginIter; - uint32_t NextUseDistance = InterferenceNodeNextUse()->ID() - CurrentLocation; + uint32_t NextUseDistance = InterferenceNodeNextUse.ID() - CurrentLocation; if (NextUseDistance >= InterferenceFarthestNextUse) { Found = true; InterferenceToSpill = j; @@ -905,11 +759,8 @@ namespace FEXCore::IR { continue; } - FEXCore::IR::OrderedNodeWrapper InterferenceOp = IR::OrderedNodeWrapper::WrapOffset(InterferenceNode * sizeof(IR::OrderedNode)); - FEXCore::IR::OrderedNode *InterferenceOrderedNode = InterferenceOp.GetNode(ListBegin); - - IR::OrderedNodeWrapper InterferenceNodeOpEnd = IR::OrderedNodeWrapper::WrapOffset(InterferenceLiveRange->End * sizeof(IR::OrderedNode)); - auto InterferenceNodeOpEndIter = IR.at(InterferenceNodeOpEnd); + auto [InterferenceOrderedNode, _] = IR.at(InterferenceNode)(); + auto InterferenceNodeOpEndIter = IR.at(InterferenceLiveRange->End); bool Found{}; @@ -932,11 +783,11 @@ namespace FEXCore::IR { OpLiveRange->End > InterferenceLiveRange->End) { auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpBeginIter, NodeOpBeginIter); - if (FirstUseLocation == IR::NodeWrapperIterator::Invalid()) { + if (FirstUseLocation == IR::NodeIterator::Invalid()) { // This means that the assignment of our register doesn't use this interference node // So we are safe to spill this interference node before assignment of our current node auto InterferenceNodeNextUse = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpBeginIter, InterferenceNodeOpEndIter); - uint32_t NextUseDistance = InterferenceNodeNextUse()->ID() - CurrentLocation; + uint32_t NextUseDistance = InterferenceNodeNextUse.ID() - CurrentLocation; if (NextUseDistance >= InterferenceFarthestNextUse) { Found = true; @@ -967,11 +818,11 @@ namespace FEXCore::IR { OpLiveRange->End <= InterferenceLiveRange->End) { auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpEndIter, NodeOpEndIter); - if (FirstUseLocation == IR::NodeWrapperIterator::Invalid()) { + if (FirstUseLocation == IR::NodeIterator::Invalid()) { // This means that the assignment of our the interference register doesn't overlap // with the final usage of our register, we can spill it and reduce usage auto InterferenceNodeNextUse = FindFirstUse(IREmit, InterferenceOrderedNode, NodeOpBeginIter, InterferenceNodeOpEndIter); - uint32_t NextUseDistance = InterferenceNodeNextUse()->ID() - CurrentLocation; + uint32_t NextUseDistance = InterferenceNodeNextUse.ID() - CurrentLocation; if (NextUseDistance >= InterferenceFarthestNextUse) { Found = true; @@ -1031,36 +882,13 @@ namespace FEXCore::IR { using namespace FEXCore; auto IR = IREmit->ViewIR(); - uintptr_t ListBegin = IR.GetListData(); - uintptr_t DataBegin = IR.GetData(); - - auto Begin = IR.begin(); - auto Op = Begin(); auto LastCursor = IREmit->GetWriteCursor(); - IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = IR.at(BlockIROp->Begin); - auto CodeLast = IR.at(BlockIROp->Last); - - auto BlockBegin = CodeBegin; - - while (1) { - auto CodeOp = CodeBegin(); - IR::OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [BlockNode, BlockIRHeader] : IR.GetBlocks()) { + for (auto [CodeNode, IROp] : IR.GetCode(BlockNode)) { if (IROp->HasDest) { - uint32_t Node = CodeOp->ID(); + uint32_t Node = IR.GetID(CodeNode); RegisterNode *CurrentNode = &Graph->Nodes[Node]; LiveRange *OpLiveRange = &LiveRanges[Node]; @@ -1076,21 +904,19 @@ namespace FEXCore::IR { uint32_t InterferenceNode = FindNodeToSpill(IREmit, CurrentNode, Node, OpLiveRange, 1); if (InterferenceNode != ~0U) { // We want to end the live range of this value here and continue it on first use - IR::OrderedNodeWrapper ConstantOp = IR::OrderedNodeWrapper::WrapOffset(InterferenceNode * sizeof(IR::OrderedNode)); - IR::OrderedNode *ConstantNode = ConstantOp.GetNode(ListBegin); - FEXCore::IR::IROp_Constant const *ConstantIROp = ConstantNode->Op(DataBegin)->C(); - LogMan::Throw::A(ConstantIROp->Header.Op == IR::OP_CONSTANT, "This needs to be const"); + auto [ConstantNode, _] = IR.at(InterferenceNode)(); + auto ConstantIROp = IR.GetOp(ConstantNode); + // First op post Spill - auto NextIter = CodeBegin; - auto FirstUseLocation = FindFirstUse(IREmit, ConstantNode, NextIter, CodeLast); - LogMan::Throw::A(FirstUseLocation != IR::NodeWrapperIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", CodeOp->ID(), InterferenceNode); - if (FirstUseLocation != IR::NodeWrapperIterator::Invalid()) { + auto NextIter = IR.at(CodeNode); + auto FirstUseLocation = FindFirstUse(IREmit, ConstantNode, NextIter, NodeIterator::Invalid()); + LogMan::Throw::A(FirstUseLocation != IR::NodeIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", Node, InterferenceNode); + if (FirstUseLocation != IR::NodeIterator::Invalid()) { --FirstUseLocation; - IR::OrderedNodeWrapper *FirstUseOp = FirstUseLocation(); - IR::OrderedNode *FirstUseOrderedNode = FirstUseOp->GetNode(ListBegin); + auto [FirstUseOrderedNode, _] = FirstUseLocation(); IREmit->SetWriteCursor(FirstUseOrderedNode); auto FilledConstant = IREmit->_Constant(ConstantIROp->Constant); - IREmit->ReplaceAllUsesWithInclusive(ConstantNode, FilledConstant, FirstUseLocation, CodeLast); + IREmit->ReplaceUsesWithAfter(ConstantNode, FilledConstant, FirstUseLocation); Spilled = true; } } @@ -1108,17 +934,17 @@ namespace FEXCore::IR { LogMan::Throw::A(InterferenceRegisterNode->Head.PhiPartner == nullptr, "We don't support spilling PHI nodes currently"); // This is the op that we need to dump - FEXCore::IR::OrderedNodeWrapper InterferenceOp = IR::OrderedNodeWrapper::WrapOffset(InterferenceNode * sizeof(IR::OrderedNode)); - FEXCore::IR::OrderedNode *InterferenceOrderedNode = InterferenceOp.GetNode(ListBegin); - FEXCore::IR::IROp_Header *InterferenceIROp = InterferenceOrderedNode->Op(DataBegin); + auto [InterferenceOrderedNode, InterferenceIROp] = IR.at(InterferenceNode)(); + // This will find the last use of this definition // Walks from CodeBegin -> BlockBegin to find the last Use // Which this is walking backwards to find the first use - auto LastUseOp = FindLastUseBefore(IREmit, InterferenceOrderedNode, BlockBegin, CodeBegin); + auto LastUseIterator = FindLastUseBefore(IREmit, InterferenceOrderedNode, NodeIterator::Invalid(), IR.at(CodeNode)); + auto [LastUseNode, LastUseIROp] = LastUseIterator(); // Set the write cursor to point of last usage - IREmit->SetWriteCursor(LastUseOp()->GetNode(ListBegin)); + IREmit->SetWriteCursor(LastUseNode); // Actually spill the node now auto SpillOp = IREmit->_SpillRegister(InterferenceOrderedNode, SpillSlot, InterferenceRegClass); @@ -1128,24 +954,23 @@ namespace FEXCore::IR { { // Search from the point of spilling to find the first use // Set the write cursor to the first location found and fill at that point - auto FirstIter = IR.at(SpillOp.Node->Wrapped(ListBegin)); + auto FirstIter = IR.at(SpillOp.Node); // Just past the spill ++FirstIter; - auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, FirstIter, CodeLast); + auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, FirstIter, NodeIterator::Invalid()); - LogMan::Throw::A(FirstUseLocation != NodeWrapperIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", CodeOp->ID(), InterferenceNode); - if (FirstUseLocation != IR::NodeWrapperIterator::Invalid()) { + LogMan::Throw::A(FirstUseLocation != NodeIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", Node, InterferenceNode); + if (FirstUseLocation != IR::NodeIterator::Invalid()) { // We want to fill just before the first use --FirstUseLocation; - IR::OrderedNodeWrapper *FirstUseOp = FirstUseLocation(); - IR::OrderedNode *FirstUseOrderedNode = FirstUseOp->GetNode(ListBegin); + auto [FirstUseOrderedNode, _] = FirstUseLocation(); IREmit->SetWriteCursor(FirstUseOrderedNode); auto FilledInterference = IREmit->_FillRegister(SpillSlot, InterferenceRegClass); FilledInterference.first->Header.Size = InterferenceIROp->Size; FilledInterference.first->Header.ElementSize = InterferenceIROp->ElementSize; - IREmit->ReplaceAllUsesWithInclusive(InterferenceOrderedNode, FilledInterference, FirstUseLocation, CodeLast); + IREmit->ReplaceUsesWithAfter(InterferenceOrderedNode, FilledInterference, FirstUseLocation); Spilled = true; } } @@ -1159,18 +984,6 @@ namespace FEXCore::IR { } } } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } } @@ -1213,16 +1026,8 @@ namespace FEXCore::IR { bool Changed = false; auto IR = IREmit->ViewIR(); - uintptr_t ListBegin = IR.GetListData(); - uintptr_t DataBegin = IR.GetData(); - - auto Begin = IR.begin(); - auto Op = Begin(); - - IR::OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + auto HeaderOp = IR.GetHeader(); if (HeaderOp->ShouldInterpret) { return false; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp b/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp index 1bb3f7c5a..27783fc24 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp @@ -14,62 +14,29 @@ public: bool SyscallOptimization::Run(IREmitter *IREmit) { bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); - auto Begin = CurrentIR.begin(); - auto Op = Begin(); - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); + for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); - - if (IROp->Op == FEXCore::IR::OP_SYSCALL) { - // Is the first argument a constant? - uint64_t Constant; - if (IREmit->IsValueConstant(IROp->Args[0], &Constant)) { - auto SyscallDef = Manager->SyscallHandler->GetDefinition(Constant); - // XXX: Once we have the ability to do real function calls then we can call directly in to the syscall handler - if (SyscallDef->NumArgs < FEXCore::HLE::SyscallArguments::MAX_ARGS) { - // If the number of args are less than what the IR op supports then we can remove arg usage - // We need +1 since we are still passing in syscall number here - for (uint8_t Arg = (SyscallDef->NumArgs + 1); Arg < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++Arg) { - IREmit->ReplaceNodeArgument(CodeNode, Arg, IREmit->Invalid()); - } - Changed = true; + if (IROp->Op == FEXCore::IR::OP_SYSCALL) { + // Is the first argument a constant? + uint64_t Constant; + if (IREmit->IsValueConstant(IROp->Args[0], &Constant)) { + auto SyscallDef = Manager->SyscallHandler->GetDefinition(Constant); + // XXX: Once we have the ability to do real function calls then we can call directly in to the syscall handler + if (SyscallDef->NumArgs < FEXCore::HLE::SyscallArguments::MAX_ARGS) { + // If the number of args are less than what the IR op supports then we can remove arg usage + // We need +1 since we are still passing in syscall number here + for (uint8_t Arg = (SyscallDef->NumArgs + 1); Arg < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++Arg) { + IREmit->ReplaceNodeArgument(CodeNode, Arg, IREmit->Invalid()); } + Changed = true; } } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); - } - } + return Changed; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp index c4ace11e0..dd6e95ee8 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp @@ -23,43 +23,23 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { bool HadError = false; bool HadWarning = false; auto CurrentIR = IREmit->ViewIR(); - uintptr_t ListBegin = CurrentIR.GetListData(); - uintptr_t DataBegin = CurrentIR.GetData(); + std::ostringstream Errors; std::ostringstream Warnings; std::unordered_map OffsetToBlockMap; - auto Begin = CurrentIR.begin(); - auto Op = Begin(); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { - OrderedNode *RealNode = Op->GetNode(ListBegin); - auto HeaderOp = RealNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader"); - - // Walk the list and calculate the control flow - OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); - - BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second; - - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + BlockInfo *CurrentBlock = &OffsetToBlockMap.try_emplace(CurrentIR.GetID(BlockNode)).first->second; + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { switch (IROp->Op) { case IR::OP_CONDJUMP: { auto Op = IROp->CW(); - OrderedNode *TrueTargetNode = Op->Header.Args[1].GetNode(ListBegin); - OrderedNode *FalseTargetNode = Op->Header.Args[2].GetNode(ListBegin); + OrderedNode *TrueTargetNode = CurrentIR.GetNode(Op->Header.Args[1]); + OrderedNode *FalseTargetNode = CurrentIR.GetNode(Op->Header.Args[2]); CurrentBlock->Successors.emplace_back(TrueTargetNode); CurrentBlock->Successors.emplace_back(FalseTargetNode); @@ -78,7 +58,7 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { } case IR::OP_JUMP: { auto Op = IROp->CW(); - OrderedNode *TargetNode = Op->Header.Args[0].GetNode(ListBegin); + OrderedNode *TargetNode = CurrentIR.GetNode(Op->Header.Args[0]); CurrentBlock->Successors.emplace_back(TargetNode); { @@ -89,34 +69,14 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { } default: break; } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } - BlockNode = HeaderOp->Blocks.GetNode(ListBegin); - while (1) { - auto BlockIROp = BlockNode->Op(DataBegin)->CW(); - LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block"); + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + auto BlockIROp = BlockHeader->CW(); - // We grab these nodes this way so we can iterate easily - auto CodeBegin = CurrentIR.at(BlockIROp->Begin); - auto CodeLast = CurrentIR.at(BlockIROp->Last); - - while (1) { - auto CodeOp = CodeBegin(); - OrderedNode *CodeNode = CodeOp->GetNode(ListBegin); - auto IROp = CodeNode->Op(DataBegin); + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { + uint32_t CodeID = CurrentIR.GetID(CodeNode); uint8_t NumArgs = IR::GetArgs(IROp->Op); for (uint32_t i = 0; i < NumArgs; ++i) { @@ -139,9 +99,9 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { // %ssa_1 = Load // %ssa_2 = %ssa_1, %ssa_3 // %ssa_3 = Load - if (Arg.ID() > CodeOp->ID()) { + if (Arg.ID() > CodeID) { HadError |= true; - Errors << "Inst %ssa" << CodeOp->ID() << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use!" << std::endl; + Errors << "Inst %ssa" << CodeID << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use!" << std::endl; } } else if (Arg.ID() < BlockIROp->Begin.ID()) { @@ -173,7 +133,7 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { std::set Predecessors; std::function AddPredecessors = [&] (IR::OrderedNode *Node) { - auto PredBlock = &OffsetToBlockMap.try_emplace(Node->Wrapped(ListBegin).ID()).first->second; + auto PredBlock = &OffsetToBlockMap.try_emplace(CurrentIR.GetID(Node)).first->second; // Current Block will always be in set // Walk each predecessor, adding their predecessors @@ -192,19 +152,19 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { for (auto it = Predecessors.begin(); it != Predecessors.end(); ++it) { IR::OrderedNode *Pred = *it; - auto PredIROp = Pred->Op(DataBegin)->CW(); + auto PredIROp = CurrentIR.GetOp(Pred); if (Arg.ID() >= PredIROp->Begin.ID() && Arg.ID() < PredIROp->Last.ID()) { FoundPredDefine = true; break; } - Errors << "\tChecking Pred %ssa" << Pred->Wrapped(ListBegin).ID() << std::endl; + Errors << "\tChecking Pred %ssa" << CurrentIR.GetID(Pred) << std::endl; } if (!FoundPredDefine) { HadError |= true; - Errors << "Inst %ssa" << CodeOp->ID() << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use! But was defined before this block!" << std::endl; + Errors << "Inst %ssa" << CodeID << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use! But was defined before this block!" << std::endl; } } else if (Arg.ID() > BlockIROp->Last.ID()) { @@ -218,21 +178,9 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) { // CodeBlock_2: // %ssa_3 = Load HadError |= true; - Errors << "Inst %ssa" << CodeOp->ID() << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use!" << std::endl; + Errors << "Inst %ssa" << CodeID << ": Arg[" << i << "] %ssa" << Arg.ID() << " definition does not dominate this use!" << std::endl; } } - - // CodeLast is inclusive. So we still need to dump the CodeLast op as well - if (CodeBegin == CodeLast) { - break; - } - ++CodeBegin; - } - - if (BlockIROp->Next.ID() == 0) { - break; - } else { - BlockNode = BlockIROp->Next.GetNode(ListBegin); } } diff --git a/External/FEXCore/docs/IR.md b/External/FEXCore/docs/IR.md index 8d337664d..16c2b1bae 100644 --- a/External/FEXCore/docs/IR.md +++ b/External/FEXCore/docs/IR.md @@ -55,15 +55,15 @@ Additionally it also points to the first `CodeBlock` IROp ``` (%%ssa3) CodeBlock %%ssa169, %%ssa173, %%ssa4 - (%%ssa169) Dummy + (%%ssa169) BeginBlock %ssa3 %ssa170 i64 = Constant 0x41a9e1 (%%ssa171) StoreContext %ssa170 i64, 0x8, 0x0 (%%ssa172) ExitFunction - (%%ssa173) EndBlock 0x0 + (%%ssa173) EndBlock %ssa3 ``` -* Dummy op is just a sentinal op and may be removed in the future -* EndBlock the ending op of a CodeBlock and may be used to increment the RIP independently of just setting it +* BeginBlock points back to the CodeBlock SSA which helps with iterating across multiple blocks +* EndBlock the ending op of a CodeBlock and also points back to the CodeBlock SSA. * ExitFunction will leave the function immediately and return back to the dispatcher * Every IR Op has an SSA value associated with it used for tracking the op itself * If the IROp doesn't have a real destination then it is invalid to use it as an argument in most other ops @@ -121,23 +121,19 @@ This is a doubly linked list of all of our IR nodes. This allows us to walk forw * Then once you have the `OrderedNode*` from GetNode, Use the `Op(IRDataBegin)` function to get the IR data. * I do **NOT** recommend using `GetNode` directly from `OpNodeWrapper` as it is VERY easy to mess it up -### NodeWrapperIterator -Provides a fairly straightforward wrapper object that allows easily walking the IR nodes with C++ increment and decrement operations +### NodeIterator +Provides a fairly straightforward interface that allows easily walking the IR nodes with C++ increment and decrement operations. +Only iterates over a single block -### Example usage +#### Example usage ```cpp - IR::NodeWrapperIterator After = ...; - IR::NodeWrapperIterator End = ...; + IR::NodeIterator After = ...; + IR::NodeIterator End = ...; while (After != End) { - // NodeWrapperIterator() returns a pointer or a reference to an OrderedNodeWrapper object - OrderedNodeWrapper *WrapperOp = After(); - // OrderedNodeWrapper::GetNode(ListBegin) will return a pointer to our OrderedNode* object - OrderedNode *RealNode = WrapperOp->GetNode(ListBegin); - - // Now to get the IROp_Header - // We need to use OrderedNode::Op(DataBegin) to get the real data backing the object - FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin); + // NodeIterator() returns a pair of pointers to the OrderedNode and IROp data + // You can unpack the result with structured bindings + auto [CodeNode, IROp] = After(); // IROp_Header contains a bunch of information about the IR object // We can convert it with the object's C or CW functions @@ -157,6 +153,48 @@ Provides a fairly straightforward wrapper object that allows easily walking the ``` +### AllNodesIterator +This is like NodeIterator, except that it will cross block boundaries. + +### IRListView.GetBlocks() +Provides a range for easy iterating over all the blocks in a multi-block with NodeIterator + +#### Example usage +```c++ + for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { + // Do stuff for each block + } +``` + +### IRListView.GetCode(BlockNode) +Provides a range for easy iterating over all the code in a block + +#### Example usage +```c++ + for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { + // Do stuff for each op + + switch(IROp->Op) { + case IR::OP_ADD: { + FEXCore::IR::IROp_Add const *Op = IROp->C(); + // Do stuff for each Add op. + + break; + } + } + } +``` + +### IRListView.GetAllCode() +Like GetCode, except it uses AllNodesIterator to allow easy iterating over every single op in the entire Multiblock + +#### Example usage +```c++ + for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { + // Do stuff for each op + } +``` + ## JSON file An example of what the IR json looks like ``` diff --git a/External/FEXCore/include/FEXCore/IR/IR.h b/External/FEXCore/include/FEXCore/IR/IR.h index 4dc134d88..fc4c0a675 100644 --- a/External/FEXCore/include/FEXCore/IR/IR.h +++ b/External/FEXCore/include/FEXCore/IR/IR.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace FEXCore::IR { class RegisterAllocationPass; @@ -84,70 +85,6 @@ struct OrderedNodeHeader { static_assert(sizeof(OrderedNodeHeader) == sizeof(uint32_t) * 3); -/** -* @brief This is our NodeWrapperIterator -* This stores both the memory base and the provided NodeWrapper to be able to walk the list of nodes directly -* Only the increment and decrement implementations of this class require understanding the implementation details of OrderedNode -*/ -class NodeWrapperIterator final { -public: - using value_type = OrderedNodeWrapper; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - using iterator = NodeWrapperIterator; - using const_iterator = const NodeWrapperIterator; - using reverse_iterator = iterator; - using const_reverse_iterator = const_iterator; - using iterator_category = std::bidirectional_iterator_tag; - - using NodeType = value_type; - using NodePtr = value_type*; - using NodeRef = value_type&; - - NodeWrapperIterator(uintptr_t Base) : BaseList {Base} {} - explicit NodeWrapperIterator(uintptr_t Base, NodeType Ptr) : BaseList {Base}, Node {Ptr} {} - - bool operator==(const NodeWrapperIterator &rhs) const { - return Node.NodeOffset == rhs.Node.NodeOffset; - } - - bool operator!=(const NodeWrapperIterator &rhs) const { - return !operator==(rhs); - } - - NodeWrapperIterator operator++() { - OrderedNodeHeader *RealNode = reinterpret_cast(Node.GetNode(BaseList)); - Node = RealNode->Next; - return *this; - } - - NodeWrapperIterator operator--() { - OrderedNodeHeader *RealNode = reinterpret_cast(Node.GetNode(BaseList)); - Node = RealNode->Previous; - return *this; - } - - NodeRef operator*() { - return Node; - } - - NodePtr operator()() { - return &Node; - } - - static NodeWrapperIterator Invalid() { - return NodeWrapperIterator(0); - } - -private: - uintptr_t BaseList{}; - NodeType Node{}; -}; - /** * @brief This is a node in our IR representation * Is a doubly linked list node that lives in a representation of a linearly allocated node list @@ -372,12 +309,137 @@ struct FenceType final { constexpr bool operator!=(FenceType const &rhs) const { return !operator==(rhs); } }; +class NodeIterator; + +/* 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. + */ +class NodeIterator { +public: + using value_type = std::tuple; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = NodeIterator; + using const_iterator = const NodeIterator; + using reverse_iterator = iterator; + using const_reverse_iterator = const_iterator; + using iterator_category = std::bidirectional_iterator_tag; + + NodeIterator(uintptr_t Base, uintptr_t IRBase) : BaseList {Base}, IRList{ IRBase } {} + explicit NodeIterator(uintptr_t Base, uintptr_t IRBase, OrderedNodeWrapper Ptr) : BaseList {Base}, IRList{ IRBase }, Node {Ptr} {} + + bool operator==(const NodeIterator &rhs) const { + return Node.NodeOffset == rhs.Node.NodeOffset; + } + + bool operator!=(const NodeIterator &rhs) const { + return !operator==(rhs); + } + + NodeIterator operator++() { + OrderedNodeHeader *RealNode = reinterpret_cast(Node.GetNode(BaseList)); + Node = RealNode->Next; + return *this; + } + + NodeIterator operator--() { + OrderedNodeHeader *RealNode = reinterpret_cast(Node.GetNode(BaseList)); + Node = RealNode->Previous; + return *this; + } + + value_type operator*() { + OrderedNode *RealNode = Node.GetNode(BaseList); + return { RealNode, RealNode->Op(IRList) }; + } + + value_type operator()() { + OrderedNode *RealNode = Node.GetNode(BaseList); + return { RealNode, RealNode->Op(IRList) }; + } + + uint32_t ID() { + return Node.ID(); + } + + static NodeIterator Invalid() { + return NodeIterator(0, 0); + } + +protected: + uintptr_t BaseList{}; + uintptr_t IRList{}; + OrderedNodeWrapper Node{}; +}; + #define IROP_ENUM #define IROP_STRUCTS #define IROP_SIZES #define IROP_REG_CLASSES #include +/* This iterator can be used to step though every single node in a multi-block in SSA order. + * + * Iterates in the order of: + * + * end <-- CodeBlockA <--> BlockAInst1 <--> BlockAInst2 <--> CodeBlockB <--> BlockBInst1 <--> BlockBInst2 --> end + */ +class AllNodesIterator : public NodeIterator { +public: + AllNodesIterator(uintptr_t Base, uintptr_t IRBase) : NodeIterator(Base, IRBase) {} + explicit AllNodesIterator(uintptr_t Base, uintptr_t IRBase, OrderedNodeWrapper Ptr) : NodeIterator(Base, IRBase, Ptr) {} + AllNodesIterator(NodeIterator other) : NodeIterator(other) {} // Allow NodeIterator to be upgraded + + AllNodesIterator operator++() { + OrderedNodeHeader *RealNode = reinterpret_cast(Node.GetNode(BaseList)); + auto IROp = Node.GetNode(BaseList)->Op(IRList); + + // If this is the last node of a codeblock, we need to continue to the next block + if (IROp->Op == OP_ENDBLOCK) { + auto EndBlock = IROp->C(); + + auto CurrentBlock = EndBlock->BlockHeader.GetNode(BaseList); + Node = CurrentBlock->Header.Next; + } else if (IROp->Op == OP_CODEBLOCK) { + auto CodeBlock = IROp->C(); + + Node = CodeBlock->Begin; + } else { + Node = RealNode->Next; + } + + return *this; + } + + AllNodesIterator operator--() { + auto IROp = Node.GetNode(BaseList)->Op(IRList); + + if (IROp->Op == OP_BEGINBLOCK) { + auto BeginBlock = IROp->C(); + + Node = BeginBlock->BlockHeader; + } else if (IROp->Op == OP_CODEBLOCK) { + auto PrevBlockWrapper = Node.GetNode(BaseList)->Header.Previous; + auto PrevCodeBlock = PrevBlockWrapper.GetNode(BaseList)->Op(IRList)->C(); + + Node = PrevCodeBlock->Last; + } else { + Node = Node.GetNode(BaseList)->Header.Previous; + } + + return *this; + } + + static AllNodesIterator Invalid() { + return AllNodesIterator(0, 0); + } +}; + template class IRListView; diff --git a/External/FEXCore/include/FEXCore/IR/IREmitter.h b/External/FEXCore/include/FEXCore/IR/IREmitter.h index 51da9b926..0a71fc597 100644 --- a/External/FEXCore/include/FEXCore/IR/IREmitter.h +++ b/External/FEXCore/include/FEXCore/IR/IREmitter.h @@ -400,25 +400,30 @@ friend class FEXCore::IR::PassManager; return false; } - // This is fairly special in how it operates - // Since the node is decoupled from the backing op then we can swap out the backing op without much overhead - // This can potentially cause problems where multiple nodes are pointing to the same IROp - OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, IROp_Header *Op) { - RemoveArgUses(Node); - Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast(Op)); - return Node; + void ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator After, AllNodesIterator End); + + void ReplaceUsesWithAfter(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator After) { + ReplaceAllUsesWithRange(Node, NewNode, After, AllNodesIterator(ListData.Begin(), Data.Begin())); } - // This is similar to the previous op except that we pass in a node - // This takes the op backing in the new node and replaces the node in the other node - // Again can cause problems where things are pointing to NewNode and haven't been decoupled - OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, OrderedNode *NewNode) { - RemoveArgUses(Node); - Node->Header.Value.NodeOffset = NewNode->Header.Value.NodeOffset; - return Node; + void ReplaceUsesWithAfter(OrderedNode *Node, OrderedNode *NewNode, OrderedNode *After) { + auto Wrapped = Node->Wrapped(ListData.Begin()); + AllNodesIterator It = AllNodesIterator(ListData.Begin(), Data.Begin(), Wrapped); + + ReplaceUsesWithAfter(Node, NewNode, It); + } + + void ReplaceAllUsesWith(OrderedNode *Node, OrderedNode *NewNode) { + auto Start = AllNodesIterator(ListData.Begin(), Data.Begin(), Node->Wrapped(ListData.Begin())); + + ReplaceUsesWithAfter(Node, NewNode, Start); + + LogMan::Throw::A(Node->NumUses == 0, "Node still used"); + + // Since we have deleted ALL uses, we can safely delete the node. + Remove(Node); } - void ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End); void ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg); void Remove(OrderedNode *Node); @@ -454,23 +459,22 @@ friend class FEXCore::IR::PassManager; * @return OrderedNode */ IRPair CreateCodeNode() { - auto CodeNode = _CodeBlock(InvalidNode, InvalidNode, InvalidNode); + SetWriteCursor(nullptr); // Orphan from any previous nodes + + auto CodeNode = _CodeBlock(InvalidNode, InvalidNode); CodeBlocks.emplace_back(CodeNode); + + SetWriteCursor(nullptr);// Orphan from any future nodes + + auto Begin = _BeginBlock(CodeNode); + CodeNode.first->Begin = Begin.Node->Wrapped(ListData.Begin()); + + auto EndBlock = _EndBlock(CodeNode); + CodeNode.first->Last = EndBlock.Node->Wrapped(ListData.Begin()); + return CodeNode; } - void SetCodeNodeBegin(OrderedNode *CodeNode, OrderedNode *Begin) { - FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW(); - LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid"); - IROp->Begin = Begin->Wrapped(ListData.Begin()); - } - - void SetCodeNodeLast(OrderedNode *CodeNode, OrderedNode *Last) { - FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW(); - LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid"); - IROp->Last = Last->Wrapped(ListData.Begin()); - } - /** * @name Links codeblocks together * Codeblocks are singly linked so we need to walk the list forward if the linked block isn't isn't the last @@ -483,22 +487,10 @@ friend class FEXCore::IR::PassManager; * @{ */ /** @} */ void LinkCodeBlocks(OrderedNode *CodeNode, OrderedNode *Next) { - FEXCore::IR::IROp_CodeBlock *CurrentIROp = CodeNode->Op(Data.Begin())->CW(); - LogMan::Throw::A(CurrentIROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid"); + FEXCore::IR::IROp_CodeBlock *CurrentIROp = CodeNode->Op(Data.Begin())->CW(); + LogMan::Throw::A(CurrentIROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid"); - OrderedNodeWrapper OldNext = CurrentIROp->Next; - // First thing is to assign CodeNode->Next to the incoming node - { - CurrentIROp->Next = Next->Wrapped(ListData.Begin()); - } - - // Second thing is to assign the incoming node's Next to what was in CodeNode->Next - { - FEXCore::IR::IROp_CodeBlock *NextIROp = Next->Op(Data.Begin())->CW(); - auto NewOldNext = NextIROp->Next; - NextIROp->Next = OldNext; - OldNext = NewOldNext; - } + CodeNode->append(ListData.Begin(), Next); } IRPair CreateNewCodeBlock(); diff --git a/External/FEXCore/include/FEXCore/IR/IntrusiveIRList.h b/External/FEXCore/include/FEXCore/IR/IntrusiveIRList.h index 85c8cb7fd..3e94bd6c8 100644 --- a/External/FEXCore/include/FEXCore/IR/IntrusiveIRList.h +++ b/External/FEXCore/include/FEXCore/IR/IntrusiveIRList.h @@ -1,6 +1,7 @@ #pragma once #include "FEXCore/IR/IR.h" +#include "LogManager.h" #include #include @@ -98,13 +99,115 @@ public: size_t GetListSize() const { return ListSize; } size_t GetSSACount() const { return ListSize / sizeof(OrderedNode); } - using iterator = NodeWrapperIterator; + uint32_t GetID(OrderedNode *Node) const { + return Node->Wrapped(GetListData()).ID(); + } + + OrderedNode* GetHeaderNode() const { + OrderedNodeWrapper Wrapped; + Wrapped.NodeOffset = sizeof(OrderedNode); + return Wrapped.GetNode(GetListData()); + } + + IROp_IRHeader *GetHeader() const { + return GetOp(GetHeaderNode()); + } + + template + T *GetOp(OrderedNode *Node) const { + auto OpHeader = Node->Op(GetData()); + auto Op = OpHeader->template CW(); + + // If we are casting to something narrower than just the header, check the opcode. + if constexpr (!std::is_same::value) { + LogMan::Throw::A(Op->OPCODE == Op->Header.Op, "Expected Node to be '%s'. Found '%s' instead", GetName(Op->OPCODE), GetName(Op->Header.Op)); + } + + return Op; + } + + template + T *GetOp(OrderedNodeWrapper Wrapper) const { + auto Node = Wrapper.GetNode(GetListData()); + return GetOp(Node); + } + + OrderedNode* GetNode(OrderedNodeWrapper Wrapper) const { + return Wrapper.GetNode(GetListData()); + } + +private: + struct BlockRange { + using iterator = NodeIterator; + const IRListView *View; + + BlockRange(const IRListView *parent) : View(parent) {}; + + iterator begin() const noexcept { + auto Header = View->GetHeader(); + return iterator(View->GetListData(), View->GetData(), Header->Blocks); + } + + iterator end() const noexcept { + return iterator(View->GetListData(), View->GetData()); + } + }; + + struct CodeRange { + using iterator = NodeIterator; + const IRListView *View; + const OrderedNodeWrapper BlockWrapper; + + CodeRange(const IRListView *parent, OrderedNodeWrapper block) : View(parent), BlockWrapper(block) {}; + + iterator begin() const noexcept { + auto Block = View->GetOp(BlockWrapper); + return iterator(View->GetListData(), View->GetData(), Block->Begin); + } + + iterator end() const noexcept { + return iterator(View->GetListData(), View->GetData()); + } + }; + + struct AllCodeRange { + using iterator = AllNodesIterator; // Diffrent Iterator + const IRListView *View; + + AllCodeRange(const IRListView *parent) : View(parent) {}; + + iterator begin() const noexcept { + auto Header = View->GetHeader(); + return iterator(View->GetListData(), View->GetData(), Header->Blocks); + } + + iterator end() const noexcept { + return iterator(View->GetListData(), View->GetData()); + } + }; + + +public: + + BlockRange GetBlocks() const { + return BlockRange(this); + } + + CodeRange GetCode(OrderedNode *block) const { + return CodeRange(this, block->Wrapped(GetListData())); + } + + AllCodeRange GetAllCode() const { + return AllCodeRange(this); + } + + using iterator = NodeIterator; iterator begin() const noexcept { OrderedNodeWrapper Wrapped; Wrapped.NodeOffset = sizeof(OrderedNode); - return iterator(reinterpret_cast(ListData), Wrapped); + return iterator(reinterpret_cast(ListData), reinterpret_cast(IRData), Wrapped); } /** @@ -116,15 +219,26 @@ public: { OrderedNodeWrapper Wrapped; Wrapped.NodeOffset = 0; - return iterator(reinterpret_cast(ListData), Wrapped); + return iterator(reinterpret_cast(ListData), reinterpret_cast(IRData), Wrapped); } /** * @brief Convert a OrderedNodeWrapper to an interator that we can iterate over * @return Iterator for this op */ - iterator at(OrderedNodeWrapper Node) const noexcept { - return iterator(reinterpret_cast(ListData), Node); + iterator at(OrderedNodeWrapper Wrapped) const noexcept { + return iterator(reinterpret_cast(ListData), reinterpret_cast(IRData), Wrapped); + } + + iterator at(uint32_t ID) const noexcept { + OrderedNodeWrapper Wrapped; + Wrapped.NodeOffset = ID * sizeof(OrderedNode); + return iterator(reinterpret_cast(ListData), reinterpret_cast(IRData), Wrapped); + } + + iterator at(OrderedNode *Node) const noexcept { + auto Wrapped = Node->Wrapped(reinterpret_cast(ListData)); + return iterator(reinterpret_cast(ListData), reinterpret_cast(IRData), Wrapped); } private: diff --git a/Source/Tests/IRLoader/Loader.cpp b/Source/Tests/IRLoader/Loader.cpp index e3458163d..3fc3c13cd 100644 --- a/Source/Tests/IRLoader/Loader.cpp +++ b/Source/Tests/IRLoader/Loader.cpp @@ -366,16 +366,13 @@ namespace FEX::IRLoader { IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second, false); } + SetWriteCursor(nullptr); // isolate the header from everything following + // Spin through the blocks and generate basic block ops - IRPair PrevBlock{}; for(size_t i = 0; i < Defs.size(); ++i) { auto &Def = Defs[i]; if (Def.OpEnum == FEXCore::IR::IROps::OP_CODEBLOCK) { - auto CodeBlock = _CodeBlock(InvalidNode, InvalidNode, InvalidNode); - if (PrevBlock.first) { - PrevBlock.first->Next = CodeBlock.Node->Wrapped(ListData.Begin()); - } - PrevBlock = CodeBlock; + auto CodeBlock = _CodeBlock(InvalidNode, InvalidNode); SSANameMapper[Def.Definition] = CodeBlock.Node; Def.Node = CodeBlock.Node; @@ -386,6 +383,7 @@ namespace FEX::IRLoader { } } } + SetWriteCursor(nullptr); // isolate the block headers too // Spin through all the definitions and add the ops to the basic blocks OrderedNode *CurrentBlock{}; @@ -405,7 +403,6 @@ namespace FEX::IRLoader { CurrentBlock = DefTarget->second; CurrentBlockOp = CurrentBlock->Op(Data.Begin())->CW(); - SetWriteCursor(CurrentBlock); } if (Def.OpEnum == FEXCore::IR::IROps::OP_ENDBLOCK) { @@ -420,7 +417,7 @@ namespace FEX::IRLoader { if (!CheckPrintError(Def, Adjust.first)) return false; - Def.Node = _EndBlock(Adjust.second); + Def.Node = _EndBlock(CurrentBlock); CurrentBlockOp->Last = Def.Node->Wrapped(ListData.Begin()); CurrentBlock = nullptr;