| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt < %s -passes=loop-deletion -S | FileCheck %s |
| |
| @G = external global i32 |
| |
| define void @test_trivial() { |
| ; CHECK-LABEL: @test_trivial( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br label [[EXIT:%.*]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br i1 false, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| |
| define void @test_bottom_tested() { |
| ; CHECK-LABEL: @test_bottom_tested( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ] |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1 |
| ; CHECK-NEXT: br label [[EXIT:%.*]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.inc, %loop ] |
| store i32 0, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %be_taken = icmp ne i32 %iv.inc, 1 |
| br i1 %be_taken, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @test_early_exit() { |
| ; CHECK-LABEL: @test_early_exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ] |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1 |
| ; CHECK-NEXT: br i1 [[BE_TAKEN]], label [[LATCH:%.*]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: unreachable |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ] |
| store i32 0, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %be_taken = icmp ne i32 %iv.inc, 1 |
| br i1 %be_taken, label %latch, label %exit |
| latch: |
| br label %loop |
| |
| exit: |
| ret void |
| } |
| |
| define void @test_multi_exit1() { |
| ; CHECK-LABEL: @test_multi_exit1( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ] |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1 |
| ; CHECK-NEXT: br i1 [[BE_TAKEN]], label [[LATCH:%.*]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 1, ptr @G, align 4 |
| ; CHECK-NEXT: [[COND2:%.*]] = icmp ult i32 [[IV_INC]], 30 |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ] |
| store i32 0, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %be_taken = icmp ne i32 %iv.inc, 1 |
| br i1 %be_taken, label %latch, label %exit |
| latch: |
| store i32 1, ptr @G |
| %cond2 = icmp ult i32 %iv.inc, 30 |
| br i1 %cond2, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @test_multi_exit2() { |
| ; CHECK-LABEL: @test_multi_exit2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 true, label [[LATCH:%.*]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 1, ptr @G, align 4 |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br i1 true, label %latch, label %exit |
| latch: |
| store i32 1, ptr @G |
| br i1 false, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @test_multi_exit3(i1 %cond1) { |
| ; CHECK-LABEL: @test_multi_exit3( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ] |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 [[COND1:%.*]], label [[LATCH:%.*]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 1, ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1 |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ] |
| store i32 0, ptr @G |
| br i1 %cond1, label %latch, label %exit |
| latch: |
| store i32 1, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %be_taken = icmp ne i32 %iv.inc, 1 |
| br i1 %be_taken, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; Subtle - This is either zero btc, or infinite, thus, can't break |
| ; backedge |
| define void @test_multi_exit4(i1 %cond1, i1 %cond2) { |
| ; CHECK-LABEL: @test_multi_exit4( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 [[COND1:%.*]], label [[LATCH:%.*]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 1, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 [[COND2:%.*]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br i1 %cond1, label %latch, label %exit |
| latch: |
| store i32 1, ptr @G |
| br i1 %cond2, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; A simple case with multiple exit blocks |
| define void @test_multi_exit5() { |
| ; CHECK-LABEL: @test_multi_exit5( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 true, label [[LATCH:%.*]], label [[EXIT1:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 1, ptr @G, align 4 |
| ; CHECK-NEXT: br label [[EXIT2:%.*]] |
| ; CHECK: exit1: |
| ; CHECK-NEXT: ret void |
| ; CHECK: exit2: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br i1 true, label %latch, label %exit1 |
| latch: |
| store i32 1, ptr @G |
| br i1 false, label %loop, label %exit2 |
| |
| exit1: |
| ret void |
| exit2: |
| ret void |
| } |
| |
| declare i1 @unknown() |
| |
| ; We can't compute an exit count for the latch, but we know the upper |
| ; bound on the trip count is zero anyways. |
| define void @test_dead_latch1() { |
| ; CHECK-LABEL: @test_dead_latch1( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br i1 false, label [[LATCH:%.*]], label [[EXIT1:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[LATCHCOND:%.*]] = call i1 @unknown() |
| ; CHECK-NEXT: br label [[EXIT2:%.*]] |
| ; CHECK: exit1: |
| ; CHECK-NEXT: ret void |
| ; CHECK: exit2: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br i1 false, label %latch, label %exit1 |
| latch: |
| %latchcond = call i1 @unknown() |
| br i1 %latchcond, label %loop, label %exit2 |
| |
| exit1: |
| ret void |
| exit2: |
| ret void |
| } |
| |
| |
| define void @test_live_inner() { |
| ; CHECK-LABEL: @test_live_inner( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br label [[INNER:%.*]] |
| ; CHECK: inner: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[LOOP]] ], [ [[IV_INC:%.*]], [[INNER]] ] |
| ; CHECK-NEXT: store i32 [[IV]], ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[CND:%.*]] = icmp ult i32 [[IV_INC]], 200 |
| ; CHECK-NEXT: br i1 [[CND]], label [[INNER]], label [[LATCH:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: br label [[EXIT:%.*]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| store i32 0, ptr @G |
| br label %inner |
| |
| inner: |
| %iv = phi i32 [0, %loop], [%iv.inc, %inner] |
| store i32 %iv, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %cnd = icmp ult i32 %iv.inc, 200 |
| br i1 %cnd, label %inner, label %latch |
| |
| latch: |
| br i1 false, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @test_live_outer() { |
| ; CHECK-LABEL: @test_live_outer( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_INC:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: br label [[INNER:%.*]] |
| ; CHECK: inner: |
| ; CHECK-NEXT: store i32 0, ptr @G, align 4 |
| ; CHECK-NEXT: br label [[LATCH]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store i32 [[IV]], ptr @G, align 4 |
| ; CHECK-NEXT: [[IV_INC]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[CND:%.*]] = icmp ult i32 [[IV_INC]], 200 |
| ; CHECK-NEXT: br i1 [[CND]], label [[LOOP]], label [[EXIT:%.*]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [0, %entry], [%iv.inc, %latch] |
| br label %inner |
| |
| inner: |
| store i32 0, ptr @G |
| br i1 false, label %inner, label %latch |
| |
| latch: |
| store i32 %iv, ptr @G |
| %iv.inc = add i32 %iv, 1 |
| %cnd = icmp ult i32 %iv.inc, 200 |
| br i1 %cnd, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; Key point is that inner_latch drops out of the outer loop when |
| ; the inner loop is deleted, and thus the lcssa phi needs to be |
| ; in the inner_latch block to preserve LCSSA. We either have to |
| ; insert the LCSSA phi, or not break the inner backedge. |
| define void @loop_nest_lcssa() { |
| ; CHECK-LABEL: @loop_nest_lcssa( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[TMP0:%.*]] = add i32 1, 2 |
| ; CHECK-NEXT: br label [[OUTER_HEADER:%.*]] |
| ; CHECK: outer_header: |
| ; CHECK-NEXT: br label [[INNER_HEADER:%.*]] |
| ; CHECK: inner_header: |
| ; CHECK-NEXT: br i1 false, label [[INNER_LATCH:%.*]], label [[OUTER_LATCH:%.*]] |
| ; CHECK: inner_latch: |
| ; CHECK-NEXT: [[DOTLCSSA:%.*]] = phi i32 [ [[TMP0]], [[INNER_HEADER]] ] |
| ; CHECK-NEXT: br label [[LOOPEXIT:%.*]] |
| ; CHECK: outer_latch: |
| ; CHECK-NEXT: br label [[OUTER_HEADER]] |
| ; CHECK: loopexit: |
| ; CHECK-NEXT: [[DOTLCSSA32:%.*]] = phi i32 [ [[DOTLCSSA]], [[INNER_LATCH]] ] |
| ; CHECK-NEXT: unreachable |
| ; |
| entry: |
| br label %outer_header |
| |
| outer_header: |
| %0 = add i32 1, 2 |
| br label %inner_header |
| |
| inner_header: |
| br i1 false, label %inner_latch, label %outer_latch |
| |
| inner_latch: |
| br i1 false, label %inner_header, label %loopexit |
| |
| outer_latch: |
| br label %outer_header |
| |
| loopexit: |
| %.lcssa32 = phi i32 [ %0, %inner_latch ] |
| unreachable |
| } |