From 676c9a9be6a15e2283396a23afb6d527197e9104 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 16:35:55 -0400 Subject: [PATCH 01/10] IR: do not return progress from passes Generally, there are three reasons to track progress: * Conditional optimizations. E.g. only run DCE if ConstProp succeeds. * Fixed point optimizations. E.g. keep running the opt loop until convergence. * Metadata shenianigans. None of these apply to FEX. We explicitly do not want a nonlinear pass ordering, instead we want just a few passes that each converge in a single iteration. We expect them all to make progress when run. As such, tracking progress is a waste of CPU cycles. Stop doing it. Signed-off-by: Alyssa Rosenzweig --- FEXCore/Source/Interface/IR/PassManager.cpp | 9 +- FEXCore/Source/Interface/IR/PassManager.h | 4 +- .../Source/Interface/IR/Passes/ConstProp.cpp | 151 ++---------------- .../IR/Passes/DeadCodeElimination.cpp | 9 +- .../IR/Passes/DeadContextStoreElimination.cpp | 47 ++---- .../IR/Passes/DeadStoreElimination.cpp | 9 +- .../Interface/IR/Passes/IRDumperPass.cpp | 6 +- .../Interface/IR/Passes/IRValidation.cpp | 4 +- .../Source/Interface/IR/Passes/IRValidation.h | 2 +- .../IR/Passes/InlineCallOptimization.cpp | 10 +- .../IR/Passes/LongDivideRemovalPass.cpp | 9 +- .../Interface/IR/Passes/RAValidation.cpp | 8 +- .../RedundantFlagCalculationElimination.cpp | 10 +- .../IR/Passes/RegisterAllocationPass.cpp | 7 +- 14 files changed, 56 insertions(+), 229 deletions(-) diff --git a/FEXCore/Source/Interface/IR/PassManager.cpp b/FEXCore/Source/Interface/IR/PassManager.cpp index 2eaccc952..55ae382f6 100644 --- a/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/FEXCore/Source/Interface/IR/PassManager.cpp @@ -100,20 +100,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/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index c20e931cb..316894967 100644 --- a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -245,16 +245,16 @@ 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 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 ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); + void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); + void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR); struct ConstPoolData { OrderedNode* Node; @@ -282,9 +282,7 @@ private: constexpr static uint32_t CONSTANT_POOL_RANGE_LIMIT = 500; }; -bool ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) { - bool Changed = false; - +void ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) { // constants are pooled per block for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { @@ -306,7 +304,6 @@ bool ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& Current auto CodeIter = CurrentIR.at(CodeNode); IREmit->ReplaceUsesWithAfter(CodeNode, it->second.Node, CodeIter); - Changed = true; } else { ConstPool[Op->Constant] = ConstPoolData { .Node = CodeNode, @@ -317,8 +314,6 @@ bool ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& Current } ConstPool.clear(); } - - return Changed; } // LoadMem / StoreMem imm pooling @@ -358,9 +353,7 @@ doneOp:; } } -bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { - bool Changed = false; - +void ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { switch (IROp->Op) { // Generic handling case OP_OR: @@ -380,7 +373,6 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C 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; @@ -397,7 +389,6 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C if (newArg.ID() != IROp->Args[i].ID()) { IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg)); - Changed = true; } } break; @@ -435,7 +426,6 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C if (newArg.ID() != Op->Src.ID()) { IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg)); - Changed = true; } break; } @@ -452,7 +442,6 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C if (newArg.ID() != Op->Src.ID()) { IREmit->ReplaceNodeArgument(CodeNode, Op->Src_Index, IREmit->UnwrapNode(newArg)); - Changed = true; } break; } @@ -476,14 +465,10 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C } 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 +486,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 +506,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 +525,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 +544,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 +565,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 +582,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 +605,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 +618,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 +628,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 +636,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 +650,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 +664,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 +675,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 +685,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 +703,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 +714,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,17 +726,14 @@ 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,17 +747,14 @@ 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; @@ -814,12 +768,10 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current 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 +781,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 +801,6 @@ bool ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current NewConstant >>= 64 - Op->Width; NewConstant &= DestMask; IREmit->ReplaceWithConstant(CodeNode, NewConstant); - - Changed = true; } break; } @@ -868,7 +817,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 +826,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 +841,22 @@ 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; } 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 +876,6 @@ bool ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) } IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2)); - - Changed = true; } break; } @@ -952,10 +891,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 +900,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 +913,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 +924,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 +935,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 +944,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 +953,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 +963,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 +994,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 +1005,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 +1015,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 +1024,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 +1037,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 +1049,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 +1061,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 +1074,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 +1088,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 +1100,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 +1110,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 +1122,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 +1130,27 @@ 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; - } - + HandleConstantPools(IREmit, CurrentIR); LoadMemStoreMemImmediatePooling(IREmit, CurrentIR); for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - if (ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp)) { - Changed = true; - } - if (ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp)) { - Changed = true; - } + ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp); + 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 index 466529987..729e9970e 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp @@ -16,16 +16,15 @@ $end_info$ namespace FEXCore::IR { class DeadCodeElimination final : public FEXCore::IR::Pass { - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: void markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp); }; -bool DeadCodeElimination::Run(IREmitter* IREmit) { +void DeadCodeElimination::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::DCE"); auto CurrentIR = IREmit->ViewIR(); - bool Changed = false; for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) { @@ -78,7 +77,6 @@ bool DeadCodeElimination::Run(IREmitter* IREmit) { case OP_ATOMICFETCHNEG: IROp->Op = OP_ATOMICNEG; break; default: FEX_UNREACHABLE; } - Changed = true; } break; } @@ -89,7 +87,6 @@ bool DeadCodeElimination::Run(IREmitter* IREmit) { // Use count tracking can't safely remove anything with side effects if (!HasSideEffects) { if (CodeNode->GetUses() == 0) { - Changed = true; IREmit->Remove(CodeNode); } } @@ -100,8 +97,6 @@ bool DeadCodeElimination::Run(IREmitter* IREmit) { --CodeLast; } } - - return Changed; } void DeadCodeElimination::markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp) {} diff --git a/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp index bce8fa7c5..f7e9282f8 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp @@ -458,7 +458,7 @@ public: 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; @@ -473,16 +473,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 +531,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 +544,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 +561,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 +578,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 +616,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 +636,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 +659,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 +679,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 +709,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..459086afe 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp @@ -27,7 +27,7 @@ constexpr int PropagationRounds = 5; class DeadStoreElimination final : public FEXCore::IR::Pass { public: explicit DeadStoreElimination() {} - bool Run(IREmitter* IREmit) override; + void Run(IREmitter* IREmit) override; private: uint64_t FPRBit(RegisterClassType Class, uint32_t Reg) const { @@ -75,12 +75,11 @@ struct Info { * 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(); // Pass 1 @@ -185,7 +184,6 @@ 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(); @@ -193,14 +191,11 @@ bool DeadStoreElimination::Run(IREmitter* IREmit) { // 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))) { 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..69d43d4f7 100644 --- a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -53,7 +53,7 @@ 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); @@ -313,10 +313,9 @@ FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) { /** * @brief This pass removes flag calculations that will otherwise be unused INSIDE of that block */ -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()) { @@ -342,7 +341,6 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { // 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 { // Optimiation algorithm: For each flag written... @@ -367,10 +365,8 @@ bool DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { if (Info.CanEliminate && 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 +388,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() { From 3c088fb414e0185f25cbee8c8d2e40446851357e Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 17:03:12 -0400 Subject: [PATCH 02/10] ConstProp: remove masking elimination opts This has been deadcode since 2020. Drop it so we can focus on what *does* work and what does matter. Signed-off-by: Alyssa Rosenzweig --- .../Source/Interface/IR/Passes/ConstProp.cpp | 108 ------------------ 1 file changed, 108 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index 316894967..b1381cbfa 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) { @@ -355,45 +328,6 @@ doneOp:; void ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { 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)); - } - } - 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)); - } - } - break; - } - case OP_BFE: { auto Op = IROp->C(); @@ -416,33 +350,6 @@ void ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C 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)); - } - 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)); - } break; } @@ -455,11 +362,6 @@ void ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& C (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; } @@ -730,11 +632,6 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current IREmit->SetWriteCursor(CodeNode); OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]); IREmit->ReplaceAllUsesWith(CodeNode, Arg); - } 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)); - } } break; } @@ -751,11 +648,6 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current IREmit->SetWriteCursor(CodeNode); OrderedNode* Arg = CurrentIR.GetNode(IROp->Args[0]); IREmit->ReplaceAllUsesWith(CodeNode, Arg); - } 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)); - } } break; } From 3089e0e6dec8363839388cbe4192fc2ad0005371 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 17:08:11 -0400 Subject: [PATCH 03/10] ConstProp: merge masking opts with const folding Single pass over the IR now. Signed-off-by: Alyssa Rosenzweig --- .../Source/Interface/IR/Passes/ConstProp.cpp | 79 ++++++++----------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index b1381cbfa..53842f522 100644 --- a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -225,7 +225,6 @@ public: private: void HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR); void LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR); - void ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR); @@ -326,49 +325,6 @@ doneOp:; } } -void ConstProp::ZextAndMaskingElimination(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { - switch (IROp->Op) { - 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; - } - } - 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; - } -} - // constprop + some more per instruction logic void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp) { switch (IROp->Op) { @@ -654,6 +610,27 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current 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; @@ -743,6 +720,19 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current } 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; } } @@ -1034,7 +1024,6 @@ void ConstProp::Run(IREmitter* IREmit) { LoadMemStoreMemImmediatePooling(IREmit, CurrentIR); for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { - ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp); ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp); } From 94d9cf54bc822ef060075dbb91caf8c58691b08a Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 17:10:38 -0400 Subject: [PATCH 04/10] ConstProp: don't push/pop cursor pointless Signed-off-by: Alyssa Rosenzweig --- FEXCore/Source/Interface/IR/Passes/ConstProp.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index 53842f522..8778cf1d3 100644 --- a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -1018,7 +1018,6 @@ void ConstProp::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::ConstProp"); auto CurrentIR = IREmit->ViewIR(); - auto OriginalWriteCursor = IREmit->GetWriteCursor(); HandleConstantPools(IREmit, CurrentIR); LoadMemStoreMemImmediatePooling(IREmit, CurrentIR); @@ -1030,8 +1029,6 @@ void ConstProp::Run(IREmitter* IREmit) { if (InlineConstants) { ConstantInlining(IREmit, CurrentIR); } - - IREmit->SetWriteCursor(OriginalWriteCursor); } fextl::unique_ptr CreateConstProp(bool InlineConstants, bool SupportsTSOImm9, bool Is64BitMode) { From 273d086a7b30e3d89d7a6626ffb463bb93bceb89 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 17:19:24 -0400 Subject: [PATCH 05/10] ConstProp: merge const pooling passes walk the IR less. Signed-off-by: Alyssa Rosenzweig --- .../Source/Interface/IR/Passes/ConstProp.cpp | 66 ++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index 8778cf1d3..05e16c8ce 100644 --- a/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -224,7 +224,6 @@ public: private: void HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR); - void LoadMemStoreMemImmediatePooling(IREmitter* IREmit, const IRListView& CurrentIR); void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, OrderedNode* CodeNode, IROp_Header* IROp); void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR); @@ -254,43 +253,9 @@ private: constexpr static uint32_t CONSTANT_POOL_RANGE_LIMIT = 500; }; +// 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) { - // 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); - } else { - ConstPool[Op->Constant] = ConstPoolData { - .Node = CodeNode, - .NodeID = NewNodeID, - }; - } - } - } - ConstPool.clear(); - } -} - -// 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) { for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) { for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { if (IROp->Op == OP_LOADMEM || IROp->Op == OP_STOREMEM) { @@ -318,10 +283,36 @@ 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(); } } @@ -1020,7 +1011,6 @@ void ConstProp::Run(IREmitter* IREmit) { auto CurrentIR = IREmit->ViewIR(); HandleConstantPools(IREmit, CurrentIR); - LoadMemStoreMemImmediatePooling(IREmit, CurrentIR); for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) { ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp); From cf77f2ae5d99896b49c8abc1bbdea7f707c1ff55 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 18:05:43 -0400 Subject: [PATCH 06/10] RedundantFlagCalculationElimination: fix convergence issue If both the destination and the flags are dead for an AddWithFlags, we need to eliminate it in one pass. If we only replace without elimiating, we would need a second DCE pass to eliminate. We want DCE to finish in one pass, so fix this. Signed-off-by: Alyssa Rosenzweig --- .../Interface/IR/Passes/RedundantFlagCalculationElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp index 69d43d4f7..0089adf17 100644 --- a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -362,7 +362,7 @@ void 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; } else if (Info.CanReplace) { From cb00d9171f965dab8d93b6997eb06564d8c1e3d4 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sun, 19 May 2024 17:44:25 -0400 Subject: [PATCH 07/10] IR: merge general DCE with flag DCE Flag DCE needs to do general DCE anyway to converge in one pass. So we can move the special syscall/atomic logic over to flag DCE and then drop the second DCE pass altogether. Now local dead code of both is eliminated in a single pass. Flag DCE is carefully written to converge in a single iteration which makes this scheme work. Signed-off-by: Alyssa Rosenzweig --- FEXCore/Source/CMakeLists.txt | 1 - FEXCore/Source/Interface/IR/PassManager.cpp | 6 +- FEXCore/Source/Interface/IR/Passes.h | 1 - .../IR/Passes/DeadCodeElimination.cpp | 108 ------------------ .../IR/Passes/DeadContextStoreElimination.cpp | 3 - .../RedundantFlagCalculationElimination.cpp | 70 ++++++++++-- FEXCore/include/FEXCore/IR/IR.h | 2 +- docs/SourceOutline.md | 1 - 8 files changed, 65 insertions(+), 127 deletions(-) delete mode 100644 FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp 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 55ae382f6..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()); } } 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/DeadCodeElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp deleted file mode 100644 index 729e9970e..000000000 --- a/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp +++ /dev/null @@ -1,108 +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 { - void Run(IREmitter* IREmit) override; - -private: - void markUsed(OrderedNodeWrapper* CodeOp, IROp_Header* IROp); -}; - -void DeadCodeElimination::Run(IREmitter* IREmit) { - FEXCORE_PROFILE_SCOPED("PassManager::DCE"); - auto CurrentIR = IREmit->ViewIR(); - - 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; - } - } - 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) { - IREmit->Remove(CodeNode); - } - } - - if (CodeLast == CodeBegin) { - break; - } - --CodeLast; - } - } -} - -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 f7e9282f8..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(); } void Run(FEXCore::IR::IREmitter* IREmit) override; private: - fextl::unique_ptr DCE; - ContextInfo ClassifiedStruct; fextl::unordered_map OffsetToBlockMap; diff --git a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp index 0089adf17..1f0ec1ee4 100644 --- a/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -59,6 +59,7 @@ 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,8 +311,68 @@ 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. */ void DeadFlagCalculationEliminination::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::DFE"); @@ -337,12 +398,7 @@ void 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) { - 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 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 From 83e417b2c662f57f3786349606236e8f57d3b438 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Wed, 22 May 2024 15:08:02 -0400 Subject: [PATCH 08/10] DeadStoreElimination: combine GPR/FPR handling slight speed up per profile. Signed-off-by: Alyssa Rosenzweig --- .../IR/Passes/DeadStoreElimination.cpp | 74 ++++++------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp index 459086afe..8c3301b8c 100644 --- a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp +++ b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp @@ -24,53 +24,36 @@ 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() {} void Run(IREmitter* IREmit) override; - -private: - uint64_t FPRBit(RegisterClassType Class, uint32_t Reg) const { - return (Class == FPRClass) ? (1UL << Reg) : 0; - } }; -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. * @@ -83,7 +66,7 @@ void DeadStoreElimination::Run(IREmitter* IREmit) { auto CurrentIR = IREmit->ViewIR(); // 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()) { @@ -106,21 +89,17 @@ void 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()) { @@ -137,13 +116,11 @@ void DeadStoreElimination::Run(IREmitter* IREmit) { // 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(); @@ -156,17 +133,14 @@ void DeadStoreElimination::Run(IREmitter* IREmit) { // 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; } } } @@ -189,7 +163,7 @@ void DeadStoreElimination::Run(IREmitter* IREmit) { 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); } } From bc1669b163dda7fdb7c5b09851e7a5bec8f02994 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Wed, 22 May 2024 16:06:05 -0400 Subject: [PATCH 09/10] DeadStoreElimination: eliminate map use a vec. block indices will be dense in the new IR. This is memory intensive but seems faster in practice. Signed-off-by: Alyssa Rosenzweig --- .../IR/Passes/DeadStoreElimination.cpp | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp b/FEXCore/Source/Interface/IR/Passes/DeadStoreElimination.cpp index 8c3301b8c..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 @@ -61,16 +60,15 @@ struct Info { void DeadStoreElimination::Run(IREmitter* IREmit) { FEXCORE_PROFILE_SCOPED("PassManager::DSE"); - fextl::unordered_map InfoMap; - auto CurrentIR = IREmit->ViewIR(); + fextl::vector InfoMap(CurrentIR.GetSSACount()); // Pass 1 // 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) { @@ -109,10 +107,8 @@ void 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; @@ -124,12 +120,9 @@ void DeadStoreElimination::Run(IREmitter* IREmit) { } 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; @@ -149,7 +142,7 @@ void 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) { From d3eed27d17f0c319840dc0cb55fecd771b319a64 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Fri, 24 May 2024 15:45:32 -0400 Subject: [PATCH 10/10] InstCountCI: Update Signed-off-by: Alyssa Rosenzweig --- .../InstructionCountCI/FlagM/SecondaryGroup.json | 12 ++++++------ unittests/InstructionCountCI/SecondaryGroup.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) 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" ]