mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-23 19:00:49 +02:00
Merge pull request #365 from phire/IR_iterators
Refactor the IR Interface to prevent "foot-shooting"
This commit is contained in:
25 files changed
+861
-1388
No files matched your search
+22
-28
@@ -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<IROp_%s>::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")
|
||||
|
||||
|
||||
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<uint64_t*>(SSAData, WrapperOp)
|
||||
#define GDP GetDest<void*>(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<FEXCore::IR::IROp_Header>(Node);
|
||||
return IROp->Size;
|
||||
};
|
||||
|
||||
while (1) {
|
||||
using namespace FEXCore::IR;
|
||||
auto BlockIROp = BlockNode->Op(DataBegin)->C<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto [BlockNode, BlockHeader] = BlockIterator();
|
||||
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
|
||||
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<IR::IROp_EndBlock>();
|
||||
Thread->State.State.rip += Op->RIPIncrement;
|
||||
break;
|
||||
}
|
||||
case IR::OP_FENCE: {
|
||||
auto Op = IROp->C<IR::IROp_Fence>();
|
||||
switch (Op->Fence) {
|
||||
@@ -187,10 +176,10 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) {
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
uint64_t Arg = *GetSrc<uint64_t*>(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<IR::IROp_Jump>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-34
@@ -577,15 +577,8 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> 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<FEXCore::IR::IROp_IRHeader>();
|
||||
LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader");
|
||||
|
||||
auto HeaderOp = CurrentIR->GetHeader();
|
||||
if (HeaderOp->ShouldInterpret) {
|
||||
return reinterpret_cast<void*>(InterpreterFallbackHelperAddress);
|
||||
}
|
||||
@@ -633,19 +626,13 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<true> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IR::IROp_EndBlock>();
|
||||
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<IR::IROp_Fence>();
|
||||
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);
|
||||
|
||||
+10
-52
@@ -419,15 +419,8 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> 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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<true> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IROp_CodeBlock>();
|
||||
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<true> 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<true> 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<IR::IROp_EndBlock>();
|
||||
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<true> 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<true> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4027,12 +4027,6 @@ void OpDispatchBuilder::CreateJumpBlocks(std::vector<FEXCore::Frontend::Decoder:
|
||||
for (auto &Target : *Blocks) {
|
||||
auto CodeNode = CreateCodeNode();
|
||||
|
||||
auto NewNode = _Dummy();
|
||||
SetCodeNodeBegin(CodeNode, NewNode);
|
||||
|
||||
auto EndBlock = _EndBlock(0);
|
||||
SetCodeNodeLast(CodeNode, EndBlock);
|
||||
|
||||
JumpTargets.try_emplace(Target.Entry, JumpTargetInfo{CodeNode, false});
|
||||
|
||||
if (PrevCodeBlock) {
|
||||
|
||||
+22
-62
@@ -52,11 +52,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, IRListView<false> 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<false>
|
||||
}
|
||||
|
||||
void Dump(std::stringstream *out, IRListView<false> 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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<false> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
|
||||
// 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<false> 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<false> 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<false> 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 <FEXCore/IR/IRDefines.inc>
|
||||
@@ -236,19 +209,18 @@ void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAlloc
|
||||
*out << " ";
|
||||
|
||||
while (NodeBegin != NodeBegin.Invalid()) {
|
||||
OrderedNodeWrapper *NodeOp = NodeBegin();
|
||||
OrderedNode *NodeNode = NodeOp->GetNode(ListBegin);
|
||||
auto IRNodeOp = NodeNode->Op(DataBegin)->C<IR::IROp_PhiValue>();
|
||||
auto [NodeNode, IROp] = NodeBegin();
|
||||
auto PhiOp = IROp->C<IR::IROp_PhiValue>();
|
||||
*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<false> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-6
@@ -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"
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
+4
-13
@@ -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<IROp_CodeBlock> 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);
|
||||
}
|
||||
|
||||
+189
-224
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_Add>();
|
||||
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<IR::IROp_Sub>();
|
||||
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<IR::IROp_And>();
|
||||
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<IR::IROp_Or>();
|
||||
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<IR::IROp_Xor>();
|
||||
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<IR::IROp_Lshl>();
|
||||
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<IR::IROp_Bfe>();
|
||||
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<IR::IROp_Mul>();
|
||||
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<IR::IROp_Add>();
|
||||
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<IR::IROp_Sub>();
|
||||
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<IR::IROp_And>();
|
||||
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<IR::IROp_Or>();
|
||||
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<IR::IROp_Xor>();
|
||||
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<IR::IROp_Lshl>();
|
||||
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<IR::IROp_Bfe>();
|
||||
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<IROp_Header>(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<IR::IROp_Mul>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-88
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_CondJump>();
|
||||
|
||||
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<IR::IROp_Jump>();
|
||||
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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_StoreContext>();
|
||||
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<IR::IROp_StoreFlag>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_StoreFlag>();
|
||||
@@ -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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_Jump>();
|
||||
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<IR::IROp_CondJump>();
|
||||
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_StoreFlag>();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-59
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto BlockIROp = CurrentIR.GetOp<FEXCore::IR::IROp_CodeBlock>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-54
@@ -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 <iostream>
|
||||
|
||||
@@ -27,40 +28,33 @@ bool IRValidation::Run(IREmitter *IREmit) {
|
||||
|
||||
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, BlockInfo> 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<uint64_t> NodeIsLive;
|
||||
NodeIsLive.Allocate(CurrentIR.GetSSACount());
|
||||
|
||||
OrderedNode *RealNode = Op->GetNode(ListBegin);
|
||||
auto HeaderOp = RealNode->Op(DataBegin)->CW<FEXCore::IR::IROp_IRHeader>();
|
||||
std::vector<uint32_t> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_CondJump>();
|
||||
|
||||
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<IROp_Header>(TrueTargetNode);
|
||||
FEXCore::IR::IROp_Header const *FalseTargetOp = CurrentIR.GetOp<IROp_Header>(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<IR::IROp_Jump>();
|
||||
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<IROp_Header>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-35
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_StoreFlag>();
|
||||
// Set this node as the last one valid for this flag
|
||||
@@ -57,18 +36,6 @@ bool DeadFlagCalculationEliminination::Run(IREmitter *IREmit) {
|
||||
auto Op = IROp->CW<IR::IROp_LoadFlag>();
|
||||
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
|
||||
|
||||
@@ -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<false> *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<IR::IROp_PhiValue>();
|
||||
return GetRegClassFromNode(ListBegin, DataBegin, Op->Value);
|
||||
return GetRegClassFromNode(IR, IR->GetOp<IR::IROp_Header>(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<IR::IROp_Phi>();
|
||||
return GetRegClassFromNode(ListBegin, DataBegin, Op->PhiBegin);
|
||||
return GetRegClassFromNode(IR, IR->GetOp<IR::IROp_Header>(Op->PhiBegin));
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
@@ -258,47 +256,10 @@ namespace {
|
||||
|
||||
// Walk the IR and set the node classes
|
||||
void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView<false> *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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<false> *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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_PhiValue>();
|
||||
auto [ValueNode, ValueHeader] = NodeBegin();
|
||||
auto ValueOp = ValueHeader->CW<IROp_PhiValue>();
|
||||
|
||||
// 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<false> *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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<uint32_t> 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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_Constant>();
|
||||
LogMan::Throw::A(ConstantIROp->Header.Op == IR::OP_CONSTANT, "This needs to be const");
|
||||
auto [ConstantNode, _] = IR.at(InterferenceNode)();
|
||||
auto ConstantIROp = IR.GetOp<IR::IROp_Constant>(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<FEXCore::IR::IROp_IRHeader>();
|
||||
LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader");
|
||||
|
||||
auto HeaderOp = IR.GetHeader();
|
||||
if (HeaderOp->ShouldInterpret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IR::OrderedNodeWrapper::NodeOffsetType, BlockInfo> 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<FEXCore::IR::IROp_IRHeader>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<IR::IROp_CondJump>();
|
||||
|
||||
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<IR::IROp_Jump>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
|
||||
// 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 = <Op> %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<IR::OrderedNode *> Predecessors;
|
||||
|
||||
std::function<void(IR::OrderedNode*)> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto PredIROp = CurrentIR.GetOp<FEXCore::IR::IROp_CodeBlock>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+55
-17
@@ -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<typename Type> or CW<typename Type> 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<FEXCore::IR::IROp_Add>();
|
||||
// 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
|
||||
```
|
||||
|
||||
+126
-64
@@ -3,6 +3,7 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
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<OrderedNodeHeader*>(Node.GetNode(BaseList));
|
||||
Node = RealNode->Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NodeWrapperIterator operator--() {
|
||||
OrderedNodeHeader *RealNode = reinterpret_cast<OrderedNodeHeader*>(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<OrderedNode*, IROp_Header*>;
|
||||
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<OrderedNodeHeader*>(Node.GetNode(BaseList));
|
||||
Node = RealNode->Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NodeIterator operator--() {
|
||||
OrderedNodeHeader *RealNode = reinterpret_cast<OrderedNodeHeader*>(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 <FEXCore/IR/IRDefines.inc>
|
||||
|
||||
/* 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<OrderedNodeHeader*>(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<IROp_EndBlock>();
|
||||
|
||||
auto CurrentBlock = EndBlock->BlockHeader.GetNode(BaseList);
|
||||
Node = CurrentBlock->Header.Next;
|
||||
} else if (IROp->Op == OP_CODEBLOCK) {
|
||||
auto CodeBlock = IROp->C<IROp_CodeBlock>();
|
||||
|
||||
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<IROp_EndBlock>();
|
||||
|
||||
Node = BeginBlock->BlockHeader;
|
||||
} else if (IROp->Op == OP_CODEBLOCK) {
|
||||
auto PrevBlockWrapper = Node.GetNode(BaseList)->Header.Previous;
|
||||
auto PrevCodeBlock = PrevBlockWrapper.GetNode(BaseList)->Op(IRList)->C<IROp_CodeBlock>();
|
||||
|
||||
Node = PrevCodeBlock->Last;
|
||||
} else {
|
||||
Node = Node.GetNode(BaseList)->Header.Previous;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
static AllNodesIterator Invalid() {
|
||||
return AllNodesIterator(0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<bool>
|
||||
class IRListView;
|
||||
|
||||
|
||||
+35
-43
@@ -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<uintptr_t>(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<IROp_CodeBlock> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(CurrentIROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
FEXCore::IR::IROp_CodeBlock *CurrentIROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
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<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto NewOldNext = NextIROp->Next;
|
||||
NextIROp->Next = OldNext;
|
||||
OldNext = NewOldNext;
|
||||
}
|
||||
CodeNode->append(ListData.Begin(), Next);
|
||||
}
|
||||
|
||||
IRPair<IROp_CodeBlock> CreateNewCodeBlock();
|
||||
|
||||
+119
-5
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "FEXCore/IR/IR.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
@@ -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<IROp_IRHeader>(GetHeaderNode());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *GetOp(OrderedNode *Node) const {
|
||||
auto OpHeader = Node->Op(GetData());
|
||||
auto Op = OpHeader->template CW<T>();
|
||||
|
||||
// If we are casting to something narrower than just the header, check the opcode.
|
||||
if constexpr (!std::is_same<T, IROp_Header>::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 <typename T>
|
||||
T *GetOp(OrderedNodeWrapper Wrapper) const {
|
||||
auto Node = Wrapper.GetNode(GetListData());
|
||||
return GetOp<T>(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<IROp_CodeBlock>(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<uintptr_t>(ListData), Wrapped);
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), reinterpret_cast<uintptr_t>(IRData), Wrapped);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,15 +219,26 @@ public:
|
||||
{
|
||||
OrderedNodeWrapper Wrapped;
|
||||
Wrapped.NodeOffset = 0;
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), Wrapped);
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), reinterpret_cast<uintptr_t>(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<uintptr_t>(ListData), Node);
|
||||
iterator at(OrderedNodeWrapper Wrapped) const noexcept {
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), reinterpret_cast<uintptr_t>(IRData), Wrapped);
|
||||
}
|
||||
|
||||
iterator at(uint32_t ID) const noexcept {
|
||||
OrderedNodeWrapper Wrapped;
|
||||
Wrapped.NodeOffset = ID * sizeof(OrderedNode);
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), reinterpret_cast<uintptr_t>(IRData), Wrapped);
|
||||
}
|
||||
|
||||
iterator at(OrderedNode *Node) const noexcept {
|
||||
auto Wrapped = Node->Wrapped(reinterpret_cast<uintptr_t>(ListData));
|
||||
return iterator(reinterpret_cast<uintptr_t>(ListData), reinterpret_cast<uintptr_t>(IRData), Wrapped);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -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<IROp_CodeBlock> 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<FEXCore::IR::IROp_CodeBlock>();
|
||||
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;
|
||||
|
||||
Reference in new issue
Block a user