| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py | |
| ; RUN: opt < %s -passes=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]], ptr @A, align 4 | |
| ; 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, ptr @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, ptr @A, align 4 | |
| ; 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]], ptr @A, align 4 | |
| ; 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, ptr @A | |
| %earlycnd = icmp ne i32 %vol, 0 | |
| br i1 %earlycnd, label %latch, label %exit | |
| latch: | |
| %iv.next = add i32 %iv, 1 | |
| store i32 %iv, ptr @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]], ptr @A, align 4 | |
| ; 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]], ptr @A, align 4 | |
| ; 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, ptr @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, ptr @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: [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[M:%.*]], i32 [[N:%.*]]) | |
| ; CHECK-NEXT: br label [[LOOP:%.*]] | |
| ; CHECK: loop: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LATCH:%.*]] ] | |
| ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV]], [[UMIN]] | |
| ; 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]], ptr @A, align 4 | |
| ; 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 | |
| %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, ptr @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]], ptr @A, align 4 | |
| ; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, ptr @A, align 4 | |
| ; 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, ptr @A | |
| %vol = load volatile i32, ptr @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]], ptr @A, align 4 | |
| ; 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, ptr @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]], ptr @A, align 4 | |
| ; 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]], ptr @A, align 4 | |
| ; 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, ptr @A | |
| %earlycnd2 = icmp ult i32 %iv, %m | |
| br i1 %earlycnd2, label %latch, label %exit | |
| latch: | |
| store volatile i32 %iv, ptr @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]], ptr @A, align 4 | |
| ; 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, ptr @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]], ptr @A, align 4 | |
| ; 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, ptr @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]], ptr @A, align 4 | |
| ; 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, ptr @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, ptr @A, align 4 | |
| ; 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]], ptr @A, align 4 | |
| ; 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, ptr @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, ptr @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]], ptr @A, align 4 | |
| ; 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]], ptr @A, align 4 | |
| ; 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, ptr @A | |
| %iv1.next = add i32 %iv1, 1 | |
| br label %inner | |
| inner: | |
| %iv2 = phi i32 [ 0, %outer ], [ %iv2.next, %inner_latch ] | |
| store volatile i32 %iv2, ptr @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 | |
| } |