Fixes #148052 . Last PR did not account for the scenario, when more than one instruction used the `catchpad` label. In that case I have deleted uses, which were already "choosen to be iterated over" by the early increment iterator. This issue was not visible in normal release build on x86, but luckily later on the address sanitizer build it has found it on the buildbot. Here is the diff from the last version of this PR: #158435 ```diff diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index 91e245e5e8f5..1dd8cb4ee584 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -106,7 +106,8 @@ void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs, // first block, the we would have possible cleanupret and catchret // instructions with poison arguments, which wouldn't be valid. if (isa<FuncletPadInst>(I)) { - for (User *User : make_early_inc_range(I.users())) { + SmallPtrSet<BasicBlock *, 4> UniqueEHRetBlocksToDelete; + for (User *User : 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 @@ -114,33 +115,12 @@ void llvm::detachDeadBlocks(ArrayRef<BasicBlock *> BBs, 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. 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); + UniqueEHRetBlocksToDelete.insert(ReturnInstrBB); } } + for (BasicBlock *EHRetBB : + make_early_inc_range(UniqueEHRetBlocksToDelete)) + emptyAndDetachBlock(EHRetBB, Updates, KeepOneInputPHIs); } } ```
237 lines
6.1 KiB
LLVM
237 lines
6.1 KiB
LLVM
; 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_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 {
|
|
; 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 {
|
|
; 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
|
|
|
|
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)
|