| ; 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 void @a() |
| declare void @b() |
| declare void @c() |
| declare void @d() |
| |
| declare void @sink1(i32) |
| declare void @sink2(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 void @a() convergent |
| br label %loop_latch |
| |
| loop_b: |
| call void @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 void @a() noduplicate |
| br label %loop_latch |
| |
| loop_b: |
| call void @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 void @a() |
| br label %loop_cont |
| |
| loop_b: |
| call void @b() |
| br label %loop_cont |
| |
| loop_cont: |
| invoke void @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 void @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 void @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 void @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 void @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 void @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 void @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 '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_exit.split |
| ; |
| ; CHECK: loop_exit.split: |
| ; CHECK-NEXT: %[[A_LCSSA:.*]] = phi i32 [ %[[A]], %loop_begin ] |
| ; 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_LCSSA]], %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: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit1.split.us ] |
| ; 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: %[[B_PHI:.*]] = phi i32 [ %[[B]], %loop_b ] |
| ; CHECK-NEXT: call void @sink2(i32 %[[B_PHI]]) |
| ; 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: %[[B_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_exit2.split.us ] |
| ; CHECK-NEXT: call void @sink2(i32 %[[B_PHI]]) |
| ; 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: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit1.split.us ] |
| ; CHECK-NEXT: call void @sink1(i32 %[[A_PHI]]) |
| ; 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_PHI:.*]] = phi i32 [ %[[B]], %loop_b ] |
| ; CHECK-NEXT: call void @sink2(i32 %[[B_PHI]]) |
| ; 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_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: %[[A_PHI:.*]] = phi i32 [ %[[A_LCSSA]], %loop_exit.loopexit.split.us ] |
| ; CHECK-NEXT: br label %loop_exit |
| ; |
| ; CHECK: loop_exit.loopexit1: |
| ; CHECK-NEXT: %[[B_PHI:.*]] = phi i32 [ %[[B_LCSSA]], %loop_latch ] |
| ; CHECK-NEXT: br label %loop_exit |
| ; |
| ; CHECK: loop_exit: |
| ; CHECK-NEXT: %[[AB_PHI:.*]] = phi i32 [ %[[A_PHI]], %loop_exit.loopexit ], [ %[[B_PHI]], %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: %[[B_PHI:.*]] = phi i32 [ %[[B_INNER_LCSSA]], %loop_b_inner_exit ] |
| ; CHECK-NEXT: br i1 %[[V]], label %loop_begin, label %loop_exit.split |
| ; |
| ; CHECK: loop_exit.split: |
| ; CHECK-NEXT: %[[B_LCSSA:.*]] = phi i32 [ %[[B_PHI]], %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: %[[A_INNER_LCSSA:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %inner_loop_exit.split.us ] |
| ; 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]] |
| } |
| |
| ; 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 %[[AB_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 %[[AB_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: %[[A_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %loop_exit.loopexit.split.us ] |
| ; 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_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: %[[A_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_LCSSA_US]], %inner_loop_exit.split.us ] |
| ; 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_PHI]], %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: %[[A_INNER_LCSSA_US:.*]] = phi i32 [ %[[A_INNER_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_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_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: %[[A_INNER_INNER_PHI:.*]] = phi i32 [ %[[A_INNER_INNER_LCSSA_US]], %inner_inner_loop_exit.split.us ] |
| ; 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_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_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: br label %loop_begin |
| |
| loop_begin: |
| %var_val = load i32, i32* %var |
| switch i32 %cond2, label %loop_a [ |
| i32 0, label %loop_b |
| i32 1, label %loop_b |
| i32 13, label %loop_c |
| i32 2, label %loop_b |
| i32 42, label %loop_exit |
| ] |
| ; CHECK: loop_begin: |
| ; CHECK-NEXT: %[[V:.*]] = load i32, i32* %var |
| ; CHECK-NEXT: switch i32 %cond2, label %loop_a [ |
| ; CHECK-NEXT: i32 0, label %loop_b |
| ; CHECK-NEXT: i32 1, label %loop_b |
| ; CHECK-NEXT: i32 13, label %loop_c |
| ; CHECK-NEXT: i32 2, label %loop_b |
| ; CHECK-NEXT: i32 42, label %loop_exit |
| ; CHECK-NEXT: ] |
| |
| loop_a: |
| call void @a() |
| br label %loop_latch |
| ; CHECK: loop_a: |
| ; CHECK-NEXT: call void @a() |
| ; CHECK-NEXT: br label %loop_latch |
| |
| loop_b: |
| call void @b() |
| br label %loop_latch |
| ; CHECK: loop_b: |
| ; CHECK-NEXT: call void @b() |
| ; CHECK-NEXT: br label %loop_latch |
| |
| loop_c: |
| call void @c() noreturn nounwind |
| br label %loop_latch |
| ; CHECK: loop_c: |
| ; CHECK-NEXT: call void @c() |
| ; CHECK-NEXT: br label %loop_latch |
| |
| loop_latch: |
| br label %loop_begin |
| ; CHECK: loop_latch: |
| ; CHECK-NEXT: br label %loop_begin |
| |
| loop_exit: |
| %lcssa = phi i32 [ %var_val, %loop_begin ] |
| ret i32 %lcssa |
| ; CHECK: loop_exit: |
| ; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %[[V]], %loop_begin ] |
| ; CHECK-NEXT: ret i32 %[[LCSSA]] |
| } |