diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index a094807e3..694361abc 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -137,7 +137,6 @@ set (SRCS Interface/IR/IREmitter.cpp Interface/IR/PassManager.cpp Interface/IR/Passes/ConstProp.cpp - Interface/IR/Passes/DeadCodeElimination.cpp Interface/IR/Passes/DeadContextStoreElimination.cpp Interface/IR/Passes/IRDumperPass.cpp Interface/IR/Passes/IRValidation.cpp diff --git a/FEXCore/Source/Interface/IR/PassManager.cpp b/FEXCore/Source/Interface/IR/PassManager.cpp index 2eaccc952..f8da219de 100644 --- a/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/FEXCore/Source/Interface/IR/PassManager.cpp @@ -79,13 +79,9 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx, bool Inli } InsertPass(CreateDeadStoreElimination()); - InsertPass(CreatePassDeadCodeElimination()); InsertPass(CreateConstProp(InlineConstants, ctx->HostFeatures.SupportsTSOImm9, Is64BitMode())); - - InsertPass(CreateDeadFlagCalculationEliminination()); - InsertPass(CreateInlineCallOptimization(&ctx->CPUID)); - InsertPass(CreatePassDeadCodeElimination()); + InsertPass(CreateDeadFlagCalculationEliminination()); } } @@ -100,20 +96,17 @@ void PassManager::InsertRegisterAllocationPass() { InsertPass(IR::CreateRegisterAllocationPass(), "RA"); } -bool PassManager::Run(IREmitter* IREmit) { +void PassManager::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::Run"); - bool Changed = false; for (const auto& Pass : Passes) { - Changed |= Pass->Run(IREmit); + Pass->Run(IREmit); } #if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED for (const auto& Pass : ValidationPasses) { - Changed |= Pass->Run(IREmit); + Pass->Run(IREmit); } #endif - - return Changed; } } // namespace FEXCore::IR diff --git a/FEXCore/Source/Interface/IR/PassManager.h b/FEXCore/Source/Interface/IR/PassManager.h index c7d56b83e..7b67be756 100644 --- a/FEXCore/Source/Interface/IR/PassManager.h +++ b/FEXCore/Source/Interface/IR/PassManager.h @@ -32,7 +32,7 @@ class IREmitter; class Pass { public: virtual ~Pass() = default; - virtual bool Run(IREmitter* IREmit) = 0; + virtual void Run(IREmitter* IREmit) = 0; void RegisterPassManager(PassManager* _Manager) { Manager = _Manager; @@ -58,7 +58,7 @@ public: void InsertRegisterAllocationPass(); - bool Run(IREmitter* IREmit); + void Run(IREmitter* IREmit); bool HasPass(fextl::string Name) const { return NameToPassMaping.contains(Name); diff --git a/FEXCore/Source/Interface/IR/Passes.h b/FEXCore/Source/Interface/IR/Passes.h index 04e414745..2250ef920 100644 --- a/FEXCore/Source/Interface/IR/Passes.h +++ b/FEXCore/Source/Interface/IR/Passes.h @@ -21,7 +21,6 @@ fextl::unique_ptr CreateContextLoadStoreElimination(bool Supp fextl::unique_ptr CreateInlineCallOptimization(const FEXCore::CPUIDEmu* CPUID); fextl::unique_ptr CreateDeadFlagCalculationEliminination(); fextl::unique_ptr CreateDeadStoreElimination(); -fextl::unique_ptr CreatePassDeadCodeElimination(); fextl::unique_ptr CreateRegisterAllocationPass(); fextl::unique_ptr CreateLongDivideEliminationPass(); diff --git a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index c20e931cb..05e16c8ce 100644 --- a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -200,33 +200,6 @@ static std::optional MemExtendedAddressing(IREmitter* IRE return std::nullopt; } -static OrderedNodeWrapper RemoveUselessMasking(IREmitter* IREmit, OrderedNodeWrapper src, uint64_t mask) { -#if 1 // HOTFIX: We need to clear up the meaning of opsize and dest size. See #594 - return src; -#else - auto IROp = IREmit->GetOpHeader(src); - if (IROp->Op == OP_AND) { - auto Op = IROp->C(); - uint64_t imm; - if (IREmit->IsValueConstant(IROp->Args[1], &imm) && ((imm & mask) == mask)) { - return RemoveUselessMasking(IREmit, IROp->Args[0], mask); - } - } else if (IROp->Op == OP_BFE) { - auto Op = IROp->C(); - if (Op->lsb == 0) { - uint64_t imm = 1ULL << (Op->Width - 1); - imm = (imm - 1) * 2 + 1; - - if ((imm & mask) == mask) { - return RemoveUselessMasking(IREmit, IROp->Args[0], mask); - } - } - } - - return src; -#endif -} - static bool IsBfeAlreadyDone(IREmitter* IREmit, OrderedNodeWrapper src, uint64_t Width) { auto IROp = IREmit->GetOpHeader(src); if (IROp->Op == OP_BFE) { @@ -245,16 +218,14 @@ public: , SupportsTSOImm9 {SupportsTSOImm9} , Is64BitMode(Is64BitMode) {} - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; bool InlineConstants; private: - bool HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR); - void LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR); - bool ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); - bool ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); - bool ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR); + void HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR); + void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); + void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR); struct ConstPoolData { OrderedNode* Node; @@ -282,48 +253,9 @@ private: constexpr static uint32_t CONSTANT_POOL_RANGE_LIMIT = 500; }; -bool ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) { - bool Changed = false; - - // constants are pooled per block - for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { - for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { - if (IROp->Op == OP_CONSTANT) { - auto Op = IROp->C(); - const auto NewNodeID = CurrentIR.GetID(CodeNode); - - auto it = ConstPool.find(Op->Constant); - if (it != ConstPool.end()) { - const auto OldNodeID = it->second.NodeID; - - if ((NewNodeID.Value - OldNodeID.Value) > CONSTANT_POOL_RANGE_LIMIT) { - // Don't reuse if the live range is beyond the heurstic range. - // Update the tracked value to this new constant. - it->second.Node = CodeNode; - it->second.NodeID = NewNodeID; - continue; - } - - auto CodeIter = CurrentIR.at(CodeNode); - IREmit->ReplaceUsesWithAfter(CodeNode, it->second.Node, CodeIter); - Changed = true; - } else { - ConstPool[Op->Constant] = ConstPoolData { - .Node = CodeNode, - .NodeID = NewNodeID, - }; - } - } - } - ConstPool.clear(); - } - - return Changed; -} - -// LoadMem / StoreMem imm pooling -// If imms are close by, use address gen to generate the values instead of using a new imm -void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR) { +// Constants are pooled per block. Similarly for LoadMem / StoreMem, if imms are +// close by, use address gen to generate the values instead of using a new imm. +void ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) { for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_LOADMEM || IROp->Op == OP_STOREMEM) { @@ -351,139 +283,41 @@ void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListV AddressgenConsts[IREmit->UnwrapNode(IROp->Args[AddrIndex])] = Addr; } doneOp:; + } else if (IROp->Op == OP_CONSTANT) { + auto Op = IROp->C(); + const auto NewNodeID = CurrentIR.GetID(CodeNode); + + auto it = ConstPool.find(Op->Constant); + if (it != ConstPool.end()) { + const auto OldNodeID = it->second.NodeID; + + if ((NewNodeID.Value - OldNodeID.Value) > CONSTANT_POOL_RANGE_LIMIT) { + // Don't reuse if the live range is beyond the heurstic range. + // Update the tracked value to this new constant. + it->second.Node = CodeNode; + it->second.NodeID = NewNodeID; + continue; + } + + auto CodeIter = CurrentIR.at(CodeNode); + IREmit->ReplaceUsesWithAfter(CodeNode, it->second.Node, CodeIter); + } else { + ConstPool[Op->Constant] = ConstPoolData { + .Node = CodeNode, + .NodeID = NewNodeID, + }; + } } + IREmit->SetWriteCursor(CodeNode); } AddressgenConsts.clear(); + ConstPool.clear(); } } -bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { - bool Changed = false; - - switch (IROp->Op) { - // Generic handling - case OP_OR: - case OP_XOR: - case OP_NOT: - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_UMUL: - case OP_DIV: - case OP_UDIV: - case OP_LSHR: - case OP_ASHR: - case OP_LSHL: - case OP_ROR: { - for (int i = 0; i < IR::GetArgs(IROp->Op); i++) { - auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp)); - if (newArg.ID() != IROp->Args[i].ID()) { - IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg)); - Changed = true; - } - } - break; - } - case OP_AND: { - // if AND's arguments are imms, they are masking - for (int i = 0; i < IR::GetArgs(IROp->Op); i++) { - uint64_t imm = 0; - if (!IREmit->IsValueConstant(IROp->Args[i ^ 1], &imm)) { - continue; - } - - auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm); - - if (newArg.ID() != IROp->Args[i].ID()) { - IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg)); - Changed = true; - } - } - break; - } - - case OP_BFE: { - auto Op = IROp->C(); - - // Is this value already BFE'd? - if (IsBfeAlreadyDone(IREmit, Op->Src, Op->Width)) { - IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Src)); - break; - } - - // Is this value already ZEXT'd? - if (Op->lsb == 0) { - // LoadMem, LoadMemTSO & LoadContext ZExt - auto source = Op->Src; - auto sourceHeader = IREmit->GetOpHeader(source); - - if (Op->Width >= (sourceHeader->Size * 8) && - (sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) { - // Load mem / load ctx zexts, no need to vmem - IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source)); - break; - } - } - - // BFE does implicit masking, remove any masks leading to this, if possible - uint64_t imm = 1ULL << (Op->Width - 1); - imm = (imm - 1) * 2 + 1; - imm <<= Op->lsb; - - auto newArg = RemoveUselessMasking(IREmit, Op->Src, imm); - - if (newArg.ID() != Op->Src.ID()) { - IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg)); - Changed = true; - } - break; - } - - case OP_SBFE: { - auto Op = IROp->C(); - - // BFE does implicit masking - uint64_t imm = 1ULL << (Op->Width - 1); - imm = (imm - 1) * 2 + 1; - imm <<= Op->lsb; - - auto newArg = RemoveUselessMasking(IREmit, Op->Src, imm); - - if (newArg.ID() != Op->Src.ID()) { - IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg)); - Changed = true; - } - break; - } - - case OP_VMOV: { - // elim from load mem - auto source = IROp->Args[0]; - auto sourceHeader = IREmit->GetOpHeader(source); - - if (IROp->Size >= sourceHeader->Size && - (sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) { - // Load mem / load ctx zexts, no need to vmem - IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source)); - } else if (IROp->Size == sourceHeader->Size) { - // VMOV of same size - // XXX: This is unsafe of an optimization since in some cases we can't see through garbage data in the upper bits of a vector - // RCLSE generates VMOV instructions which are being used as a zero extension - // IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source)); - } - break; - } - default: break; - } - - return Changed; -} - // constprop + some more per instruction logic -bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { - bool Changed = false; - +void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { switch (IROp->Op) { case OP_LOADMEMTSO: { auto Op = IROp->CW(); @@ -501,8 +335,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current Op->OffsetScale = OffsetScale; IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset - - Changed = true; } break; } @@ -523,8 +355,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current Op->OffsetScale = OffsetScale; IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset - - Changed = true; } break; } @@ -544,8 +374,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current Op->OffsetScale = OffsetScale; IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset - - Changed = true; } break; } @@ -565,8 +393,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current Op->OffsetScale = OffsetScale; IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset - - Changed = true; } break; } @@ -588,8 +414,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current Op->OffsetScale = OffsetScale; IREmit->ReplaceNodeArgument(CodeNode, Op->Addr_Index, Arg0); // Addr IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, Arg1); // Offset - - Changed = true; } break; } @@ -607,11 +431,9 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IsConstant1 && IsConstant2 && IROp->Op == OP_ADD) { uint64_t NewConstant = (Constant1 + Constant2) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IsConstant1 && IsConstant2 && IROp->Op == OP_SUB) { uint64_t NewConstant = (Constant1 - Constant2) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IsConstant2 && !IsImmAddSub(Constant2) && IsImmAddSub(-Constant2)) { // If the second argument is constant, the immediate is not ImmAddSub, but when negated is. // So, negate the operation to negate (and inline) the constant. @@ -632,7 +454,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current // Replace the second source with the negated constant. IREmit->ReplaceNodeArgument(CodeNode, Op->Src2_Index, NegConstant); - Changed = true; } break; } @@ -646,7 +467,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current // This is a pattern that shows up with direction flag calculations if DF was set just before the operation. uint64_t NewConstant = (Constant1 - (Constant2 << Op->ShiftAmount)) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } break; } @@ -657,7 +477,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = (Constant1 & Constant2) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (Constant2 == 1) { // happens from flag calcs auto val = IREmit->GetOpHeader(IROp->Args[0]); @@ -666,12 +485,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) && Constant2 == 1 && Constant3 == 0) { IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0])); - Changed = true; } } else if (IROp->Args[0].ID() == IROp->Args[1].ID()) { // AND with same value results in original value IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0])); - Changed = true; } break; } @@ -682,11 +499,9 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = Constant1 | Constant2; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IROp->Args[0].ID() == IROp->Args[1].ID()) { // OR with same value results in original value IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0])); - Changed = true; } break; } @@ -698,7 +513,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = Constant1 | (Constant2 << Op->BitShift); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } break; } @@ -710,7 +524,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = Constant1 | (Constant2 >> Op->BitShift); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } break; } @@ -721,12 +534,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = Constant1 ^ Constant2; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IROp->Args[0].ID() == IROp->Args[1].ID()) { // XOR with same value results to zero IREmit->SetWriteCursor(CodeNode); IREmit->ReplaceAllUsesWith(CodeNode, IREmit->_Constant(0)); - Changed = true; } else { // XOR with zero results in the nonzero source for (unsigned i = 0; i < 2; ++i) { @@ -741,7 +552,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current IREmit->SetWriteCursor(CodeNode); OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[1 - i]); IREmit->ReplaceAllUsesWith(CodeNode, Arg); - Changed = true; break; } } @@ -753,7 +563,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant)) { uint64_t NewConstant = -Constant; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } break; } @@ -766,18 +575,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current uint64_t ShiftMask = IROp->Size == 8 ? 63 : 31; uint64_t NewConstant = (Constant1 << (Constant2 & ShiftMask)) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) { IREmit->SetWriteCursor(CodeNode); OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]); IREmit->ReplaceAllUsesWith(CodeNode, Arg); - Changed = true; - } else { - auto newArg = RemoveUselessMasking(IREmit, IROp->Args[1], IROp->Size * 8 - 1); - if (newArg.ID() != IROp->Args[1].ID()) { - IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->UnwrapNode(newArg)); - Changed = true; - } } break; } @@ -790,36 +591,47 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current uint64_t ShiftMask = IROp->Size == 8 ? 63 : 31; uint64_t NewConstant = (Constant1 >> (Constant2 & ShiftMask)) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) { IREmit->SetWriteCursor(CodeNode); OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]); IREmit->ReplaceAllUsesWith(CodeNode, Arg); - Changed = true; - } else { - auto newArg = RemoveUselessMasking(IREmit, IROp->Args[1], IROp->Size * 8 - 1); - if (newArg.ID() != IROp->Args[1].ID()) { - IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->UnwrapNode(newArg)); - Changed = true; - } } break; } case OP_BFE: { auto Op = IROp->C(); uint64_t Constant; + + // Is this value already BFE'd? + if (IsBfeAlreadyDone(IREmit, Op->Src, Op->Width)) { + IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Src)); + break; + } + + // Is this value already ZEXT'd? + if (Op->lsb == 0) { + // LoadMem, LoadMemTSO & LoadContext ZExt + auto source = Op->Src; + auto sourceHeader = IREmit->GetOpHeader(source); + + if (Op->Width >= (sourceHeader->Size * 8) && + (sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) { + // Load mem / load ctx zexts, no need to vmem + IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source)); + break; + } + } + if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Src, &Constant)) { uint64_t SourceMask = Op->Width == 64 ? ~0ULL : ((1ULL << Op->Width) - 1); SourceMask <<= Op->lsb; uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IROp->Size == CurrentIR.GetOp(IROp->Args[0])->Size && Op->Width == (IROp->Size * 8) && Op->lsb == 0) { // A BFE that extracts all bits results in original value // XXX - This is broken for now - see https://github.com/FEX-Emu/FEX/issues/351 // IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0])); - // Changed = true; } else if (Op->Width == 1 && Op->lsb == 0) { // common from flag codegen auto val = IREmit->GetOpHeader(IROp->Args[0]); @@ -829,7 +641,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) && Constant2 == 1 && Constant3 == 0) { IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0])); - Changed = true; } } @@ -850,8 +661,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current NewConstant >>= 64 - Op->Width; NewConstant &= DestMask; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - - Changed = true; } break; } @@ -868,7 +677,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current NewConstant |= (ConstantSrc & SourceMask) << Op->lsb; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (SrcIsConstant && HasConsecutiveBits(ConstantSrc, Op->Width)) { // We are trying to insert constant, if it is a bitfield of only set bits then we can orr or and it. IREmit->SetWriteCursor(CodeNode); @@ -878,12 +686,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (ConstantSrc & 1) { auto orr = IREmit->_Or(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(NewConstant)); IREmit->ReplaceAllUsesWith(CodeNode, orr); - Changed = true; } else { // We are wanting to clear the bitfield. auto andn = IREmit->_Andn(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(NewConstant)); IREmit->ReplaceAllUsesWith(CodeNode, andn); - Changed = true; } } break; @@ -895,27 +701,35 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { uint64_t NewConstant = (Constant1 * Constant2) & getMask(IROp); IREmit->ReplaceWithConstant(CodeNode, NewConstant); - Changed = true; } else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && std::popcount(Constant2) == 1) { if (IROp->Size == 4 || IROp->Size == 8) { uint64_t amt = std::countr_zero(Constant2); IREmit->SetWriteCursor(CodeNode); auto shift = IREmit->_Lshl(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(amt)); IREmit->ReplaceAllUsesWith(CodeNode, shift); - Changed = true; } } break; } + + case OP_VMOV: { + // elim from load mem + auto source = IROp->Args[0]; + auto sourceHeader = IREmit->GetOpHeader(source); + + if (IROp->Size >= sourceHeader->Size && + (sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) { + // Load mem / load ctx zexts, no need to vmem + IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source)); + } + break; + } default: break; } - - return Changed; } -bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) { +void ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) { InlineConstantGen.clear(); - bool Changed = false; for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { switch (IROp->Op) { @@ -935,8 +749,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) } IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } break; } @@ -952,10 +764,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) // constant would be in bounds after the JIT's 24/16 shift. if (IsImmAddSub(Constant2) && IROp->Size >= 4) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } else if (IROp->Op == OP_SUBNZCV || IROp->Op == OP_SUBWITHFLAGS || IROp->Op == OP_SUB) { // TODO: Generalize this @@ -964,7 +773,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Constant1 == 0) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0])); IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0)); - Changed = true; } } } @@ -978,7 +786,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Constant1 == 0) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0])); IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0)); - Changed = true; } } @@ -990,7 +797,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Constant1 == 0) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0])); IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0)); - Changed = true; } } @@ -1002,10 +808,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { if (IsImmAddSub(Constant2)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } @@ -1014,7 +817,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Constant1 == 0) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0])); IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0)); - Changed = true; } } break; @@ -1024,10 +826,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) { if (IsImmLogical(Constant1, IROp->Size * 8)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1)); - - Changed = true; } } break; @@ -1037,10 +836,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) { if (IsImmAddSub(Constant1)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1)); - - Changed = true; } } @@ -1071,7 +867,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[0], &Constant0) && (Constant0 == 1 || Constant0 == AllOnes)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0])); - IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant0)); } } @@ -1083,10 +878,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { if (IsImmAddSub(Constant2)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } break; @@ -1096,12 +888,8 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) uint64_t Constant {}; if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) { - IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP)); - IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant)); - - Changed = true; } else { auto NewRIP = IREmit->GetOpHeader(Op->NewRIP); if (NewRIP->Op == OP_ENTRYPOINTOFFSET) { @@ -1109,7 +897,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP)); IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(IR::SizeToOpSize(EO->Header.Size), EO->Offset)); - Changed = true; } } break; @@ -1123,10 +910,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) { if (IsImmLogical(Constant2, IROp->Size * 8)) { IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1])); - IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } break; @@ -1138,10 +922,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) { if (IsImmMemory(Constant2, IROp->Size)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } break; @@ -1153,10 +934,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) { if (IsImmMemory(Constant2, IROp->Size)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } break; @@ -1169,10 +947,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) { if (IsTSOImm9(Constant2)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } } @@ -1186,10 +961,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) { if (IsTSOImm9(Constant2)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } } @@ -1201,10 +973,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) uint64_t Constant {}; if (IREmit->IsValueConstant(Op->Direction, &Constant)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, CreateInlineConstant(IREmit, Constant)); - - Changed = true; } break; } @@ -1214,10 +983,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) uint64_t Constant {}; if (IREmit->IsValueConstant(Op->Direction, &Constant)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, CreateInlineConstant(IREmit, Constant)); - - Changed = true; } break; } @@ -1229,10 +995,7 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Offset, &Constant2)) { if (IsImmMemory(Constant2, IROp->Size)) { IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset)); - IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } } break; @@ -1240,38 +1003,22 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) default: break; } } - - return Changed; } -bool ConstProp::Run(IREmitter* IREmit) { +void ConstProp::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::ConstProp"); - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); - auto OriginalWriteCursor = IREmit->GetWriteCursor(); - if (HandleConstantPools(IREmit, CurrentIR)) { - Changed = true; - } - - LoadMemStoreMemImmediatePooling(IREmit, CurrentIR); + HandleConstantPools(IREmit, CurrentIR); for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - if (ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp)) { - Changed = true; - } - if (ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp)) { - Changed = true; - } + ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp); } - if (InlineConstants && ConstantInlining(IREmit, CurrentIR)) { - Changed = true; + if (InlineConstants) { + ConstantInlining(IREmit, CurrentIR); } - - IREmit->SetWriteCursor(OriginalWriteCursor); - return Changed; } fextl::unique_ptr CreateConstProp(bool InlineConstants, bool SupportsTSOImm9, bool Is64BitMode) { diff --git a/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp deleted file mode 100644 index 466529987..000000000 --- a/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MIT -/* -$info$ -tags: ir|opts -$end_info$ -*/ - -#include "Interface/IR/IREmitter.h" -#include "Interface/IR/PassManager.h" - -#include -#include - -#include - -namespace FEXCore::IR { - -class DeadCodeElimination final : public FEXCore::IR::Pass { - bool Run(IREmitter* IREmit) override; - -private: - void markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp); -}; - -bool DeadCodeElimination::Run(IREmitter* IREmit) { - FEXCORE_PROFILE_SCOPED("PassManager::DCE"); - auto CurrentIR = IREmit->ViewIR(); - bool Changed = false; - - for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { - - // Reverse iteration is not yet working with the iterators - auto BlockIROp = BlockHeader->CW(); - - // 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 [CodeNode, IROp] = CodeLast(); - - bool HasSideEffects = IR::HasSideEffects(IROp->Op); - - switch (IROp->Op) { - case OP_SYSCALL: - case OP_INLINESYSCALL: { - FEXCore::IR::SyscallFlags Flags {}; - if (IROp->Op == OP_SYSCALL) { - auto Op = IROp->C(); - Flags = Op->Flags; - } else { - auto Op = IROp->C(); - Flags = Op->Flags; - } - - if ((Flags & FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) == FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) { - HasSideEffects = false; - } - - break; - } - case OP_ATOMICFETCHADD: - case OP_ATOMICFETCHSUB: - case OP_ATOMICFETCHAND: - case OP_ATOMICFETCHCLR: - case OP_ATOMICFETCHOR: - case OP_ATOMICFETCHXOR: - case OP_ATOMICFETCHNEG: { - // If the result of the atomic fetch is completely unused, convert it to a non-fetching atomic operation. - if (CodeNode->GetUses() == 0) { - switch (IROp->Op) { - case OP_ATOMICFETCHADD: IROp->Op = OP_ATOMICADD; break; - case OP_ATOMICFETCHSUB: IROp->Op = OP_ATOMICSUB; break; - case OP_ATOMICFETCHAND: IROp->Op = OP_ATOMICAND; break; - case OP_ATOMICFETCHCLR: IROp->Op = OP_ATOMICCLR; break; - case OP_ATOMICFETCHOR: IROp->Op = OP_ATOMICOR; break; - case OP_ATOMICFETCHXOR: IROp->Op = OP_ATOMICXOR; break; - case OP_ATOMICFETCHNEG: IROp->Op = OP_ATOMICNEG; break; - default: FEX_UNREACHABLE; - } - Changed = true; - } - break; - } - default: break; - } - - // Skip over anything that has side effects - // Use count tracking can't safely remove anything with side effects - if (!HasSideEffects) { - if (CodeNode->GetUses() == 0) { - Changed = true; - IREmit->Remove(CodeNode); - } - } - - if (CodeLast == CodeBegin) { - break; - } - --CodeLast; - } - } - - return Changed; -} - -void DeadCodeElimination::markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp) {} - -fextl::unique_ptr CreatePassDeadCodeElimination() { - return fextl::make_unique(); -} - -} // namespace FEXCore::IR diff --git a/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp index bce8fa7c5..e2a6a108d 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp @@ -456,12 +456,9 @@ public: explicit RCLSE(bool SupportsAVX_) : SupportsAVX {SupportsAVX_} { ClassifyContextStruct(&ClassifiedStruct, SupportsAVX); - DCE = FEXCore::IR::CreatePassDeadCodeElimination(); } - bool Run(FEXCore::IR::IREmitter* IREmit) override; + void Run(FEXCore::IR::IREmitter* IREmit) override; private: - fextl::unique_ptr DCE; - ContextInfo ClassifiedStruct; fextl::unordered_map OffsetToBlockMap; @@ -473,16 +470,16 @@ private: ContextMemberInfo* RecordAccess(ContextInfo* ClassifiedInfo, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode* Node, FEXCore::IR::OrderedNode* StoreNode = nullptr); - bool HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag); + void HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag); // Classify context loads and stores. - bool ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, + void ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::NodeIterator BlockEnd); - bool ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, + void ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::OrderedNode* ValueNode); // Block local Passes - bool RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit); + void RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit); unsigned OffsetForReg(FEXCore::IR::RegisterClassType Class, unsigned Reg, unsigned Size) { if (Class == FEXCore::IR::FPRClass) { @@ -531,7 +528,7 @@ ContextMemberInfo* RCLSE::RecordAccess(ContextInfo* ClassifiedInfo, FEXCore::IR: return RecordAccess(Info, RegClass, Offset, Size, AccessType, ValueNode, StoreNode); } -bool RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, +void RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::NodeIterator BlockEnd) { auto Info = FindMemberInfo(LocalInfo, Offset, Size); ContextMemberInfo PreviousMemberInfoCopy = *Info; @@ -544,13 +541,11 @@ bool RCLSE::ClassifyContextLoad(FEXCore::IR::IREmitter* IREmit, ContextInfo* Loc // - Previous access was a store, and we are redundantly loading immediately after the store. Eliminating the store. IREmit->ReplaceAllUsesWithRange(CodeNode, PreviousMemberInfoCopy.ValueNode, IREmit->GetIterator(IREmit->WrapNode(CodeNode)), BlockEnd); RecordAccess(Info, Class, Offset, Size, LastAccessType::READ, PreviousMemberInfoCopy.ValueNode); - return true; } // TODO: Optimize the case of partial loads. - return false; } -bool RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, +void RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::RegisterClassType Class, uint32_t Offset, uint8_t Size, FEXCore::IR::OrderedNode* CodeNode, FEXCore::IR::OrderedNode* ValueNode) { auto Info = FindMemberInfo(LocalInfo, Offset, Size); ContextMemberInfo PreviousMemberInfoCopy = *Info; @@ -563,15 +558,13 @@ bool RCLSE::ClassifyContextStore(FEXCore::IR::IREmitter* IREmit, ContextInfo* Lo // Revisit when the new RA lands. #if 0 IREmit->Remove(PreviousMemberInfoCopy.StoreNode); - return true; #endif } // TODO: Optimize the case of partial stores. - return false; } -bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag) { +void RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInfo, FEXCore::IR::OrderedNode* CodeNode, unsigned Flag) { const auto FlagOffset = offsetof(FEXCore::Core::CPUState, flags[Flag]); auto Info = FindMemberInfo(LocalInfo, FlagOffset, 1); LastAccessType LastAccess = Info->Accessed; @@ -582,14 +575,10 @@ bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInf IREmit->SetWriteCursor(CodeNode); IREmit->ReplaceAllUsesWith(CodeNode, LastValueNode); RecordAccess(Info, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::READ, LastValueNode); - return true; } else if (IsReadAccess(LastAccess)) { IREmit->ReplaceAllUsesWith(CodeNode, LastValueNode); RecordAccess(Info, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::READ, LastValueNode); - return true; } - - return false; } /** @@ -624,11 +613,10 @@ bool RCLSE::HandleLoadFlag(FEXCore::IR::IREmitter* IREmit, ContextInfo* LocalInf * (%%176) StoreContext %175 i128, 0x10, 0xa0 */ -bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { +void RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { using namespace FEXCore; using namespace FEXCore::IR; - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); auto OriginalWriteCursor = IREmit->GetWriteCursor(); @@ -645,19 +633,19 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STORECONTEXT) { auto Op = IROp->CW(); - Changed |= ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value)); + ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value)); } else if (IROp->Op == OP_STOREREGISTER) { auto Op = IROp->CW(); auto Offset = OffsetForReg(Op->Class, Op->Reg, IROp->Size); - Changed |= ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value)); + ClassifyContextStore(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, CurrentIR.GetNode(Op->Value)); } else if (IROp->Op == OP_LOADREGISTER) { auto Op = IROp->CW(); auto Offset = OffsetForReg(Op->Class, Op->Reg, IROp->Size); - Changed |= ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, BlockEnd); + ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Offset, IROp->Size, CodeNode, BlockEnd); } else if (IROp->Op == OP_LOADCONTEXT) { auto Op = IROp->CW(); - Changed |= ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, BlockEnd); + ClassifyContextLoad(IREmit, &LocalInfo, Op->Class, Op->Offset, IROp->Size, CodeNode, BlockEnd); } else if (IROp->Op == OP_STOREFLAG) { const auto Op = IROp->CW(); const auto FlagOffset = offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag; @@ -668,7 +656,6 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { // Flags don't alias, so we can take the simple route here. Kill any flags that have been overwritten if (LastStoreNode != nullptr) { IREmit->Remove(LastStoreNode); - Changed = true; } } else if (IROp->Op == OP_INVALIDATEFLAGS) { auto Op = IROp->CW(); @@ -689,15 +676,14 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, FlagOffset, 1, LastAccessType::WRITE, IREmit->_Constant(0), CodeNode); IREmit->Remove(LastStoreNode); - Changed = true; } } } else if (IROp->Op == OP_LOADFLAG) { const auto Op = IROp->CW(); - Changed |= HandleLoadFlag(IREmit, &LocalInfo, CodeNode, Op->Flag); + HandleLoadFlag(IREmit, &LocalInfo, CodeNode, Op->Flag); } else if (IROp->Op == OP_LOADDF) { - Changed |= HandleLoadFlag(IREmit, &LocalInfo, CodeNode, X86State::RFLAG_DF_RAW_LOC); + HandleLoadFlag(IREmit, &LocalInfo, CodeNode, X86State::RFLAG_DF_RAW_LOC); } else if (IROp->Op == OP_SYSCALL || IROp->Op == OP_INLINESYSCALL) { FEXCore::IR::SyscallFlags Flags {}; if (IROp->Op == OP_SYSCALL) { @@ -720,13 +706,11 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter* IREmit) { } IREmit->SetWriteCursor(OriginalWriteCursor); - - return Changed; } -bool RCLSE::Run(FEXCore::IR::IREmitter* IREmit) { +void RCLSE::Run(FEXCore::IR::IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::RCLSE"); - return RedundantStoreLoadElimination(IREmit); + RedundantStoreLoadElimination(IREmit); } } // namespace diff --git a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp index a3f921f5a..d203f6c74 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp @@ -14,7 +14,6 @@ $end_info$ #include #include #include -#include #include #include @@ -24,71 +23,52 @@ namespace FEXCore::IR { constexpr int PropagationRounds = 5; +// Return a bit representing a single GPR or FPR. +static inline uint64_t RegBit(RegisterClassType Class, uint32_t Reg) { + uint32_t AdjustedReg = (Class == FPRClass) ? (32 + Reg) : Reg; + + return 1UL << AdjustedReg; +} + class DeadStoreElimination final : public FEXCore::IR::Pass { public: - explicit DeadStoreElimination() {} - bool Run(IREmitter* IREmit) override; - -private: - uint64_t FPRBit(RegisterClassType Class, uint32_t Reg) const { - return (Class == FPRClass) ? (1UL << Reg) : 0; - } + void Run(IREmitter* IREmit) override; }; -struct FlagInfo { +struct ReadWriteKill { uint64_t reads {0}; uint64_t writes {0}; uint64_t kill {0}; }; - -struct GPRInfo { - uint32_t reads {0}; - uint32_t writes {0}; - uint32_t kill {0}; -}; - -uint32_t GPRBit(RegisterClassType Class, uint32_t Reg) { - return (Class == GPRClass) ? (1U << Reg) : 0; -} - -struct FPRInfo { - uint32_t reads {0}; - uint32_t writes {0}; - uint32_t kill {0}; -}; - struct Info { - FlagInfo flag; - GPRInfo gpr; - FPRInfo fpr; + ReadWriteKill flag; + ReadWriteKill reg; }; /** - * @brief This is a temporary pass to detect simple multiblock dead flag/gpr/fpr stores + * @brief This is a temporary pass to detect simple multiblock dead flag/reg stores * - * First pass computes which flags/gprs/fprs are read and written per block + * First pass computes which flags/regs are read and written per block * - * Second pass computes which flags/gprs/fprs are stored, but overwritten by the next block(s). - * It also propagates this information a few times to catch dead flags/gprs/fprs across multiple blocks. + * Second pass computes which flags/regs are stored, but overwritten by the next block(s). + * It also propagates this information a few times to catch dead flags/regs across multiple blocks. * * Third pass removes the dead stores. * */ -bool DeadStoreElimination::Run(IREmitter* IREmit) { +void DeadStoreElimination::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::DSE"); - fextl::unordered_map InfoMap; - - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); + fextl::vector InfoMap(CurrentIR.GetSSACount()); // Pass 1 - // Compute flags/gprs/fprs read/writes per block + // Compute flags/regs read/writes per block // This is conservative and doesn't try to be smart about loads after writes { for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { - auto& BlockInfo = InfoMap[BlockNode]; + auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value]; for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STOREFLAG) { @@ -107,21 +87,17 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) { BlockInfo.flag.reads |= 1UL << X86State::RFLAG_DF_RAW_LOC; } else if (IROp->Op == OP_STOREREGISTER) { auto Op = IROp->C(); - - BlockInfo.gpr.writes |= GPRBit(Op->Class, Op->Reg); - BlockInfo.fpr.writes |= FPRBit(Op->Class, Op->Reg); + BlockInfo.reg.writes |= RegBit(Op->Class, Op->Reg); } else if (IROp->Op == OP_LOADREGISTER) { auto Op = IROp->C(); - - BlockInfo.gpr.reads |= GPRBit(Op->Class, Op->Reg); - BlockInfo.fpr.reads |= FPRBit(Op->Class, Op->Reg); + BlockInfo.reg.reads |= RegBit(Op->Class, Op->Reg); } } } } // Pass 2 - // Compute flags/gprs/fprs that are stored, but always ovewritten in the next blocks + // Compute flags/registers that are stored, but always ovewritten in the next blocks // Propagate the information a few times to eliminate more for (int i = 0; i < PropagationRounds; i++) { for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { @@ -131,43 +107,33 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) { if (IROp->Op == OP_JUMP) { auto Op = IROp->C(); - OrderedNode* TargetNode = CurrentIR.GetNode(Op->Header.Args[0]); - - auto& BlockInfo = InfoMap[BlockNode]; - auto& TargetInfo = InfoMap[TargetNode]; + auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value]; + auto& TargetInfo = InfoMap[Op->Header.Args[0].ID().Value]; // stores to remove are written by the next block but not read BlockInfo.flag.kill = TargetInfo.flag.writes & ~(TargetInfo.flag.reads) & ~BlockInfo.flag.reads; - BlockInfo.gpr.kill = TargetInfo.gpr.writes & ~(TargetInfo.gpr.reads) & ~BlockInfo.gpr.reads; - BlockInfo.fpr.kill = TargetInfo.fpr.writes & ~(TargetInfo.fpr.reads) & ~BlockInfo.fpr.reads; + BlockInfo.reg.kill = TargetInfo.reg.writes & ~(TargetInfo.reg.reads) & ~BlockInfo.reg.reads; // Flags that are written by the next block can be considered as written by this block, if not read BlockInfo.flag.writes |= BlockInfo.flag.kill & ~BlockInfo.flag.reads; - BlockInfo.gpr.writes |= BlockInfo.gpr.kill & ~BlockInfo.gpr.reads; - BlockInfo.fpr.writes |= BlockInfo.fpr.kill & ~BlockInfo.fpr.reads; + BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads; } else if (IROp->Op == OP_CONDJUMP) { auto Op = IROp->C(); - OrderedNode* TrueTargetNode = CurrentIR.GetNode(Op->TrueBlock); - OrderedNode* FalseTargetNode = CurrentIR.GetNode(Op->FalseBlock); - - auto& BlockInfo = InfoMap[BlockNode]; - auto& TrueTargetInfo = InfoMap[TrueTargetNode]; - auto& FalseTargetInfo = InfoMap[FalseTargetNode]; + auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value]; + auto& TrueTargetInfo = InfoMap[Op->TrueBlock.ID().Value]; + auto& FalseTargetInfo = InfoMap[Op->FalseBlock.ID().Value]; // stores to remove are written by the next blocks but not read BlockInfo.flag.kill = TrueTargetInfo.flag.writes & ~(TrueTargetInfo.flag.reads) & ~BlockInfo.flag.reads; - BlockInfo.gpr.kill = TrueTargetInfo.gpr.writes & ~(TrueTargetInfo.gpr.reads) & ~BlockInfo.gpr.reads; - BlockInfo.fpr.kill = TrueTargetInfo.fpr.writes & ~(TrueTargetInfo.fpr.reads) & ~BlockInfo.fpr.reads; + BlockInfo.reg.kill = TrueTargetInfo.reg.writes & ~(TrueTargetInfo.reg.reads) & ~BlockInfo.reg.reads; BlockInfo.flag.kill &= FalseTargetInfo.flag.writes & ~(FalseTargetInfo.flag.reads) & ~BlockInfo.flag.reads; - BlockInfo.gpr.kill &= FalseTargetInfo.gpr.writes & ~(FalseTargetInfo.gpr.reads) & ~BlockInfo.gpr.reads; - BlockInfo.fpr.kill &= FalseTargetInfo.fpr.writes & ~(FalseTargetInfo.fpr.reads) & ~BlockInfo.fpr.reads; + BlockInfo.reg.kill &= FalseTargetInfo.reg.writes & ~(FalseTargetInfo.reg.reads) & ~BlockInfo.reg.reads; // Flags that are written by the next blocks can be considered as written by this block, if not read BlockInfo.flag.writes |= BlockInfo.flag.kill & ~BlockInfo.flag.reads; - BlockInfo.gpr.writes |= BlockInfo.gpr.kill & ~BlockInfo.gpr.reads; - BlockInfo.fpr.writes |= BlockInfo.fpr.kill & ~BlockInfo.fpr.reads; + BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads; } } } @@ -176,7 +142,7 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) { // Remove the dead stores { for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { - auto& BlockInfo = InfoMap[BlockNode]; + auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value]; for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_STOREFLAG) { @@ -185,22 +151,18 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) { // If this StoreFlag is never read, remove it if (BlockInfo.flag.kill & (1UL << Op->Flag)) { IREmit->Remove(CodeNode); - Changed = true; } } else if (IROp->Op == OP_STOREREGISTER) { auto Op = IROp->C(); // If this OP_STOREREGISTER is never read, remove it - if ((BlockInfo.gpr.kill & GPRBit(Op->Class, Op->Reg)) || (BlockInfo.fpr.kill & FPRBit(Op->Class, Op->Reg))) { + if (BlockInfo.reg.kill & RegBit(Op->Class, Op->Reg)) { IREmit->Remove(CodeNode); - Changed = true; } } } } } - - return Changed; } fextl::unique_ptr CreateDeadStoreElimination() { diff --git a/FEXCore/Source/Interface/IR/Passes/IRDumperPass.cpp b/FEXCore/Source/Interface/IR/Passes/IRDumperPass.cpp index 235b87b1d..7ff615677 100644 --- a/FEXCore/Source/Interface/IR/Passes/IRDumperPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/IRDumperPass.cpp @@ -18,7 +18,7 @@ namespace FEXCore::IR::Debug { class IRDumper final : public FEXCore::IR::Pass { public: IRDumper(); - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: FEX_CONFIG_OPT(DumpIR, DUMPIR); @@ -37,7 +37,7 @@ IRDumper::IRDumper() { } } -bool IRDumper::Run(IREmitter* IREmit) { +void IRDumper::Run(IREmitter* IREmit) { auto RAPass = Manager->GetPass("RA"); IR::RegisterAllocationData* RA {}; if (RAPass) { @@ -71,8 +71,6 @@ bool IRDumper::Run(IREmitter* IREmit) { LogMan::Msg::IFmt("IR-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", HeaderOp->OriginalRIP, out.str()); } } - - return false; } fextl::unique_ptr CreateIRDumper() { diff --git a/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp b/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp index 76d6c8897..082c8f59b 100644 --- a/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp +++ b/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp @@ -32,7 +32,7 @@ IRValidation::~IRValidation() { NodeIsLive.Free(); } -bool IRValidation::Run(IREmitter* IREmit) { +void IRValidation::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::IRValidation"); bool HadError = false; @@ -267,8 +267,6 @@ bool IRValidation::Run(IREmitter* IREmit) { Errors.clear(); Warnings.clear(); } - - return false; } fextl::unique_ptr CreateIRValidation() { diff --git a/FEXCore/Source/Interface/IR/Passes/IRValidation.h b/FEXCore/Source/Interface/IR/Passes/IRValidation.h index 1a28de4d8..09e60f6dc 100644 --- a/FEXCore/Source/Interface/IR/Passes/IRValidation.h +++ b/FEXCore/Source/Interface/IR/Passes/IRValidation.h @@ -21,7 +21,7 @@ class RAValidation; class IRValidation final : public FEXCore::IR::Pass { public: ~IRValidation(); - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: diff --git a/FEXCore/Source/Interface/IR/Passes/InlineCallOptimization.cpp b/FEXCore/Source/Interface/IR/Passes/InlineCallOptimization.cpp index 709b8dba9..714239624 100644 --- a/FEXCore/Source/Interface/IR/Passes/InlineCallOptimization.cpp +++ b/FEXCore/Source/Interface/IR/Passes/InlineCallOptimization.cpp @@ -23,15 +23,14 @@ class InlineCallOptimization final : public FEXCore::IR::Pass { public: InlineCallOptimization(const FEXCore::CPUIDEmu* CPUID) : CPUID {CPUID} {} - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: const FEXCore::CPUIDEmu* CPUID; }; -bool InlineCallOptimization::Run(IREmitter* IREmit) { +void InlineCallOptimization::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::SyscallOpt"); - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { @@ -73,8 +72,6 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) { } #endif } - - Changed = true; } } else if (IROp->Op == FEXCore::IR::OP_CPUID) { auto Op = IROp->CW(); @@ -98,7 +95,6 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) { auto ElementPair = IREmit->_CreateElementPair(IR::OpSize::i128Bit, IREmit->_Constant(ResultsLower), IREmit->_Constant(ResultsUpper)); // Replace all CPUID uses with this inline one IREmit->ReplaceAllUsesWith(CodeNode, ElementPair); - Changed = true; } } } @@ -115,11 +111,9 @@ bool InlineCallOptimization::Run(IREmitter* IREmit) { IREmit->_CreateElementPair(IR::OpSize::i64Bit, IREmit->_Constant(ConstantXCRResult.eax), IREmit->_Constant(ConstantXCRResult.edx)); // Replace all xgetbv uses with this inline one IREmit->ReplaceAllUsesWith(CodeNode, ElementPair); - Changed = true; } } } - return Changed; } fextl::unique_ptr CreateInlineCallOptimization(const FEXCore::CPUIDEmu* CPUID) { diff --git a/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp b/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp index f1033f9f7..655d7e2e0 100644 --- a/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/LongDivideRemovalPass.cpp @@ -18,7 +18,7 @@ namespace FEXCore::IR { class LongDivideEliminationPass final : public FEXCore::IR::Pass { public: - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: bool IsZeroOp(IREmitter* IREmit, OrderedNodeWrapper Arg); bool IsSextOp(IREmitter* IREmit, OrderedNodeWrapper Lower, OrderedNodeWrapper Upper); @@ -48,10 +48,9 @@ bool LongDivideEliminationPass::IsSextOp(IREmitter* IREmit, OrderedNodeWrapper L return false; } -bool LongDivideEliminationPass::Run(IREmitter* IREmit) { +void LongDivideEliminationPass::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::LDE"); - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); auto OriginalWriteCursor = IREmit->GetWriteCursor(); @@ -74,7 +73,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) { SDivOp = IREmit->_Rem(OpSize::i64Bit, Lower, Divisor); } IREmit->ReplaceAllUsesWith(CodeNode, SDivOp); - Changed = true; } } else if (IROp->Op == OP_LUDIV || IROp->Op == OP_LUREM) { auto Op = IROp->C(); @@ -91,7 +89,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) { UDivOp = IREmit->_URem(OpSize::i64Bit, Lower, Divisor); } IREmit->ReplaceAllUsesWith(CodeNode, UDivOp); - Changed = true; } } } @@ -99,8 +96,6 @@ bool LongDivideEliminationPass::Run(IREmitter* IREmit) { } IREmit->SetWriteCursor(OriginalWriteCursor); - - return Changed; } fextl::unique_ptr CreateLongDivideEliminationPass() { diff --git a/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp b/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp index 00b06f226..dc039532c 100644 --- a/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp @@ -102,13 +102,13 @@ private: class RAValidation final : public FEXCore::IR::Pass { public: ~RAValidation() {} - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; }; -bool RAValidation::Run(IREmitter* IREmit) { +void RAValidation::Run(IREmitter* IREmit) { if (!Manager->HasPass("RA")) { - return false; + return; } FEXCORE_PROFILE_SCOPED("PassManager::RAValidation"); @@ -210,8 +210,6 @@ bool RAValidation::Run(IREmitter* IREmit) { Errors.clear(); } - - return false; } fextl::unique_ptr CreateRAValidation() { diff --git a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp index 23277a8d1..1f0ec1ee4 100644 --- a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -53,12 +53,13 @@ struct FlagInfo { class DeadFlagCalculationEliminination final : public FEXCore::IR::Pass { public: - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: FlagInfo Classify(IROp_Header* Node); unsigned FlagForReg(unsigned Reg); unsigned FlagsForCondClassType(CondClassType Cond); + bool EliminateDeadCode(IREmitter* IREmit, OrderedNode* CodeNode, IROp_Header* IROp); }; unsigned DeadFlagCalculationEliminination::FlagForReg(unsigned Reg) { @@ -310,13 +311,72 @@ FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) { return {.Trivial = true}; } +// General purpose dead code elimination. Returns whether flag handling should +// be skipped (because it was removed or could not possibly affect flags). +bool DeadFlagCalculationEliminination::EliminateDeadCode(IREmitter* IREmit, OrderedNode* CodeNode, IROp_Header* IROp) { + bool HasSideEffects = IR::HasSideEffects(IROp->Op); + + switch (IROp->Op) { + case OP_SYSCALL: + case OP_INLINESYSCALL: { + FEXCore::IR::SyscallFlags Flags {}; + if (IROp->Op == OP_SYSCALL) { + auto Op = IROp->C(); + Flags = Op->Flags; + } else { + auto Op = IROp->C(); + Flags = Op->Flags; + } + + if ((Flags & FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) == FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) { + HasSideEffects = false; + } + + break; + } + case OP_ATOMICFETCHADD: + case OP_ATOMICFETCHSUB: + case OP_ATOMICFETCHAND: + case OP_ATOMICFETCHCLR: + case OP_ATOMICFETCHOR: + case OP_ATOMICFETCHXOR: + case OP_ATOMICFETCHNEG: { + // If the result of the atomic fetch is completely unused, convert it to a non-fetching atomic operation. + if (CodeNode->GetUses() == 0) { + switch (IROp->Op) { + case OP_ATOMICFETCHADD: IROp->Op = OP_ATOMICADD; break; + case OP_ATOMICFETCHSUB: IROp->Op = OP_ATOMICSUB; break; + case OP_ATOMICFETCHAND: IROp->Op = OP_ATOMICAND; break; + case OP_ATOMICFETCHCLR: IROp->Op = OP_ATOMICCLR; break; + case OP_ATOMICFETCHOR: IROp->Op = OP_ATOMICOR; break; + case OP_ATOMICFETCHXOR: IROp->Op = OP_ATOMICXOR; break; + case OP_ATOMICFETCHNEG: IROp->Op = OP_ATOMICNEG; break; + default: FEX_UNREACHABLE; + } + } + return true; + } + default: break; + } + + // Skip over anything that has side effects + // Use count tracking can't safely remove anything with side effects + if (!HasSideEffects) { + if (CodeNode->GetUses() == 0) { + IREmit->Remove(CodeNode); + return true; + } + } + + return false; +} + /** - * @brief This pass removes flag calculations that will otherwise be unused INSIDE of that block + * @brief This pass removes dead code locally. */ -bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { +void DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::DFE"); - bool Changed = false; auto CurrentIR = IREmit->ViewIR(); for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { @@ -338,13 +398,7 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { // Optimizing flags can cause earlier flag reads to become dead but dead // flag reads should not impede optimiation of earlier dead flag writes. // We must DCE as we go to ensure we converge in a single iteration. - // - // TODO: This whole pass could be merged with DCE? - bool HasSideEffects = IR::HasSideEffects(IROp->Op); - if (!HasSideEffects && CodeNode->GetUses() == 0) { - Changed = true; - IREmit->Remove(CodeNode); - } else { + if (!EliminateDeadCode(IREmit, CodeNode, IROp)) { // Optimiation algorithm: For each flag written... // // If the flag has a later read (per FlagsRead), remove the flag from @@ -364,13 +418,11 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { bool Eliminated = false; if ((FlagsRead & Info.Write) == 0) { - if (Info.CanEliminate && CodeNode->GetUses() == 0) { + if ((Info.CanEliminate || Info.CanReplace) && CodeNode->GetUses() == 0) { IREmit->Remove(CodeNode); Eliminated = true; - Changed = true; } else if (Info.CanReplace) { IROp->Op = Info.Replacement; - Changed = true; } } else { FlagsRead &= ~Info.Write; @@ -392,8 +444,6 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { --CodeLast; } } - - return Changed; } fextl::unique_ptr CreateDeadFlagCalculationEliminination() { diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index deab7b490..8058adbcf 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -58,7 +58,7 @@ namespace { class ConstrainedRAPass final : public RegisterAllocationPass { public: - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override; RegisterAllocationData* GetAllocationData() override; @@ -495,7 +495,7 @@ RegisterAllocationData::UniquePtr ConstrainedRAPass::PullAllocationData() { return std::move(AllocData); } -bool ConstrainedRAPass::Run(IREmitter* IREmit_) { +void ConstrainedRAPass::Run(IREmitter* IREmit_) { FEXCORE_PROFILE_SCOPED("PassManager::RA"); IREmit = IREmit_; @@ -708,9 +708,6 @@ bool ConstrainedRAPass::Run(IREmitter* IREmit_) { SSAToReg.clear(); SpillSlots.clear(); NextUses.clear(); - - /* No point tracking this finely, RA is always one-shot */ - return true; } fextl::unique_ptr CreateRegisterAllocationPass() { diff --git a/FEXCore/include/FEXCore/IR/IR.h b/FEXCore/include/FEXCore/IR/IR.h index 4d579a97f..3cba91f5d 100644 --- a/FEXCore/include/FEXCore/IR/IR.h +++ b/FEXCore/include/FEXCore/IR/IR.h @@ -22,7 +22,7 @@ class RegisterAllocationData; enum class SyscallFlags : uint8_t { DEFAULT = 0, // Syscalldoesn't care about CPUState being serialized up to the syscall instruction. - // Means DeadCodeElimination can optimize through a syscall operation. + // Means dead code elimination can optimize through a syscall operation. OPTIMIZETHROUGH = 1 << 0, // Syscall only reads the passed in arguments. Doesn't read CPUState. NOSYNCSTATEONENTRY = 1 << 1, diff --git a/docs/SourceOutline.md b/docs/SourceOutline.md index bee7cb9c8..751407498 100644 --- a/docs/SourceOutline.md +++ b/docs/SourceOutline.md @@ -109,7 +109,6 @@ IR to IR Optimization - [PassManager.cpp](../FEXCore/Source/Interface/IR/PassManager.cpp): Defines which passes are run, and runs them - [PassManager.h](../FEXCore/Source/Interface/IR/PassManager.h) - [ConstProp.cpp](../FEXCore/Source/Interface/IR/Passes/ConstProp.cpp): ConstProp, ZExt elim, addressgen coalesce, const pooling, fcmp reduction, const inlining -- [DeadCodeElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp) - [DeadContextStoreElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp): Transforms ContextLoad/Store to temporaries, similar to mem2reg - [DeadStoreElimination.cpp](../FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp): Cross block store-after-store elimination - [IRValidation.cpp](../FEXCore/Source/Interface/IR/Passes/IRValidation.cpp): Sanity checking pass diff --git a/unittests/InstructionCountCI/FlagM/SecondaryGroup.json b/unittests/InstructionCountCI/FlagM/SecondaryGroup.json index 7cbc7aa0c..a6dc600bc 100644 --- a/unittests/InstructionCountCI/FlagM/SecondaryGroup.json +++ b/unittests/InstructionCountCI/FlagM/SecondaryGroup.json @@ -701,8 +701,8 @@ "cset x21, ne", "mov x22, x21", "bfxil x4, x20, #0, #16", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -715,8 +715,8 @@ "cset x21, ne", "mov x22, x21", "mov w4, w20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -729,8 +729,8 @@ "cset x21, ne", "mov x22, x21", "mov x4, x20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -743,8 +743,8 @@ "cset x21, ne", "mov x22, x21", "bfxil x4, x20, #0, #16", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -757,8 +757,8 @@ "cset x21, ne", "mov x22, x21", "mov w4, w20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -771,8 +771,8 @@ "cset x21, ne", "mov x22, x21", "mov x4, x20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] diff --git a/unittests/InstructionCountCI/SecondaryGroup.json b/unittests/InstructionCountCI/SecondaryGroup.json index 9bc0ffc2a..8efb9abd6 100644 --- a/unittests/InstructionCountCI/SecondaryGroup.json +++ b/unittests/InstructionCountCI/SecondaryGroup.json @@ -837,8 +837,8 @@ "cset x21, ne", "mov x22, x21", "bfxil x4, x20, #0, #16", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -851,8 +851,8 @@ "cset x21, ne", "mov x22, x21", "mov w4, w20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -865,8 +865,8 @@ "cset x21, ne", "mov x22, x21", "mov x4, x20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -879,8 +879,8 @@ "cset x21, ne", "mov x22, x21", "bfxil x4, x20, #0, #16", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -893,8 +893,8 @@ "cset x21, ne", "mov x22, x21", "mov w4, w20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ] @@ -907,8 +907,8 @@ "cset x21, ne", "mov x22, x21", "mov x4, x20", - "mov w26, #0x1", "mov w27, #0x0", + "mov w26, #0x1", "lsl x20, x22, #29", "msr nzcv, x20" ]