; 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: [[TMP0:%.*]] = icmp ult i32 [[M:%.*]], [[N:%.*]] ; CHECK-NEXT: [[UMIN:%.*]] = select i1 [[TMP0]], 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]], 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 %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 }