[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