Files
FEX-Emu--FEX/External/FEXCore/Source/Interface/IR/IREmitter.cpp
T
Ryan Houdek 14fe95bd14 IR: Removes NumArgs member from IR ops
Split off from #2243 to remove each member individually.

Shaves 8-bits off of each IR op.
No need to cart around this data when it is constant for each operation.
Especially since most optimization passes don't need the data anyway.

Needed to add a new `GetRAArgs` to get the number of SSA arguments that
get RA versus `GetArgs` which returns all SSA arguments the IR operation
owns. This is what was causing #2243 to fail CI since it needs to know
the difference in some places.
2023-01-30 11:53:05 -08:00

220 lines
5.8 KiB
C++

/*
$info$
meta: ir|emitter ~ C++ Functions to generate IR. See IR.json for spec.
tags: ir|emitter
$end_info$
*/
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <array>
#include <stdint.h>
#include <string.h>
#include <vector>
namespace FEXCore::IR {
bool IsFragmentExit(FEXCore::IR::IROps Op) {
switch (Op) {
case OP_EXITFUNCTION:
case OP_BREAK:
return true;
default:
return false;
}
}
bool IsBlockExit(FEXCore::IR::IROps Op) {
switch(Op) {
case OP_JUMP:
case OP_CONDJUMP:
return true;
default:
return IsFragmentExit(Op);
}
}
FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(OrderedNode *Node) {
auto Class = GetOpRegClass(Node);
switch (Class) {
case GPRClass:
case GPRPairClass:
case FPRClass:
case GPRFixedClass:
case FPRFixedClass:
case InvalidClass:
return Class;
default: break;
}
// Complex case, needs to be handled on an op by op basis
uintptr_t DataBegin = DualListData.DataBegin();
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
switch (IROp->Op) {
case IROps::OP_LOADREGISTER: {
auto Op = IROp->C<IROp_LoadRegister>();
return Op->Class;
break;
}
case IROps::OP_LOADCONTEXT: {
auto Op = IROp->C<IROp_LoadContext>();
return Op->Class;
break;
}
case IROps::OP_LOADCONTEXTINDEXED: {
auto Op = IROp->C<IROp_LoadContextIndexed>();
return Op->Class;
break;
}
case IROps::OP_FILLREGISTER: {
auto Op = IROp->C<IROp_FillRegister>();
return Op->Class;
break;
}
case IROps::OP_LOADMEM: {
auto Op = IROp->C<IROp_LoadMem>();
return Op->Class;
break;
}
case IROps::OP_LOADMEMTSO: {
auto Op = IROp->C<IROp_LoadMemTSO>();
return Op->Class;
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled op type: {} {} in argument class validation",
ToUnderlying(IROp->Op), GetOpName(Node));
break;
}
return InvalidClass;
}
void IREmitter::ResetWorkingList() {
DualListData.Reset();
CodeBlocks.clear();
CurrentWriteCursor = nullptr;
// This is necessary since we do "null" pointer checks
InvalidNode = reinterpret_cast<OrderedNode*>(DualListData.ListAllocate(sizeof(OrderedNode)));
memset(InvalidNode, 0, sizeof(OrderedNode));
CurrentCodeBlock = nullptr;
}
void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator Begin, AllNodesIterator End) {
uintptr_t ListBegin = DualListData.ListBegin();
auto NodeId = Node->Wrapped(ListBegin).ID();
while (Begin != End) {
auto [RealNode, IROp] = Begin();
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
if (IROp->Args[i].ID() == NodeId) {
Node->RemoveUse();
NewNode->AddUse();
IROp->Args[i].NodeOffset = NewNode->Wrapped(ListBegin).NodeOffset;
// We can stop searching once all uses of the node are gone.
if (Node->NumUses == 0) {
return;
}
}
}
++Begin;
}
}
void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg) {
uintptr_t ListBegin = DualListData.ListBegin();
uintptr_t DataBegin = DualListData.DataBegin();
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
OrderedNodeWrapper OldArgWrapper = IROp->Args[Arg];
OrderedNode *OldArg = OldArgWrapper.GetNode(ListBegin);
OldArg->RemoveUse();
NewArg->AddUse();
IROp->Args[Arg].NodeOffset = NewArg->Wrapped(ListBegin).NodeOffset;
}
void IREmitter::RemoveArgUses(OrderedNode *Node) {
uintptr_t ListBegin = DualListData.ListBegin();
uintptr_t DataBegin = DualListData.DataBegin();
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
auto ArgNode = IROp->Args[i].GetNode(ListBegin);
ArgNode->RemoveUse();
}
}
void IREmitter::Remove(OrderedNode *Node) {
RemoveArgUses(Node);
Node->Unlink(DualListData.ListBegin());
}
IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode* insertAfter) {
auto OldCursor = GetWriteCursor();
auto CodeNode = CreateCodeNode();
if (insertAfter) {
LinkCodeBlocks(insertAfter, CodeNode);
} else {
LOGMAN_THROW_AA_FMT(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
// Find last block
auto LastBlock = CurrentCodeBlock;
while (LastBlock->Header.Next.GetNode(DualListData.ListBegin()) != InvalidNode)
LastBlock = LastBlock->Header.Next.GetNode(DualListData.ListBegin());
// Append it after the last block
LinkCodeBlocks(LastBlock, CodeNode);
}
SetWriteCursor(OldCursor);
return CodeNode;
}
void IREmitter::SetCurrentCodeBlock(OrderedNode *Node) {
CurrentCodeBlock = Node;
LOGMAN_THROW_A_FMT(Node->Op(DualListData.DataBegin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '{}'", IR::GetName(Node->Op(DualListData.DataBegin())->Op));
SetWriteCursor(Node->Op(DualListData.DataBegin())->CW<IROp_CodeBlock>()->Begin.GetNode(DualListData.ListBegin()));
}
void IREmitter::ReplaceWithConstant(OrderedNode *Node, uint64_t Value) {
auto Header = Node->Op(DualListData.DataBegin());
if (IRSizes[Header->Op] >= sizeof(IROp_Constant)) {
// Unlink any arguments the node currently has
RemoveArgUses(Node);
// Overwrite data with the new constant op
Header->Op = OP_CONSTANT;
auto Const = Header->CW<IROp_Constant>();
Const->Constant = Value;
} else {
// Fallback path for when the node to overwrite is too small
auto cursor = GetWriteCursor();
SetWriteCursor(Node);
auto NewNode = _Constant(Value);
ReplaceAllUsesWith(Node, NewNode);
SetWriteCursor(cursor);
}
}
}