[llvm] [SimplifyCFG] Don't delete basic block if it is a partial cleanuppad (PR #157363)

Gábor Spaits via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 7 13:54:51 PDT 2025


https://github.com/spaits created https://github.com/llvm/llvm-project/pull/157363

Fixes #148052 .

The issue reproducer: https://godbolt.org/z/njT5ET3dj

The problem in the issue was that, SimplifyCFG deemed the basic block (`bb1`) unnecessary, that would invoke the excpetion (under basic block `panic1`). I think SimplifyCFG is right to deem it unnecessary.

This makes a few basic blocks unreachable including the one containing the `cleanuppad` instruction. The "result" of the cleanuppad is replaced with poison, it is used for the `cleanupret`. I don't think it is valid to use a poison value for `cleanupret`. See https://llvm.org/docs/LangRef.html#cleanupret-instruction :
```
The ‘cleanupret’ instruction requires one argument, which indicates which cleanuppad it exits, and must be a cleanuppad.
```

This PR disables the deletion of `cleanuppad` basic blocks, if the `cleanupret` basic block is in another block.

>From f221b6dd8a9ceb7d49380c2cc41214c7e2772269 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 3 Sep 2025 16:25:18 +0200
Subject: [PATCH] Don't delete basic block if is a cleanuppad

---
 llvm/include/llvm/IR/BasicBlock.h             |  1 +
 llvm/lib/IR/BasicBlock.cpp                    |  4 ++
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  5 +-
 .../SimplifyCFG/keep-non-linear-cleanuppad.ll | 48 +++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/keep-non-linear-cleanuppad.ll

diff --git a/llvm/include/llvm/IR/BasicBlock.h b/llvm/include/llvm/IR/BasicBlock.h
index 533808e0666d5..8c831d8380bbb 100644
--- a/llvm/include/llvm/IR/BasicBlock.h
+++ b/llvm/include/llvm/IR/BasicBlock.h
@@ -705,6 +705,7 @@ class BasicBlock final : public Value, // Basic blocks are data objects also
 
   /// Return true if this basic block is an exception handling block.
   bool isEHPad() const { return getFirstNonPHIIt()->isEHPad(); }
+  bool isEHPadWithReturn() const;
 
   /// Return true if this basic block is a landing pad.
   ///
diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index 3642e935397cb..51b303d36e862 100644
--- a/llvm/lib/IR/BasicBlock.cpp
+++ b/llvm/lib/IR/BasicBlock.cpp
@@ -658,6 +658,10 @@ void BasicBlock::replaceSuccessorsPhiUsesWith(BasicBlock *New) {
   this->replaceSuccessorsPhiUsesWith(this, New);
 }
 
+bool BasicBlock::isEHPadWithReturn() const {
+  return isEHPad() && isa<CleanupReturnInst>(getTerminator());
+}
+
 bool BasicBlock::isLandingPad() const {
   return isa<LandingPadInst>(getFirstNonPHIIt());
 }
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 055e8cadaab76..ae55cee02ecb6 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -8340,8 +8340,9 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) {
 
   // Remove basic blocks that have no predecessors (except the entry block)...
   // or that just have themself as a predecessor.  These are unreachable.
-  if ((pred_empty(BB) && BB != &BB->getParent()->getEntryBlock()) ||
-      BB->getSinglePredecessor() == BB) {
+  if (((pred_empty(BB) && BB != &BB->getParent()->getEntryBlock()) ||
+       BB->getSinglePredecessor() == BB) &&
+      (!BB->isEHPad() || BB->isEHPadWithReturn())) {
     LLVM_DEBUG(dbgs() << "Removing BB: \n" << *BB);
     DeleteDeadBlock(BB, DTU);
     return true;
diff --git a/llvm/test/Transforms/SimplifyCFG/keep-non-linear-cleanuppad.ll b/llvm/test/Transforms/SimplifyCFG/keep-non-linear-cleanuppad.ll
new file mode 100644
index 0000000000000..e91df1b903037
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/keep-non-linear-cleanuppad.ll
@@ -0,0 +1,48 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=simplifycfg -S < %s | FileCheck %s
+
+define void @_RINvCs3LQYlzOoal9_5repro13repro_genericNtB2_10ReproShapeEB2_(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @_RINvCs3LQYlzOoal9_5repro13repro_genericNtB2_10ReproShapeEB2_(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[_7]])
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  br i1 %_7, label %bb1, label %panic
+
+bb1:
+  %_11 = icmp ult i64 0, %shapes.1
+  br i1 %_11, label %bb3, label %panic1
+
+panic:
+  unreachable
+
+bb3:
+  ret void
+
+panic1:
+  invoke void @_RNvNtCshEuXqvZjEXJ_4core9panicking18panic_bounds_check(i64 0, i64 0, ptr null)
+  to label %unreachable unwind label %funclet_bb14
+
+funclet_bb14:
+  %cleanuppad = cleanuppad within none []
+  br label %bb13
+
+unreachable:
+  unreachable
+
+bb10:
+  cleanupret from %cleanuppad5 unwind to caller
+
+funclet_bb10:
+  %cleanuppad5 = cleanuppad within none []
+  br label %bb10
+
+bb13:
+  cleanupret from %cleanuppad unwind label %funclet_bb10
+}
+
+declare void @_RNvNtCshEuXqvZjEXJ_4core9panicking18panic_bounds_check(i64, i64, ptr)



More information about the llvm-commits mailing list