mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 20:00:16 +02:00
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.
220 lines
5.8 KiB
C++
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|