[llvm] [DSE] Refine mayThrowBetween for cross-BB dead store checks (PR #191210)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 9 07:46:52 PDT 2026


https://github.com/ParkHanbum created https://github.com/llvm/llvm-project/pull/191210

mayThrowBetween() currently bails out for cross-BB cases if any
unmodeled throwing block exists in the function, which is overly
conservative and can block valid DSE.

Handle same-BB cases by scanning only the relevant instruction range,
and handle cross-BB cases by walking backwards from the killing block
through blocks dominated by the dead block. Keep a conservative bailout
when the search exceeds a limit.

Proof: https://alive2.llvm.org/ce/z/vtgqNp
Fixed: https://github.com/llvm/llvm-project/issues/190583

>From ae66e77c5dbdd25ed19360ea39d6fafcd6eab1f5 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Wed, 8 Apr 2026 13:56:05 +0900
Subject: [PATCH] [DSE] Refine mayThrowBetween for cross-BB dead store checks

mayThrowBetween() currently bails out for cross-BB cases if any
unmodeled throwing block exists in the function, which is overly
conservative and can block valid DSE.

Handle same-BB cases by scanning only the relevant instruction range,
and handle cross-BB cases by walking backwards from the killing block
through blocks dominated by the dead block. Keep a conservative bailout
when the search exceeds a limit.

Proof: https://alive2.llvm.org/ce/z/vtgqNp
Fixed: #190583
---
 .../Scalar/DeadStoreElimination.cpp           | 82 +++++++++++++++++--
 .../multiblock-multipath-throwing.ll          | 11 ++-
 2 files changed, 81 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 2714074dddafc..2f54e648ce756 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -174,6 +174,13 @@ static cl::opt<bool> EnableInitializesImprovement(
     "enable-dse-initializes-attr-improvement", cl::init(true), cl::Hidden,
     cl::desc("Enable the initializes attr improvement in DSE"));
 
+
+static cl::opt<unsigned> MemorySSAThrowCheckLimit(
+    "dse-memoryssa-throw-check-limit", cl::init(50), cl::Hidden,
+    cl::desc("The maximum number of blocks to check for unmodeled may-throw "
+             "instructions between the dead and killing stores "
+             "(default = 50)"));
+
 //===----------------------------------------------------------------------===//
 // Helper functions
 //===----------------------------------------------------------------------===//
@@ -2068,15 +2075,78 @@ void DSEState::deleteDeadInstruction(Instruction *SI,
 
 bool DSEState::mayThrowBetween(Instruction *KillingI, Instruction *DeadI,
                                const Value *KillingUndObj) {
-  // First see if we can ignore it by using the fact that KillingI is an
-  // alloca/alloca like object that is not visible to the caller during
-  // execution of the function.
+  // If the object is not visible to the caller on unwind, intervening throws
+  // do not block DSE for this location.
   if (KillingUndObj && isInvisibleToCallerOnUnwind(KillingUndObj))
     return false;
 
-  if (KillingI->getParent() == DeadI->getParent())
-    return ThrowingBlocks.count(KillingI->getParent());
-  return !ThrowingBlocks.empty();
+  auto IsMemorySSAUntrackedThrow = [&](Instruction &I) {
+    // This function is only meant to catch "extra" may-throws that are not
+    // modeled as MemoryDefs in MemorySSA.
+    return I.mayThrow() && !MSSA.getMemoryAccess(&I);
+  };
+
+  auto ScanRange = [&](BasicBlock *BB, BasicBlock::iterator Begin,
+                       BasicBlock::iterator End) {
+    for (auto It = Begin; It != End; ++It)
+      if (IsMemorySSAUntrackedThrow(*It))
+        return true;
+    return false;
+  };
+
+  BasicBlock *KillingBB = KillingI->getParent();
+  BasicBlock *DeadBB = DeadI->getParent();
+  // Same block: only scan the actual instruction interval.
+  if (KillingBB == DeadBB)
+    return ScanRange(DeadBB, std::next(DeadI->getIterator()),
+                     KillingI->getIterator());
+
+  // If there are no blocks containing extra may-throws, we're done.
+  if (ThrowingBlocks.empty())
+    return false;
+
+  // Cross-BB case: walk backwards from the killing block, but stay within the
+  // region dominated by the dead block. Any block outside that region is not
+  // between DeadI and KillingI.
+  if (!DT.dominates(DeadBB, KillingBB))
+    return true;
+
+  SmallVector<BasicBlock *, 8> WorkList;
+  SmallPtrSet<BasicBlock *, 8> Visited;
+  WorkList.push_back(KillingBB);
+  Visited.insert(KillingBB);
+  unsigned NumChecked = 0;
+  while (!WorkList.empty()) {
+    BasicBlock *BB = WorkList.pop_back_val();
+    if (++NumChecked > MemorySSAThrowCheckLimit)
+      return true;
+
+    if (BB == KillingBB) {
+      // Only instructions before KillingI are relevant in the destination.
+      if (ThrowingBlocks.count(BB) &&
+          ScanRange(BB, BB->begin(), KillingI->getIterator()))
+        return true;
+    } else if (BB == DeadBB) {
+      // Only instructions after DeadI are relevant in the source.
+      if (ThrowingBlocks.count(BB) &&
+          ScanRange(BB, std::next(DeadI->getIterator()), BB->end()))
+        return true;
+      continue;
+    } else {
+      // Entire intermediate block is between DeadI and KillingI.
+      if (ThrowingBlocks.count(BB))
+        return true;
+    }
+
+    for (BasicBlock *Pred : predecessors(BB)) {
+      if (Pred != DeadBB && !DT.dominates(DeadBB, Pred))
+        continue;
+      if (Visited.insert(Pred).second)
+        WorkList.push_back(Pred);
+    }
+  }
+
+  return false;
 }
 
 bool DSEState::isDSEBarrier(const Value *KillingUndObj, Instruction *DeadI) {
diff --git a/llvm/test/Transforms/DeadStoreElimination/multiblock-multipath-throwing.ll b/llvm/test/Transforms/DeadStoreElimination/multiblock-multipath-throwing.ll
index a513c60ca265f..56463b9be24ef 100644
--- a/llvm/test/Transforms/DeadStoreElimination/multiblock-multipath-throwing.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/multiblock-multipath-throwing.ll
@@ -10,12 +10,12 @@ declare void @use(ptr)
 ; Tests where the pointer/object is accessible after the function returns.
 
 ; Cannot remove the store from the entry block, because the call in bb2 may throw.
+; The store in bb1 may still be eliminated by sinking/merging it into bb5.
 define void @accessible_after_return_1(ptr noalias %P, i1 %c1) {
 ; CHECK-LABEL: @accessible_after_return_1(
-; CHECK-NEXT:    store i32 1, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br i1 [[C1:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
-; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
+; CHECK-NEXT:    store i32 0, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[BB5:%.*]]
 ; CHECK:       bb2:
 ; CHECK-NEXT:    call void @readnone_may_throw()
@@ -46,12 +46,11 @@ bb5:
 define void @accessible_after_return6(ptr %P, i1 %c.1, i1 %c.2) {
 ; CHECK-LABEL: @accessible_after_return6(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    store i32 0, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    br i1 [[C_1:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    br i1 [[C_2:%.*]], label [[BB3:%.*]], label [[BB4:%.*]]
 ; CHECK:       bb2:
-; CHECK-NEXT:    store i32 1, ptr [[P]], align 4
+; CHECK-NEXT:    store i32 1, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ; CHECK:       bb3:
 ; CHECK-NEXT:    call void @readnone_may_throw()
@@ -90,7 +89,7 @@ bb4:
 define void @alloca_1(i1 %c1) {
 ; CHECK-LABEL: @alloca_1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[P:%.*]] = alloca i32
+; CHECK-NEXT:    [[P:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    br i1 [[C1:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
@@ -127,7 +126,7 @@ bb5:
 ; call in bb3 (which may throw) can be ignored.
 define void @alloca_2(i1 %c.1, i1 %c.2) {
 ; CHECK-LABEL: @alloca_2(
-; CHECK-NEXT:    [[P:%.*]] = alloca i32
+; CHECK-NEXT:    [[P:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    br i1 [[C_1:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    store i32 0, ptr [[P]], align 4



More information about the llvm-commits mailing list