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
4129 lines
129 KiB
LLVM
4129 lines
129 KiB
LLVM
; RUN: opt -passes='loop(unswitch),verify<loops>' -enable-nontrivial-unswitch -S < %s | FileCheck %s
|
|
; RUN: opt -simple-loop-unswitch -enable-nontrivial-unswitch -S < %s | FileCheck %s
|
|
|
|
declare i32 @a()
|
|
declare i32 @b()
|
|
declare i32 @c()
|
|
declare i32 @d()
|
|
|
|
declare void @sink1(i32)
|
|
declare void @sink2(i32)
|
|
|
|
declare i1 @cond()
|
|
declare i32 @cond.i32()
|
|
|
|
; Negative test: we cannot unswitch convergent calls.
|
|
define void @test_no_unswitch_convergent(i1* %ptr, i1 %cond) {
|
|
; CHECK-LABEL: @test_no_unswitch_convergent(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; We shouldn't have unswitched into any other block either.
|
|
; CHECK-NOT: br i1 %cond
|
|
|
|
loop_begin:
|
|
br i1 %cond, label %loop_a, label %loop_b
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br i1 %cond, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a() convergent
|
|
br label %loop_latch
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %loop_latch
|
|
|
|
loop_latch:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
ret void
|
|
}
|
|
|
|
; Negative test: we cannot unswitch noduplicate calls.
|
|
define void @test_no_unswitch_noduplicate(i1* %ptr, i1 %cond) {
|
|
; CHECK-LABEL: @test_no_unswitch_noduplicate(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; We shouldn't have unswitched into any other block either.
|
|
; CHECK-NOT: br i1 %cond
|
|
|
|
loop_begin:
|
|
br i1 %cond, label %loop_a, label %loop_b
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br i1 %cond, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a() noduplicate
|
|
br label %loop_latch
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %loop_latch
|
|
|
|
loop_latch:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
ret void
|
|
}
|
|
|
|
declare i32 @__CxxFrameHandler3(...)
|
|
|
|
; Negative test: we cannot unswitch when tokens are used across blocks as we
|
|
; might introduce PHIs.
|
|
define void @test_no_unswitch_cross_block_token(i1* %ptr, i1 %cond) nounwind personality i32 (...)* @__CxxFrameHandler3 {
|
|
; CHECK-LABEL: @test_no_unswitch_cross_block_token(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; We shouldn't have unswitched into any other block either.
|
|
; CHECK-NOT: br i1 %cond
|
|
|
|
loop_begin:
|
|
br i1 %cond, label %loop_a, label %loop_b
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br i1 %cond, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %loop_cont
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %loop_cont
|
|
|
|
loop_cont:
|
|
invoke i32 @a()
|
|
to label %loop_latch unwind label %loop_catch
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
|
|
loop_catch:
|
|
%catch = catchswitch within none [label %loop_catch_latch, label %loop_exit] unwind to caller
|
|
|
|
loop_catch_latch:
|
|
%catchpad_latch = catchpad within %catch []
|
|
catchret from %catchpad_latch to label %loop_begin
|
|
|
|
loop_exit:
|
|
%catchpad_exit = catchpad within %catch []
|
|
catchret from %catchpad_exit to label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
|
|
; Non-trivial loop unswitching where there are two distinct trivial conditions
|
|
; to unswitch within the loop.
|
|
define i32 @test1(i1* %ptr, i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @test1(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
br i1 %cond1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %latch
|
|
; The 'loop_a' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: br label %loop_a.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %latch.us
|
|
;
|
|
; CHECK: latch.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
br i1 %cond2, label %loop_b_a, label %loop_b_b
|
|
; The second unswitched condition.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br i1 %cond2, label %entry.split.split.us, label %entry.split.split
|
|
|
|
loop_b_a:
|
|
call i32 @b()
|
|
br label %latch
|
|
; The 'loop_b_a' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us1
|
|
;
|
|
; CHECK: loop_begin.us1:
|
|
; CHECK-NEXT: br label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: br label %loop_b_a.us
|
|
;
|
|
; CHECK: loop_b_a.us:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %latch.us2
|
|
;
|
|
; CHECK: latch.us2:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us1, label %loop_exit.split.split.us
|
|
;
|
|
; CHECK: loop_exit.split.split.us:
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
|
|
loop_b_b:
|
|
call i32 @c()
|
|
br label %latch
|
|
; The 'loop_b_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: br label %loop_b_b
|
|
;
|
|
; CHECK: loop_b_b:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %latch
|
|
;
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit.split.split
|
|
;
|
|
; CHECK: loop_exit.split.split:
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
|
|
latch:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
ret i32 0
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: ret
|
|
}
|
|
|
|
define i32 @test2(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr, i32* %c.ptr) {
|
|
; CHECK-LABEL: @test2(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %cond1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
%a = load i32, i32* %a.ptr
|
|
%ac = load i32, i32* %c.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; The 'loop_a' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br label %loop_a.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[AC:.*]] = load i32, i32* %c.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.backedge.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_a.us ]
|
|
; CHECK-NEXT: %[[AC_LCSSA:.*]] = phi i32 [ %[[AC]], %loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
%bc = load i32, i32* %c.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[BC:.*]] = load i32, i32* %c.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.backedge, label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b ]
|
|
; CHECK-NEXT: %[[BC_LCSSA:.*]] = phi i32 [ %[[BC]], %loop_b ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%ab.phi = phi i32 [ %a, %loop_a ], [ %b, %loop_b ]
|
|
%c.phi = phi i32 [ %ac, %loop_a ], [ %bc, %loop_b ]
|
|
%result = add i32 %ab.phi, %c.phi
|
|
ret i32 %result
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit.split ], [ %[[A_LCSSA]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[C_PHI:.*]] = phi i32 [ %[[BC_LCSSA]], %loop_exit.split ], [ %[[AC_LCSSA]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[RESULT:.*]] = add i32 %[[AB_PHI]], %[[C_PHI]]
|
|
; CHECK-NEXT: ret i32 %[[RESULT]]
|
|
}
|
|
|
|
; Test a non-trivial unswitch of an exiting edge to an exit block with other
|
|
; in-loop predecessors.
|
|
define i32 @test3a(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test3a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_exit, label %loop_b
|
|
; The 'loop_exit' clone.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%ab.phi = phi i32 [ %a, %loop_begin ], [ %b, %loop_b ]
|
|
ret i32 %ab.phi
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit.split ], [ %[[A_LCSSA]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI]]
|
|
}
|
|
|
|
; Test a non-trivial unswitch of an exiting edge to an exit block with other
|
|
; in-loop predecessors. This is the same as @test3a but with the reversed order
|
|
; of successors so that the exiting edge is *not* the cloned edge.
|
|
define i32 @test3b(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test3b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_b, label %loop_exit
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; The original loop, now non-looping due to unswitching..
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%ab.phi = phi i32 [ %b, %loop_b ], [ %a, %loop_begin ]
|
|
ret i32 %ab.phi
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[A]], %loop_exit.split ], [ %[[B_LCSSA]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI]]
|
|
}
|
|
|
|
; Test a non-trivial unswitch of an exiting edge to an exit block with no other
|
|
; in-loop predecessors.
|
|
define void @test4a(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test4a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_exit1, label %loop_b
|
|
; The 'loop_exit' clone.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit1.split.us
|
|
;
|
|
; CHECK: loop_exit1.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit1
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit2
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit2
|
|
|
|
loop_exit1:
|
|
%a.phi = phi i32 [ %a, %loop_begin ]
|
|
call void @sink1(i32 %a.phi)
|
|
ret void
|
|
; CHECK: loop_exit1:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[A_LCSSA]])
|
|
; CHECK-NEXT: ret void
|
|
|
|
loop_exit2:
|
|
%b.phi = phi i32 [ %b, %loop_b ]
|
|
call void @sink2(i32 %b.phi)
|
|
ret void
|
|
; CHECK: loop_exit2:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b ]
|
|
; CHECK-NEXT: call void @sink2(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: ret void
|
|
}
|
|
|
|
; Test a non-trivial unswitch of an exiting edge to an exit block with no other
|
|
; in-loop predecessors. This is the same as @test4a but with the edges reversed
|
|
; so that the exiting edge is *not* the cloned edge.
|
|
define void @test4b(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test4b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_b, label %loop_exit1
|
|
; The 'loop_b' clone.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us, label %loop_exit2.split.us
|
|
;
|
|
; CHECK: loop_exit2.split.us:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b.us ]
|
|
; CHECK-NEXT: br label %loop_exit2
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit2
|
|
; The 'loop_exit' unswitched path.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit1
|
|
|
|
loop_exit1:
|
|
%a.phi = phi i32 [ %a, %loop_begin ]
|
|
call void @sink1(i32 %a.phi)
|
|
ret void
|
|
; CHECK: loop_exit1:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A]], %loop_begin ]
|
|
; CHECK-NEXT: call void @sink1(i32 %[[A_PHI]])
|
|
; CHECK-NEXT: ret void
|
|
|
|
loop_exit2:
|
|
%b.phi = phi i32 [ %b, %loop_b ]
|
|
call void @sink2(i32 %b.phi)
|
|
ret void
|
|
; CHECK: loop_exit2:
|
|
; CHECK-NEXT: call void @sink2(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: ret void
|
|
}
|
|
|
|
; Test a non-trivial unswitch of an exiting edge to an exit block with no other
|
|
; in-loop predecessors. This is the same as @test4a but with a common merge
|
|
; block after the independent loop exits. This requires a different structural
|
|
; update to the dominator tree.
|
|
define void @test4c(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test4c(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_exit1, label %loop_b
|
|
; The 'loop_exit' clone.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit1.split.us
|
|
;
|
|
; CHECK: loop_exit1.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit1
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit2
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit2
|
|
|
|
loop_exit1:
|
|
%a.phi = phi i32 [ %a, %loop_begin ]
|
|
call void @sink1(i32 %a.phi)
|
|
br label %exit
|
|
; CHECK: loop_exit1:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[A_LCSSA]])
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
loop_exit2:
|
|
%b.phi = phi i32 [ %b, %loop_b ]
|
|
call void @sink2(i32 %b.phi)
|
|
br label %exit
|
|
; CHECK: loop_exit2:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b ]
|
|
; CHECK-NEXT: call void @sink2(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
}
|
|
|
|
; Test that we can unswitch a condition out of multiple layers of a loop nest.
|
|
define i32 @test5(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test5(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %loop_begin.split.us, label %entry.split
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br label %loop_begin.split
|
|
|
|
loop_begin:
|
|
br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond1, label %loop_exit, label %inner_loop_b
|
|
; The 'loop_exit' clone.
|
|
;
|
|
; CHECK: loop_begin.split.us:
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit.loopexit.split.us
|
|
;
|
|
; CHECK: loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %inner_loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
inner_loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %v, label %inner_loop_begin, label %loop_latch
|
|
; The 'inner_loop_b' unswitched loop.
|
|
;
|
|
; CHECK: loop_begin.split:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
;
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_b
|
|
;
|
|
; CHECK: inner_loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_begin, label %loop_latch
|
|
|
|
loop_latch:
|
|
%b.phi = phi i32 [ %b, %inner_loop_b ]
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_begin, label %loop_exit
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: %[[B_INNER_LCSSA:.*]] = phi i32 [ %[[B]], %inner_loop_b ]
|
|
; CHECK-NEXT: %[[V2:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V2]], label %loop_begin, label %loop_exit.loopexit1
|
|
|
|
loop_exit:
|
|
%ab.phi = phi i32 [ %a, %inner_loop_begin ], [ %b.phi, %loop_latch ]
|
|
ret i32 %ab.phi
|
|
; CHECK: loop_exit.loopexit:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B_INNER_LCSSA]], %loop_latch ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.loopexit ], [ %[[B_LCSSA]], %loop_exit.loopexit1 ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI]]
|
|
}
|
|
|
|
; Test that we can unswitch a condition where we end up only cloning some of
|
|
; the nested loops and needing to delete some of the nested loops.
|
|
define i32 @test6(i1* %ptr, i1 %cond1, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test6(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %cond1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
br label %loop_a_inner
|
|
|
|
loop_a_inner:
|
|
%va = load i1, i1* %ptr
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %va, label %loop_a_inner, label %loop_a_inner_exit
|
|
|
|
loop_a_inner_exit:
|
|
%a.lcssa = phi i32 [ %a, %loop_a_inner ]
|
|
br label %latch
|
|
; The 'loop_a' cloned loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br label %loop_a.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: br label %loop_a_inner.us
|
|
;
|
|
; CHECK: loop_a_inner.us
|
|
; CHECK-NEXT: %[[VA:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br i1 %[[VA]], label %loop_a_inner.us, label %loop_a_inner_exit.us
|
|
;
|
|
; CHECK: loop_a_inner_exit.us:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A]], %loop_a_inner.us ]
|
|
; CHECK-NEXT: br label %latch.us
|
|
;
|
|
; CHECK: latch.us:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %loop_a_inner_exit.us ]
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_PHI]], %latch.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
br label %loop_b_inner
|
|
|
|
loop_b_inner:
|
|
%vb = load i1, i1* %ptr
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %vb, label %loop_b_inner, label %loop_b_inner_exit
|
|
|
|
loop_b_inner_exit:
|
|
%b.lcssa = phi i32 [ %b, %loop_b_inner ]
|
|
br label %latch
|
|
|
|
latch:
|
|
%ab.phi = phi i32 [ %a.lcssa, %loop_a_inner_exit ], [ %b.lcssa, %loop_b_inner_exit ]
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: br label %loop_b_inner
|
|
;
|
|
; CHECK: loop_b_inner
|
|
; CHECK-NEXT: %[[VB:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[VB]], label %loop_b_inner, label %loop_b_inner_exit
|
|
;
|
|
; CHECK: loop_b_inner_exit:
|
|
; CHECK-NEXT: %[[B_INNER_LCSSA:.*]] = phi i32 [ %[[B]], %loop_b_inner ]
|
|
; CHECK-NEXT: br label %latch
|
|
;
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B_INNER_LCSSA]], %latch ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%ab.lcssa = phi i32 [ %ab.phi, %latch ]
|
|
ret i32 %ab.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit.split ], [ %[[A_LCSSA]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI]]
|
|
}
|
|
|
|
; Test that when unswitching a deeply nested loop condition in a way that
|
|
; produces a non-loop clone that can reach multiple exit blocks which are part
|
|
; of different outer loops we correctly divide the cloned loop blocks between
|
|
; the outer loops based on reachability.
|
|
define i32 @test7a(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test7a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%a.phi = phi i32 [ %a, %loop_begin ], [ %a2, %inner_inner_loop_exit ]
|
|
%cond = load i1, i1* %cond.ptr
|
|
%b = load i32, i32* %b.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A]], %loop_begin ], [ %[[A2:.*]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_begin.split.us, label %inner_loop_begin.split
|
|
|
|
inner_inner_loop_begin:
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_exit, label %inner_inner_loop_c
|
|
|
|
inner_inner_loop_b:
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %inner_inner_loop_exit, label %inner_inner_loop_c
|
|
|
|
inner_inner_loop_c:
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %inner_loop_exit, label %inner_inner_loop_d
|
|
|
|
inner_inner_loop_d:
|
|
br i1 %cond, label %inner_loop_exit, label %inner_inner_loop_begin
|
|
; The cloned copy that always exits with the adjustments required to fix up
|
|
; loop exits.
|
|
;
|
|
; CHECK: inner_loop_begin.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a.us, label %inner_inner_loop_b.us
|
|
;
|
|
; CHECK: inner_inner_loop_b.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit.split.us, label %inner_inner_loop_c.us.loopexit
|
|
;
|
|
; CHECK: inner_inner_loop_a.us:
|
|
; CHECK-NEXT: %[[A_NEW_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_begin.us ]
|
|
; CHECK-NEXT: %[[B_NEW_LCSSA:.*]] = phi i32 [ %[[B]], %inner_inner_loop_begin.us ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.us, label %inner_inner_loop_c.us
|
|
;
|
|
; CHECK: inner_inner_loop_c.us.loopexit:
|
|
; CHECK-NEXT: br label %inner_inner_loop_c.us
|
|
;
|
|
; CHECK: inner_inner_loop_c.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit.split.us, label %inner_inner_loop_d.us
|
|
;
|
|
; CHECK: inner_inner_loop_d.us:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit.split
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A_NEW_LCSSA]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: %[[B_LCSSA_US:.*]] = phi i32 [ %[[B_NEW_LCSSA]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; The original copy that continues to loop.
|
|
;
|
|
; CHECK: inner_loop_begin.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
;
|
|
; CHECK: inner_inner_loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split, label %inner_inner_loop_c
|
|
;
|
|
; CHECK: inner_inner_loop_b:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit.split, label %inner_inner_loop_c
|
|
;
|
|
; CHECK: inner_inner_loop_c:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit.split, label %inner_inner_loop_d
|
|
;
|
|
; CHECK: inner_inner_loop_d:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
|
|
inner_inner_loop_exit:
|
|
%a2 = load i32, i32* %a.ptr
|
|
%v5 = load i1, i1* %ptr
|
|
br i1 %v5, label %inner_loop_exit, label %inner_loop_begin
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[A2]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit1, label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
br label %loop_begin
|
|
; CHECK: inner_loop_exit.loopexit.split:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.phi, %inner_inner_loop_a ]
|
|
%b.lcssa = phi i32 [ %b, %inner_inner_loop_a ]
|
|
%result = add i32 %a.lcssa, %b.lcssa
|
|
ret i32 %result
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.split ], [ %[[A_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[B_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit.split ], [ %[[B_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[RESULT:.*]] = add i32 %[[A_PHI]], %[[B_PHI]]
|
|
; CHECK-NEXT: ret i32 %[[RESULT]]
|
|
}
|
|
|
|
; Same pattern as @test7a but here the original loop becomes a non-loop that
|
|
; can reach multiple exit blocks which are part of different outer loops.
|
|
define i32 @test7b(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test7b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%a.phi = phi i32 [ %a, %loop_begin ], [ %a2, %inner_inner_loop_exit ]
|
|
%cond = load i1, i1* %cond.ptr
|
|
%b = load i32, i32* %b.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A]], %loop_begin ], [ %[[A2:.*]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_begin.split.us, label %inner_loop_begin.split
|
|
|
|
inner_inner_loop_begin:
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_exit, label %inner_inner_loop_c
|
|
|
|
inner_inner_loop_b:
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %inner_inner_loop_exit, label %inner_inner_loop_c
|
|
|
|
inner_inner_loop_c:
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %inner_loop_exit, label %inner_inner_loop_d
|
|
|
|
inner_inner_loop_d:
|
|
br i1 %cond, label %inner_inner_loop_begin, label %inner_loop_exit
|
|
; The cloned copy that continues looping.
|
|
;
|
|
; CHECK: inner_loop_begin.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a.us, label %inner_inner_loop_b.us
|
|
;
|
|
; CHECK: inner_inner_loop_b.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit.split.us, label %inner_inner_loop_c.us
|
|
;
|
|
; CHECK: inner_inner_loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.us, label %inner_inner_loop_c.us
|
|
;
|
|
; CHECK: inner_inner_loop_c.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit.split.us, label %inner_inner_loop_d.us
|
|
;
|
|
; CHECK: inner_inner_loop_d.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: %[[B_LCSSA_US:.*]] = phi i32 [ %[[B]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; The original copy that now always exits and needs adjustments for exit
|
|
; blocks.
|
|
;
|
|
; CHECK: inner_loop_begin.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
;
|
|
; CHECK: inner_inner_loop_a:
|
|
; CHECK-NEXT: %[[A_NEW_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_begin ]
|
|
; CHECK-NEXT: %[[B_NEW_LCSSA:.*]] = phi i32 [ %[[B]], %inner_inner_loop_begin ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split, label %inner_inner_loop_c
|
|
;
|
|
; CHECK: inner_inner_loop_b:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit.split, label %inner_inner_loop_c.loopexit
|
|
;
|
|
; CHECK: inner_inner_loop_c.loopexit:
|
|
; CHECK-NEXT: br label %inner_inner_loop_c
|
|
;
|
|
; CHECK: inner_inner_loop_c:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit.split, label %inner_inner_loop_d
|
|
;
|
|
; CHECK: inner_inner_loop_d:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit.split
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
|
|
inner_inner_loop_exit:
|
|
%a2 = load i32, i32* %a.ptr
|
|
%v5 = load i1, i1* %ptr
|
|
br i1 %v5, label %inner_loop_exit, label %inner_loop_begin
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[A2]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit1, label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
br label %loop_begin
|
|
; CHECK: inner_loop_exit.loopexit.split:
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.phi, %inner_inner_loop_a ]
|
|
%b.lcssa = phi i32 [ %b, %inner_inner_loop_a ]
|
|
%result = add i32 %a.lcssa, %b.lcssa
|
|
ret i32 %result
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_NEW_LCSSA]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B_NEW_LCSSA]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.split ], [ %[[A_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[B_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit.split ], [ %[[B_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: %[[RESULT:.*]] = add i32 %[[A_PHI]], %[[B_PHI]]
|
|
; CHECK-NEXT: ret i32 %[[RESULT]]
|
|
}
|
|
|
|
; Test that when the exit block set of an inner loop changes to start at a less
|
|
; high level of the loop nest we correctly hoist the loop up the nest.
|
|
define i32 @test8a(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test8a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%a.phi = phi i32 [ %a, %loop_begin ], [ %a2, %inner_inner_loop_exit ]
|
|
%cond = load i1, i1* %cond.ptr
|
|
%b = load i32, i32* %b.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A]], %loop_begin ], [ %[[A2:.*]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_begin.split.us, label %inner_loop_begin.split
|
|
|
|
inner_inner_loop_begin:
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_inner_loop_latch, label %inner_loop_exit
|
|
|
|
inner_inner_loop_b:
|
|
br i1 %cond, label %inner_inner_loop_latch, label %inner_inner_loop_exit
|
|
|
|
inner_inner_loop_latch:
|
|
br label %inner_inner_loop_begin
|
|
; The cloned region is now an exit from the inner loop.
|
|
;
|
|
; CHECK: inner_loop_begin.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_begin ]
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a.us, label %inner_inner_loop_b.us
|
|
;
|
|
; CHECK: inner_inner_loop_b.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_latch.us
|
|
;
|
|
; CHECK: inner_inner_loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_latch.us, label %inner_loop_exit.loopexit.split.us
|
|
;
|
|
; CHECK: inner_inner_loop_latch.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; The original region exits the loop earlier.
|
|
;
|
|
; CHECK: inner_loop_begin.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
;
|
|
; CHECK: inner_inner_loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_latch, label %inner_loop_exit.loopexit.split
|
|
;
|
|
; CHECK: inner_inner_loop_b:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
;
|
|
; CHECK: inner_inner_loop_latch:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
|
|
inner_inner_loop_exit:
|
|
%a2 = load i32, i32* %a.ptr
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %inner_loop_exit, label %inner_loop_begin
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[A2]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit1, label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%v5 = load i1, i1* %ptr
|
|
br i1 %v5, label %loop_exit, label %loop_begin
|
|
; CHECK: inner_loop_exit.loopexit.split:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: %[[A_INNER_US_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %inner_loop_exit.loopexit.split ], [ %[[A_INNER_LCSSA_US]], %inner_loop_exit.loopexit.split.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA2:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA2]], %inner_loop_exit.loopexit1 ], [ %[[A_INNER_US_PHI]], %inner_loop_exit.loopexit ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit, label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.phi, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; Same pattern as @test8a but where the original loop looses an exit block and
|
|
; needs to be hoisted up the nest.
|
|
define i32 @test8b(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test8b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%a.phi = phi i32 [ %a, %loop_begin ], [ %a2, %inner_inner_loop_exit ]
|
|
%cond = load i1, i1* %cond.ptr
|
|
%b = load i32, i32* %b.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A]], %loop_begin ], [ %[[A2:.*]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_begin.split.us, label %inner_loop_begin.split
|
|
|
|
inner_inner_loop_begin:
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_inner_loop_latch, label %inner_loop_exit
|
|
|
|
inner_inner_loop_b:
|
|
br i1 %cond, label %inner_inner_loop_exit, label %inner_inner_loop_latch
|
|
|
|
inner_inner_loop_latch:
|
|
br label %inner_inner_loop_begin
|
|
; The cloned region is similar to before but with one earlier exit.
|
|
;
|
|
; CHECK: inner_loop_begin.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a.us, label %inner_inner_loop_b.us
|
|
;
|
|
; CHECK: inner_inner_loop_b.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit.split.us
|
|
;
|
|
; CHECK: inner_inner_loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_latch.us, label %inner_loop_exit.loopexit.split.us
|
|
;
|
|
; CHECK: inner_inner_loop_latch.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; The original region is now an exit in the preheader.
|
|
;
|
|
; CHECK: inner_loop_begin.split:
|
|
; CHECK-NEXT: %[[A_INNER_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_begin ]
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_a, label %inner_inner_loop_b
|
|
;
|
|
; CHECK: inner_inner_loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_latch, label %inner_loop_exit.loopexit.split
|
|
;
|
|
; CHECK: inner_inner_loop_b:
|
|
; CHECK-NEXT: br label %inner_inner_loop_latch
|
|
;
|
|
; CHECK: inner_inner_loop_latch:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
|
|
inner_inner_loop_exit:
|
|
%a2 = load i32, i32* %a.ptr
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %inner_loop_exit, label %inner_loop_begin
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[A2]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.loopexit1, label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%v5 = load i1, i1* %ptr
|
|
br i1 %v5, label %loop_exit, label %loop_begin
|
|
; CHECK: inner_loop_exit.loopexit.split:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: %[[A_INNER_US_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %inner_loop_exit.loopexit.split ], [ %[[A_INNER_LCSSA_US]], %inner_loop_exit.loopexit.split.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA2:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA2]], %inner_loop_exit.loopexit1 ], [ %[[A_INNER_US_PHI]], %inner_loop_exit.loopexit ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit, label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.phi, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; Test for when unswitching produces a clone of an inner loop but
|
|
; the clone no longer has an exiting edge *at all* and loops infinitely.
|
|
; Because it doesn't ever exit to the outer loop it is no longer an inner loop
|
|
; but needs to be hoisted up the nest to be a top-level loop.
|
|
define i32 @test9a(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test9a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %loop_begin.split.us, label %loop_begin.split
|
|
|
|
inner_loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %inner_loop_latch, label %inner_loop_exit
|
|
|
|
inner_loop_latch:
|
|
call void @sink1(i32 %b)
|
|
br label %inner_loop_begin
|
|
; The cloned inner loop ends up as an infinite loop and thus being a top-level
|
|
; loop with the preheader as an exit block of the outer loop.
|
|
;
|
|
; CHECK: loop_begin.split.us
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_begin ]
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_latch.us
|
|
;
|
|
; CHECK: inner_loop_latch.us:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; The original loop becomes boring non-loop code.
|
|
;
|
|
; CHECK: loop_begin.split
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
;
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_loop_begin ]
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A]], %inner_loop_begin ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; The same core pattern as @test9a, but instead of the cloned loop becoming an
|
|
; infinite loop, the original loop has its only exit unswitched and the
|
|
; original loop becomes infinite and must be hoisted out of the loop nest.
|
|
define i32 @test9b(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test9b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %loop_begin.split.us, label %loop_begin.split
|
|
|
|
inner_loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %inner_loop_exit, label %inner_loop_latch
|
|
|
|
inner_loop_latch:
|
|
call void @sink1(i32 %b)
|
|
br label %inner_loop_begin
|
|
; The cloned inner loop becomes a boring non-loop.
|
|
;
|
|
; CHECK: loop_begin.split.us
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_exit.split.us
|
|
;
|
|
; CHECK: inner_loop_exit.split.us
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A]], %inner_loop_begin.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; The original loop becomes an infinite loop and thus a top-level loop with the
|
|
; preheader as an exit block for the outer loop.
|
|
;
|
|
; CHECK: loop_begin.split
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %loop_begin ]
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
;
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_latch
|
|
;
|
|
; CHECK: inner_loop_latch:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_loop_begin ]
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; Test that requires re-forming dedicated exits for the cloned loop.
|
|
define i32 @test10a(i1* %ptr, i1 %cond, i32* %a.ptr) {
|
|
; CHECK-LABEL: @test10a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_exit, label %loop_begin
|
|
|
|
loop_b:
|
|
br i1 %cond, label %loop_exit, label %loop_begin
|
|
; The cloned loop with one edge as a direct exit.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a.us, label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: %[[A_LCSSA_B:.*]] = phi i32 [ %[[A]], %loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.us.loopexit, label %loop_begin.backedge.us
|
|
;
|
|
; CHECK: loop_begin.backedge.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_exit.split.us.loopexit:
|
|
; CHECK-NEXT: %[[A_LCSSA_A:.*]] = phi i32 [ %[[A]], %loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_PHI_US:.*]] = phi i32 [ %[[A_LCSSA_B]], %loop_b.us ], [ %[[A_LCSSA_A]], %loop_exit.split.us.loopexit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
; The original loop without one 'loop_exit' edge.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a, label %loop_b
|
|
;
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split, label %loop_begin.backedge
|
|
;
|
|
; CHECK: loop_begin.backedge:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: br label %loop_begin.backedge
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a, %loop_a ], [ %a, %loop_b ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.split ], [ %[[A_PHI_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[A_PHI]]
|
|
}
|
|
|
|
; Test that requires re-forming dedicated exits for the original loop.
|
|
define i32 @test10b(i1* %ptr, i1 %cond, i32* %a.ptr) {
|
|
; CHECK-LABEL: @test10b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_begin, label %loop_exit
|
|
|
|
loop_b:
|
|
br i1 %cond, label %loop_begin, label %loop_exit
|
|
; The cloned loop without one of the exits.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a.us, label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: br label %loop_begin.backedge.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.backedge.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_begin.backedge.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A]], %loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
; The original loop without one 'loop_exit' edge.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a, label %loop_b
|
|
;
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.backedge, label %loop_exit.split.loopexit
|
|
;
|
|
; CHECK: loop_begin.backedge:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[A_LCSSA_B:.*]] = phi i32 [ %[[A]], %loop_begin ]
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split.loopexit:
|
|
; CHECK-NEXT: %[[A_LCSSA_A:.*]] = phi i32 [ %[[A]], %loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[A_PHI_SPLIT:.*]] = phi i32 [ %[[A_LCSSA_B]], %loop_b ], [ %[[A_LCSSA_A]], %loop_exit.split.loopexit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a, %loop_a ], [ %a, %loop_b ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_PHI_SPLIT]], %loop_exit.split ], [ %[[A_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[A_PHI]]
|
|
}
|
|
|
|
; Check that if a cloned inner loop after unswitching doesn't loop and directly
|
|
; exits even an outer loop, we don't add the cloned preheader to the outer
|
|
; loop and do add the needed LCSSA phi nodes for the new exit block from the
|
|
; outer loop.
|
|
define i32 @test11a(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test11a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_latch, label %inner_loop_ph
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_latch, label %inner_loop_ph
|
|
|
|
inner_loop_ph:
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: inner_loop_ph:
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_ph.split.us, label %inner_loop_ph.split
|
|
|
|
inner_loop_begin:
|
|
call void @sink1(i32 %b)
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %loop_exit, label %inner_loop_a
|
|
|
|
inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_loop_exit, label %inner_loop_begin
|
|
; The cloned path doesn't actually loop and is an exit from the outer loop as
|
|
; well.
|
|
;
|
|
; CHECK: inner_loop_ph.split.us:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %inner_loop_ph ]
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_begin.us:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit.loopexit.split.us
|
|
;
|
|
; CHECK: loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A]], %inner_loop_begin.us ]
|
|
; CHECK-NEXT: br label %loop_exit.loopexit
|
|
;
|
|
; The original remains a loop losing the exit edge.
|
|
;
|
|
; CHECK: inner_loop_ph.split:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
;
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_a
|
|
;
|
|
; CHECK: inner_loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit, label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_loop_a ]
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %loop_latch, label %loop_exit
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A]], %inner_loop_a ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_latch, label %loop_exit.loopexit1
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a, %inner_loop_begin ], [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit.loopexit:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %inner_loop_exit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %loop_exit.loopexit ], [ %[[A_LCSSA]], %loop_exit.loopexit1 ]
|
|
; CHECK-NEXT: ret i32 %[[A_PHI]]
|
|
}
|
|
|
|
; Check that if the original inner loop after unswitching doesn't loop and
|
|
; directly exits even an outer loop, we remove the original preheader from the
|
|
; outer loop and add needed LCSSA phi nodes for the new exit block from the
|
|
; outer loop.
|
|
define i32 @test11b(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test11b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_latch, label %inner_loop_ph
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_latch, label %inner_loop_ph
|
|
|
|
inner_loop_ph:
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_loop_begin
|
|
; CHECK: inner_loop_ph:
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_loop_ph.split.us, label %inner_loop_ph.split
|
|
|
|
inner_loop_begin:
|
|
call void @sink1(i32 %b)
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %inner_loop_a, label %loop_exit
|
|
|
|
inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_loop_exit, label %inner_loop_begin
|
|
; The cloned path continues to loop without the exit out of the entire nest.
|
|
;
|
|
; CHECK: inner_loop_ph.split.us:
|
|
; CHECK-NEXT: br label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_begin.us:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_a.us
|
|
;
|
|
; CHECK: inner_loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_exit.split.us, label %inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A]], %inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; The original remains a loop losing the exit edge.
|
|
;
|
|
; CHECK: inner_loop_ph.split:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %inner_loop_ph ]
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
;
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %loop_exit.loopexit
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_loop_a ]
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %loop_latch, label %loop_exit
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_latch, label %loop_exit.loopexit1
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a, %inner_loop_begin ], [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit.loopexit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %inner_loop_begin ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %inner_loop_exit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.loopexit ], [ %[[A_LCSSA_US]], %loop_exit.loopexit1 ]
|
|
; CHECK-NEXT: ret i32 %[[A_PHI]]
|
|
}
|
|
|
|
; Like test11a, but checking that when the whole thing is wrapped in yet
|
|
; another loop, we correctly attribute the cloned preheader to that outermost
|
|
; loop rather than only handling the case where the preheader is not in any loop
|
|
; at all.
|
|
define i32 @test12a(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test12a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_loop_latch, label %inner_inner_loop_ph
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_latch, label %inner_inner_loop_ph
|
|
|
|
inner_inner_loop_ph:
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_inner_loop_ph:
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_inner_loop_ph.split.us, label %inner_inner_loop_ph.split
|
|
|
|
inner_inner_loop_begin:
|
|
call void @sink1(i32 %b)
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %inner_loop_exit, label %inner_inner_loop_a
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_inner_loop_exit, label %inner_inner_loop_begin
|
|
; The cloned path doesn't actually loop and is an exit from the outer loop as
|
|
; well.
|
|
;
|
|
; CHECK: inner_inner_loop_ph.split.us:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %inner_inner_loop_ph ]
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit.split.us
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_INNER_LCSSA_US:.*]] = phi i32 [ %[[A]], %inner_inner_loop_begin.us ]
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
;
|
|
; The original remains a loop losing the exit edge.
|
|
;
|
|
; CHECK: inner_inner_loop_ph.split:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_inner_loop_a
|
|
;
|
|
; CHECK: inner_inner_loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit, label %inner_inner_loop_begin
|
|
|
|
inner_inner_loop_exit:
|
|
%a.inner_inner_lcssa = phi i32 [ %a, %inner_inner_loop_a ]
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %inner_loop_latch, label %inner_loop_exit
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_INNER_LCSSA:.*]] = phi i32 [ %[[A]], %inner_inner_loop_a ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_latch, label %inner_loop_exit.loopexit1
|
|
|
|
inner_loop_latch:
|
|
br label %inner_loop_begin
|
|
; CHECK: inner_loop_latch:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_inner_loop_begin ], [ %a.inner_inner_lcssa, %inner_inner_loop_exit ]
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %loop_begin, label %loop_exit
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA_US]], %inner_loop_exit.loopexit ], [ %[[A_INNER_LCSSA]], %inner_loop_exit.loopexit1 ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; Like test11b, but checking that when the whole thing is wrapped in yet
|
|
; another loop, we correctly sink the preheader to the outermost loop rather
|
|
; than only handling the case where the preheader is completely removed from
|
|
; a loop.
|
|
define i32 @test12b(i1* %ptr, i1* %cond.ptr, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test12b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_begin:
|
|
br label %inner_loop_begin
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_begin:
|
|
%b = load i32, i32* %b.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %inner_loop_latch, label %inner_inner_loop_ph
|
|
; CHECK: inner_loop_begin:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_latch, label %inner_inner_loop_ph
|
|
|
|
inner_inner_loop_ph:
|
|
%cond = load i1, i1* %cond.ptr
|
|
br label %inner_inner_loop_begin
|
|
; CHECK: inner_inner_loop_ph:
|
|
; CHECK-NEXT: %[[COND:.*]] = load i1, i1* %cond.ptr
|
|
; CHECK-NEXT: br i1 %[[COND]], label %inner_inner_loop_ph.split.us, label %inner_inner_loop_ph.split
|
|
|
|
inner_inner_loop_begin:
|
|
call void @sink1(i32 %b)
|
|
%a = load i32, i32* %a.ptr
|
|
br i1 %cond, label %inner_inner_loop_a, label %inner_loop_exit
|
|
|
|
inner_inner_loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %inner_inner_loop_exit, label %inner_inner_loop_begin
|
|
; The cloned path continues to loop without the exit out of the entire nest.
|
|
;
|
|
; CHECK: inner_inner_loop_ph.split.us:
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_begin.us:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_inner_loop_a.us
|
|
;
|
|
; CHECK: inner_inner_loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_inner_loop_exit.split.us, label %inner_inner_loop_begin.us
|
|
;
|
|
; CHECK: inner_inner_loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_INNER_INNER_LCSSA_US:.*]] = phi i32 [ %[[A]], %inner_inner_loop_a.us ]
|
|
; CHECK-NEXT: br label %inner_inner_loop_exit
|
|
;
|
|
; The original remains a loop losing the exit edge.
|
|
;
|
|
; CHECK: inner_inner_loop_ph.split:
|
|
; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B]], %inner_inner_loop_ph ]
|
|
; CHECK-NEXT: br label %inner_inner_loop_begin
|
|
;
|
|
; CHECK: inner_inner_loop_begin:
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B_LCSSA]])
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: br label %inner_loop_exit.loopexit
|
|
|
|
inner_inner_loop_exit:
|
|
%a.inner_inner_lcssa = phi i32 [ %a, %inner_inner_loop_a ]
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %inner_loop_latch, label %inner_loop_exit
|
|
; CHECK: inner_inner_loop_exit:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %inner_loop_latch, label %inner_loop_exit.loopexit1
|
|
|
|
inner_loop_latch:
|
|
br label %inner_loop_begin
|
|
; CHECK: inner_loop_latch:
|
|
; CHECK-NEXT: br label %inner_loop_begin
|
|
|
|
inner_loop_exit:
|
|
%a.inner_lcssa = phi i32 [ %a, %inner_inner_loop_begin ], [ %a.inner_inner_lcssa, %inner_inner_loop_exit ]
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %loop_begin, label %loop_exit
|
|
; CHECK: inner_loop_exit.loopexit:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A]], %inner_inner_loop_begin ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit.loopexit1:
|
|
; CHECK-NEXT: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA_US]], %inner_inner_loop_exit ]
|
|
; CHECK-NEXT: br label %inner_loop_exit
|
|
;
|
|
; CHECK: inner_loop_exit:
|
|
; CHECK-NEXT: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA]], %inner_loop_exit.loopexit ], [ %[[A_INNER_LCSSA_US]], %inner_loop_exit.loopexit1 ]
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
%a.lcssa = phi i32 [ %a.inner_lcssa, %inner_loop_exit ]
|
|
ret i32 %a.lcssa
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A_INNER_PHI]], %inner_loop_exit ]
|
|
; CHECK-NEXT: ret i32 %[[A_LCSSA]]
|
|
}
|
|
|
|
; Test where the cloned loop has an inner loop that has to be traversed to form
|
|
; the cloned loop, and where this inner loop has multiple blocks, and where the
|
|
; exiting block that connects the inner loop to the cloned loop is not the header
|
|
; block. This ensures that we correctly handle interesting corner cases of
|
|
; traversing back to the header when establishing the cloned loop.
|
|
define i32 @test13a(i1* %ptr, i1 %cond, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test13a(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_exit, label %loop_latch
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %cond, label %loop_b_inner_ph, label %loop_exit
|
|
|
|
loop_b_inner_ph:
|
|
br label %loop_b_inner_header
|
|
|
|
loop_b_inner_header:
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %loop_b_inner_latch, label %loop_b_inner_body
|
|
|
|
loop_b_inner_body:
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %loop_b_inner_latch, label %loop_b_inner_exit
|
|
|
|
loop_b_inner_latch:
|
|
br label %loop_b_inner_header
|
|
|
|
loop_b_inner_exit:
|
|
br label %loop_latch
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
; The cloned loop contains an inner loop within it.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a.us, label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br label %loop_b_inner_ph.us
|
|
;
|
|
; CHECK: loop_b_inner_ph.us:
|
|
; CHECK-NEXT: br label %loop_b_inner_header.us
|
|
;
|
|
; CHECK: loop_b_inner_header.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_b_inner_latch.us, label %loop_b_inner_body.us
|
|
;
|
|
; CHECK: loop_b_inner_body.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_b_inner_latch.us, label %loop_b_inner_exit.us
|
|
;
|
|
; CHECK: loop_b_inner_exit.us:
|
|
; CHECK-NEXT: br label %loop_latch.us
|
|
;
|
|
; CHECK: loop_b_inner_latch.us:
|
|
; CHECK-NEXT: br label %loop_b_inner_header.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.us, label %loop_latch.us
|
|
;
|
|
; CHECK: loop_latch.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A]], %loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; And the original loop no longer contains an inner loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a, label %loop_b
|
|
;
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.loopexit, label %loop_latch
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
;
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%lcssa = phi i32 [ %a, %loop_a ], [ %b, %loop_b ]
|
|
ret i32 %lcssa
|
|
; CHECK: loop_exit.split.loopexit:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[B]], %loop_b ], [ %[[A_LCSSA]], %loop_exit.split.loopexit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI_US:.*]] = phi i32 [ %[[AB_PHI]], %loop_exit.split ], [ %[[A_LCSSA_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI_US]]
|
|
}
|
|
|
|
; Test where the original loop has an inner loop that has to be traversed to
|
|
; rebuild the loop, and where this inner loop has multiple blocks, and where
|
|
; the exiting block that connects the inner loop to the original loop is not
|
|
; the header block. This ensures that we correctly handle interesting corner
|
|
; cases of traversing back to the header when re-establishing the original loop
|
|
; still exists after unswitching.
|
|
define i32 @test13b(i1* %ptr, i1 %cond, i32* %a.ptr, i32* %b.ptr) {
|
|
; CHECK-LABEL: @test13b(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%a = load i32, i32* %a.ptr
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_exit, label %loop_latch
|
|
|
|
loop_b:
|
|
%b = load i32, i32* %b.ptr
|
|
br i1 %cond, label %loop_exit, label %loop_b_inner_ph
|
|
|
|
loop_b_inner_ph:
|
|
br label %loop_b_inner_header
|
|
|
|
loop_b_inner_header:
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %loop_b_inner_latch, label %loop_b_inner_body
|
|
|
|
loop_b_inner_body:
|
|
%v4 = load i1, i1* %ptr
|
|
br i1 %v4, label %loop_b_inner_latch, label %loop_b_inner_exit
|
|
|
|
loop_b_inner_latch:
|
|
br label %loop_b_inner_header
|
|
|
|
loop_b_inner_exit:
|
|
br label %loop_latch
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
; The cloned loop doesn't contain an inner loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a.us, label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split.us.loopexit, label %loop_latch.us
|
|
;
|
|
; CHECK: loop_latch.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_exit.split.us.loopexit:
|
|
; CHECK-NEXT: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A]], %loop_a.us ]
|
|
; CHECK-NEXT: br label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: %[[AB_PHI_US:.*]] = phi i32 [ %[[B]], %loop_b.us ], [ %[[A_LCSSA_US]], %loop_exit.split.us.loopexit ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; But the original loop contains an inner loop that must be traversed.;
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[A:.*]] = load i32, i32* %a.ptr
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_a, label %loop_b
|
|
;
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_exit.split, label %loop_latch
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: %[[B:.*]] = load i32, i32* %b.ptr
|
|
; CHECK-NEXT: br label %loop_b_inner_ph
|
|
;
|
|
; CHECK: loop_b_inner_ph:
|
|
; CHECK-NEXT: br label %loop_b_inner_header
|
|
;
|
|
; CHECK: loop_b_inner_header:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_b_inner_latch, label %loop_b_inner_body
|
|
;
|
|
; CHECK: loop_b_inner_body:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_b_inner_latch, label %loop_b_inner_exit
|
|
;
|
|
; CHECK: loop_b_inner_latch:
|
|
; CHECK-NEXT: br label %loop_b_inner_header
|
|
;
|
|
; CHECK: loop_b_inner_exit:
|
|
; CHECK-NEXT: br label %loop_latch
|
|
;
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
|
|
loop_exit:
|
|
%lcssa = phi i32 [ %a, %loop_a ], [ %b, %loop_b ]
|
|
ret i32 %lcssa
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_a ]
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.split ], [ %[[AB_PHI_US]], %loop_exit.split.us ]
|
|
; CHECK-NEXT: ret i32 %[[AB_PHI]]
|
|
}
|
|
|
|
define i32 @test20(i32* %var, i32 %cond1, i32 %cond2) {
|
|
; CHECK-LABEL: @test20(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 %cond2, label %[[ENTRY_SPLIT_EXIT:.*]] [
|
|
; CHECK-NEXT: i32 0, label %[[ENTRY_SPLIT_A:.*]]
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_A]]
|
|
; CHECK-NEXT: i32 13, label %[[ENTRY_SPLIT_B:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[ENTRY_SPLIT_A]]
|
|
; CHECK-NEXT: i32 42, label %[[ENTRY_SPLIT_C:.*]]
|
|
; CHECK-NEXT: ]
|
|
|
|
loop_begin:
|
|
%var_val = load i32, i32* %var
|
|
switch i32 %cond2, label %loop_exit [
|
|
i32 0, label %loop_a
|
|
i32 1, label %loop_a
|
|
i32 13, label %loop_b
|
|
i32 2, label %loop_a
|
|
i32 42, label %loop_c
|
|
]
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %loop_latch
|
|
; Unswitched 'a' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_A]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_A]]:
|
|
; CHECK-NEXT: %{{.*}} = load i32, i32* %var
|
|
; CHECK-NEXT: br label %[[LOOP_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_A]]:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_A]]:
|
|
; CHECK: br label %[[LOOP_BEGIN_A]]
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %loop_latch
|
|
; Unswitched 'b' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_B]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_B]]:
|
|
; CHECK-NEXT: %{{.*}} = load i32, i32* %var
|
|
; CHECK-NEXT: br label %[[LOOP_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_B]]:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_B]]:
|
|
; CHECK: br label %[[LOOP_BEGIN_B]]
|
|
|
|
loop_c:
|
|
call i32 @c() noreturn nounwind
|
|
br label %loop_latch
|
|
; Unswitched 'c' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_C]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_C]]:
|
|
; CHECK-NEXT: %{{.*}} = load i32, i32* %var
|
|
; CHECK-NEXT: br label %[[LOOP_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_C]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_C]]:
|
|
; CHECK: br label %[[LOOP_BEGIN_C]]
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
|
|
loop_exit:
|
|
%lcssa = phi i32 [ %var_val, %loop_begin ]
|
|
ret i32 %lcssa
|
|
; Unswitched exit edge (no longer a loop).
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_EXIT]]:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V:.*]] = load i32, i32* %var
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %[[V]], %loop_begin ]
|
|
; CHECK-NEXT: ret i32 %[[LCSSA]]
|
|
}
|
|
|
|
; Negative test: we do not switch when the loop contains unstructured control
|
|
; flows as it would significantly complicate the process as novel loops might
|
|
; be formed, etc.
|
|
define void @test_no_unswitch_unstructured_cfg(i1* %ptr, i1 %cond) {
|
|
; CHECK-LABEL: @test_no_unswitch_unstructured_cfg(
|
|
entry:
|
|
br label %loop_begin
|
|
|
|
loop_begin:
|
|
br i1 %cond, label %loop_left, label %loop_right
|
|
|
|
loop_left:
|
|
%v1 = load i1, i1* %ptr
|
|
br i1 %v1, label %loop_right, label %loop_merge
|
|
|
|
loop_right:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_left, label %loop_merge
|
|
|
|
loop_merge:
|
|
%v3 = load i1, i1* %ptr
|
|
br i1 %v3, label %loop_latch, label %loop_exit
|
|
|
|
loop_latch:
|
|
br label %loop_begin
|
|
|
|
loop_exit:
|
|
ret void
|
|
}
|
|
|
|
; A test reduced out of 403.gcc with interesting nested loops that trigger
|
|
; multiple unswitches. A key component of this test is that there are multiple
|
|
; paths to reach an inner loop after unswitching, and one of them is via the
|
|
; predecessors of the unswitched loop header. That can allow us to find the loop
|
|
; through multiple different paths.
|
|
define void @test21(i1 %a, i1 %b) {
|
|
; CHECK-LABEL: @test21(
|
|
bb:
|
|
br label %bb3
|
|
; CHECK-NOT: br i1 %a
|
|
;
|
|
; CHECK: br i1 %a, label %[[BB_SPLIT_US:.*]], label %[[BB_SPLIT:.*]]
|
|
;
|
|
; CHECK-NOT: br i1 %a
|
|
; CHECK-NOT: br i1 %b
|
|
;
|
|
; CHECK: [[BB_SPLIT]]:
|
|
; CHECK: br i1 %b
|
|
;
|
|
; CHECK-NOT: br i1 %a
|
|
; CHECK-NOT: br i1 %b
|
|
|
|
bb3:
|
|
%tmp1.0 = phi i32 [ 0, %bb ], [ %tmp1.3, %bb23 ]
|
|
br label %bb7
|
|
|
|
bb7:
|
|
%tmp.0 = phi i1 [ true, %bb3 ], [ false, %bb19 ]
|
|
%tmp1.1 = phi i32 [ %tmp1.0, %bb3 ], [ %tmp1.2.lcssa, %bb19 ]
|
|
br i1 %tmp.0, label %bb11.preheader, label %bb23
|
|
|
|
bb11.preheader:
|
|
br i1 %a, label %bb19, label %bb14.lr.ph
|
|
|
|
bb14.lr.ph:
|
|
br label %bb14
|
|
|
|
bb14:
|
|
%tmp2.02 = phi i32 [ 0, %bb14.lr.ph ], [ 1, %bb14 ]
|
|
br i1 %b, label %bb11.bb19_crit_edge, label %bb14
|
|
|
|
bb11.bb19_crit_edge:
|
|
%split = phi i32 [ %tmp2.02, %bb14 ]
|
|
br label %bb19
|
|
|
|
bb19:
|
|
%tmp1.2.lcssa = phi i32 [ %split, %bb11.bb19_crit_edge ], [ %tmp1.1, %bb11.preheader ]
|
|
%tmp21 = icmp eq i32 %tmp1.2.lcssa, 0
|
|
br i1 %tmp21, label %bb23, label %bb7
|
|
|
|
bb23:
|
|
%tmp1.3 = phi i32 [ %tmp1.2.lcssa, %bb19 ], [ %tmp1.1, %bb7 ]
|
|
br label %bb3
|
|
}
|
|
|
|
; A test reduced out of 400.perlbench that when unswitching the `%stop`
|
|
; condition clones a loop nest outside of a containing loop. This excercises a
|
|
; different cloning path from our other test cases and in turn verifying the
|
|
; resulting structure can catch any failures to correctly clone these nested
|
|
; loops.
|
|
declare void @f()
|
|
declare void @g()
|
|
declare i32 @h(i32 %arg)
|
|
define void @test22(i32 %arg) {
|
|
; CHECK-LABEL: define void @test22(
|
|
entry:
|
|
br label %loop1.header
|
|
|
|
loop1.header:
|
|
%stop = phi i1 [ true, %loop1.latch ], [ false, %entry ]
|
|
%i = phi i32 [ %i.lcssa, %loop1.latch ], [ %arg, %entry ]
|
|
; CHECK: %[[I:.*]] = phi i32 [ %{{.*}}, %loop1.latch ], [ %arg, %entry ]
|
|
br i1 %stop, label %loop1.exit, label %loop1.body.loop2.ph
|
|
; CHECK: br i1 %stop, label %loop1.exit, label %loop1.body.loop2.ph
|
|
|
|
loop1.body.loop2.ph:
|
|
br label %loop2.header
|
|
; Just check that the we unswitched the key condition and that leads to the
|
|
; inner loop header.
|
|
;
|
|
; CHECK: loop1.body.loop2.ph:
|
|
; CHECK-NEXT: br i1 %stop, label %[[SPLIT_US:.*]], label %[[SPLIT:.*]]
|
|
;
|
|
; CHECK: [[SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[LOOP2_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[LOOP2_HEADER_US]]:
|
|
; CHECK-NEXT: %{{.*}} = phi i32 [ %[[I]], %[[SPLIT_US]] ]
|
|
;
|
|
; CHECK: [[SPLIT]]:
|
|
; CHECK-NEXT: br label %[[LOOP2_HEADER:.*]]
|
|
;
|
|
; CHECK: [[LOOP2_HEADER]]:
|
|
; CHECK-NEXT: %{{.*}} = phi i32 [ %[[I]], %[[SPLIT]] ]
|
|
|
|
loop2.header:
|
|
%i.inner = phi i32 [ %i, %loop1.body.loop2.ph ], [ %i.next, %loop2.latch ]
|
|
br label %loop3.header
|
|
|
|
loop3.header:
|
|
%sw = call i32 @h(i32 %i.inner)
|
|
switch i32 %sw, label %loop3.exit [
|
|
i32 32, label %loop3.header
|
|
i32 59, label %loop2.latch
|
|
i32 36, label %loop1.latch
|
|
]
|
|
|
|
loop2.latch:
|
|
%i.next = add i32 %i.inner, 1
|
|
br i1 %stop, label %loop2.exit, label %loop2.header
|
|
|
|
loop1.latch:
|
|
%i.lcssa = phi i32 [ %i.inner, %loop3.header ]
|
|
br label %loop1.header
|
|
|
|
loop3.exit:
|
|
call void @f()
|
|
ret void
|
|
|
|
loop2.exit:
|
|
call void @g()
|
|
ret void
|
|
|
|
loop1.exit:
|
|
call void @g()
|
|
ret void
|
|
}
|
|
|
|
; Test that when we are unswitching and need to rebuild the loop block set we
|
|
; correctly skip past inner loops. We want to use the inner loop to efficiently
|
|
; skip whole subregions of the outer loop blocks but just because the header of
|
|
; the outer loop is also the preheader of an inner loop shouldn't confuse this
|
|
; walk.
|
|
define void @test23(i1 %arg, i1* %ptr) {
|
|
; CHECK-LABEL: define void @test23(
|
|
entry:
|
|
br label %outer.header
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: br i1 %arg,
|
|
;
|
|
; Just verify that we unswitched the correct bits. We should call `@f` twice in
|
|
; one unswitch and `@f` and then `@g` in the other.
|
|
; CHECK: call void
|
|
; CHECK-SAME: @f
|
|
; CHECK: call void
|
|
; CHECK-SAME: @f
|
|
;
|
|
; CHECK: call void
|
|
; CHECK-SAME: @f
|
|
; CHECK: call void
|
|
; CHECK-SAME: @g
|
|
|
|
outer.header:
|
|
br label %inner.header
|
|
|
|
inner.header:
|
|
call void @f()
|
|
br label %inner.latch
|
|
|
|
inner.latch:
|
|
%inner.cond = load i1, i1* %ptr
|
|
br i1 %inner.cond, label %inner.header, label %outer.body
|
|
|
|
outer.body:
|
|
br i1 %arg, label %outer.body.left, label %outer.body.right
|
|
|
|
outer.body.left:
|
|
call void @f()
|
|
br label %outer.latch
|
|
|
|
outer.body.right:
|
|
call void @g()
|
|
br label %outer.latch
|
|
|
|
outer.latch:
|
|
%outer.cond = load i1, i1* %ptr
|
|
br i1 %outer.cond, label %outer.header, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Non-trivial loop unswitching where there are two invariant conditions, but the
|
|
; second one is only in the cloned copy of the loop after unswitching.
|
|
define i32 @test24(i1* %ptr, i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @test24(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond1, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
br i1 %cond1, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
br i1 %cond2, label %loop_a_a, label %loop_a_c
|
|
; The second unswitched condition.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br i1 %cond2, label %entry.split.us.split.us, label %entry.split.us.split
|
|
|
|
loop_a_a:
|
|
call i32 @a()
|
|
br label %latch
|
|
; The 'loop_a_a' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us.us
|
|
;
|
|
; CHECK: loop_begin.us.us:
|
|
; CHECK-NEXT: br label %loop_a.us.us
|
|
;
|
|
; CHECK: loop_a.us.us:
|
|
; CHECK-NEXT: br label %loop_a_a.us.us
|
|
;
|
|
; CHECK: loop_a_a.us.us:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %latch.us.us
|
|
;
|
|
; CHECK: latch.us.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us.us, label %loop_exit.split.us.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us.split.us:
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
|
|
loop_a_c:
|
|
call i32 @c()
|
|
br label %latch
|
|
; The 'loop_a_c' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us.split:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: br label %loop_a.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: br label %loop_a_c.us
|
|
;
|
|
; CHECK: loop_a_c.us:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %latch
|
|
;
|
|
; CHECK: latch.us:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin.us, label %loop_exit.split.us.split
|
|
;
|
|
; CHECK: loop_exit.split.us.split:
|
|
; CHECK-NEXT: br label %loop_exit.split
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %latch
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: br label %loop_b
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %latch
|
|
;
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: %[[V:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit.split
|
|
;
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
latch:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
|
|
loop_exit:
|
|
ret i32 0
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: ret
|
|
}
|
|
|
|
; Non-trivial partial loop unswitching of an invariant input to an 'or'.
|
|
define i32 @test25(i1* %ptr, i1 %cond) {
|
|
; CHECK-LABEL: @test25(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 %cond, label %entry.split.us, label %entry.split
|
|
|
|
loop_begin:
|
|
%v1 = load i1, i1* %ptr
|
|
%cond_or = or i1 %v1, %cond
|
|
br i1 %cond_or, label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %latch
|
|
; The 'loop_a' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V1_US:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[OR_US:.*]] = or i1 %[[V1_US]], true
|
|
; CHECK-NEXT: br label %loop_a.us
|
|
;
|
|
; CHECK: loop_a.us:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %latch.us
|
|
;
|
|
; CHECK: latch.us:
|
|
; CHECK-NEXT: %[[V2_US:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V2_US]], label %loop_begin.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %latch
|
|
; The original loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V1:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: %[[OR:.*]] = or i1 %[[V1]], false
|
|
; CHECK-NEXT: br i1 %[[OR]], label %loop_a, label %loop_b
|
|
;
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %latch
|
|
;
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %latch
|
|
|
|
latch:
|
|
%v2 = load i1, i1* %ptr
|
|
br i1 %v2, label %loop_begin, label %loop_exit
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: %[[V2:.*]] = load i1, i1* %ptr
|
|
; CHECK-NEXT: br i1 %[[V2]], label %loop_begin, label %loop_exit.split
|
|
|
|
loop_exit:
|
|
ret i32 0
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: ret
|
|
}
|
|
|
|
; Non-trivial partial loop unswitching of multiple invariant inputs to an `and`
|
|
; chain.
|
|
define i32 @test26(i1* %ptr1, i1* %ptr2, i1* %ptr3, i1 %cond1, i1 %cond2, i1 %cond3) {
|
|
; CHECK-LABEL: @test26(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: %[[INV_AND:.*]] = and i1 %cond3, %cond1
|
|
; CHECK-NEXT: br i1 %[[INV_AND]], label %entry.split, label %entry.split.us
|
|
|
|
loop_begin:
|
|
%v1 = load i1, i1* %ptr1
|
|
%v2 = load i1, i1* %ptr2
|
|
%cond_and1 = and i1 %v1, %cond1
|
|
%cond_or1 = or i1 %v2, %cond2
|
|
%cond_and2 = and i1 %cond_and1, %cond_or1
|
|
%cond_and3 = and i1 %cond_and2, %cond3
|
|
br i1 %cond_and3, label %loop_a, label %loop_b
|
|
; The 'loop_b' unswitched loop.
|
|
;
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label %loop_begin.us
|
|
;
|
|
; CHECK: loop_begin.us:
|
|
; CHECK-NEXT: %[[V1_US:.*]] = load i1, i1* %ptr1
|
|
; CHECK-NEXT: %[[V2_US:.*]] = load i1, i1* %ptr2
|
|
; CHECK-NEXT: %[[AND1_US:.*]] = and i1 %[[V1_US]], false
|
|
; CHECK-NEXT: %[[OR1_US:.*]] = or i1 %[[V2_US]], %cond2
|
|
; CHECK-NEXT: %[[AND2_US:.*]] = and i1 %[[AND1_US]], %[[OR1_US]]
|
|
; CHECK-NEXT: %[[AND3_US:.*]] = and i1 %[[AND2_US]], false
|
|
; CHECK-NEXT: br label %loop_b.us
|
|
;
|
|
; CHECK: loop_b.us:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %latch.us
|
|
;
|
|
; CHECK: latch.us:
|
|
; CHECK-NEXT: %[[V3_US:.*]] = load i1, i1* %ptr3
|
|
; CHECK-NEXT: br i1 %[[V3_US]], label %loop_begin.us, label %loop_exit.split.us
|
|
;
|
|
; CHECK: loop_exit.split.us:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
; The original loop.
|
|
;
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label %loop_begin
|
|
;
|
|
; CHECK: loop_begin:
|
|
; CHECK-NEXT: %[[V1:.*]] = load i1, i1* %ptr1
|
|
; CHECK-NEXT: %[[V2:.*]] = load i1, i1* %ptr2
|
|
; CHECK-NEXT: %[[AND1:.*]] = and i1 %[[V1]], true
|
|
; CHECK-NEXT: %[[OR1:.*]] = or i1 %[[V2]], %cond2
|
|
; CHECK-NEXT: %[[AND2:.*]] = and i1 %[[AND1]], %[[OR1]]
|
|
; CHECK-NEXT: %[[AND3:.*]] = and i1 %[[AND2]], true
|
|
; CHECK-NEXT: br i1 %[[AND3]], label %loop_a, label %loop_b
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %latch
|
|
; CHECK: loop_a:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %latch
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %latch
|
|
; CHECK: loop_b:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %latch
|
|
|
|
latch:
|
|
%v3 = load i1, i1* %ptr3
|
|
br i1 %v3, label %loop_begin, label %loop_exit
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: %[[V3:.*]] = load i1, i1* %ptr3
|
|
; CHECK-NEXT: br i1 %[[V3]], label %loop_begin, label %loop_exit.split
|
|
|
|
loop_exit:
|
|
ret i32 0
|
|
; CHECK: loop_exit.split:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
;
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: ret
|
|
}
|
|
|
|
; Non-trivial unswitching of a switch.
|
|
define i32 @test27(i1* %ptr, i32 %cond) {
|
|
; CHECK-LABEL: @test27(
|
|
entry:
|
|
br label %loop_begin
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 %cond, label %[[ENTRY_SPLIT_LATCH:.*]] [
|
|
; CHECK-NEXT: i32 0, label %[[ENTRY_SPLIT_A:.*]]
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_B:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[ENTRY_SPLIT_C:.*]]
|
|
; CHECK-NEXT: ]
|
|
|
|
loop_begin:
|
|
switch i32 %cond, label %latch [
|
|
i32 0, label %loop_a
|
|
i32 1, label %loop_b
|
|
i32 2, label %loop_c
|
|
]
|
|
|
|
loop_a:
|
|
call i32 @a()
|
|
br label %latch
|
|
; Unswitched 'a' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_A]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_A]]:
|
|
; CHECK-NEXT: br label %[[LOOP_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_A]]:
|
|
; CHECK-NEXT: call i32 @a()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_A]]:
|
|
; CHECK-NEXT: %[[V_A:.*]] = load i1, i1* %ptr
|
|
; CHECK: br i1 %[[V_A]], label %[[LOOP_BEGIN_A]], label %[[LOOP_EXIT_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_A]]:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_b:
|
|
call i32 @b()
|
|
br label %latch
|
|
; Unswitched 'b' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_B]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_B]]:
|
|
; CHECK-NEXT: br label %[[LOOP_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_B]]:
|
|
; CHECK-NEXT: call i32 @b()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_B]]:
|
|
; CHECK-NEXT: %[[V_B:.*]] = load i1, i1* %ptr
|
|
; CHECK: br i1 %[[V_B]], label %[[LOOP_BEGIN_B]], label %[[LOOP_EXIT_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_B]]:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_c:
|
|
call i32 @c()
|
|
br label %latch
|
|
; Unswitched 'c' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_C]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_C]]:
|
|
; CHECK-NEXT: br label %[[LOOP_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_C]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_C]]:
|
|
; CHECK-NEXT: %[[V_C:.*]] = load i1, i1* %ptr
|
|
; CHECK: br i1 %[[V_C]], label %[[LOOP_BEGIN_C]], label %[[LOOP_EXIT_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_C]]:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
latch:
|
|
%v = load i1, i1* %ptr
|
|
br i1 %v, label %loop_begin, label %loop_exit
|
|
; Unswitched the 'latch' only loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_LATCH]]:
|
|
; CHECK-NEXT: br label %[[LOOP_BEGIN_LATCH:.*]]
|
|
;
|
|
; CHECK: [[LOOP_BEGIN_LATCH]]:
|
|
; CHECK-NEXT: br label %[[LOOP_LATCH_LATCH:.*]]
|
|
;
|
|
; CHECK: [[LOOP_LATCH_LATCH]]:
|
|
; CHECK-NEXT: %[[V_LATCH:.*]] = load i1, i1* %ptr
|
|
; CHECK: br i1 %[[V_LATCH]], label %[[LOOP_BEGIN_LATCH]], label %[[LOOP_EXIT_LATCH:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_LATCH]]:
|
|
; CHECK-NEXT: br label %loop_exit
|
|
|
|
loop_exit:
|
|
ret i32 0
|
|
; CHECK: loop_exit:
|
|
; CHECK-NEXT: ret i32 0
|
|
}
|
|
|
|
; A test case designed to exercise unusual properties of switches: they
|
|
; can introduce multiple edges to successors. These need lots of special case
|
|
; handling as they get collapsed in many cases (domtree, the unswitch itself)
|
|
; but not in all cases (the PHI node operands).
|
|
define i32 @test28(i32 %arg) {
|
|
; CHECK-LABEL: @test28(
|
|
entry:
|
|
br label %header
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 %arg, label %[[ENTRY_SPLIT_C:.*]] [
|
|
; CHECK-NEXT: i32 0, label %[[ENTRY_SPLIT_A:.*]]
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_A]]
|
|
; CHECK-NEXT: i32 2, label %[[ENTRY_SPLIT_B:.*]]
|
|
; CHECK-NEXT: i32 3, label %[[ENTRY_SPLIT_C]]
|
|
; CHECK-NEXT: ]
|
|
|
|
header:
|
|
%tmp = call i32 @d()
|
|
%cmp1 = icmp eq i32 %tmp, 0
|
|
; We set up a chain through all the successors of the switch that doesn't
|
|
; involve the switch so that we can have interesting PHI nodes in them.
|
|
br i1 %cmp1, label %body.a, label %dispatch
|
|
|
|
dispatch:
|
|
; Switch with multiple successors. We arrange the last successor to be the
|
|
; default to make the test case easier to read. This has a duplicate edge
|
|
; both to the default destination (which is completely superfluous but
|
|
; technically valid IR) and to a regular successor.
|
|
switch i32 %arg, label %body.c [
|
|
i32 0, label %body.a
|
|
i32 1, label %body.a
|
|
i32 2, label %body.b
|
|
i32 3, label %body.c
|
|
]
|
|
|
|
body.a:
|
|
%tmp.a.phi = phi i32 [ 0, %header ], [ %tmp, %dispatch ], [ %tmp, %dispatch ]
|
|
%tmp.a = call i32 @a()
|
|
%tmp.a.sum = add i32 %tmp.a.phi, %tmp.a
|
|
br label %body.b
|
|
; Unswitched 'a' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_A]]:
|
|
; CHECK-NEXT: br label %[[HEADER_A:.*]]
|
|
;
|
|
; CHECK: [[HEADER_A]]:
|
|
; CHECK-NEXT: %[[TMP_A:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_A:.*]] = icmp eq i32 %[[TMP_A]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_A]], label %[[BODY_A_A:.*]], label %[[DISPATCH_A:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_A]]:
|
|
; CHECK-NEXT: br label %[[BODY_A_A]]
|
|
;
|
|
; CHECK: [[BODY_A_A]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_A:.*]] = phi i32 [ 0, %[[HEADER_A]] ], [ %[[TMP_A]], %[[DISPATCH_A]] ]
|
|
; CHECK-NEXT: %[[TMP_A_A:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_A:.*]] = add i32 %[[TMP_A_PHI_A]], %[[TMP_A_A]]
|
|
; CHECK-NEXT: br label %[[BODY_B_A:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_A]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_A:.*]] = phi i32 [ %[[TMP_A_SUM_A]], %[[BODY_A_A]] ]
|
|
; CHECK-NEXT: %[[TMP_B_A:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_A:.*]] = add i32 %[[TMP_B_PHI_A]], %[[TMP_B_A]]
|
|
; CHECK-NEXT: br label %[[BODY_C_A:.*]]
|
|
;
|
|
; CHECK: [[BODY_C_A]]:
|
|
; CHECK-NEXT: %[[TMP_C_PHI_A:.*]] = phi i32 [ %[[TMP_B_SUM_A]], %[[BODY_B_A]] ]
|
|
; CHECK-NEXT: %[[TMP_C_A:.*]] = call i32 @c()
|
|
; CHECK-NEXT: %[[TMP_C_SUM_A:.*]] = add i32 %[[TMP_C_PHI_A]], %[[TMP_C_A]]
|
|
; CHECK-NEXT: br label %[[LATCH_A:.*]]
|
|
;
|
|
; CHECK: [[LATCH_A]]:
|
|
; CHECK-NEXT: %[[CMP2_A:.*]] = icmp slt i32 %[[TMP_C_SUM_A]], 42
|
|
; CHECK: br i1 %[[CMP2_A]], label %[[HEADER_A]], label %[[LOOP_EXIT_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_A]]:
|
|
; CHECK-NEXT: %[[LCSSA_A:.*]] = phi i32 [ %[[TMP_C_SUM_A]], %[[LATCH_A]] ]
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
body.b:
|
|
%tmp.b.phi = phi i32 [ %tmp, %dispatch ], [ %tmp.a.sum, %body.a ]
|
|
%tmp.b = call i32 @b()
|
|
%tmp.b.sum = add i32 %tmp.b.phi, %tmp.b
|
|
br label %body.c
|
|
; Unswitched 'b' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_B]]:
|
|
; CHECK-NEXT: br label %[[HEADER_B:.*]]
|
|
;
|
|
; CHECK: [[HEADER_B]]:
|
|
; CHECK-NEXT: %[[TMP_B:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_B:.*]] = icmp eq i32 %[[TMP_B]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_B]], label %[[BODY_A_B:.*]], label %[[DISPATCH_B:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_B]]:
|
|
; CHECK-NEXT: br label %[[BODY_B_B:.*]]
|
|
;
|
|
; CHECK: [[BODY_A_B]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_B:.*]] = phi i32 [ 0, %[[HEADER_B]] ]
|
|
; CHECK-NEXT: %[[TMP_A_B:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_B:.*]] = add i32 %[[TMP_A_PHI_B]], %[[TMP_A_B]]
|
|
; CHECK-NEXT: br label %[[BODY_B_B:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_B]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_B:.*]] = phi i32 [ %[[TMP_B]], %[[DISPATCH_B]] ], [ %[[TMP_A_SUM_B]], %[[BODY_A_B]] ]
|
|
; CHECK-NEXT: %[[TMP_B_B:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_B:.*]] = add i32 %[[TMP_B_PHI_B]], %[[TMP_B_B]]
|
|
; CHECK-NEXT: br label %[[BODY_C_B:.*]]
|
|
;
|
|
; CHECK: [[BODY_C_B]]:
|
|
; CHECK-NEXT: %[[TMP_C_PHI_B:.*]] = phi i32 [ %[[TMP_B_SUM_B]], %[[BODY_B_B]] ]
|
|
; CHECK-NEXT: %[[TMP_C_B:.*]] = call i32 @c()
|
|
; CHECK-NEXT: %[[TMP_C_SUM_B:.*]] = add i32 %[[TMP_C_PHI_B]], %[[TMP_C_B]]
|
|
; CHECK-NEXT: br label %[[LATCH_B:.*]]
|
|
;
|
|
; CHECK: [[LATCH_B]]:
|
|
; CHECK-NEXT: %[[CMP2_B:.*]] = icmp slt i32 %[[TMP_C_SUM_B]], 42
|
|
; CHECK: br i1 %[[CMP2_B]], label %[[HEADER_B]], label %[[LOOP_EXIT_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_B]]:
|
|
; CHECK-NEXT: %[[LCSSA_B:.*]] = phi i32 [ %[[TMP_C_SUM_B]], %[[LATCH_B]] ]
|
|
; CHECK-NEXT: br label %[[EXIT_SPLIT:.*]]
|
|
|
|
body.c:
|
|
%tmp.c.phi = phi i32 [ %tmp, %dispatch ], [ %tmp, %dispatch ], [ %tmp.b.sum, %body.b ]
|
|
%tmp.c = call i32 @c()
|
|
%tmp.c.sum = add i32 %tmp.c.phi, %tmp.c
|
|
br label %latch
|
|
; Unswitched 'c' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_C]]:
|
|
; CHECK-NEXT: br label %[[HEADER_C:.*]]
|
|
;
|
|
; CHECK: [[HEADER_C]]:
|
|
; CHECK-NEXT: %[[TMP_C:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_C:.*]] = icmp eq i32 %[[TMP_C]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_C]], label %[[BODY_A_C:.*]], label %[[DISPATCH_C:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_C]]:
|
|
; CHECK-NEXT: br label %[[BODY_C_C:.*]]
|
|
;
|
|
; CHECK: [[BODY_A_C]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_C:.*]] = phi i32 [ 0, %[[HEADER_C]] ]
|
|
; CHECK-NEXT: %[[TMP_A_C:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_C:.*]] = add i32 %[[TMP_A_PHI_C]], %[[TMP_A_C]]
|
|
; CHECK-NEXT: br label %[[BODY_B_C:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_C]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_C:.*]] = phi i32 [ %[[TMP_A_SUM_C]], %[[BODY_A_C]] ]
|
|
; CHECK-NEXT: %[[TMP_B_C:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_C:.*]] = add i32 %[[TMP_B_PHI_C]], %[[TMP_B_C]]
|
|
; CHECK-NEXT: br label %[[BODY_C_C:.*]]
|
|
;
|
|
; CHECK: [[BODY_C_C]]:
|
|
; CHECK-NEXT: %[[TMP_C_PHI_C:.*]] = phi i32 [ %[[TMP_C]], %[[DISPATCH_C]] ], [ %[[TMP_B_SUM_C]], %[[BODY_B_C]] ]
|
|
; CHECK-NEXT: %[[TMP_C_C:.*]] = call i32 @c()
|
|
; CHECK-NEXT: %[[TMP_C_SUM_C:.*]] = add i32 %[[TMP_C_PHI_C]], %[[TMP_C_C]]
|
|
; CHECK-NEXT: br label %[[LATCH_C:.*]]
|
|
;
|
|
; CHECK: [[LATCH_C]]:
|
|
; CHECK-NEXT: %[[CMP2_C:.*]] = icmp slt i32 %[[TMP_C_SUM_C]], 42
|
|
; CHECK: br i1 %[[CMP2_C]], label %[[HEADER_C]], label %[[LOOP_EXIT_C:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_C]]:
|
|
; CHECK-NEXT: %[[LCSSA_C:.*]] = phi i32 [ %[[TMP_C_SUM_C]], %[[LATCH_C]] ]
|
|
; CHECK-NEXT: br label %[[EXIT_SPLIT]]
|
|
|
|
latch:
|
|
%cmp2 = icmp slt i32 %tmp.c.sum, 42
|
|
br i1 %cmp2, label %header, label %exit
|
|
|
|
exit:
|
|
%lcssa.phi = phi i32 [ %tmp.c.sum, %latch ]
|
|
ret i32 %lcssa.phi
|
|
; CHECK: [[EXIT_SPLIT]]:
|
|
; CHECK-NEXT: %[[EXIT_PHI1:.*]] = phi i32 [ %[[LCSSA_C]], %[[LOOP_EXIT_C]] ], [ %[[LCSSA_B]], %[[LOOP_EXIT_B]] ]
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: %[[EXIT_PHI2:.*]] = phi i32 [ %[[EXIT_PHI1]], %[[EXIT_SPLIT]] ], [ %[[LCSSA_A]], %[[LOOP_EXIT_A]] ]
|
|
; CHECK-NEXT: ret i32 %[[EXIT_PHI2]]
|
|
}
|
|
|
|
; Similar to @test28 but designed to have one of the duplicate edges be
|
|
; a loop exit edge as those can in some cases be special. Among other things,
|
|
; this includes an LCSSA phi with multiple entries despite being a dedicated
|
|
; exit block.
|
|
define i32 @test29(i32 %arg) {
|
|
; CHECK-LABEL: define i32 @test29(
|
|
entry:
|
|
br label %header
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 %arg, label %[[ENTRY_SPLIT_EXIT:.*]] [
|
|
; CHECK-NEXT: i32 -1, label %[[ENTRY_SPLIT_EXIT]]
|
|
; CHECK-NEXT: i32 0, label %[[ENTRY_SPLIT_A:.*]]
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_B:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[ENTRY_SPLIT_B]]
|
|
; CHECK-NEXT: ]
|
|
|
|
header:
|
|
%tmp = call i32 @d()
|
|
%cmp1 = icmp eq i32 %tmp, 0
|
|
br i1 %cmp1, label %body.a, label %dispatch
|
|
|
|
dispatch:
|
|
switch i32 %arg, label %loop.exit1 [
|
|
i32 -1, label %loop.exit1
|
|
i32 0, label %body.a
|
|
i32 1, label %body.b
|
|
i32 2, label %body.b
|
|
]
|
|
|
|
body.a:
|
|
%tmp.a.phi = phi i32 [ 0, %header ], [ %tmp, %dispatch ]
|
|
%tmp.a = call i32 @a()
|
|
%tmp.a.sum = add i32 %tmp.a.phi, %tmp.a
|
|
br label %body.b
|
|
; Unswitched 'a' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_A]]:
|
|
; CHECK-NEXT: br label %[[HEADER_A:.*]]
|
|
;
|
|
; CHECK: [[HEADER_A]]:
|
|
; CHECK-NEXT: %[[TMP_A:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_A:.*]] = icmp eq i32 %[[TMP_A]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_A]], label %[[BODY_A_A:.*]], label %[[DISPATCH_A:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_A]]:
|
|
; CHECK-NEXT: br label %[[BODY_A_A]]
|
|
;
|
|
; CHECK: [[BODY_A_A]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_A:.*]] = phi i32 [ 0, %[[HEADER_A]] ], [ %[[TMP_A]], %[[DISPATCH_A]] ]
|
|
; CHECK-NEXT: %[[TMP_A_A:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_A:.*]] = add i32 %[[TMP_A_PHI_A]], %[[TMP_A_A]]
|
|
; CHECK-NEXT: br label %[[BODY_B_A:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_A]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_A:.*]] = phi i32 [ %[[TMP_A_SUM_A]], %[[BODY_A_A]] ]
|
|
; CHECK-NEXT: %[[TMP_B_A:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_A:.*]] = add i32 %[[TMP_B_PHI_A]], %[[TMP_B_A]]
|
|
; CHECK-NEXT: br label %[[LATCH_A:.*]]
|
|
;
|
|
; CHECK: [[LATCH_A]]:
|
|
; CHECK-NEXT: %[[CMP2_A:.*]] = icmp slt i32 %[[TMP_B_SUM_A]], 42
|
|
; CHECK: br i1 %[[CMP2_A]], label %[[HEADER_A]], label %[[LOOP_EXIT_A:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_A]]:
|
|
; CHECK-NEXT: %[[LCSSA_A:.*]] = phi i32 [ %[[TMP_B_SUM_A]], %[[LATCH_A]] ]
|
|
; CHECK-NEXT: br label %loop.exit2
|
|
|
|
body.b:
|
|
%tmp.b.phi = phi i32 [ %tmp, %dispatch ], [ %tmp, %dispatch ], [ %tmp.a.sum, %body.a ]
|
|
%tmp.b = call i32 @b()
|
|
%tmp.b.sum = add i32 %tmp.b.phi, %tmp.b
|
|
br label %latch
|
|
; Unswitched 'b' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_B]]:
|
|
; CHECK-NEXT: br label %[[HEADER_B:.*]]
|
|
;
|
|
; CHECK: [[HEADER_B]]:
|
|
; CHECK-NEXT: %[[TMP_B:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_B:.*]] = icmp eq i32 %[[TMP_B]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_B]], label %[[BODY_A_B:.*]], label %[[DISPATCH_B:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_B]]:
|
|
; CHECK-NEXT: br label %[[BODY_B_B]]
|
|
;
|
|
; CHECK: [[BODY_A_B]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_B:.*]] = phi i32 [ 0, %[[HEADER_B]] ]
|
|
; CHECK-NEXT: %[[TMP_A_B:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_B:.*]] = add i32 %[[TMP_A_PHI_B]], %[[TMP_A_B]]
|
|
; CHECK-NEXT: br label %[[BODY_B_B:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_B]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_B:.*]] = phi i32 [ %[[TMP_B]], %[[DISPATCH_B]] ], [ %[[TMP_A_SUM_B]], %[[BODY_A_B]] ]
|
|
; CHECK-NEXT: %[[TMP_B_B:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_B:.*]] = add i32 %[[TMP_B_PHI_B]], %[[TMP_B_B]]
|
|
; CHECK-NEXT: br label %[[LATCH_B:.*]]
|
|
;
|
|
; CHECK: [[LATCH_B]]:
|
|
; CHECK-NEXT: %[[CMP2_B:.*]] = icmp slt i32 %[[TMP_B_SUM_B]], 42
|
|
; CHECK: br i1 %[[CMP2_B]], label %[[HEADER_B]], label %[[LOOP_EXIT_B:.*]]
|
|
;
|
|
; CHECK: [[LOOP_EXIT_B]]:
|
|
; CHECK-NEXT: %[[LCSSA_B:.*]] = phi i32 [ %[[TMP_B_SUM_B]], %[[LATCH_B]] ]
|
|
; CHECK-NEXT: br label %[[LOOP_EXIT2_SPLIT:.*]]
|
|
|
|
latch:
|
|
%cmp2 = icmp slt i32 %tmp.b.sum, 42
|
|
br i1 %cmp2, label %header, label %loop.exit2
|
|
|
|
loop.exit1:
|
|
%l1.phi = phi i32 [ %tmp, %dispatch ], [ %tmp, %dispatch ]
|
|
br label %exit
|
|
; Unswitched 'exit' loop.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_EXIT]]:
|
|
; CHECK-NEXT: br label %[[HEADER_EXIT:.*]]
|
|
;
|
|
; CHECK: [[HEADER_EXIT]]:
|
|
; CHECK-NEXT: %[[TMP_EXIT:.*]] = call i32 @d()
|
|
; CHECK-NEXT: %[[CMP1_EXIT:.*]] = icmp eq i32 %[[TMP_EXIT]], 0
|
|
; CHECK-NEXT: br i1 %[[CMP1_EXIT]], label %[[BODY_A_EXIT:.*]], label %[[DISPATCH_EXIT:.*]]
|
|
;
|
|
; CHECK: [[DISPATCH_EXIT]]:
|
|
; CHECK-NEXT: %[[TMP_LCSSA:.*]] = phi i32 [ %[[TMP_EXIT]], %[[HEADER_EXIT]] ]
|
|
; CHECK-NEXT: br label %loop.exit1
|
|
;
|
|
; CHECK: [[BODY_A_EXIT]]:
|
|
; CHECK-NEXT: %[[TMP_A_PHI_EXIT:.*]] = phi i32 [ 0, %[[HEADER_EXIT]] ]
|
|
; CHECK-NEXT: %[[TMP_A_EXIT:.*]] = call i32 @a()
|
|
; CHECK-NEXT: %[[TMP_A_SUM_EXIT:.*]] = add i32 %[[TMP_A_PHI_EXIT]], %[[TMP_A_EXIT]]
|
|
; CHECK-NEXT: br label %[[BODY_B_EXIT:.*]]
|
|
;
|
|
; CHECK: [[BODY_B_EXIT]]:
|
|
; CHECK-NEXT: %[[TMP_B_PHI_EXIT:.*]] = phi i32 [ %[[TMP_A_SUM_EXIT]], %[[BODY_A_EXIT]] ]
|
|
; CHECK-NEXT: %[[TMP_B_EXIT:.*]] = call i32 @b()
|
|
; CHECK-NEXT: %[[TMP_B_SUM_EXIT:.*]] = add i32 %[[TMP_B_PHI_EXIT]], %[[TMP_B_EXIT]]
|
|
; CHECK-NEXT: br label %[[LATCH_EXIT:.*]]
|
|
;
|
|
; CHECK: [[LATCH_EXIT]]:
|
|
; CHECK-NEXT: %[[CMP2_EXIT:.*]] = icmp slt i32 %[[TMP_B_SUM_EXIT]], 42
|
|
; CHECK: br i1 %[[CMP2_EXIT]], label %[[HEADER_EXIT]], label %[[LOOP_EXIT_EXIT:.*]]
|
|
;
|
|
; CHECK: loop.exit1:
|
|
; CHECK-NEXT: %[[L1_PHI:.*]] = phi i32 [ %[[TMP_LCSSA]], %[[DISPATCH_EXIT]] ]
|
|
; CHECK-NEXT: br label %exit
|
|
;
|
|
; CHECK: [[LOOP_EXIT_EXIT]]:
|
|
; CHECK-NEXT: %[[L2_PHI:.*]] = phi i32 [ %[[TMP_B_SUM_EXIT]], %[[LATCH_EXIT]] ]
|
|
; CHECK-NEXT: br label %[[LOOP_EXIT2_SPLIT]]
|
|
|
|
loop.exit2:
|
|
%l2.phi = phi i32 [ %tmp.b.sum, %latch ]
|
|
br label %exit
|
|
; CHECK: [[LOOP_EXIT2_SPLIT]]:
|
|
; CHECK-NEXT: %[[LOOP_EXIT_PHI1:.*]] = phi i32 [ %[[L2_PHI]], %[[LOOP_EXIT_EXIT]] ], [ %[[LCSSA_B]], %[[LOOP_EXIT_B]] ]
|
|
; CHECK-NEXT: br label %loop.exit2
|
|
;
|
|
; CHECK: loop.exit2:
|
|
; CHECK-NEXT: %[[LOOP_EXIT_PHI2:.*]] = phi i32 [ %[[LOOP_EXIT_PHI1]], %[[LOOP_EXIT2_SPLIT]] ], [ %[[LCSSA_A]], %[[LOOP_EXIT_A]] ]
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
exit:
|
|
%l.phi = phi i32 [ %l1.phi, %loop.exit1 ], [ %l2.phi, %loop.exit2 ]
|
|
ret i32 %l.phi
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: %[[EXIT_PHI:.*]] = phi i32 [ %[[L1_PHI]], %loop.exit1 ], [ %[[LOOP_EXIT_PHI2]], %loop.exit2 ]
|
|
; CHECK-NEXT: ret i32 %[[EXIT_PHI]]
|
|
}
|
|
|
|
; 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_HEADER_SPLIT_US:.*]], label %[[B_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[C_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[B_LATCH_SPLIT_US:.*]]
|
|
;
|
|
; CHECK: [[B_LATCH_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %b.latch
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT]]:
|
|
; CHECK-NEXT: br label %c.header
|
|
|
|
c.header:
|
|
call i32 @c()
|
|
br i1 %v1, label %b.latch, label %c.latch
|
|
; CHECK: c.header:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; 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_SPLIT:.*]]
|
|
|
|
b.latch:
|
|
%v3 = call i1 @cond()
|
|
br i1 %v3, label %b.header, label %a.latch
|
|
; CHECK: [[B_LATCH_SPLIT]]:
|
|
; CHECK-NEXT: br label %b.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
|
|
}
|
|
|
|
; 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_HEADER_SPLIT_US:.*]], label %[[B_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[C_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[B_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[B_LATCH_US]]:
|
|
; CHECK-NEXT: br label %b.latch
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT]]:
|
|
; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ]
|
|
; CHECK-NEXT: br label %c.header
|
|
|
|
c.header:
|
|
call i32 @c()
|
|
br i1 %v1, label %b.latch, label %c.latch
|
|
; CHECK: c.header:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; 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_HEADER_SPLIT_US:.*]], label %[[B_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[C_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[B_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[B_LATCH_US]]:
|
|
; CHECK-NEXT: br label %b.latch
|
|
;
|
|
; 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:
|
|
call i32 @c()
|
|
br i1 %v1, label %b.latch, label %c.latch
|
|
; CHECK: c.header:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; 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_HEADER_SPLIT_US:.*]], label %[[B_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[C_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[B_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[B_LATCH_US]]:
|
|
; CHECK-NEXT: br label %b.latch
|
|
;
|
|
; 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:
|
|
call i32 @c()
|
|
br i1 %v1, label %b.latch, label %c.body
|
|
; CHECK: c.header:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; 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_US:.*]], label %[[C_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[D_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[D_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @d()
|
|
; CHECK-NEXT: br label %[[C_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[C_LATCH_US]]:
|
|
; CHECK-NEXT: br label %c.latch
|
|
;
|
|
; CHECK: [[C_HEADER_SPLIT]]:
|
|
; CHECK-NEXT: br label %d.header
|
|
|
|
d.header:
|
|
call i32 @d()
|
|
br i1 %v1, label %c.latch, label %d.exiting1
|
|
; CHECK: d.header:
|
|
; CHECK-NEXT: call i32 @d()
|
|
; 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_HEADER_SPLIT_US:.*]], label %[[C_HEADER_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[D_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[D_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @d()
|
|
; CHECK-NEXT: br label %[[C_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[C_LATCH_US]]:
|
|
; CHECK-NEXT: br label %c.latch
|
|
;
|
|
; 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:
|
|
call i32 @d()
|
|
br i1 %v1, label %c.latch, label %d.latch
|
|
; CHECK: d.header:
|
|
; CHECK-NEXT: call i32 @d()
|
|
; 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
|
|
}
|
|
|
|
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_HEADER_SPLIT_US:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[B_HEADER_SPLIT_US]]
|
|
; CHECK-NEXT: i32 3, label %[[B_HEADER_SPLIT_US]]
|
|
; CHECK-NEXT: ]
|
|
;
|
|
; CHECK: [[B_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[C_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[C_HEADER_US]]:
|
|
; CHECK-NEXT: call i32 @c()
|
|
; CHECK-NEXT: br label %[[B_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[B_LATCH_US]]:
|
|
; CHECK-NEXT: br label %b.latch
|
|
;
|
|
; 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:
|
|
call i32 @c()
|
|
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: call i32 @c()
|
|
; 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
|
|
}
|
|
|
|
; A devilish pattern. This is a crafty, crafty test case designed to risk
|
|
; creating indirect cycles with trivial and non-trivial unswitching. The inner
|
|
; loop has a switch with a trivial exit edge that can be unswitched, but the
|
|
; rest of the switch cannot be unswitched because its cost is too high.
|
|
; However, the unswitching of the trivial edge creates a new switch in the
|
|
; outer loop. *This* switch isn't trivial, but has a low cost to unswitch. When
|
|
; we unswitch this switch from the outer loop, we will remove it completely and
|
|
; create a clone of the inner loop on one side. This clone will then again be
|
|
; viable for unswitching the inner-most loop. This lets us check that the
|
|
; unswitching doesn't end up cycling infinitely even when the cycle is
|
|
; indirect and due to revisiting a loop after cloning.
|
|
define void @test30(i32 %arg) {
|
|
; CHECK-LABEL: define void @test30(
|
|
entry:
|
|
br label %outer.header
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 %arg, label %[[ENTRY_SPLIT:.*]] [
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_US:.*]]
|
|
; CHECK-NEXT: i32 2, label %[[ENTRY_SPLIT_US]]
|
|
; CHECK-NEXT: ]
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_US]]:
|
|
; CHECK-NEXT: switch i32 %arg, label %[[ENTRY_SPLIT_US_SPLIT:.*]] [
|
|
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_US_SPLIT_US:.*]]
|
|
; CHECK-NEXT: ]
|
|
|
|
outer.header:
|
|
br label %inner.header
|
|
|
|
inner.header:
|
|
switch i32 %arg, label %inner.loopexit1 [
|
|
i32 1, label %inner.body1
|
|
i32 2, label %inner.body2
|
|
]
|
|
|
|
inner.body1:
|
|
%a = call i32 @a()
|
|
br label %inner.latch
|
|
; The (super convoluted) fully unswitched loop around `@a`.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_US_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_US_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_US_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_SPLIT_US_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_LATCH_US_US:.*]]:
|
|
; CHECK-NEXT: %[[OUTER_COND_US_US:.*]] = call i1 @cond()
|
|
; CHECK-NEXT: br i1 %[[OUTER_COND_US_US]], label %[[OUTER_HEADER_US_US]], label %[[EXIT_SPLIT_US_SPLIT_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_SPLIT_US_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_SPLIT_SPLIT_US_US_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_LOOPEXIT2_US_US:.*]]:
|
|
; CHECK-NEXT: br label %[[OUTER_LATCH_US_US]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_SPLIT_SPLIT_US_US_US]]:
|
|
; CHECK-NEXT: br label %[[INNER_HEADER_US_US_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_HEADER_US_US_US]]:
|
|
; CHECK-NEXT: br label %[[INNER_BODY1_US_US_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_BODY1_US_US_US]]:
|
|
; CHECK-NEXT: %[[A:.*]] = call i32 @a()
|
|
; CHECK-NEXT: br label %[[INNER_LATCH_US_US_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_LATCH_US_US_US]]:
|
|
; CHECK-NEXT: %[[PHI_A:.*]] = phi i32 [ %[[A]], %[[INNER_BODY1_US_US_US]] ]
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 %[[PHI_A]])
|
|
; CHECK-NEXT: %[[INNER_COND_US_US_US:.*]] = call i1 @cond()
|
|
; CHECK-NEXT: br i1 %[[INNER_COND_US_US_US]], label %[[INNER_HEADER_US_US_US]], label %[[INNER_LOOPEXIT2_SPLIT_US_US_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_LOOPEXIT2_SPLIT_US_US_US]]:
|
|
; CHECK-NEXT: br label %[[INNER_LOOPEXIT2_US_US]]
|
|
;
|
|
; CHECK: [[EXIT_SPLIT_US_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[EXIT_SPLIT_US:.*]]
|
|
|
|
|
|
inner.body2:
|
|
%b = call i32 @b()
|
|
br label %inner.latch
|
|
; The fully unswitched loop around `@b`.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT_US_SPLIT]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_SPLIT_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_HEADER_US:.*]]:
|
|
; CHECK-NEXT: br label %[[INNER_BODY2_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_BODY2_US]]:
|
|
; CHECK-NEXT: %[[B:.*]] = call i32 @b()
|
|
; CHECK-NEXT: br label %[[INNER_LATCH_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_LATCH_US]]:
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 0)
|
|
; CHECK-NEXT: call void @sink1(i32 %[[B]])
|
|
; CHECK-NEXT: %[[INNER_COND_US:.*]] = call i1 @cond()
|
|
; CHECK-NEXT: br i1 %[[INNER_COND_US]], label %[[INNER_HEADER_US]], label %[[INNER_LOOPEXIT2_SPLIT_US:.*]]
|
|
;
|
|
; CHECK: [[INNER_LOOPEXIT2_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[INNER_LOOPEXIT2_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_LATCH_US:.*]]:
|
|
; CHECK-NEXT: %[[OUTER_COND_US:.*]] = call i1 @cond()
|
|
; CHECK-NEXT: br i1 %[[OUTER_COND_US]], label %[[OUTER_HEADER_US]], label %[[EXIT_SPLIT_US_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER_SPLIT_SPLIT_US:.*]]
|
|
;
|
|
; CHECK: [[OUTER_HEADER_SPLIT_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %[[INNER_HEADER_US]]
|
|
;
|
|
; CHECK: [[INNER_LOOPEXIT2_US]]:
|
|
; CHECK-NEXT: br label %[[OUTER_LATCH_US]]
|
|
;
|
|
; CHECK: [[EXIT_SPLIT_US]]:
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
inner.latch:
|
|
%phi = phi i32 [ %a, %inner.body1 ], [ %b, %inner.body2 ]
|
|
; Make 10 junk calls here to ensure we're over the "50" cost threshold of
|
|
; non-trivial unswitching for this inner switch.
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 0)
|
|
call void @sink1(i32 %phi)
|
|
%inner.cond = call i1 @cond()
|
|
br i1 %inner.cond, label %inner.header, label %inner.loopexit2
|
|
|
|
inner.loopexit1:
|
|
br label %outer.latch
|
|
; The unswitched `loopexit1` path.
|
|
;
|
|
; CHECK: [[ENTRY_SPLIT]]:
|
|
; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
|
|
;
|
|
; CHECK: outer.header:
|
|
; CHECK-NEXT: br label %inner.loopexit1
|
|
;
|
|
; CHECK: inner.loopexit1:
|
|
; CHECK-NEXT: br label %outer.latch
|
|
;
|
|
; CHECK: outer.latch:
|
|
; CHECK-NEXT: %outer.cond = call i1 @cond()
|
|
; CHECK-NEXT: br i1 %outer.cond, label %outer.header, label %[[EXIT_SPLIT:.*]]
|
|
;
|
|
; CHECK: [[EXIT_SPLIT]]:
|
|
; CHECK-NEXT: br label %exit
|
|
|
|
inner.loopexit2:
|
|
br label %outer.latch
|
|
|
|
outer.latch:
|
|
%outer.cond = call i1 @cond()
|
|
br i1 %outer.cond, label %outer.header, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
}
|