mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-08 15:00:18 +02:00
IR: Add DeadGPRStoreElimination
This is based on the Flag Elimination pass, and is not optimal - but does eliminate quite a few dead stores.
This commit is contained in:
1 parent
462cb39043
commit
5fa5e49c58
4 files changed
+157
No files matched your search
+1
@@ -200,6 +200,7 @@ set (SRCS
|
||||
Interface/IR/Passes/PhiValidation.cpp
|
||||
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
|
||||
Interface/IR/Passes/DeadFlagStoreElimination.cpp
|
||||
Interface/IR/Passes/DeadGPRStoreElimination.cpp
|
||||
Interface/IR/Passes/RegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/SyscallOptimization.cpp
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ void PassManager::AddDefaultPasses(bool InlineConstants) {
|
||||
InsertPass(CreateConstProp(InlineConstants));
|
||||
////// InsertPass(CreateDeadFlagCalculationEliminination());
|
||||
InsertPass(CreateDeadFlagStoreElimination());
|
||||
InsertPass(CreateDeadGPRStoreElimination());
|
||||
InsertPass(CreateSyscallOptimization());
|
||||
InsertPass(CreatePassDeadCodeElimination());
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ FEXCore::IR::Pass* CreateContextLoadStoreElimination();
|
||||
FEXCore::IR::Pass* CreateSyscallOptimization();
|
||||
FEXCore::IR::Pass* CreateDeadFlagCalculationEliminination();
|
||||
FEXCore::IR::Pass* CreateDeadFlagStoreElimination();
|
||||
FEXCore::IR::Pass* CreateDeadGPRStoreElimination();
|
||||
FEXCore::IR::Pass* CreatePassDeadCodeElimination();
|
||||
FEXCore::IR::Pass* CreateIRCompaction();
|
||||
FEXCore::IR::RegisterAllocationPass* CreateRegisterAllocationPass();
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "Interface/IR/PassManager.h"
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
class DeadGPRStoreElimination final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
struct GPRInfo {
|
||||
uint32_t reads { 0 };
|
||||
uint32_t writes { 0 };
|
||||
uint32_t kill { 0 };
|
||||
};
|
||||
|
||||
bool IsFullGPR(uint32_t Offset, uint8_t Size) {
|
||||
if (Size != 8)
|
||||
return false;
|
||||
if (Offset & 7)
|
||||
return false;
|
||||
|
||||
if (Offset < 8 || Offset >= (17 * 8))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsGPR(uint32_t Offset) {
|
||||
|
||||
if (Offset < 8 || Offset >= (17 * 8))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t GPRBit(uint32_t Offset) {
|
||||
if (!IsGPR(Offset)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1 << ((Offset - 8)/8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is a temporary pass to detect simple multiblock dead GPR stores
|
||||
*
|
||||
* First pass computes which GPRs are read and written per block
|
||||
*
|
||||
* Second pass computes which GPRs are stored, but overwritten by the next block(s).
|
||||
* It also propagates this information a few times to catch dead GPRs across multiple blocks.
|
||||
*
|
||||
* Third pass removes the dead stores.
|
||||
*
|
||||
*/
|
||||
bool DeadGPRStoreElimination::Run(IREmitter *IREmit) {
|
||||
std::map<OrderedNode*, GPRInfo> GPRMap;
|
||||
|
||||
bool Changed = false;
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
|
||||
// Pass 1
|
||||
// Compute GPRs read/writes per block
|
||||
// This is conservative and doesn't try to be smart about loads after writes
|
||||
{
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
|
||||
if (IROp->Op == OP_STORECONTEXT) {
|
||||
auto Op = IROp->CW<IR::IROp_StoreContext>();
|
||||
if (IsFullGPR(Op->Offset, IROp->Size))
|
||||
GPRMap[BlockNode].writes |= GPRBit(Op->Offset);
|
||||
else
|
||||
GPRMap[BlockNode].reads |= GPRBit(Op->Offset);
|
||||
}
|
||||
else if (IROp->Op == OP_STORECONTEXTINDEXED ||
|
||||
IROp->Op == OP_LOADCONTEXTINDEXED ||
|
||||
IROp->Op == OP_LOADCONTEXTPAIR ||
|
||||
IROp->Op == OP_STORECONTEXTPAIR) {
|
||||
// We can't track through these
|
||||
GPRMap[BlockNode].reads = -1;
|
||||
}
|
||||
else if (IROp->Op == OP_LOADCONTEXT) {
|
||||
auto Op = IROp->CW<IR::IROp_LoadContext>();
|
||||
GPRMap[BlockNode].reads |= GPRBit(Op->Offset);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
// Compute GPRs that are stored, but always ovewritten in the next blocks
|
||||
// Propagate the information a few times to eliminate more
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
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 = CurrentIR.GetNode(Op->Header.Args[0]);
|
||||
|
||||
// stores to remove are written by the next block but not read
|
||||
GPRMap[BlockNode].kill = GPRMap[TargetNode].writes & ~(GPRMap[TargetNode].reads) & ~GPRMap[BlockNode].reads;
|
||||
|
||||
// GPRs that are written by the next block can be considered as written by this block, if not read
|
||||
GPRMap[BlockNode].writes |= GPRMap[BlockNode].kill & ~GPRMap[BlockNode].reads;
|
||||
}
|
||||
else if (IROp->Op == OP_CONDJUMP) {
|
||||
auto Op = IROp->CW<IR::IROp_CondJump>();
|
||||
|
||||
OrderedNode *TrueTargetNode = CurrentIR.GetNode(Op->TrueBlock);
|
||||
OrderedNode *FalseTargetNode = CurrentIR.GetNode(Op->FalseBlock);
|
||||
|
||||
// stores to remove are written by the next blocks but not read
|
||||
GPRMap[BlockNode].kill = GPRMap[TrueTargetNode].writes & ~(GPRMap[TrueTargetNode].reads) & ~GPRMap[BlockNode].reads;
|
||||
GPRMap[BlockNode].kill &= GPRMap[FalseTargetNode].writes & ~(GPRMap[FalseTargetNode].reads) & ~GPRMap[BlockNode].reads;
|
||||
|
||||
// GPRs that are written by the next blocks can be considered as written by this block, if not read
|
||||
GPRMap[BlockNode].writes |= GPRMap[BlockNode].kill & ~GPRMap[BlockNode].reads;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3
|
||||
// Remove the dead stores
|
||||
{
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
|
||||
if (IROp->Op == OP_STORECONTEXT) {
|
||||
auto Op = IROp->CW<IR::IROp_StoreContext>();
|
||||
// If this OP_STORECONTEXT is never read, remove it
|
||||
if (GPRMap[BlockNode].kill & GPRBit(Op->Offset)) {
|
||||
IREmit->Remove(CodeNode);
|
||||
//printf("Removed dead store %d, %d\n", Op->Offset, IROp->Size);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
FEXCore::IR::Pass* CreateDeadGPRStoreElimination() {
|
||||
return new DeadGPRStoreElimination{};
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user