mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
eb30ce19c4
These intrinsics, not the icmp+select are the canonical form nowadays, so we might as well directly emit them. This should not cause any regressions, but if it does, then then they would needed to be fixed regardless. Note that this doesn't deal with `SCEVExpander::isHighCostExpansion()`, but that is a pessimization, not a correctness issue. Additionally, the non-intrinsic form has issues with undef, see https://reviews.llvm.org/D88287#2587863
493 lines
16 KiB
LLVM
493 lines
16 KiB
LLVM
; 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, 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, 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, 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]], i32* @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, 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, 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]], i32* @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, 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: [[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]], i32* @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, 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, align 4
|
|
; CHECK-NEXT: [[VOL:%.*]] = load volatile i32, i32* @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, 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, 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, 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, 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]], i32* @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, 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, 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, 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, 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, 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, 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, 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, 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]], i32* @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, 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, 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]], i32* @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, 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
|
|
}
|