llvm-project/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch.ll

1246 lines
34 KiB
LLVM

; RUN: opt -passes='loop(unswitch),verify<loops>' -S < %s | FileCheck %s
; RUN: opt -enable-mssa-loop-dependency=true -verify-memoryssa -passes='loop(unswitch),verify<loops>' -S < %s | FileCheck %s
declare void @some_func() noreturn
declare void @sink(i32)
declare i1 @cond()
declare i32 @cond.i32()
; This test contains two trivial unswitch condition in one loop.
; LoopUnswitch pass should be able to unswitch the second one
; after unswitching the first one.
define i32 @test1(i32* %var, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @test1(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit.split
;
; CHECK: entry.split:
; CHECK-NEXT: br i1 %{{.*}}, label %entry.split.split, label %loop_exit
;
; CHECK: entry.split.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
br i1 %cond1, label %continue, label %loop_exit ; first trivial condition
; CHECK: loop_begin:
; CHECK-NEXT: br label %continue
continue:
%var_val = load i32, i32* %var
br i1 %cond2, label %do_something, label %loop_exit ; second trivial condition
; CHECK: continue:
; CHECK-NEXT: load
; CHECK-NEXT: br label %do_something
do_something:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
ret i32 0
; CHECK: loop_exit:
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: ret
}
; Test for two trivially unswitchable switches.
define i32 @test3(i32* %var, i32 %cond1, i32 %cond2) {
; CHECK-LABEL: @test3(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond1, label %entry.split [
; CHECK-NEXT: i32 0, label %loop_exit1
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: switch i32 %cond2, label %loop_exit2 [
; CHECK-NEXT: i32 42, label %loop_exit2
; CHECK-NEXT: i32 0, label %entry.split.split
; CHECK-NEXT: ]
;
; CHECK: entry.split.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
switch i32 %cond1, label %continue [
i32 0, label %loop_exit1
]
; CHECK: loop_begin:
; CHECK-NEXT: br label %continue
continue:
%var_val = load i32, i32* %var
switch i32 %cond2, label %loop_exit2 [
i32 0, label %do_something
i32 42, label %loop_exit2
]
; CHECK: continue:
; CHECK-NEXT: load
; CHECK-NEXT: br label %do_something
do_something:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit1:
ret i32 0
; CHECK: loop_exit1:
; CHECK-NEXT: ret
loop_exit2:
ret i32 0
; CHECK: loop_exit2:
; CHECK-NEXT: ret
;
; We shouldn't have any unreachable blocks here because the unswitched switches
; turn into branches instead.
; CHECK-NOT: unreachable
}
; Test for a trivially unswitchable switch with multiple exiting cases and
; multiple looping cases.
define i32 @test4(i32* %var, i32 %cond1, i32 %cond2) {
; CHECK-LABEL: @test4(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond2, label %loop_exit2 [
; CHECK-NEXT: i32 13, label %loop_exit1
; CHECK-NEXT: i32 42, label %loop_exit3
; CHECK-NEXT: i32 0, label %entry.split
; CHECK-NEXT: i32 1, label %entry.split
; CHECK-NEXT: i32 2, label %entry.split
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
%var_val = load i32, i32* %var
switch i32 %cond2, label %loop_exit2 [
i32 0, label %loop0
i32 1, label %loop1
i32 13, label %loop_exit1
i32 2, label %loop2
i32 42, label %loop_exit3
]
; CHECK: loop_begin:
; CHECK-NEXT: load
; CHECK-NEXT: switch i32 %cond2, label %loop2 [
; CHECK-NEXT: i32 0, label %loop0
; CHECK-NEXT: i32 1, label %loop1
; CHECK-NEXT: ]
loop0:
call void @some_func() noreturn nounwind
br label %loop_latch
; CHECK: loop0:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_latch
loop1:
call void @some_func() noreturn nounwind
br label %loop_latch
; CHECK: loop1:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_latch
loop2:
call void @some_func() noreturn nounwind
br label %loop_latch
; CHECK: loop2:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_latch
loop_latch:
br label %loop_begin
; CHECK: loop_latch:
; CHECK-NEXT: br label %loop_begin
loop_exit1:
ret i32 0
; CHECK: loop_exit1:
; CHECK-NEXT: ret
loop_exit2:
ret i32 0
; CHECK: loop_exit2:
; CHECK-NEXT: ret
loop_exit3:
ret i32 0
; CHECK: loop_exit3:
; CHECK-NEXT: ret
}
; This test contains a trivially unswitchable branch with an LCSSA phi node in
; a loop exit block.
define i32 @test5(i1 %cond1, i32 %x, i32 %y) {
; CHECK-LABEL: @test5(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
br i1 %cond1, label %latch, label %loop_exit
; CHECK: loop_begin:
; CHECK-NEXT: br label %latch
latch:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: latch:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
%result1 = phi i32 [ %x, %loop_begin ]
%result2 = phi i32 [ %y, %loop_begin ]
%result = add i32 %result1, %result2
ret i32 %result
; CHECK: loop_exit:
; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ]
; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ]
; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]]
; CHECK-NEXT: ret i32 %[[R]]
}
; This test contains a trivially unswitchable branch with a real phi node in LCSSA
; position in a shared exit block where a different path through the loop
; produces a non-invariant input to the PHI node.
define i32 @test6(i32* %var, i1 %cond1, i1 %cond2, i32 %x, i32 %y) {
; CHECK-LABEL: @test6(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit.split
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
br i1 %cond1, label %continue, label %loop_exit
; CHECK: loop_begin:
; CHECK-NEXT: br label %continue
continue:
%var_val = load i32, i32* %var
br i1 %cond2, label %latch, label %loop_exit
; CHECK: continue:
; CHECK-NEXT: load
; CHECK-NEXT: br i1 %cond2, label %latch, label %loop_exit
latch:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: latch:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
%result1 = phi i32 [ %x, %loop_begin ], [ %var_val, %continue ]
%result2 = phi i32 [ %var_val, %continue ], [ %y, %loop_begin ]
%result = add i32 %result1, %result2
ret i32 %result
; CHECK: loop_exit:
; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %var_val, %continue ]
; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %var_val, %continue ]
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: %[[R1S:.*]] = phi i32 [ %x, %entry ], [ %[[R1]], %loop_exit ]
; CHECK-NEXT: %[[R2S:.*]] = phi i32 [ %y, %entry ], [ %[[R2]], %loop_exit ]
; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1S]], %[[R2S]]
; CHECK-NEXT: ret i32 %[[R]]
}
; This test contains a trivially unswitchable switch with an LCSSA phi node in
; a loop exit block.
define i32 @test7(i32 %cond1, i32 %x, i32 %y) {
; CHECK-LABEL: @test7(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond1, label %entry.split [
; CHECK-NEXT: i32 0, label %loop_exit
; CHECK-NEXT: i32 1, label %loop_exit
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
switch i32 %cond1, label %latch [
i32 0, label %loop_exit
i32 1, label %loop_exit
]
; CHECK: loop_begin:
; CHECK-NEXT: br label %latch
latch:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: latch:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
%result1 = phi i32 [ %x, %loop_begin ], [ %x, %loop_begin ]
%result2 = phi i32 [ %y, %loop_begin ], [ %y, %loop_begin ]
%result = add i32 %result1, %result2
ret i32 %result
; CHECK: loop_exit:
; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ], [ %x, %entry ]
; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ], [ %y, %entry ]
; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]]
; CHECK-NEXT: ret i32 %[[R]]
}
; This test contains a trivially unswitchable switch with a real phi node in
; LCSSA position in a shared exit block where a different path through the loop
; produces a non-invariant input to the PHI node.
define i32 @test8(i32* %var, i32 %cond1, i32 %cond2, i32 %x, i32 %y) {
; CHECK-LABEL: @test8(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond1, label %entry.split [
; CHECK-NEXT: i32 0, label %loop_exit.split
; CHECK-NEXT: i32 1, label %loop_exit2
; CHECK-NEXT: i32 2, label %loop_exit.split
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
switch i32 %cond1, label %continue [
i32 0, label %loop_exit
i32 1, label %loop_exit2
i32 2, label %loop_exit
]
; CHECK: loop_begin:
; CHECK-NEXT: br label %continue
continue:
%var_val = load i32, i32* %var
switch i32 %cond2, label %latch [
i32 0, label %loop_exit
]
; CHECK: continue:
; CHECK-NEXT: load
; CHECK-NEXT: switch i32 %cond2, label %latch [
; CHECK-NEXT: i32 0, label %loop_exit
; CHECK-NEXT: ]
latch:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: latch:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
%result1.1 = phi i32 [ %x, %loop_begin ], [ %x, %loop_begin ], [ %var_val, %continue ]
%result1.2 = phi i32 [ %var_val, %continue ], [ %y, %loop_begin ], [ %y, %loop_begin ]
%result1 = add i32 %result1.1, %result1.2
ret i32 %result1
; CHECK: loop_exit:
; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %var_val, %continue ]
; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %var_val, %continue ]
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: %[[R1S:.*]] = phi i32 [ %x, %entry ], [ %x, %entry ], [ %[[R1]], %loop_exit ]
; CHECK-NEXT: %[[R2S:.*]] = phi i32 [ %y, %entry ], [ %y, %entry ], [ %[[R2]], %loop_exit ]
; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1S]], %[[R2S]]
; CHECK-NEXT: ret i32 %[[R]]
loop_exit2:
%result2.1 = phi i32 [ %x, %loop_begin ]
%result2.2 = phi i32 [ %y, %loop_begin ]
%result2 = add i32 %result2.1, %result2.2
ret i32 %result2
; CHECK: loop_exit2:
; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ]
; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ]
; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]]
; CHECK-NEXT: ret i32 %[[R]]
}
; This test, extracted from the LLVM test suite, has an interesting dominator
; tree to update as there are edges to sibling domtree nodes within child
; domtree nodes of the unswitched node.
define void @xgets(i1 %cond1, i1* %cond2.ptr) {
; CHECK-LABEL: @xgets(
entry:
br label %for.cond.preheader
; CHECK: entry:
; CHECK-NEXT: br label %for.cond.preheader
for.cond.preheader:
br label %for.cond
; CHECK: for.cond.preheader:
; CHECK-NEXT: br i1 %cond1, label %for.cond.preheader.split, label %if.end17.thread.loopexit
;
; CHECK: for.cond.preheader.split:
; CHECK-NEXT: br label %for.cond
for.cond:
br i1 %cond1, label %land.lhs.true, label %if.end17.thread.loopexit
; CHECK: for.cond:
; CHECK-NEXT: br label %land.lhs.true
land.lhs.true:
br label %if.then20
; CHECK: land.lhs.true:
; CHECK-NEXT: br label %if.then20
if.then20:
%cond2 = load volatile i1, i1* %cond2.ptr
br i1 %cond2, label %if.then23, label %if.else
; CHECK: if.then20:
; CHECK-NEXT: %[[COND2:.*]] = load volatile i1, i1* %cond2.ptr
; CHECK-NEXT: br i1 %[[COND2]], label %if.then23, label %if.else
if.else:
br label %for.cond
; CHECK: if.else:
; CHECK-NEXT: br label %for.cond
if.end17.thread.loopexit:
br label %if.end17.thread
; CHECK: if.end17.thread.loopexit:
; CHECK-NEXT: br label %if.end17.thread
if.end17.thread:
br label %cleanup
; CHECK: if.end17.thread:
; CHECK-NEXT: br label %cleanup
if.then23:
br label %cleanup
; CHECK: if.then23:
; CHECK-NEXT: br label %cleanup
cleanup:
ret void
; CHECK: cleanup:
; CHECK-NEXT: ret void
}
define i32 @test_partial_condition_unswitch_and(i32* %var, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @test_partial_condition_unswitch_and(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %cond1, label %entry.split, label %loop_exit.split
;
; CHECK: entry.split:
; CHECK-NEXT: br i1 %cond2, label %entry.split.split, label %loop_exit
;
; CHECK: entry.split.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
br i1 %cond1, label %continue, label %loop_exit
; CHECK: loop_begin:
; CHECK-NEXT: br label %continue
continue:
%var_val = load i32, i32* %var
%var_cond = trunc i32 %var_val to i1
%cond_and = and i1 %var_cond, %cond2
br i1 %cond_and, label %do_something, label %loop_exit
; CHECK: continue:
; CHECK-NEXT: %[[VAR:.*]] = load i32
; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1
; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true
; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit
do_something:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
ret i32 0
; CHECK: loop_exit:
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: ret
}
define i32 @test_partial_condition_unswitch_or(i32* %var, i1 %cond1, i1 %cond2, i1 %cond3, i1 %cond4, i1 %cond5, i1 %cond6) {
; CHECK-LABEL: @test_partial_condition_unswitch_or(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[INV_OR1:.*]] = or i1 %cond4, %cond2
; CHECK-NEXT: %[[INV_OR2:.*]] = or i1 %[[INV_OR1]], %cond3
; CHECK-NEXT: %[[INV_OR3:.*]] = or i1 %[[INV_OR2]], %cond1
; CHECK-NEXT: br i1 %[[INV_OR3]], label %loop_exit.split, label %entry.split
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
%var_val = load i32, i32* %var
%var_cond = trunc i32 %var_val to i1
%cond_or1 = or i1 %var_cond, %cond1
%cond_or2 = or i1 %cond2, %cond3
%cond_or3 = or i1 %cond_or1, %cond_or2
%cond_xor1 = xor i1 %cond5, %var_cond
%cond_and1 = and i1 %cond6, %var_cond
%cond_or4 = or i1 %cond_xor1, %cond_and1
%cond_or5 = or i1 %cond_or3, %cond_or4
%cond_or6 = or i1 %cond_or5, %cond4
br i1 %cond_or6, label %loop_exit, label %do_something
; CHECK: loop_begin:
; CHECK-NEXT: %[[VAR:.*]] = load i32
; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1
; CHECK-NEXT: %[[COND_OR1:.*]] = or i1 %[[VAR_COND]], false
; CHECK-NEXT: %[[COND_OR2:.*]] = or i1 false, false
; CHECK-NEXT: %[[COND_OR3:.*]] = or i1 %[[COND_OR1]], %[[COND_OR2]]
; CHECK-NEXT: %[[COND_XOR:.*]] = xor i1 %cond5, %[[VAR_COND]]
; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %cond6, %[[VAR_COND]]
; CHECK-NEXT: %[[COND_OR4:.*]] = or i1 %[[COND_XOR]], %[[COND_AND]]
; CHECK-NEXT: %[[COND_OR5:.*]] = or i1 %[[COND_OR3]], %[[COND_OR4]]
; CHECK-NEXT: %[[COND_OR6:.*]] = or i1 %[[COND_OR5]], false
; CHECK-NEXT: br i1 %[[COND_OR6]], label %loop_exit, label %do_something
do_something:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
ret i32 0
; CHECK: loop_exit.split:
; CHECK-NEXT: ret
}
define i32 @test_partial_condition_unswitch_with_lcssa_phi1(i32* %var, i1 %cond, i32 %x) {
; CHECK-LABEL: @test_partial_condition_unswitch_with_lcssa_phi1(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %cond, label %entry.split, label %loop_exit.split
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
%var_val = load i32, i32* %var
%var_cond = trunc i32 %var_val to i1
%cond_and = and i1 %var_cond, %cond
br i1 %cond_and, label %do_something, label %loop_exit
; CHECK: loop_begin:
; CHECK-NEXT: %[[VAR:.*]] = load i32
; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1
; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true
; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit
do_something:
call void @some_func() noreturn nounwind
br label %loop_begin
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br label %loop_begin
loop_exit:
%x.lcssa = phi i32 [ %x, %loop_begin ]
ret i32 %x.lcssa
; CHECK: loop_exit:
; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %x, %loop_begin ]
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: %[[LCSSA_SPLIT:.*]] = phi i32 [ %x, %entry ], [ %[[LCSSA]], %loop_exit ]
; CHECK-NEXT: ret i32 %[[LCSSA_SPLIT]]
}
define i32 @test_partial_condition_unswitch_with_lcssa_phi2(i32* %var, i1 %cond, i32 %x, i32 %y) {
; CHECK-LABEL: @test_partial_condition_unswitch_with_lcssa_phi2(
entry:
br label %loop_begin
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 %cond, label %entry.split, label %loop_exit.split
;
; CHECK: entry.split:
; CHECK-NEXT: br label %loop_begin
loop_begin:
%var_val = load i32, i32* %var
%var_cond = trunc i32 %var_val to i1
%cond_and = and i1 %var_cond, %cond
br i1 %cond_and, label %do_something, label %loop_exit
; CHECK: loop_begin:
; CHECK-NEXT: %[[VAR:.*]] = load i32
; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1
; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true
; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit
do_something:
call void @some_func() noreturn nounwind
br i1 %var_cond, label %loop_begin, label %loop_exit
; CHECK: do_something:
; CHECK-NEXT: call
; CHECK-NEXT: br i1 %[[VAR_COND]], label %loop_begin, label %loop_exit
loop_exit:
%xy.lcssa = phi i32 [ %x, %loop_begin ], [ %y, %do_something ]
ret i32 %xy.lcssa
; CHECK: loop_exit:
; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %x, %loop_begin ], [ %y, %do_something ]
; CHECK-NEXT: br label %loop_exit.split
;
; CHECK: loop_exit.split:
; CHECK-NEXT: %[[LCSSA_SPLIT:.*]] = phi i32 [ %x, %entry ], [ %[[LCSSA]], %loop_exit ]
; CHECK-NEXT: ret i32 %[[LCSSA_SPLIT]]
}
; Unswitch will not actually change the loop nest from:
; A < B < C
define void @hoist_inner_loop0() {
; CHECK-LABEL: define void @hoist_inner_loop0(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: br label %b.header
b.header:
%v1 = call i1 @cond()
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %[[B_LATCH_SPLIT:.*]], label %[[B_HEADER_SPLIT:.*]]
;
; CHECK: [[B_HEADER_SPLIT]]:
; CHECK-NEXT: br label %c.header
c.header:
br i1 %v1, label %b.latch, label %c.latch
; CHECK: c.header:
; CHECK-NEXT: br label %c.latch
c.latch:
%v2 = call i1 @cond()
br i1 %v2, label %c.header, label %b.latch
; CHECK: c.latch:
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %c.header, label %b.latch
b.latch:
%v3 = call i1 @cond()
br i1 %v3, label %b.header, label %a.latch
; CHECK: b.latch:
; CHECK-NEXT: br label %[[B_LATCH_SPLIT]]
;
; CHECK: [[B_LATCH_SPLIT]]:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; Unswitch will transform the loop nest from:
; A < B < C
; into
; A < (B, C)
define void @hoist_inner_loop1(i32* %ptr) {
; CHECK-LABEL: define void @hoist_inner_loop1(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
%x.a = load i32, i32* %ptr
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: %x.a = load i32, i32* %ptr
; CHECK-NEXT: br label %b.header
b.header:
%x.b = load i32, i32* %ptr
%v1 = call i1 @cond()
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %x.b = load i32, i32* %ptr
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]]
;
; CHECK: [[B_HEADER_SPLIT]]:
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ]
; CHECK-NEXT: br label %c.header
c.header:
br i1 %v1, label %b.latch, label %c.latch
; CHECK: c.header:
; CHECK-NEXT: br label %c.latch
c.latch:
; Use values from other loops to check LCSSA form.
store i32 %x.a, i32* %ptr
store i32 %x.b, i32* %ptr
%v2 = call i1 @cond()
br i1 %v2, label %c.header, label %a.exit.c
; CHECK: c.latch:
; CHECK-NEXT: store i32 %x.a, i32* %ptr
; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %c.header, label %a.exit.c
b.latch:
%v3 = call i1 @cond()
br i1 %v3, label %b.header, label %a.exit.b
; CHECK: b.latch:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %b.header, label %a.exit.b
a.exit.c:
br label %a.latch
; CHECK: a.exit.c
; CHECK-NEXT: br label %a.latch
a.exit.b:
br label %a.latch
; CHECK: a.exit.b:
; CHECK-NEXT: br label %a.latch
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; Unswitch will transform the loop nest from:
; A < B < C
; into
; (A < B), C
define void @hoist_inner_loop2(i32* %ptr) {
; CHECK-LABEL: define void @hoist_inner_loop2(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
%x.a = load i32, i32* %ptr
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: %x.a = load i32, i32* %ptr
; CHECK-NEXT: br label %b.header
b.header:
%x.b = load i32, i32* %ptr
%v1 = call i1 @cond()
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %x.b = load i32, i32* %ptr
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]]
;
; CHECK: [[B_HEADER_SPLIT]]:
; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ]
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ]
; CHECK-NEXT: br label %c.header
c.header:
br i1 %v1, label %b.latch, label %c.latch
; CHECK: c.header:
; CHECK-NEXT: br label %c.latch
c.latch:
; Use values from other loops to check LCSSA form.
store i32 %x.a, i32* %ptr
store i32 %x.b, i32* %ptr
%v2 = call i1 @cond()
br i1 %v2, label %c.header, label %exit
; CHECK: c.latch:
; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr
; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %c.header, label %exit
b.latch:
%v3 = call i1 @cond()
br i1 %v3, label %b.header, label %a.latch
; CHECK: b.latch:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; Same as @hoist_inner_loop2 but with a nested loop inside the hoisted loop.
; Unswitch will transform the loop nest from:
; A < B < C < D
; into
; (A < B), (C < D)
define void @hoist_inner_loop3(i32* %ptr) {
; CHECK-LABEL: define void @hoist_inner_loop3(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
%x.a = load i32, i32* %ptr
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: %x.a = load i32, i32* %ptr
; CHECK-NEXT: br label %b.header
b.header:
%x.b = load i32, i32* %ptr
%v1 = call i1 @cond()
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %x.b = load i32, i32* %ptr
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]]
;
; CHECK: [[B_HEADER_SPLIT]]:
; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ]
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ]
; CHECK-NEXT: br label %c.header
c.header:
br i1 %v1, label %b.latch, label %c.body
; CHECK: c.header:
; CHECK-NEXT: br label %c.body
c.body:
%x.c = load i32, i32* %ptr
br label %d.header
; CHECK: c.body:
; CHECK-NEXT: %x.c = load i32, i32* %ptr
; CHECK-NEXT: br label %d.header
d.header:
; Use values from other loops to check LCSSA form.
store i32 %x.a, i32* %ptr
store i32 %x.b, i32* %ptr
store i32 %x.c, i32* %ptr
%v2 = call i1 @cond()
br i1 %v2, label %d.header, label %c.latch
; CHECK: d.header:
; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr
; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr
; CHECK-NEXT: store i32 %x.c, i32* %ptr
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %d.header, label %c.latch
c.latch:
%v3 = call i1 @cond()
br i1 %v3, label %c.header, label %exit
; CHECK: c.latch:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %c.header, label %exit
b.latch:
%v4 = call i1 @cond()
br i1 %v4, label %b.header, label %a.latch
; CHECK: b.latch:
; CHECK-NEXT: %v4 = call i1 @cond()
; CHECK-NEXT: br i1 %v4, label %b.header, label %a.latch
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; This test is designed to exercise checking multiple remaining exits from the
; loop being unswitched.
; Unswitch will transform the loop nest from:
; A < B < C < D
; into
; A < B < (C, D)
define void @hoist_inner_loop4() {
; CHECK-LABEL: define void @hoist_inner_loop4(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: br label %b.header
b.header:
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: br label %c.header
c.header:
%v1 = call i1 @cond()
br label %d.header
; CHECK: c.header:
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %[[C_HEADER_SPLIT:.*]], label %c.latch
;
; CHECK: [[C_HEADER_SPLIT]]:
; CHECK-NEXT: br label %d.header
d.header:
br i1 %v1, label %d.exiting1, label %c.latch
; CHECK: d.header:
; CHECK-NEXT: br label %d.exiting1
d.exiting1:
%v2 = call i1 @cond()
br i1 %v2, label %d.exiting2, label %a.latch
; CHECK: d.exiting1:
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %d.exiting2, label %a.latch
d.exiting2:
%v3 = call i1 @cond()
br i1 %v3, label %d.exiting3, label %loopexit.d
; CHECK: d.exiting2:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %d.exiting3, label %loopexit.d
d.exiting3:
%v4 = call i1 @cond()
br i1 %v4, label %d.latch, label %b.latch
; CHECK: d.exiting3:
; CHECK-NEXT: %v4 = call i1 @cond()
; CHECK-NEXT: br i1 %v4, label %d.latch, label %b.latch
d.latch:
br label %d.header
; CHECK: d.latch:
; CHECK-NEXT: br label %d.header
c.latch:
%v5 = call i1 @cond()
br i1 %v5, label %c.header, label %loopexit.c
; CHECK: c.latch:
; CHECK-NEXT: %v5 = call i1 @cond()
; CHECK-NEXT: br i1 %v5, label %c.header, label %loopexit.c
b.latch:
br label %b.header
; CHECK: b.latch:
; CHECK-NEXT: br label %b.header
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
loopexit.d:
br label %exit
; CHECK: loopexit.d:
; CHECK-NEXT: br label %exit
loopexit.c:
br label %exit
; CHECK: loopexit.c:
; CHECK-NEXT: br label %exit
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; Unswitch will transform the loop nest from:
; A < B < C < D
; into
; A < ((B < C), D)
define void @hoist_inner_loop5(i32* %ptr) {
; CHECK-LABEL: define void @hoist_inner_loop5(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
%x.a = load i32, i32* %ptr
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: %x.a = load i32, i32* %ptr
; CHECK-NEXT: br label %b.header
b.header:
%x.b = load i32, i32* %ptr
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %x.b = load i32, i32* %ptr
; CHECK-NEXT: br label %c.header
c.header:
%x.c = load i32, i32* %ptr
%v1 = call i1 @cond()
br label %d.header
; CHECK: c.header:
; CHECK-NEXT: %x.c = load i32, i32* %ptr
; CHECK-NEXT: %v1 = call i1 @cond()
; CHECK-NEXT: br i1 %v1, label %c.latch, label %[[C_HEADER_SPLIT:.*]]
;
; CHECK: [[C_HEADER_SPLIT]]:
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %c.header ]
; CHECK-NEXT: %[[X_C_LCSSA:.*]] = phi i32 [ %x.c, %c.header ]
; CHECK-NEXT: br label %d.header
d.header:
br i1 %v1, label %c.latch, label %d.latch
; CHECK: d.header:
; CHECK-NEXT: br label %d.latch
d.latch:
; Use values from other loops to check LCSSA form.
store i32 %x.a, i32* %ptr
store i32 %x.b, i32* %ptr
store i32 %x.c, i32* %ptr
%v2 = call i1 @cond()
br i1 %v2, label %d.header, label %a.latch
; CHECK: d.latch:
; CHECK-NEXT: store i32 %x.a, i32* %ptr
; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr
; CHECK-NEXT: store i32 %[[X_C_LCSSA]], i32* %ptr
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %d.header, label %a.latch
c.latch:
%v3 = call i1 @cond()
br i1 %v3, label %c.header, label %b.latch
; CHECK: c.latch:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %c.header, label %b.latch
b.latch:
br label %b.header
; CHECK: b.latch:
; CHECK-NEXT: br label %b.header
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
; Same as `@hoist_inner_loop2` but using a switch.
; Unswitch will transform the loop nest from:
; A < B < C
; into
; (A < B), C
define void @hoist_inner_loop_switch(i32* %ptr) {
; CHECK-LABEL: define void @hoist_inner_loop_switch(
entry:
br label %a.header
; CHECK: entry:
; CHECK-NEXT: br label %a.header
a.header:
%x.a = load i32, i32* %ptr
br label %b.header
; CHECK: a.header:
; CHECK-NEXT: %x.a = load i32, i32* %ptr
; CHECK-NEXT: br label %b.header
b.header:
%x.b = load i32, i32* %ptr
%v1 = call i32 @cond.i32()
br label %c.header
; CHECK: b.header:
; CHECK-NEXT: %x.b = load i32, i32* %ptr
; CHECK-NEXT: %v1 = call i32 @cond.i32()
; CHECK-NEXT: switch i32 %v1, label %[[B_HEADER_SPLIT:.*]] [
; CHECK-NEXT: i32 1, label %b.latch
; CHECK-NEXT: i32 2, label %b.latch
; CHECK-NEXT: i32 3, label %b.latch
; CHECK-NEXT: ]
;
; CHECK: [[B_HEADER_SPLIT]]:
; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ]
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ]
; CHECK-NEXT: br label %c.header
c.header:
switch i32 %v1, label %c.latch [
i32 1, label %b.latch
i32 2, label %b.latch
i32 3, label %b.latch
]
; CHECK: c.header:
; CHECK-NEXT: br label %c.latch
c.latch:
; Use values from other loops to check LCSSA form.
store i32 %x.a, i32* %ptr
store i32 %x.b, i32* %ptr
%v2 = call i1 @cond()
br i1 %v2, label %c.header, label %exit
; CHECK: c.latch:
; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr
; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr
; CHECK-NEXT: %v2 = call i1 @cond()
; CHECK-NEXT: br i1 %v2, label %c.header, label %exit
b.latch:
%v3 = call i1 @cond()
br i1 %v3, label %b.header, label %a.latch
; CHECK: b.latch:
; CHECK-NEXT: %v3 = call i1 @cond()
; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch
a.latch:
br label %a.header
; CHECK: a.latch:
; CHECK-NEXT: br label %a.header
exit:
ret void
; CHECK: exit:
; CHECK-NEXT: ret void
}
define void @test_unswitch_to_common_succ_with_phis(i32* %var, i32 %cond) {
; CHECK-LABEL: @test_unswitch_to_common_succ_with_phis(
entry:
br label %header
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond, label %loopexit1 [
; CHECK-NEXT: i32 13, label %loopexit2
; CHECK-NEXT: i32 0, label %entry.split
; CHECK-NEXT: i32 1, label %entry.split
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: br label %header
header:
%var_val = load i32, i32* %var
switch i32 %cond, label %loopexit1 [
i32 0, label %latch
i32 1, label %latch
i32 13, label %loopexit2
]
; CHECK: header:
; CHECK-NEXT: load
; CHECK-NEXT: br label %latch
latch:
; No-op PHI node to exercise weird PHI update scenarios.
%phi = phi i32 [ %var_val, %header ], [ %var_val, %header ]
call void @sink(i32 %phi)
br label %header
; CHECK: latch:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %var_val, %header ]
; CHECK-NEXT: call void @sink(i32 %[[PHI]])
; CHECK-NEXT: br label %header
loopexit1:
ret void
; CHECK: loopexit1:
; CHECK-NEXT: ret
loopexit2:
ret void
; CHECK: loopexit2:
; CHECK-NEXT: ret
}
define void @test_unswitch_to_default_common_succ_with_phis(i32* %var, i32 %cond) {
; CHECK-LABEL: @test_unswitch_to_default_common_succ_with_phis(
entry:
br label %header
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 %cond, label %entry.split [
; CHECK-NEXT: i32 13, label %loopexit
; CHECK-NEXT: ]
;
; CHECK: entry.split:
; CHECK-NEXT: br label %header
header:
%var_val = load i32, i32* %var
switch i32 %cond, label %latch [
i32 0, label %latch
i32 1, label %latch
i32 13, label %loopexit
]
; CHECK: header:
; CHECK-NEXT: load
; CHECK-NEXT: br label %latch
latch:
; No-op PHI node to exercise weird PHI update scenarios.
%phi = phi i32 [ %var_val, %header ], [ %var_val, %header ], [ %var_val, %header ]
call void @sink(i32 %phi)
br label %header
; CHECK: latch:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %var_val, %header ]
; CHECK-NEXT: call void @sink(i32 %[[PHI]])
; CHECK-NEXT: br label %header
loopexit:
ret void
; CHECK: loopexit:
; CHECK-NEXT: ret
}