Skip to content

Commit b2fe5d1

Browse files
authored
[SimplifyCFG] Hoist common code when succ is unreachable block (llvm#165570)
Previously, `hoistCommonCodeFromSuccessors` returned early if one of the succ of BB has >1 predecessors. However, if the succ is an unreachable BB, we can relax the condition to perform `hoistCommonCodeFromSuccessors` based on the assumption of not reaching UB. See discussion dtcxzyw/llvm-opt-benchmark#2989 for details. Alive2 proof: https://alive2.llvm.org/ce/z/OJOw0s Promising optimization impact: dtcxzyw/llvm-opt-benchmark#2995
1 parent 7b98280 commit b2fe5d1

File tree

2 files changed

+128
-3
lines changed

2 files changed

+128
-3
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,10 +1866,19 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
18661866
// If either of the blocks has it's address taken, then we can't do this fold,
18671867
// because the code we'd hoist would no longer run when we jump into the block
18681868
// by it's address.
1869-
for (auto *Succ : successors(BB))
1870-
if (Succ->hasAddressTaken() || !Succ->getSinglePredecessor())
1869+
for (auto *Succ : successors(BB)) {
1870+
if (Succ->hasAddressTaken())
18711871
return false;
1872-
1872+
if (Succ->getSinglePredecessor())
1873+
continue;
1874+
// If Succ has >1 predecessors, continue to check if the Succ contains only
1875+
// one `unreachable` inst. Since executing `unreachable` inst is an UB, we
1876+
// can relax the condition based on the assumptiom that the program would
1877+
// never enter Succ and trigger such an UB.
1878+
if (isa<UnreachableInst>(*Succ->begin()))
1879+
continue;
1880+
return false;
1881+
}
18731882
// The second of pair is a SkipFlags bitmask.
18741883
using SuccIterPair = std::pair<BasicBlock::iterator, unsigned>;
18751884
SmallVector<SuccIterPair, 8> SuccIterPairs;

llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,119 @@ else:
486486
call void @bar()
487487
ret float %op2
488488
}
489+
490+
define void @test_switch_with_unreachable_block_as_default(i1 %c, i32 %x, ptr %ptr) {
491+
; CHECK-LABEL: @test_switch_with_unreachable_block_as_default(
492+
; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
493+
; CHECK: sw1:
494+
; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
495+
; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
496+
; CHECK-NEXT: i32 2, label [[BAR:%.*]]
497+
; CHECK-NEXT: ]
498+
; CHECK: sw2:
499+
; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
500+
; CHECK-NEXT: br label [[COMMON_RET]]
501+
; CHECK: common.ret:
502+
; CHECK-NEXT: ret void
503+
; CHECK: unreachable:
504+
; CHECK-NEXT: unreachable
505+
; CHECK: bar:
506+
; CHECK-NEXT: call void @bar()
507+
; CHECK-NEXT: br label [[COMMON_RET]]
508+
;
509+
br i1 %c, label %sw1, label %sw2
510+
511+
sw1:
512+
; This switch only exists to have an %unreachable block with multiple predecessors.
513+
switch i32 %x, label %unreachable [
514+
i32 1, label %foo
515+
i32 2, label %bar
516+
]
517+
518+
sw2:
519+
switch i32 %x, label %unreachable [
520+
i32 1, label %bb1
521+
i32 2, label %bb2
522+
i32 3, label %bb3
523+
]
524+
525+
bb1:
526+
store i64 42, ptr %ptr
527+
ret void
528+
529+
bb2:
530+
store i64 42, ptr %ptr
531+
ret void
532+
533+
bb3:
534+
store i64 42, ptr %ptr
535+
ret void
536+
537+
unreachable:
538+
unreachable
539+
540+
foo:
541+
ret void
542+
543+
bar:
544+
call void @bar()
545+
ret void
546+
}
547+
548+
define void @test_switch_with_unreachable_block_as_case(i1 %c, i32 %x, ptr %ptr) {
549+
; CHECK-LABEL: @test_switch_with_unreachable_block_as_case(
550+
; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
551+
; CHECK: sw1:
552+
; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
553+
; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
554+
; CHECK-NEXT: i32 2, label [[BAR:%.*]]
555+
; CHECK-NEXT: ]
556+
; CHECK: sw2:
557+
; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
558+
; CHECK-NEXT: br label [[COMMON_RET]]
559+
; CHECK: common.ret:
560+
; CHECK-NEXT: ret void
561+
; CHECK: unreachable:
562+
; CHECK-NEXT: unreachable
563+
; CHECK: bar:
564+
; CHECK-NEXT: call void @bar()
565+
; CHECK-NEXT: br label [[COMMON_RET]]
566+
;
567+
br i1 %c, label %sw1, label %sw2
568+
569+
sw1:
570+
; This switch only exists to have an %unreachable block with multiple predecessors.
571+
switch i32 %x, label %unreachable [
572+
i32 1, label %foo
573+
i32 2, label %bar
574+
]
575+
576+
sw2:
577+
switch i32 %x, label %bb3 [
578+
i32 1, label %bb1
579+
i32 2, label %bb2
580+
i32 3, label %unreachable
581+
]
582+
583+
bb1:
584+
store i64 42, ptr %ptr
585+
ret void
586+
587+
bb2:
588+
store i64 42, ptr %ptr
589+
ret void
590+
591+
bb3:
592+
store i64 42, ptr %ptr
593+
ret void
594+
595+
unreachable:
596+
unreachable
597+
598+
foo:
599+
ret void
600+
601+
bar:
602+
call void @bar()
603+
ret void
604+
}

0 commit comments

Comments
 (0)