1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00
Chandler Carruth cb049b799a [PM/Unswitch] Fix a collection of closely related issues with trivial
switch unswitching.

The core problem was that the way we handled unswitching trivial exit
edges through the default successor of a switch. For some reason
I thought the right way to do this was to add a block containing
unreachable and point the default successor at this block. In
retrospect, this has an amazing number of problems.

The first issue is the one that this pass has always worked around -- we
have to *detect* such edges and avoid unswitching them again. This
seemed pretty easy really. You juts look for an edge to a block
containing unreachable. However, this pattern is woefully unsound. So
many things can break it. The amazing thing is that I found a test case
where *simple-loop-unswitch itself* breaks this! When we do
a *non-trivial* unswitch of a switch we will end up splitting this exit
edge. The result will be a default successor that is an exit and
terminates in ... a perfectly normal branch. So the first test case that
I started trying to fix is added to the nontrivial test cases. This is
a ridiculous example that did just amazing things previously. With just
unswitch, it would create 10+ copies of this stuff stamped out. But if
you combine it *just right* with a bunch of other passes (like
simplify-cfg, loop rotate, and some LICM) you can get it to do this
infinitely. Or at least, I never got it to finish. =[

This, in turn, uncovered another related issue. When we are manipulating
these switches after doing a trivial unswitch we never correctly updated
PHI nodes to reflect our edits. As soon as I started changing how these
edges were managed, it became obvious there were more issues that
I couldn't realistically leave unaddressed, so I wrote more test cases
around PHI updates here and ensured all of that works now.

And this, in turn, required some adjustment to how we collect and manage
the exit successor when it is the default successor. That showed a clear
bug where we failed to include it in our search for the outer-most loop
reached by an unswitched exit edge. This was actually already tested and
the test case didn't work. I (wrongly) thought that was due to SCEV
failing to analyze the switch. In fact, it was just a simple bug in the
code that skipped the default successor. While changing this, I handled
it correctly and have updated the test to reflect that we now get
precise SCEV analysis of trip counts for the outer loop in one of these
cases.

llvm-svn: 336646
2018-07-10 08:36:05 +00:00

1245 lines
34 KiB
LLVM

; RUN: opt -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
}