diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index ddc40e138..6de0d03b3 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -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 ) diff --git a/External/FEXCore/Source/Interface/IR/PassManager.cpp b/External/FEXCore/Source/Interface/IR/PassManager.cpp index a1211331b..a28750f82 100644 --- a/External/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/External/FEXCore/Source/Interface/IR/PassManager.cpp @@ -9,6 +9,7 @@ void PassManager::AddDefaultPasses(bool InlineConstants) { InsertPass(CreateConstProp(InlineConstants)); ////// InsertPass(CreateDeadFlagCalculationEliminination()); InsertPass(CreateDeadFlagStoreElimination()); + InsertPass(CreateDeadGPRStoreElimination()); InsertPass(CreateSyscallOptimization()); InsertPass(CreatePassDeadCodeElimination()); diff --git a/External/FEXCore/Source/Interface/IR/Passes.h b/External/FEXCore/Source/Interface/IR/Passes.h index 55d95996b..1e5a37b34 100644 --- a/External/FEXCore/Source/Interface/IR/Passes.h +++ b/External/FEXCore/Source/Interface/IR/Passes.h @@ -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(); diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadGPRStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadGPRStoreElimination.cpp new file mode 100644 index 000000000..685daf9f0 --- /dev/null +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadGPRStoreElimination.cpp @@ -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 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(); + 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(); + 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(); + 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(); + + 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(); + // 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{}; +} + +} \ No newline at end of file