From ece2d37856107e1a9bb0033af7d366c5589a62bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Spaits?= 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 MaxDeoptOrUnreachableSuccessorCheckDepth( "is followed by a block that either has a terminating " "deoptimizing call or is terminated with an unreachable")); -void llvm::detachDeadBlocks( - ArrayRef BBs, - SmallVectorImpl *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(BB->getTerminator()) && + "The successor list of BB isn't empty before " + "applying corresponding DTU updates."); +} + +static void deleteBasicBlockFromSuccessor( + BasicBlock *BB, SmallVectorImpl *Updates, bool KeepOneInputPHIs) { + SmallPtrSet 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 *Updates, + bool KeepOneInputPHIs) { + assert(isa(I) && "Instruction must be a funclet pad!"); + for (User *User : make_early_inc_range(I.users())) { + Instruction *ReturnInstr = dyn_cast(User); + // If we have a cleanupret or catchret block, replace it with just an + // unreachable. + if (isa(ReturnInstr) || + isa(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 BBs, + SmallVectorImpl *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 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(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(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 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 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 *Updates, bool KeepOneInputPHIs) { + // Loop through all of our successors and make sure they know that one + // of their predecessors is going away. SmallPtrSet 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(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(ReturnInstr) || isa(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 BBs, SmallVectorImpl *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 UniqueSuccessors; auto NonFirstPhiIt = BB->getFirstNonPHIIt(); if (NonFirstPhiIt != BB->end()) { Instruction &I = *NonFirstPhiIt; From 9dcf6e52af6b11575f6210871805ac53193323f7 Mon Sep 17 00:00:00 2001 From: Gabor Spaits 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 | 131 ++++++++---------- 1 file changed, 55 insertions(+), 76 deletions(-) diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index d4d00487850cc..91e245e5e8f54 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -58,7 +58,23 @@ static cl::opt 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 *Updates, + bool KeepOneInputPHIs) { + // Loop through all of our successors and make sure they know that one + // of their predecessors is going away. + SmallPtrSet 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 *Updates, - bool KeepOneInputPHIs) { - // Loop through all of our successors and make sure they know that one - // of their predecessors is going away. - SmallPtrSet 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 *Updates, - bool KeepOneInputPHIs) { - assert(isa(I) && "Instruction must be a funclet pad!"); - for (User *User : make_early_inc_range(I.users())) { - Instruction *ReturnInstr = dyn_cast(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(ReturnInstr) || - isa(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 BBs, SmallVectorImpl *Updates, bool KeepOneInputPHIs) { @@ -160,13 +105,47 @@ void llvm::detachDeadBlocks(ArrayRef 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(I)) - replaceFuncletPadsRetWithUnreachable(I, Updates, KeepOneInputPHIs); + if (isa(I)) { + for (User *User : make_early_inc_range(I.users())) { + Instruction *ReturnInstr = dyn_cast(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(ReturnInstr) || + isa(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. There is no need to replace regular_block with unreachable + // in this context now. + // On the other hand, the cleanupret/catchret block's successors + // need to know about the deletion of their predecessors. + emptyAndDetachBlock(ReturnInstrBB, Updates, KeepOneInputPHIs); + } + } + } } // Detaching and emptying the current basic block. - deleteBasicBlockFromSuccessor(BB, Updates, KeepOneInputPHIs); - zapAllInstructionInDeadBasicBlock(BB); + emptyAndDetachBlock(BB, Updates, KeepOneInputPHIs); } }