Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,10 +1866,19 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
// If either of the blocks has it's address taken, then we can't do this fold,
// because the code we'd hoist would no longer run when we jump into the block
// by it's address.
for (auto *Succ : successors(BB))
if (Succ->hasAddressTaken() || !Succ->getSinglePredecessor())
for (auto *Succ : successors(BB)) {
if (Succ->hasAddressTaken())
return false;

if (Succ->getSinglePredecessor())
continue;
// If Succ has >1 predecessors, continue to check if the Succ contains only
// one `unreachable` inst. Since executing `unreachable` inst is an UB, we
// can relax the condition based on the assumptiom that the program would
// never enter Succ and trigger such an UB.
if (isa<UnreachableInst>(*Succ->begin()))
continue;
return false;
}
// The second of pair is a SkipFlags bitmask.
using SuccIterPair = std::pair<BasicBlock::iterator, unsigned>;
SmallVector<SuccIterPair, 8> SuccIterPairs;
Expand Down
116 changes: 116 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,119 @@ else:
call void @bar()
ret float %op2
}

define void @test_switch_with_unreachable_block_as_default(i1 %c, i32 %x, ptr %ptr) {
; CHECK-LABEL: @test_switch_with_unreachable_block_as_default(
; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
; CHECK: sw1:
; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
; CHECK-NEXT: i32 2, label [[BAR:%.*]]
; CHECK-NEXT: ]
; CHECK: sw2:
; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: unreachable:
; CHECK-NEXT: unreachable
; CHECK: bar:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[COMMON_RET]]
;
br i1 %c, label %sw1, label %sw2

sw1:
; This switch only exists to have an %unreachable block with multiple predecessors.
switch i32 %x, label %unreachable [
i32 1, label %foo
i32 2, label %bar
]

sw2:
switch i32 %x, label %unreachable [
i32 1, label %bb1
i32 2, label %bb2
i32 3, label %bb3
]

bb1:
store i64 42, ptr %ptr
ret void

bb2:
store i64 42, ptr %ptr
ret void

bb3:
store i64 42, ptr %ptr
ret void

unreachable:
unreachable

foo:
ret void

bar:
call void @bar()
ret void
}

define void @test_switch_with_unreachable_block_as_case(i1 %c, i32 %x, ptr %ptr) {
; CHECK-LABEL: @test_switch_with_unreachable_block_as_case(
; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
; CHECK: sw1:
; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
; CHECK-NEXT: i32 2, label [[BAR:%.*]]
; CHECK-NEXT: ]
; CHECK: sw2:
; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: unreachable:
; CHECK-NEXT: unreachable
; CHECK: bar:
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[COMMON_RET]]
;
br i1 %c, label %sw1, label %sw2

sw1:
; This switch only exists to have an %unreachable block with multiple predecessors.
switch i32 %x, label %unreachable [
i32 1, label %foo
i32 2, label %bar
]

sw2:
switch i32 %x, label %bb3 [
i32 1, label %bb1
i32 2, label %bb2
i32 3, label %unreachable
]

bb1:
store i64 42, ptr %ptr
ret void

bb2:
store i64 42, ptr %ptr
ret void

bb3:
store i64 42, ptr %ptr
ret void

unreachable:
unreachable

foo:
ret void

bar:
call void @bar()
ret void
}