| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt < %s -indvars -S | FileCheck %s |
| ; This is a collection of tests specifically for LFTR of multiple exit loops. |
| ; The actual LFTR performed is trivial so as to focus on the loop structure |
| ; aspects. |
| |
| ; Provide legal integer types. |
| target datalayout = "n8:16:32:64" |
| |
| @A = external global i32 |
| |
| define void @analyzeable_early_exit(i32 %n) { |
| ; CHECK-LABEL: @analyzeable_early_exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV_NEXT]], 1000 |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store i32 %iv, i32* @A |
| %c = icmp ult i32 %iv.next, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @unanalyzeable_early_exit() { |
| ; CHECK-LABEL: @unanalyzeable_early_exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, i32* @A |
| ; CHECK-NEXT: [[EARLYCND:%.*]] = icmp ne i32 [[VOL]], 0 |
| ; CHECK-NEXT: br i1 [[EARLYCND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], 1000 |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %vol = load volatile i32, i32* @A |
| %earlycnd = icmp ne i32 %vol, 0 |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store i32 %iv, i32* @A |
| %c = icmp ult i32 %iv.next, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| |
| define void @multiple_early_exits(i32 %n, i32 %m) { |
| ; CHECK-LABEL: @multiple_early_exits( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[CONTINUE:%.*]], label [[EXIT:%.*]] |
| ; CHECK: continue: |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV]], [[M:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LATCH]], label [[EXIT]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND2:%.*]] = icmp ne i32 [[IV_NEXT]], 1000 |
| ; CHECK-NEXT: br i1 [[EXITCOND2]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %continue, label %exit |
| |
| continue: |
| store volatile i32 %iv, i32* @A |
| %earlycnd2 = icmp ult i32 %iv, %m |
| br i1 %earlycnd2, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ult i32 %iv.next, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; Note: This slightly odd form is what indvars itself produces for multiple |
| ; exits without a side effect between them. |
| define void @compound_early_exit(i32 %n, i32 %m) { |
| ; CHECK-LABEL: @compound_early_exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EARLYCND:%.*]] = icmp ult i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: [[EARLYCND2:%.*]] = icmp ult i32 [[IV]], [[M:%.*]] |
| ; CHECK-NEXT: [[AND:%.*]] = and i1 [[EARLYCND]], [[EARLYCND2]] |
| ; CHECK-NEXT: br i1 [[AND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], 1000 |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| %earlycnd2 = icmp ult i32 %iv, %m |
| %and = and i1 %earlycnd, %earlycnd2 |
| br i1 %and, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ult i32 %iv.next, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| |
| define void @unanalyzeable_latch(i32 %n) { |
| ; CHECK-LABEL: @unanalyzeable_latch( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: store i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, i32* @A |
| ; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[VOL]], 1000 |
| ; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store i32 %iv, i32* @A |
| %vol = load volatile i32, i32* @A |
| %c = icmp ult i32 %vol, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| define void @single_exit_no_latch(i32 %n) { |
| ; CHECK-LABEL: @single_exit_no_latch( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: store i32 [[IV]], i32* @A |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| store i32 %iv, i32* @A |
| br label %loop |
| |
| exit: |
| ret void |
| } |
| |
| ; Multiple exits which could be LFTRed, but the latch itself is not an |
| ; exiting block. |
| define void @no_latch_exit(i32 %n, i32 %m) { |
| ; CHECK-LABEL: @no_latch_exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[CONTINUE:%.*]], label [[EXIT:%.*]] |
| ; CHECK: continue: |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV]], [[M:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LATCH]], label [[EXIT]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %continue, label %exit |
| |
| continue: |
| store volatile i32 %iv, i32* @A |
| %earlycnd2 = icmp ult i32 %iv, %m |
| br i1 %earlycnd2, label %latch, label %exit |
| |
| latch: |
| store volatile i32 %iv, i32* @A |
| %iv.next = add i32 %iv, 1 |
| br label %loop |
| |
| exit: |
| ret void |
| } |
| |
| ;; Show the value of multiple exit LFTR (being able to eliminate all but |
| ;; one IV when exit tests involve multiple IVs). |
| define void @combine_ivs(i32 %n) { |
| ; CHECK-LABEL: @combine_ivs( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV_NEXT]], 999 |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %iv2 = phi i32 [ 1, %entry], [ %iv2.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| %iv2.next = add i32 %iv2, 1 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ult i32 %iv2.next, 1000 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; We can remove the decrementing IV entirely |
| define void @combine_ivs2(i32 %n) { |
| ; CHECK-LABEL: @combine_ivs2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV_NEXT]], 1000 |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %iv2 = phi i32 [ 1000, %entry], [ %iv2.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| %iv2.next = sub i32 %iv2, 1 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ugt i32 %iv2.next, 0 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; An example where we can eliminate an f(i) computation entirely |
| ; from a multiple exit loop with LFTR. |
| define void @simplify_exit_test(i32 %n) { |
| ; CHECK-LABEL: @simplify_exit_test( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[N:%.*]] |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[EXITCOND1:%.*]] = icmp ne i32 [[IV_NEXT]], 65 |
| ; CHECK-NEXT: br i1 [[EXITCOND1]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %earlycnd = icmp ult i32 %iv, %n |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| %fx = shl i32 %iv, 4 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ult i32 %fx, 1024 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| |
| ; Another example where we can remove an f(i) type computation, but this |
| ; time in a loop w/o a statically computable exit count. |
| define void @simplify_exit_test2(i32 %n) { |
| ; CHECK-LABEL: @simplify_exit_test2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] |
| ; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, i32* @A |
| ; CHECK-NEXT: [[EARLYCND:%.*]] = icmp ne i32 [[VOL]], 0 |
| ; CHECK-NEXT: br i1 [[EARLYCND]], label [[LATCH]], label [[EXIT:%.*]] |
| ; CHECK: latch: |
| ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 |
| ; CHECK-NEXT: [[FX:%.*]] = udiv i32 [[IV]], 4 |
| ; CHECK-NEXT: store volatile i32 [[IV]], i32* @A |
| ; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[FX]], 1024 |
| ; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %iv = phi i32 [ 0, %entry], [ %iv.next, %latch] |
| %vol = load volatile i32, i32* @A |
| %earlycnd = icmp ne i32 %vol, 0 |
| br i1 %earlycnd, label %latch, label %exit |
| |
| latch: |
| %iv.next = add i32 %iv, 1 |
| %fx = udiv i32 %iv, 4 |
| store volatile i32 %iv, i32* @A |
| %c = icmp ult i32 %fx, 1024 |
| br i1 %c, label %loop, label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; Demonstrate a case where two nested loops share a single exiting block. |
| ; The key point is that the exit count is *different* for the two loops, and |
| ; thus we can't rewrite the exit for the outer one. There are three sub-cases |
| ; which can happen here: a) the outer loop has a backedge taken count of zero |
| ; (for the case where we know the inner exit is known taken), b) the exit is |
| ; known never taken (but may have an exit count outside the range of the IV) |
| ; or c) the outer loop has an unanalyzable exit count (where we can't tell). |
| define void @nested(i32 %n) { |
| ; CHECK-LABEL: @nested( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], 1 |
| ; CHECK-NEXT: br label [[OUTER:%.*]] |
| ; CHECK: outer: |
| ; CHECK-NEXT: [[IV1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV1_NEXT:%.*]], [[OUTER_LATCH:%.*]] ] |
| ; CHECK-NEXT: store volatile i32 [[IV1]], i32* @A |
| ; CHECK-NEXT: [[IV1_NEXT]] = add nuw nsw i32 [[IV1]], 1 |
| ; CHECK-NEXT: br label [[INNER:%.*]] |
| ; CHECK: inner: |
| ; CHECK-NEXT: [[IV2:%.*]] = phi i32 [ 0, [[OUTER]] ], [ [[IV2_NEXT:%.*]], [[INNER_LATCH:%.*]] ] |
| ; CHECK-NEXT: store volatile i32 [[IV2]], i32* @A |
| ; CHECK-NEXT: [[IV2_NEXT]] = add nuw nsw i32 [[IV2]], 1 |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV2]], 20 |
| ; CHECK-NEXT: br i1 [[EXITCOND]], label [[INNER_LATCH]], label [[EXIT_LOOPEXIT:%.*]] |
| ; CHECK: inner_latch: |
| ; CHECK-NEXT: [[EXITCOND2:%.*]] = icmp ne i32 [[IV2_NEXT]], [[TMP0]] |
| ; CHECK-NEXT: br i1 [[EXITCOND2]], label [[INNER]], label [[OUTER_LATCH]] |
| ; CHECK: outer_latch: |
| ; CHECK-NEXT: [[EXITCOND3:%.*]] = icmp ne i32 [[IV1_NEXT]], 21 |
| ; CHECK-NEXT: br i1 [[EXITCOND3]], label [[OUTER]], label [[EXIT_LOOPEXIT1:%.*]] |
| ; CHECK: exit.loopexit: |
| ; CHECK-NEXT: br label [[EXIT:%.*]] |
| ; CHECK: exit.loopexit1: |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %outer |
| |
| outer: |
| %iv1 = phi i32 [ 0, %entry ], [ %iv1.next, %outer_latch ] |
| store volatile i32 %iv1, i32* @A |
| %iv1.next = add i32 %iv1, 1 |
| br label %inner |
| |
| inner: |
| %iv2 = phi i32 [ 0, %outer ], [ %iv2.next, %inner_latch ] |
| store volatile i32 %iv2, i32* @A |
| %iv2.next = add i32 %iv2, 1 |
| %innertest = icmp ult i32 %iv2, 20 |
| br i1 %innertest, label %inner_latch, label %exit |
| |
| inner_latch: |
| %innertestb = icmp ult i32 %iv2, %n |
| br i1 %innertestb, label %inner, label %outer_latch |
| |
| outer_latch: |
| %outertest = icmp ult i32 %iv1, 20 |
| br i1 %outertest, label %outer, label %exit |
| |
| exit: |
| ret void |
| } |