Merge pull request #365 from phire/IR_iterators

Refactor the IR Interface to prevent "foot-shooting"
This commit is contained in:
Ryan Houdek authored and GitHub committed 2020-09-16 11:24:44 -07:00
commit d424cf0eee
25 files changed
+861 -1388

No files matched your search

+22 -28
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
@@ -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);
}
}
+55 -17
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+5 -8
View File
@@ -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;