mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-26 02:00:47 +01:00
I had to change how blocks are represented to make it easier to parse This required a fairly substantial refactor that makes it so blocks are represented differently and we can walk them sequentially. This will make future analysis easier to deal with. Had to rewrite the passes and core's parsing of the IR afterwards. Moved RA in to a optimization pass to be shared between the JIT backends This works because x86-64 and AArch64 RA can be identical. Still doesn't support PHI nodes or spilling correctly, this is the first step in the process of getting there.
137 lines
4.3 KiB
C++
137 lines
4.3 KiB
C++
#include "Interface/IR/PassManager.h"
|
|
#include "Interface/Core/OpcodeDispatcher.h"
|
|
#include <FEXCore/Core/CoreState.h>
|
|
|
|
namespace FEXCore::IR {
|
|
class DCSE final : public FEXCore::IR::Pass {
|
|
public:
|
|
bool Run(OpDispatchBuilder *Disp) override;
|
|
};
|
|
|
|
bool DCSE::Run(OpDispatchBuilder *Disp) {
|
|
//printf("Doing DCSE run\n");
|
|
return false;
|
|
}
|
|
|
|
FEXCore::IR::Pass* CreatePassDeadContextStoreElimination() {
|
|
return new DCSE{};
|
|
}
|
|
|
|
class RCLE final : public FEXCore::IR::Pass {
|
|
public:
|
|
bool Run(OpDispatchBuilder *Disp) override;
|
|
};
|
|
|
|
static bool IsAlignedGPR(uint8_t Size, uint32_t Offset, uint8_t *greg) {
|
|
if (Size != 8) return false;
|
|
if (Offset & 0b111) return false;
|
|
if (Offset < offsetof(FEXCore::Core::CPUState, gregs[0]) || Offset > offsetof(FEXCore::Core::CPUState, gregs[15])) return false;
|
|
|
|
*greg = (Offset - offsetof(FEXCore::Core::CPUState, gregs[0])) / 8;
|
|
return true;
|
|
}
|
|
|
|
static bool IsGPR(uint32_t Offset, uint8_t *greg) {
|
|
if (Offset < offsetof(FEXCore::Core::CPUState, gregs[0]) || Offset > offsetof(FEXCore::Core::CPUState, gregs[15])) return false;
|
|
|
|
*greg = (Offset - offsetof(FEXCore::Core::CPUState, gregs[0])) / 8;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool RCLE::Run(OpDispatchBuilder *Disp) {
|
|
bool Changed = false;
|
|
auto CurrentIR = Disp->ViewIR();
|
|
std::array<OrderedNodeWrapper::NodeOffsetType, 16> LastValidGPRStores{};
|
|
auto OriginalWriteCursor = Disp->GetWriteCursor();
|
|
|
|
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);
|
|
|
|
if (IROp->Op == OP_STORECONTEXT) {
|
|
auto Op = IROp->CW<IR::IROp_StoreContext>();
|
|
// Make sure we are within GREG state
|
|
uint8_t greg = ~0;
|
|
if (IsAlignedGPR(Op->Size, Op->Offset, &greg)) {
|
|
LastValidGPRStores[greg] = Op->Header.Args[0].NodeOffset;
|
|
} else if (IsGPR(Op->Offset, &greg)) {
|
|
// If we aren't overwriting the whole state then we don't want to track this value
|
|
LastValidGPRStores[greg] = 0;
|
|
}
|
|
}
|
|
|
|
if (IROp->Op == OP_LOADCONTEXT) {
|
|
auto Op = IROp->C<IR::IROp_LoadContext>();
|
|
|
|
// Make sure we are within GREG state
|
|
uint8_t greg = ~0;
|
|
if (IsAlignedGPR(Op->Size, Op->Offset, &greg)) {
|
|
if (LastValidGPRStores[greg] != 0) {
|
|
// If the last store matches this load value then we can replace the loaded value with the previous valid one
|
|
if (1) {
|
|
Disp->SetWriteCursor(CodeNode);
|
|
auto MovVal = Disp->_Mov(OrderedNodeWrapper::WrapOffset(LastValidGPRStores[greg]).GetNode(ListBegin));
|
|
Disp->ReplaceAllUsesWith(CodeNode, MovVal);
|
|
}
|
|
else {
|
|
Disp->ReplaceAllUsesWith(CodeNode, OrderedNodeWrapper::WrapOffset(LastValidGPRStores[greg]).GetNode(ListBegin));
|
|
}
|
|
Changed = true;
|
|
}
|
|
} else if (IsGPR(Op->Offset, &greg)) {
|
|
// If we aren't overwriting the whole state then we don't want to track this value
|
|
LastValidGPRStores[greg] = 0; // 0 is invalid
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// We don't track across block boundaries
|
|
LastValidGPRStores.fill(0);
|
|
}
|
|
|
|
Disp->SetWriteCursor(OriginalWriteCursor);
|
|
|
|
return Changed;
|
|
}
|
|
|
|
FEXCore::IR::Pass* CreateRedundantContextLoadElimination() {
|
|
return new RCLE{};
|
|
}
|
|
|
|
}
|
|
|
|
|