[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