[llvm] [CodeGenPrepare] Cache known-live PHIs when deleting dead PHI chains (PR #207191)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 2 07:17:03 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Jolyon (Jolyon0202)
<details>
<summary>Changes</summary>
This patch fixes a compile-time issue in CodeGenPrepare for huge functions.
`DeleteDeadPHIs` may repeatedly prove overlapping PHI chains non-dead.
For very large functions, many PHIs can share the same non-dead def-use
suffix, causing the same suffix to be scanned many times.
Add an `KnownNonDeadPHIs` cache to `RecursivelyDeleteDeadPHINode`
and `DeleteDeadPHIs`. When a chain is proven non-dead, visited PHIs are
recorded so later queries can stop once they reach one of them.
`CodeGenPrepare` enables the cache only for huge functions. Other callers keep
the default `nullptr` behavior. If a deletion happens, `DeleteDeadPHIs` clears
the cache because the IR change may invalidate cached non-dead conclusions.
This reduces the pathological CodeGenPrepare case from ~30mins to ~30s.
---
Full diff: https://github.com/llvm/llvm-project/pull/207191.diff
5 Files Affected:
- (modified) llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h (+4-3)
- (modified) llvm/include/llvm/Transforms/Utils/Local.h (+5-4)
- (modified) llvm/lib/CodeGen/CodeGenPrepare.cpp (+8-5)
- (modified) llvm/lib/Transforms/Utils/BasicBlockUtils.cpp (+10-5)
- (modified) llvm/lib/Transforms/Utils/Local.cpp (+16-3)
``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
index f9bfff09673f0..0e829ecbcc352 100644
--- a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
@@ -91,9 +91,10 @@ FoldSingleEntryPHINodes(BasicBlock *BB,
/// recursively delete any operands that become dead as a result. This includes
/// tracing the def-use list from the PHI to see if it is ultimately unused or
/// if it reaches an unused cycle. Return true if any PHIs were deleted.
-LLVM_ABI bool DeleteDeadPHIs(BasicBlock *BB,
- const TargetLibraryInfo *TLI = nullptr,
- MemorySSAUpdater *MSSAU = nullptr);
+LLVM_ABI bool
+DeleteDeadPHIs(BasicBlock *BB, const TargetLibraryInfo *TLI = nullptr,
+ MemorySSAUpdater *MSSAU = nullptr,
+ SmallPtrSetImpl<PHINode *> *KnownNonDeadPHIs = nullptr);
/// Attempts to merge a block into its predecessor, if possible. The return
/// value indicates success or failure.
diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index bf39947f2dfc9..62fa8ada3905f 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -28,6 +28,7 @@ class DataLayout;
class Value;
class WeakTrackingVH;
class WeakVH;
+template <typename PtrType> class SmallPtrSetImpl;
template <typename T> class SmallVectorImpl;
class AAResults;
class AllocaInst;
@@ -129,10 +130,10 @@ LLVM_ABI bool RecursivelyDeleteTriviallyDeadInstructionsPermissive(
/// by a trivially dead instruction, delete it. If that makes any of its
/// operands trivially dead, delete them too, recursively. Return true if a
/// change was made.
-LLVM_ABI bool
-RecursivelyDeleteDeadPHINode(PHINode *PN,
- const TargetLibraryInfo *TLI = nullptr,
- MemorySSAUpdater *MSSAU = nullptr);
+LLVM_ABI bool RecursivelyDeleteDeadPHINode(
+ PHINode *PN, const TargetLibraryInfo *TLI = nullptr,
+ MemorySSAUpdater *MSSAU = nullptr,
+ SmallPtrSetImpl<PHINode *> *KnownNonDeadPHIs = nullptr);
/// Scan the specified basic block and try to simplify any instructions in it
/// and recursively delete dead instructions.
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index aa14d2586a534..119c6632facfb 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -629,6 +629,10 @@ bool CodeGenPrepare::_run(Function &F) {
// (plus arguments that we can get rid of).
EverMadeChange |= eliminateAssumptions(F);
+ // If we are optimzing huge function, we need to consider the build time.
+ // Because the basic algorithm's complex is near O(N!).
+ IsHugeFunc = F.size() > HugeFuncThresholdInCGPP;
+
auto resetLoopInfo = [this]() {
LI->releaseMemory();
LI->analyze(DTU->getDomTree());
@@ -661,10 +665,6 @@ bool CodeGenPrepare::_run(Function &F) {
LI->verify(getDT());
#endif
- // If we are optimzing huge function, we need to consider the build time.
- // Because the basic algorithm's complex is near O(N!).
- IsHugeFunc = F.size() > HugeFuncThresholdInCGPP;
-
bool MadeChange = true;
bool FuncIterated = false;
while (MadeChange) {
@@ -946,11 +946,14 @@ bool CodeGenPrepare::eliminateMostlyEmptyBlocks(Function &F, bool &ResetLI) {
ResetLI = false;
bool MadeChange = false;
+ SmallPtrSet<PHINode *, 32> KnownNonDeadPHIs;
+ SmallPtrSet<PHINode *, 32> *KnownNonDeadPHIsPtr =
+ IsHugeFunc ? &KnownNonDeadPHIs : nullptr;
// Note that this intentionally skips the entry block.
for (auto &Block : llvm::drop_begin(F)) {
// Delete phi nodes that could block deleting other empty blocks.
if (!DisableDeletePHIs)
- MadeChange |= DeleteDeadPHIs(&Block, TLInfo);
+ MadeChange |= DeleteDeadPHIs(&Block, TLInfo, nullptr, KnownNonDeadPHIsPtr);
}
for (auto &Block : llvm::drop_begin(F)) {
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index 425faf57f8b63..74de746449c54 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -212,16 +212,21 @@ bool llvm::FoldSingleEntryPHINodes(BasicBlock *BB,
}
bool llvm::DeleteDeadPHIs(BasicBlock *BB, const TargetLibraryInfo *TLI,
- MemorySSAUpdater *MSSAU) {
+ MemorySSAUpdater *MSSAU,
+ SmallPtrSetImpl<PHINode *> *KnownNonDeadPHIs) {
// Recursively deleting a PHI may cause multiple PHIs to be deleted
// or RAUW'd undef, so use an array of WeakTrackingVH for the PHIs to delete.
SmallVector<WeakTrackingVH, 8> PHIs(llvm::make_pointer_range(BB->phis()));
bool Changed = false;
- for (const auto &PHI : PHIs)
- if (PHINode *PN = dyn_cast_or_null<PHINode>(PHI.operator Value *()))
- Changed |= RecursivelyDeleteDeadPHINode(PN, TLI, MSSAU);
-
+ for (const auto &PHI : PHIs) {
+ if (PHINode *PN = dyn_cast_or_null<PHINode>(PHI.operator Value *())) {
+ bool PHIChanged = RecursivelyDeleteDeadPHINode(PN, TLI, MSSAU, KnownNonDeadPHIs);
+ Changed |= PHIChanged;
+ if (PHIChanged && KnownNonDeadPHIs)
+ KnownNonDeadPHIs->clear();
+ }
+ }
return Changed;
}
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 1e1c3ac56d9c1..b17740c0bc192 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -640,10 +640,12 @@ static bool areAllUsesEqual(Instruction *I) {
/// either forms a cycle or is terminated by a trivially dead instruction,
/// delete it. If that makes any of its operands trivially dead, delete them
/// too, recursively. Return true if a change was made.
-bool llvm::RecursivelyDeleteDeadPHINode(PHINode *PN,
- const TargetLibraryInfo *TLI,
- llvm::MemorySSAUpdater *MSSAU) {
+bool llvm::RecursivelyDeleteDeadPHINode(
+ PHINode *PN, const TargetLibraryInfo *TLI, llvm::MemorySSAUpdater *MSSAU,
+ SmallPtrSetImpl<PHINode *> *KnownNonDeadPHIs) {
SmallPtrSet<Instruction*, 4> Visited;
+ SmallVector<PHINode *, 8> VisitedPHIs;
+
for (Instruction *I = PN; areAllUsesEqual(I) && !I->mayHaveSideEffects();
I = cast<Instruction>(*I->user_begin())) {
if (I->use_empty())
@@ -657,7 +659,18 @@ bool llvm::RecursivelyDeleteDeadPHINode(PHINode *PN,
(void)RecursivelyDeleteTriviallyDeadInstructions(I, TLI, MSSAU);
return true;
}
+
+ if (PHINode *CurPN = dyn_cast<PHINode>(I)) {
+ if (KnownNonDeadPHIs && KnownNonDeadPHIs->contains(CurPN))
+ break;
+ VisitedPHIs.push_back(CurPN);
+ }
}
+
+ if (KnownNonDeadPHIs)
+ for (PHINode *VisitedPN : VisitedPHIs)
+ KnownNonDeadPHIs->insert(VisitedPN);
+
return false;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207191
More information about the llvm-commits
mailing list