[llvm] Reland "[BasicBlockUtils] Handle funclets when detaching EH pad blocks" (PR #158435)

Gábor Spaits via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 16 07:08:51 PDT 2025


https://github.com/spaits updated https://github.com/llvm/llvm-project/pull/158435

>From ece2d37856107e1a9bb0033af7d366c5589a62bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?= <gaborspaits1 at gmail.com>
Date: Thu, 11 Sep 2025 22:52:16 +0200
Subject: [PATCH 1/4] Reland [BasicBlockUtils] Handle funclets when detaching
 EH pad blocks (#157363)

Fixes #148052 .

When removing EH Pad blocks, the value defined by them becomes poison. These poison values are then used by `catchret` and `cleanupret`, which is invalid. This commit replaces those unreachable `catchret` and `cleanupret` instructions with `unreachable`.
---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp |  88 ++++++--
 .../unreachable-multi-basic-block-funclet.ll  | 206 ++++++++++++++++++
 2 files changed, 270 insertions(+), 24 deletions(-)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index cad0b4c12b54e..69d530b3cf52c 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -58,37 +58,77 @@ static cl::opt<unsigned> MaxDeoptOrUnreachableSuccessorCheckDepth(
              "is followed by a block that either has a terminating "
              "deoptimizing call or is terminated with an unreachable"));
 
-void llvm::detachDeadBlocks(
-    ArrayRef<BasicBlock *> BBs,
-    SmallVectorImpl<DominatorTree::UpdateType> *Updates,
+static void zapAllInstructionInDeadBasicBlock(BasicBlock *BB) {
+  while (!BB->empty()) {
+    Instruction &I = BB->back();
+    // If this instruction is used, replace uses with an arbitrary value.
+    // Because control flow can't get here, we don't care what we replace the
+    // value with. Note that since this block is unreachable, and all values
+    // contained within it must dominate their uses, that all uses will
+    // eventually be removed (they are themselves dead).
+    if (!I.use_empty())
+      I.replaceAllUsesWith(PoisonValue::get(I.getType()));
+    BB->back().eraseFromParent();
+  }
+  new UnreachableInst(BB->getContext(), BB);
+  assert(BB->size() == 1 && isa<UnreachableInst>(BB->getTerminator()) &&
+         "The successor list of BB isn't empty before "
+         "applying corresponding DTU updates.");
+}
+
+static void deleteBasicBlockFromSuccessor(
+    BasicBlock *BB, SmallVectorImpl<DominatorTree::UpdateType> *Updates,
     bool KeepOneInputPHIs) {
+  SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
+  for (BasicBlock *Succ : successors(BB)) {
+    Succ->removePredecessor(BB, KeepOneInputPHIs);
+    if (Updates && UniqueSuccessors.insert(Succ).second)
+      Updates->push_back({DominatorTree::Delete, BB, Succ});
+  }
+}
+
+static void replaceFuncletPadsRetWithUnreachable(
+    Instruction &I, SmallVectorImpl<DominatorTree::UpdateType> *Updates,
+    bool KeepOneInputPHIs) {
+  assert(isa<FuncletPadInst>(I) && "Instruction must be a funclet pad!");
+  for (User *User : make_early_inc_range(I.users())) {
+    Instruction *ReturnInstr = dyn_cast<Instruction>(User);
+    // If we have a cleanupret or catchret block, replace it with just an
+    // unreachable.
+    if (isa<CatchReturnInst>(ReturnInstr) ||
+        isa<CleanupReturnInst>(ReturnInstr)) {
+      BasicBlock *ReturnInstrBB = ReturnInstr->getParent();
+      // This catchret or catchpad basic block is detached now. Let the
+      // successors know it.
+      deleteBasicBlockFromSuccessor(ReturnInstrBB, Updates, KeepOneInputPHIs);
+      zapAllInstructionInDeadBasicBlock(ReturnInstrBB);
+    }
+  }
+}
+
+void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs,
+                            SmallVectorImpl<DominatorTree::UpdateType> *Updates,
+                            bool KeepOneInputPHIs) {
   for (auto *BB : BBs) {
     // Loop through all of our successors and make sure they know that one
     // of their predecessors is going away.
     SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
-    for (BasicBlock *Succ : successors(BB)) {
-      Succ->removePredecessor(BB, KeepOneInputPHIs);
-      if (Updates && UniqueSuccessors.insert(Succ).second)
-        Updates->push_back({DominatorTree::Delete, BB, Succ});
+    auto NonFirstPhiIt = BB->getFirstNonPHIIt();
+    if (NonFirstPhiIt != BB->end()) {
+      Instruction &I = *NonFirstPhiIt;
+      // Exception handling funclets need to be explicitly addressed.
+      // These funclets must begin with cleanuppad or catchpad and end with
+      // cleanupred or catchret. The return instructions can be in different
+      // basic blocks than the pad instruction. If we would only delete the
+      // first block, the we would have possible cleanupret and catchret
+      // instructions with poison arguments, which wouldn't be valid.
+      if (isa<FuncletPadInst>(I))
+        replaceFuncletPadsRetWithUnreachable(I, Updates, KeepOneInputPHIs);
     }
 
-    // Zap all the instructions in the block.
-    while (!BB->empty()) {
-      Instruction &I = BB->back();
-      // If this instruction is used, replace uses with an arbitrary value.
-      // Because control flow can't get here, we don't care what we replace the
-      // value with.  Note that since this block is unreachable, and all values
-      // contained within it must dominate their uses, that all uses will
-      // eventually be removed (they are themselves dead).
-      if (!I.use_empty())
-        I.replaceAllUsesWith(PoisonValue::get(I.getType()));
-      BB->back().eraseFromParent();
-    }
-    new UnreachableInst(BB->getContext(), BB);
-    assert(BB->size() == 1 &&
-           isa<UnreachableInst>(BB->getTerminator()) &&
-           "The successor list of BB isn't empty before "
-           "applying corresponding DTU updates.");
+    // Detaching and emptying the current basic block.
+    deleteBasicBlockFromSuccessor(BB, Updates, KeepOneInputPHIs);
+    zapAllInstructionInDeadBasicBlock(BB);
   }
 }
 
diff --git a/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll b/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll
new file mode 100644
index 0000000000000..d915b0c5272ee
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll
@@ -0,0 +1,206 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=simplifycfg -S < %s | FileCheck %s
+
+; cleanuppad/cleanupret
+
+define void @unreachable_cleanuppad_linear(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @unreachable_cleanuppad_linear(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  ret void
+
+funclet:
+  %cleanuppad = cleanuppad within none []
+  br label %funclet_end
+
+funclet_end:
+  cleanupret from %cleanuppad unwind to caller
+}
+
+define void @unreachable_cleanuppad_multiple_predecessors(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @unreachable_cleanuppad_multiple_predecessors(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  ret void
+
+funclet:
+  %cleanuppad = cleanuppad within none []
+  switch i64 %shapes.1, label %otherwise [ i64 0, label %one
+  i64 1, label %two
+  i64 42, label %three ]
+one:
+  br label %funclet_end
+
+two:
+  br label %funclet_end
+
+three:
+  br label %funclet_end
+
+otherwise:
+  br label %funclet_end
+
+funclet_end:
+  cleanupret from %cleanuppad unwind to caller
+}
+
+; catchpad/catchret
+
+define void @unreachable_catchpad_linear(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @unreachable_catchpad_linear(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  ret void
+
+dispatch:
+  %cs = catchswitch within none [label %funclet] unwind to caller
+
+funclet:
+  %cleanuppad = catchpad within %cs []
+  br label %funclet_end
+
+
+funclet_end:
+  catchret from %cleanuppad to label %unreachable
+
+unreachable:
+  unreachable
+}
+
+define void @unreachable_catchpad_multiple_predecessors(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @unreachable_catchpad_multiple_predecessors(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  ret void
+
+dispatch:
+  %cs = catchswitch within none [label %funclet] unwind to caller
+
+funclet:
+  %cleanuppad = catchpad within %cs []
+  switch i64 %shapes.1, label %otherwise [ i64 0, label %one
+  i64 1, label %two
+  i64 42, label %three ]
+one:
+  br label %funclet_end
+
+two:
+  br label %funclet_end
+
+three:
+  br label %funclet_end
+
+otherwise:
+  br label %funclet_end
+
+funclet_end:
+  catchret from %cleanuppad to label %unreachable
+
+unreachable:
+  unreachable
+}
+
+; Issue reproducer
+
+define void @gh148052(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @gh148052(
+; 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 @func(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
+}
+
+%struct.foo = type { ptr, %struct.eggs, ptr }
+%struct.eggs = type { ptr, ptr, ptr }
+
+declare x86_thiscallcc ptr @quux(ptr, ptr, i32)
+
+define x86_thiscallcc ptr @baz(ptr %arg, ptr %arg1, ptr %arg2, i1 %arg3, ptr %arg4) personality ptr null {
+bb:
+  %alloca = alloca [2 x %struct.foo], align 4
+  %invoke = invoke x86_thiscallcc ptr @quux(ptr null, ptr null, i32 0)
+          to label %bb5 unwind label %bb10
+
+bb5:                                              ; preds = %bb
+  %getelementptr = getelementptr i8, ptr %arg, i32 20
+  %call = call x86_thiscallcc ptr null(ptr null, ptr null, i32 0)
+  br label %bb6
+
+bb6:                                              ; preds = %bb10, %bb5
+  %phi = phi ptr [ null, %bb10 ], [ null, %bb5 ]
+  ret ptr %phi
+
+bb7:                                              ; No predecessors!
+  %cleanuppad = cleanuppad within none []
+  %getelementptr8 = getelementptr i8, ptr %arg2, i32 -20
+  %icmp = icmp eq ptr %arg, null
+  br label %bb9
+
+bb9:                                              ; preds = %bb7
+  cleanupret from %cleanuppad unwind label %bb10
+
+bb10:                                             ; preds = %bb9, %bb
+  %phi11 = phi ptr [ %arg, %bb9 ], [ null, %bb ]
+  %cleanuppad12 = cleanuppad within none []
+  %getelementptr13 = getelementptr i8, ptr %phi11, i32 -20
+  store i32 0, ptr %phi11, align 4
+  br label %bb6
+}
+
+declare void @func(i64, i64, ptr)

>From 06a11ef2ad18d3c8d3e9915a0067fdb8fb90b270 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Tue, 16 Sep 2025 13:43:47 +0200
Subject: [PATCH 2/4] [NFC] Add more tests

---
 .../unreachable-multi-basic-block-funclet.ll  | 32 ++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll b/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll
index d915b0c5272ee..0f0fc78ec7add 100644
--- a/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll
+++ b/llvm/test/Transforms/SimplifyCFG/unreachable-multi-basic-block-funclet.ll
@@ -22,6 +22,29 @@ funclet_end:
   cleanupret from %cleanuppad unwind to caller
 }
 
+define void @unreachable_cleanuppad_linear_middle_block(i64 %shapes.1) personality ptr null {
+; CHECK-LABEL: define void @unreachable_cleanuppad_linear_middle_block(
+; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
+; CHECK-NEXT:    ret void
+;
+start:
+  %_7 = icmp ult i64 0, %shapes.1
+  ret void
+
+funclet:
+  %cleanuppad = cleanuppad within none []
+  br label %middle_block
+
+middle_block:
+  %tmp1 = add i64 %shapes.1, 42
+  br label %funclet_end
+
+funclet_end:
+  cleanupret from %cleanuppad unwind to caller
+}
+
 define void @unreachable_cleanuppad_multiple_predecessors(i64 %shapes.1) personality ptr null {
 ; CHECK-LABEL: define void @unreachable_cleanuppad_multiple_predecessors(
 ; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
@@ -172,10 +195,17 @@ bb13:
 declare x86_thiscallcc ptr @quux(ptr, ptr, i32)
 
 define x86_thiscallcc ptr @baz(ptr %arg, ptr %arg1, ptr %arg2, i1 %arg3, ptr %arg4) personality ptr null {
+; CHECK-LABEL: define x86_thiscallcc ptr @baz(
+; CHECK-SAME: ptr [[ARG:%.*]], ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], i1 [[ARG3:%.*]], ptr [[ARG4:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[BB:.*:]]
+; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca [2 x %struct.foo], align 4
+; CHECK-NEXT:    [[INVOKE:%.*]] = call x86_thiscallcc ptr @quux(ptr null, ptr null, i32 0) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    unreachable
+;
 bb:
   %alloca = alloca [2 x %struct.foo], align 4
   %invoke = invoke x86_thiscallcc ptr @quux(ptr null, ptr null, i32 0)
-          to label %bb5 unwind label %bb10
+  to label %bb5 unwind label %bb10
 
 bb5:                                              ; preds = %bb
   %getelementptr = getelementptr i8, ptr %arg, i32 20

>From 54deed066fc5fac50c0589ce3e436b98f334bf85 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Tue, 16 Sep 2025 13:44:05 +0200
Subject: [PATCH 3/4] Add more comments

---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 46 +++++++++++++++++--
 1 file changed, 42 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index 69d530b3cf52c..d4d00487850cc 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -79,6 +79,8 @@ static void zapAllInstructionInDeadBasicBlock(BasicBlock *BB) {
 static void deleteBasicBlockFromSuccessor(
     BasicBlock *BB, SmallVectorImpl<DominatorTree::UpdateType> *Updates,
     bool KeepOneInputPHIs) {
+  // Loop through all of our successors and make sure they know that one
+  // of their predecessors is going away.
   SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
   for (BasicBlock *Succ : successors(BB)) {
     Succ->removePredecessor(BB, KeepOneInputPHIs);
@@ -94,12 +96,51 @@ static void replaceFuncletPadsRetWithUnreachable(
   for (User *User : make_early_inc_range(I.users())) {
     Instruction *ReturnInstr = dyn_cast<Instruction>(User);
     // If we have a cleanupret or catchret block, replace it with just an
-    // unreachable.
+    // unreachable. The other alternative, that may use a catchpad is a 
+    // catchswitch. That is not handled here.
     if (isa<CatchReturnInst>(ReturnInstr) ||
         isa<CleanupReturnInst>(ReturnInstr)) {
       BasicBlock *ReturnInstrBB = ReturnInstr->getParent();
       // This catchret or catchpad basic block is detached now. Let the
       // successors know it.
+      // This basic block also may have some predecessors too. For
+      // example the following LLVM-IR legit:
+      //
+      //   +-------------------------------------------+
+      //   | funclet:                    %unreachable  |
+      //   |   %cleanuppad = cleanuppad within none [] |
+      //   |   br label %middle_block                  |
+      //   +-------------------------------------------+
+      //                          |
+      //             +-------------------------+
+      //             | middle_block:           |
+      //             |   br label %funclet_end |
+      //             +-------------------------+
+      //                          |
+      // +------------------------------------------------+
+      // | funclet_end:                                   |
+      // |   cleanupret from %cleanuppad unwind to caller |
+      // +------------------------------------------------+
+      //
+      // The IR after the cleanup will look like this:
+      //
+      //   +-------------------------------------------+
+      //   | funclet:                    %unreachable  |
+      //   |   %cleanuppad = cleanuppad within none [] |
+      //   |   br label %middle_block                  |
+      //   +-------------------------------------------+
+      //                          |
+      //             +-------------------------+
+      //             | middle_block:           |
+      //             |   br label %funclet_end |
+      //             +-------------------------+
+      //                          |
+      //                   +-------------+
+      //                   | unreachable |
+      //                   +-------------+
+      //
+      // So middle_block will lead to an unreachable block, which is also legit.
+
       deleteBasicBlockFromSuccessor(ReturnInstrBB, Updates, KeepOneInputPHIs);
       zapAllInstructionInDeadBasicBlock(ReturnInstrBB);
     }
@@ -110,9 +151,6 @@ void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs,
                             SmallVectorImpl<DominatorTree::UpdateType> *Updates,
                             bool KeepOneInputPHIs) {
   for (auto *BB : BBs) {
-    // Loop through all of our successors and make sure they know that one
-    // of their predecessors is going away.
-    SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
     auto NonFirstPhiIt = BB->getFirstNonPHIIt();
     if (NonFirstPhiIt != BB->end()) {
       Instruction &I = *NonFirstPhiIt;

>From ae456f28774f423a80f9e4968875b4613e399113 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Tue, 16 Sep 2025 14:03:28 +0200
Subject: [PATCH 4/4] [NFC] zapAllInstructionInDeadBasicBlock +
 deleteBasicBlockFromSuccessor -> emptyAndDetachBlock and delete
 replaceFuncletPadsRetWithUnreachable

---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 140 ++++++++----------
 1 file changed, 64 insertions(+), 76 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index d4d00487850cc..159593ec8b5a6 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -58,7 +58,23 @@ static cl::opt<unsigned> MaxDeoptOrUnreachableSuccessorCheckDepth(
              "is followed by a block that either has a terminating "
              "deoptimizing call or is terminated with an unreachable"));
 
-static void zapAllInstructionInDeadBasicBlock(BasicBlock *BB) {
+/// Zap all the instructions in the block and replace them with an unreachable
+/// instruction and notify the basic block's successors that one of their
+/// predecessors is going away.
+static void
+emptyAndDetachBlock(BasicBlock *BB,
+                    SmallVectorImpl<DominatorTree::UpdateType> *Updates,
+                    bool KeepOneInputPHIs) {
+  // Loop through all of our successors and make sure they know that one
+  // of their predecessors is going away.
+  SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
+  for (BasicBlock *Succ : successors(BB)) {
+    Succ->removePredecessor(BB, KeepOneInputPHIs);
+    if (Updates && UniqueSuccessors.insert(Succ).second)
+      Updates->push_back({DominatorTree::Delete, BB, Succ});
+  }
+
+  // Zap all the instructions in the block.
   while (!BB->empty()) {
     Instruction &I = BB->back();
     // If this instruction is used, replace uses with an arbitrary value.
@@ -76,77 +92,6 @@ static void zapAllInstructionInDeadBasicBlock(BasicBlock *BB) {
          "applying corresponding DTU updates.");
 }
 
-static void deleteBasicBlockFromSuccessor(
-    BasicBlock *BB, SmallVectorImpl<DominatorTree::UpdateType> *Updates,
-    bool KeepOneInputPHIs) {
-  // Loop through all of our successors and make sure they know that one
-  // of their predecessors is going away.
-  SmallPtrSet<BasicBlock *, 4> UniqueSuccessors;
-  for (BasicBlock *Succ : successors(BB)) {
-    Succ->removePredecessor(BB, KeepOneInputPHIs);
-    if (Updates && UniqueSuccessors.insert(Succ).second)
-      Updates->push_back({DominatorTree::Delete, BB, Succ});
-  }
-}
-
-static void replaceFuncletPadsRetWithUnreachable(
-    Instruction &I, SmallVectorImpl<DominatorTree::UpdateType> *Updates,
-    bool KeepOneInputPHIs) {
-  assert(isa<FuncletPadInst>(I) && "Instruction must be a funclet pad!");
-  for (User *User : make_early_inc_range(I.users())) {
-    Instruction *ReturnInstr = dyn_cast<Instruction>(User);
-    // If we have a cleanupret or catchret block, replace it with just an
-    // unreachable. The other alternative, that may use a catchpad is a 
-    // catchswitch. That is not handled here.
-    if (isa<CatchReturnInst>(ReturnInstr) ||
-        isa<CleanupReturnInst>(ReturnInstr)) {
-      BasicBlock *ReturnInstrBB = ReturnInstr->getParent();
-      // This catchret or catchpad basic block is detached now. Let the
-      // successors know it.
-      // This basic block also may have some predecessors too. For
-      // example the following LLVM-IR legit:
-      //
-      //   +-------------------------------------------+
-      //   | funclet:                    %unreachable  |
-      //   |   %cleanuppad = cleanuppad within none [] |
-      //   |   br label %middle_block                  |
-      //   +-------------------------------------------+
-      //                          |
-      //             +-------------------------+
-      //             | middle_block:           |
-      //             |   br label %funclet_end |
-      //             +-------------------------+
-      //                          |
-      // +------------------------------------------------+
-      // | funclet_end:                                   |
-      // |   cleanupret from %cleanuppad unwind to caller |
-      // +------------------------------------------------+
-      //
-      // The IR after the cleanup will look like this:
-      //
-      //   +-------------------------------------------+
-      //   | funclet:                    %unreachable  |
-      //   |   %cleanuppad = cleanuppad within none [] |
-      //   |   br label %middle_block                  |
-      //   +-------------------------------------------+
-      //                          |
-      //             +-------------------------+
-      //             | middle_block:           |
-      //             |   br label %funclet_end |
-      //             +-------------------------+
-      //                          |
-      //                   +-------------+
-      //                   | unreachable |
-      //                   +-------------+
-      //
-      // So middle_block will lead to an unreachable block, which is also legit.
-
-      deleteBasicBlockFromSuccessor(ReturnInstrBB, Updates, KeepOneInputPHIs);
-      zapAllInstructionInDeadBasicBlock(ReturnInstrBB);
-    }
-  }
-}
-
 void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs,
                             SmallVectorImpl<DominatorTree::UpdateType> *Updates,
                             bool KeepOneInputPHIs) {
@@ -160,13 +105,56 @@ void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs,
       // basic blocks than the pad instruction. If we would only delete the
       // first block, the we would have possible cleanupret and catchret
       // instructions with poison arguments, which wouldn't be valid.
-      if (isa<FuncletPadInst>(I))
-        replaceFuncletPadsRetWithUnreachable(I, Updates, KeepOneInputPHIs);
+      if (isa<FuncletPadInst>(I)) {
+        for (User *User : make_early_inc_range(I.users())) {
+          Instruction *ReturnInstr = dyn_cast<Instruction>(User);
+          // If we have a cleanupret or catchret block, replace it with just an
+          // unreachable. The other alternative, that may use a catchpad is a
+          // catchswitch. That does not need special handling for now.
+          if (isa<CatchReturnInst>(ReturnInstr) ||
+              isa<CleanupReturnInst>(ReturnInstr)) {
+            BasicBlock *ReturnInstrBB = ReturnInstr->getParent();
+            // This catchret or catchpad basic block is detached now. Let the
+            // successors know it.
+            // This basic block also may have some predecessors too. For
+            // example the following LLVM-IR is valid:
+            //
+            //   +------------------+
+            //   | cleanuppad_block |
+            //   +------------------+
+            //            |
+            //    +---------------+
+            //    | regular_block |
+            //    +---------------+
+            //            |
+            //   +------------------+
+            //   | cleanupret_block |
+            //   +------------------+
+            //
+            // The IR after the cleanup will look like this:
+            //
+            //   +------------------+
+            //   | cleanuppad_block |
+            //   +------------------+
+            //            |
+            //    +---------------+
+            //    | regular_block |
+            //    +---------------+
+            //            |
+            //     +-------------+
+            //     | unreachable |
+            //     +-------------+
+            //
+            // So regular_block will lead to an unreachable block, which is also
+            // valid.
+            emptyAndDetachBlock(ReturnInstrBB, Updates, KeepOneInputPHIs);
+          }
+        }
+      }
     }
 
     // Detaching and emptying the current basic block.
-    deleteBasicBlockFromSuccessor(BB, Updates, KeepOneInputPHIs);
-    zapAllInstructionInDeadBasicBlock(BB);
+    emptyAndDetachBlock(BB, Updates, KeepOneInputPHIs);
   }
 }
 



More information about the llvm-commits mailing list