[llvm-branch-commits] [llvm] [SCEV] Prove monotonic predicates from loop entry in isKnownViaInduction (PR #231697)
Dmitry Makogon via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 13 16:51:43 PDT 2026
https://github.com/d-makogon updated https://github.com/llvm/llvm-project/pull/231697
>From 0ab3f168a1595f9d90382154aa6fd7bb29228bdf Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Fri, 9 Oct 2026 17:10:50 +0000
Subject: [PATCH 1/5] [IndVarSimplify][NFC] Add a negative test for zext
elimination
Add @foo3_neg: the loop from @foo3 with a guard on an unrelated value,
so %add3 may be negative and its zext must stay.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
.../IndVarSimplify/iv-widen-elim-ext.ll | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
index a83e9ce74b12a..e1965b6b330c6 100644
--- a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
+++ b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
@@ -274,6 +274,77 @@ for.end: ; preds = %for.cond.for.end_cr
ret i32 0
}
+; A case where zext should not be eliminated when its operands could only be
+; extended by sext: same as @foo3, but the loop is guarded by %N, so %M and
+; %add3 may be negative.
+define i32 @foo3_neg(i32 %M, i32 %N) {
+; CHECK-LABEL: @foo3_neg(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 0, [[N:%.*]]
+; CHECK-NEXT: br i1 [[CMP1]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_END:%.*]]
+; CHECK: for.body.lr.ph:
+; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[M:%.*]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[M]], i32 1)
+; CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SMAX]] to i64
+; CHECK-NEXT: br label [[FOR_BODY:%.*]]
+; CHECK: for.body:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ], [ 0, [[FOR_BODY_LR_PH]] ]
+; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [100 x i32], ptr @b, i64 0, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[INDVARS_IV]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = trunc nsw i64 [[TMP3]] to i32
+; CHECK-NEXT: [[IDXPROM4:%.*]] = zext i32 [[TMP4]] to i64
+; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[IDXPROM4]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX5]], align 4
+; CHECK-NEXT: br label [[FOR_INC]]
+; CHECK: for.inc:
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]]
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_BODY]], label [[FOR_COND_FOR_END_CRIT_EDGE:%.*]]
+; CHECK: for.cond.for.end_crit_edge:
+; CHECK-NEXT: br label [[FOR_END]]
+; CHECK: for.end:
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @dummy(ptr @a, ptr @b)
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp1 = icmp slt i32 0, %N
+ br i1 %cmp1, label %for.body.lr.ph, label %for.end
+
+for.body.lr.ph: ; preds = %entry
+ br label %for.body
+
+for.body: ; preds = %for.body.lr.ph, %for.inc
+ %i.02 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.inc ]
+ %idxprom = sext i32 %i.02 to i64
+ %arrayidx = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 %idxprom
+ %0 = load i32, ptr %arrayidx, align 4
+ %idxprom1 = sext i32 %i.02 to i64
+ %arrayidx2 = getelementptr inbounds [100 x i32], ptr @b, i64 0, i64 %idxprom1
+ %1 = load i32, ptr %arrayidx2, align 4
+ %add = add nsw i32 %0, %1
+ %add3 = add nsw i32 %i.02, %M
+ %idxprom4 = zext i32 %add3 to i64
+ %arrayidx5 = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 %idxprom4
+ store i32 %add, ptr %arrayidx5, align 4
+ br label %for.inc
+
+for.inc: ; preds = %for.body
+ %inc = add nsw i32 %i.02, 1
+ %cmp = icmp slt i32 %inc, %M
+ br i1 %cmp, label %for.body, label %for.cond.for.end_crit_edge
+
+for.cond.for.end_crit_edge: ; preds = %for.inc
+ br label %for.end
+
+for.end: ; preds = %for.cond.for.end_crit_edge, %entry
+ %call = call i32 @dummy(ptr @a, ptr @b)
+ ret i32 0
+}
+
%struct.image = type {i32, i32}
define i32 @foo4(ptr %input, i32 %length, ptr %in) {
; CHECK-LABEL: @foo4(
>From 49666ef7bda3865ad7250f7b18c0933f097f64cb Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Fri, 9 Oct 2026 17:11:22 +0000
Subject: [PATCH 2/5] [IndVarSimplify][PhaseOrdering][NFC] Add tests for
widening nsw IV arithmetic
Add tests for for (int i = start; i < N; i++) a[i] = a[i - 32] + 1,
with zext nneg indices and i - 32 nsw only:
- IndVarSimplify: with guards that do and do not prove start
non-negative, i - 32 is computed in i64 or i32.
- PhaseOrdering (indvars, loop-vectorize): whether a runtime alias check
is emitted.
Move @runtime_start out of ult-nonneg-bound-implies-nonneg.ll, which
duplicated it.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
.../ult-nonneg-bound-implies-nonneg.ll | 67 --
.../widen-zext-nneg-add-nsw-runtime-start.ll | 709 ++++++++++++++++++
.../indvars-vectorize-runtime-start.ll | 125 +++
3 files changed, 834 insertions(+), 67 deletions(-)
create mode 100644 llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
create mode 100644 llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
diff --git a/llvm/test/Transforms/IndVarSimplify/ult-nonneg-bound-implies-nonneg.ll b/llvm/test/Transforms/IndVarSimplify/ult-nonneg-bound-implies-nonneg.ll
index b2a04db953e23..4544088021bf0 100644
--- a/llvm/test/Transforms/IndVarSimplify/ult-nonneg-bound-implies-nonneg.ll
+++ b/llvm/test/Transforms/IndVarSimplify/ult-nonneg-bound-implies-nonneg.ll
@@ -253,73 +253,6 @@ exit:
ret void
}
-; A loop from the following source code:
-; for (int i = start; i < 10000; i++) a[i] = a[i - 32] + 1;
-; FIXME: i - 32 should be widened to "add nsw i64 %indvars.iv, -32".
-define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
-; CHECK-LABEL: define void @runtime_start(
-; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
-; CHECK-NEXT: [[LEN_OK:%.*]] = icmp ugt i32 [[LEN]], 9999
-; CHECK-NEXT: [[START_OK:%.*]] = icmp ult i32 [[START]], [[LEN]]
-; CHECK-NEXT: [[START_M32:%.*]] = add i32 [[START]], -32
-; CHECK-NEXT: [[START_M32_OK:%.*]] = icmp ult i32 [[START_M32]], [[LEN]]
-; CHECK-NEXT: [[C0:%.*]] = and i1 [[LEN_OK]], [[START_OK]]
-; CHECK-NEXT: [[C1:%.*]] = and i1 [[C0]], [[START_M32_OK]]
-; CHECK-NEXT: [[START_LT:%.*]] = icmp slt i32 [[START]], 10000
-; CHECK-NEXT: [[C:%.*]] = and i1 [[C1]], [[START_LT]]
-; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK: [[LOOP_PREHEADER]]:
-; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
-; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
-; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
-; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
-; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
-; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
-; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
-; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
-; CHECK: [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT: br label %[[EXIT]]
-; CHECK: [[EXIT]]:
-; CHECK-NEXT: ret void
-;
-entry:
- %len = load i32, ptr %len.p, align 4, !range !0
- %len.ok = icmp ugt i32 %len, 9999
- %start.ok = icmp ult i32 %start, %len
- %start.m32 = add i32 %start, -32
- %start.m32.ok = icmp ult i32 %start.m32, %len
- %c0 = and i1 %len.ok, %start.ok
- %c1 = and i1 %c0, %start.m32.ok
- %start.lt = icmp slt i32 %start, 10000
- %c = and i1 %c1, %start.lt
- br i1 %c, label %loop, label %exit
-loop:
- %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
- %idx = add nsw i32 %iv, -32
- %idx.ext = zext nneg i32 %idx to i64
- %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
- %v = load i8, ptr %src, align 1
- %v1 = add i8 %v, 1
- %iv.ext = zext nneg i32 %iv to i64
- %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
- store i8 %v1, ptr %dst, align 1
- %iv.next = add nuw nsw i32 %iv, 1
- %done = icmp sgt i32 %iv, 9998
- br i1 %done, label %exit, label %loop
-exit:
- ret void
-}
-
declare void @side()
; [0, INT_MAX)
diff --git a/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll b/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
new file mode 100644
index 0000000000000..bbb78084c1c0a
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
@@ -0,0 +1,709 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=indvars -S %s | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
+
+; for (int i = start; i < 10000; i++) a[i] = a[i - 32] + 1;
+; Array indices are zext nneg, and i - 32 is nsw but not nuw.
+;
+; Where start is known non-negative on loop entry, i - 32 is computed in i64.
+; Where start may be negative, i - 32 is computed in i32.
+
+; ---- start is known non-negative: i - 32 should be widened ----
+; FIXME: Only @const_start is computed in i64.
+
+; Compound guard: start and start - 32 are both <u len, len is an array length.
+define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @runtime_start(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT: [[LEN_OK:%.*]] = icmp ugt i32 [[LEN]], 9999
+; CHECK-NEXT: [[START_OK:%.*]] = icmp ult i32 [[START]], [[LEN]]
+; CHECK-NEXT: [[START_M32:%.*]] = add i32 [[START]], -32
+; CHECK-NEXT: [[START_M32_OK:%.*]] = icmp ult i32 [[START_M32]], [[LEN]]
+; CHECK-NEXT: [[C0:%.*]] = and i1 [[LEN_OK]], [[START_OK]]
+; CHECK-NEXT: [[C1:%.*]] = and i1 [[C0]], [[START_M32_OK]]
+; CHECK-NEXT: [[START_LT:%.*]] = icmp slt i32 [[START]], 10000
+; CHECK-NEXT: [[C:%.*]] = and i1 [[C1]], [[START_LT]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP3]], -32
+; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP1]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %len.ok = icmp ugt i32 %len, 9999
+ %start.ok = icmp ult i32 %start, %len
+ %start.m32 = add i32 %start, -32
+ %start.m32.ok = icmp ult i32 %start.m32, %len
+ %c0 = and i1 %len.ok, %start.ok
+ %c1 = and i1 %c0, %start.m32.ok
+ %start.lt = icmp slt i32 %start, 10000
+ %c = and i1 %c1, %start.lt
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; start <u len with len in [0, INT_MAX) proves start >= 0.
+define void @ult_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @ult_guard_len_range(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp ult i32 %start, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; start <=u len with len in [0, INT_MAX) proves start >= 0 too.
+define void @ule_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @ule_guard_len_range(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp ule i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp ule i32 %start, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; len >u start, written the other way around.
+define void @ugt_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @ugt_guard_len_range(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[LEN]], [[START]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp ugt i32 %len, %start
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; A signed guard on start itself.
+define void @sge_guard(ptr %a, i32 %start) {
+; CHECK-LABEL: define void @sge_guard(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[START]], 32
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %c = icmp sge i32 %start, 32
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The loop is on the false edge of start >=u len; len is in [0, INT_MAX), so
+; start <u len there.
+define void @ult_guard_on_false_edge(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @ult_guard_on_false_edge(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp uge i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[LOOP_PREHEADER:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp uge i32 %start, %len
+ br i1 %c, label %exit, label %loop
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; A constant start needs no guard: the range of the IV is known.
+define void @const_start(ptr %a) {
+; CHECK-LABEL: define void @const_start(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ], [ 1000, %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = add nsw i64 [[INDVARS_IV]], -32
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP0]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 10000
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 1000, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; ---- start may be negative: i - 32 is computed in i32 ----
+
+; len is not known non-negative: start = -2, len = -1 satisfies start <u len.
+define void @ult_guard_len_unknown(ptr %a, i32 %start, i32 %len) {
+; CHECK-LABEL: define void @ult_guard_len_unknown(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %c = icmp ult i32 %start, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; No guard at all.
+define void @no_guard(ptr %a, i32 %start) {
+; CHECK-LABEL: define void @no_guard(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ], [ [[TMP0]], %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The guard constrains another value, not start.
+define void @guard_on_other_value(ptr %a, i32 %start, i32 %other, ptr %len.p) {
+; CHECK-LABEL: define void @guard_on_other_value(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], i32 [[OTHER:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[OTHER]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp ult i32 %other, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; start <s len says nothing about the sign of start.
+define void @slt_guard_wrong_direction(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @slt_guard_wrong_direction(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp slt i32 %start, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The loop is on the true edge of start >=u len: start is not below len there.
+define void @ult_guard_wrong_edge(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @ult_guard_wrong_edge(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0]]
+; CHECK-NEXT: [[C:%.*]] = icmp uge i32 [[START]], [[LEN]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %c = icmp uge i32 %start, %len
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; start >= 0 holds only on one of the two paths that reach the preheader.
+define void @guard_on_one_path_only(ptr %a, i32 %start) {
+; CHECK-LABEL: define void @guard_on_one_path_only(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[G:%.*]] = icmp sge i32 [[START]], 0
+; CHECK-NEXT: br i1 [[G]], label %[[POS:.*]], label %[[NEG:.*]]
+; CHECK: [[POS]]:
+; CHECK-NEXT: br label %[[PRE:.*]]
+; CHECK: [[NEG]]:
+; CHECK-NEXT: br label %[[PRE]]
+; CHECK: [[PRE]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ], [ [[TMP0]], %[[PRE]] ]
+; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
+; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %g = icmp sge i32 %start, 0
+ br i1 %g, label %pos, label %neg
+
+pos:
+ br label %pre
+
+neg:
+ br label %pre
+
+pre:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ %start, %pre ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i8, ptr %a, i64 %idx.ext
+ %v = load i8, ptr %src, align 1
+ %v1 = add i8 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i8, ptr %a, i64 %iv.ext
+ store i8 %v1, ptr %dst, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+!0 = !{i32 0, i32 2147483647}
+;.
+; CHECK: [[RNG0]] = !{i32 0, i32 2147483647}
+;.
diff --git a/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll b/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
new file mode 100644
index 0000000000000..272ed1e5a3a1a
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
@@ -0,0 +1,125 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='loop(indvars),loop-vectorize' -force-vector-width=4 -force-vector-interleave=1 -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
+
+; for (int i = start; i < 10000; i++) a[i] = a[i - 32] + 1;
+; Array indices are zext nneg, and i - 32 is nsw but not nuw. start is
+; non-negative on loop entry, so the dependence distance between a[i] and
+; a[i - 32] is known and no runtime alias check is needed.
+; FIXME: a runtime alias check is emitted.
+define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
+; CHECK-LABEL: define void @runtime_start(
+; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_P]], align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT: [[LEN_OK:%.*]] = icmp ugt i32 [[LEN]], 9999
+; CHECK-NEXT: [[START_OK:%.*]] = icmp ult i32 [[START]], [[LEN]]
+; CHECK-NEXT: [[START_M32:%.*]] = add i32 [[START]], -32
+; CHECK-NEXT: [[START_M32_OK:%.*]] = icmp ult i32 [[START_M32]], [[LEN]]
+; CHECK-NEXT: [[C0:%.*]] = and i1 [[LEN_OK]], [[START_OK]]
+; CHECK-NEXT: [[C1:%.*]] = and i1 [[C0]], [[START_M32_OK]]
+; CHECK-NEXT: [[START_LT:%.*]] = icmp slt i32 [[START]], 10000
+; CHECK-NEXT: [[C:%.*]] = and i1 [[C1]], [[START_LT]]
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = sub i32 9999, [[START]]
+; CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64
+; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 1
+; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[TMP3]], 4
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_SCEVCHECK:.*]]
+; CHECK: [[VECTOR_SCEVCHECK]]:
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i32 9967, [[START_M32]]
+; CHECK-NEXT: br i1 [[TMP4]], label %[[SCALAR_PH]], label %[[VECTOR_MEMCHECK:.*]]
+; CHECK: [[VECTOR_MEMCHECK]]:
+; CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP0]], 2
+; CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[START_M32]] to i64
+; CHECK-NEXT: [[TMP7:%.*]] = shl nuw nsw i64 [[TMP6]], 2
+; CHECK-NEXT: [[TMP8:%.*]] = sub i64 [[TMP5]], [[TMP7]]
+; CHECK-NEXT: [[TMP9:%.*]] = sub i64 [[TMP8]], 1
+; CHECK-NEXT: [[DIFF_CHECK:%.*]] = icmp ult i64 [[TMP9]], 15
+; CHECK-NEXT: br i1 [[DIFF_CHECK]], label %[[SCALAR_PH]], label %[[VECTOR_PH:.*]]
+; CHECK: [[VECTOR_PH]]:
+; CHECK-NEXT: [[TMP10:%.*]] = and i64 [[TMP3]], 3
+; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[TMP3]], [[TMP10]]
+; CHECK-NEXT: [[TMP11:%.*]] = add i64 [[TMP0]], [[N_VEC]]
+; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
+; CHECK: [[VECTOR_BODY]]:
+; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[TMP12:%.*]] = add nuw i64 [[TMP0]], [[INDEX]]
+; CHECK-NEXT: [[TMP13:%.*]] = trunc nuw i64 [[TMP12]] to i32
+; CHECK-NEXT: [[TMP14:%.*]] = add nsw i32 [[TMP13]], -32
+; CHECK-NEXT: [[TMP15:%.*]] = zext nneg i32 [[TMP14]] to i64
+; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[TMP15]]
+; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <4 x i32>, ptr [[TMP16]], align 4
+; CHECK-NEXT: [[TMP17:%.*]] = add <4 x i32> [[WIDE_LOAD]], splat (i32 1)
+; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[TMP12]]
+; CHECK-NEXT: store <4 x i32> [[TMP17]], ptr [[TMP18]], align 4
+; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4
+; CHECK-NEXT: [[TMP19:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[TMP19]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP1:![0-9]+]]
+; CHECK: [[MIDDLE_BLOCK]]:
+; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[TMP3]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT_LOOPEXIT:.*]], label %[[SCALAR_PH]]
+; CHECK: [[SCALAR_PH]]:
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[TMP11]], %[[MIDDLE_BLOCK]] ], [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[TMP0]], %[[VECTOR_SCEVCHECK]] ], [ [[TMP0]], %[[VECTOR_MEMCHECK]] ]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[BC_RESUME_VAL]], %[[SCALAR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[TMP20:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP20]], -32
+; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[SRC]], align 4
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[V]], 1
+; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i32 [[V1]], ptr [[DST]], align 4
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[TMP21:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
+; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP21]], 9998
+; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT]], label %[[LOOP]], !llvm.loop [[LOOP4:![0-9]+]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.p, align 4, !range !0
+ %len.ok = icmp ugt i32 %len, 9999
+ %start.ok = icmp ult i32 %start, %len
+ %start.m32 = add i32 %start, -32
+ %start.m32.ok = icmp ult i32 %start.m32, %len
+ %c0 = and i1 %len.ok, %start.ok
+ %c1 = and i1 %c0, %start.m32.ok
+ %start.lt = icmp slt i32 %start, 10000
+ %c = and i1 %c1, %start.lt
+ br i1 %c, label %loop, label %exit
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %idx = add nsw i32 %iv, -32
+ %idx.ext = zext nneg i32 %idx to i64
+ %src = getelementptr inbounds nuw i32, ptr %a, i64 %idx.ext
+ %v = load i32, ptr %src, align 4
+ %v1 = add i32 %v, 1
+ %iv.ext = zext nneg i32 %iv to i64
+ %dst = getelementptr inbounds nuw i32, ptr %a, i64 %iv.ext
+ store i32 %v1, ptr %dst, align 4
+ %iv.next = add nuw nsw i32 %iv, 1
+ %done = icmp sgt i32 %iv, 9998
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; [0, INT_MAX)
+!0 = !{i32 0, i32 2147483647}
+;.
+; CHECK: [[RNG0]] = !{i32 0, i32 2147483647}
+; CHECK: [[LOOP1]] = distinct !{[[LOOP1]], [[META2:![0-9]+]], [[META3:![0-9]+]]}
+; CHECK: [[META2]] = !{!"llvm.loop.isvectorized", i32 1}
+; CHECK: [[META3]] = !{!"llvm.loop.unroll.runtime.disable"}
+; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META2]]}
+;.
>From 8a5d5953ed99f2c585de94eb15ed0f21ca16af52 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Tue, 13 Oct 2026 21:20:52 +0000
Subject: [PATCH 3/5] [SCEV][NFC] Use isKnownOnEveryIteration in
isKnownViaInduction
isKnownViaInduction and isKnownOnEveryIteration both prove a predicate
on every iteration from loop entry and the backedge, so the next change
can extend one place.
Add a private isKnownOnEveryIteration overload that takes any LHS and
RHS and the loop, and move the entry/backedge check from
isKnownViaInduction into it. The public overload is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 7 +++
llvm/lib/Analysis/ScalarEvolution.cpp | 54 +++++++++++---------
2 files changed, 37 insertions(+), 24 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 1376edbd70725..77100cc7212d6 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2446,6 +2446,13 @@ class ScalarEvolution {
getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS,
ICmpInst::Predicate Pred);
+ /// Test if the condition described by Pred, LHS, RHS is known to be true on
+ /// every iteration of \p L, by checking it on entry to \p L and on its
+ /// backedge. Unlike the public overload, LHS and RHS may be any expressions
+ /// with recurrences of \p L and values available on entry to \p L.
+ bool isKnownOnEveryIteration(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L);
+
/// Return SCEV no-wrap flags that can be proven based on reasoning about
/// how poison produced from no-wrap flags on this value (e.g. a nuw add)
/// would trigger undefined behavior on overflow.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 432ac4d465170..cd1d69219c1d1 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11385,30 +11385,7 @@ bool ScalarEvolution::isKnownViaInduction(CmpPredicate Pred, SCEVUse LHS,
return DT.properlyDominates(L1->getHeader(), L2->getHeader());
});
- // Get init and post increment value for LHS.
- auto SplitLHS = SplitIntoInitAndPostInc(MDL, LHS);
- // if LHS contains unknown non-invariant SCEV then bail out.
- if (SplitLHS.first == getCouldNotCompute())
- return false;
- assert (SplitLHS.second != getCouldNotCompute() && "Unexpected CNC");
- // Get init and post increment value for RHS.
- auto SplitRHS = SplitIntoInitAndPostInc(MDL, RHS);
- // if RHS contains unknown non-invariant SCEV then bail out.
- if (SplitRHS.first == getCouldNotCompute())
- return false;
- assert (SplitRHS.second != getCouldNotCompute() && "Unexpected CNC");
- // It is possible that init SCEV contains an invariant load but it does
- // not dominate MDL and is not available at MDL loop entry, so we should
- // check it here.
- if (!isAvailableAtLoopEntry(SplitLHS.first, MDL) ||
- !isAvailableAtLoopEntry(SplitRHS.first, MDL))
- return false;
-
- // It seems backedge guard check is faster than entry one so in some cases
- // it can speed up whole estimation by short circuit
- return isLoopBackedgeGuardedByCond(MDL, Pred, SplitLHS.second,
- SplitRHS.second) &&
- isLoopEntryGuardedByCond(MDL, Pred, SplitLHS.first, SplitRHS.first);
+ return isKnownOnEveryIteration(Pred, LHS, RHS, MDL);
}
bool ScalarEvolution::isKnownPredicate(CmpPredicate Pred, SCEVUse LHS,
@@ -11462,6 +11439,35 @@ bool ScalarEvolution::isKnownOnEveryIteration(CmpPredicate Pred,
isLoopBackedgeGuardedByCond(L, Pred, LHS->getPostIncExpr(*this), RHS);
}
+bool ScalarEvolution::isKnownOnEveryIteration(CmpPredicate Pred,
+ const SCEV *LHS, const SCEV *RHS,
+ const Loop *L) {
+ // Get init and post increment value for LHS.
+ auto SplitLHS = SplitIntoInitAndPostInc(L, LHS);
+ // if LHS contains unknown non-invariant SCEV then bail out.
+ if (SplitLHS.first == getCouldNotCompute())
+ return false;
+ assert(SplitLHS.second != getCouldNotCompute() && "Unexpected CNC");
+ // Get init and post increment value for RHS.
+ auto SplitRHS = SplitIntoInitAndPostInc(L, RHS);
+ // if RHS contains unknown non-invariant SCEV then bail out.
+ if (SplitRHS.first == getCouldNotCompute())
+ return false;
+ assert(SplitRHS.second != getCouldNotCompute() && "Unexpected CNC");
+ // It is possible that init SCEV contains an invariant load but it does
+ // not dominate L and is not available at L loop entry, so we should
+ // check it here.
+ if (!isAvailableAtLoopEntry(SplitLHS.first, L) ||
+ !isAvailableAtLoopEntry(SplitRHS.first, L))
+ return false;
+
+ // It seems backedge guard check is faster than entry one so in some cases
+ // it can speed up whole estimation by short circuit
+ return isLoopBackedgeGuardedByCond(L, Pred, SplitLHS.second,
+ SplitRHS.second) &&
+ isLoopEntryGuardedByCond(L, Pred, SplitLHS.first, SplitRHS.first);
+}
+
std::optional<ScalarEvolution::MonotonicPredicateType>
ScalarEvolution::getMonotonicPredicateType(const SCEVAddRecExpr *LHS,
ICmpInst::Predicate Pred) {
>From 93ebf748bc7c5dd89b824a387e718b00c6404c77 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Tue, 13 Oct 2026 22:37:30 +0000
Subject: [PATCH 4/5] [SCEV][NFC] Factor getMonotonicPredicate out of
getLoopInvariantPredicate
The next change needs the same setup as getLoopInvariantPredicate.
Move it into a private helper: put the loop-invariant operand on the
RHS, check that the other side is a recurrence of the loop, and compute
the predicate's monotonicity.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 19 +++++++++++
llvm/lib/Analysis/ScalarEvolution.cpp | 33 ++++++++++++++------
2 files changed, 43 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 77100cc7212d6..d00642aecd58a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2453,6 +2453,25 @@ class ScalarEvolution {
bool isKnownOnEveryIteration(CmpPredicate Pred, const SCEV *LHS,
const SCEV *RHS, const Loop *L);
+ /// "AR Pred RHS", with AR a recurrence of a loop and RHS invariant in it,
+ /// and how the predicate changes as the loop iterates.
+ struct MonotonicPredicate {
+ CmpPredicate Pred;
+ const SCEVAddRecExpr *AR;
+ const SCEV *RHS;
+ MonotonicPredicateType Type;
+ /// Whether the operands were swapped to put the recurrence on the left.
+ bool Swapped;
+ };
+
+ /// If one of LHS and RHS is a recurrence of \p L and the other is invariant
+ /// in \p L, and "LHS Pred RHS" is monotonic in \p L, return it with the
+ /// recurrence on the left. Otherwise, return std::nullopt.
+ std::optional<MonotonicPredicate> getMonotonicPredicate(CmpPredicate Pred,
+ const SCEV *LHS,
+ const SCEV *RHS,
+ const Loop *L);
+
/// Return SCEV no-wrap flags that can be proven based on reasoning about
/// how poison produced from no-wrap flags on this value (e.g. a nuw add)
/// would trigger undefined behavior on overflow.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index cd1d69219c1d1..c97934dfcd43b 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11531,26 +11531,41 @@ ScalarEvolution::getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS,
return std::nullopt;
}
-std::optional<ScalarEvolution::LoopInvariantPredicate>
-ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
- const SCEV *RHS, const Loop *L,
- const Instruction *CtxI) {
+std::optional<ScalarEvolution::MonotonicPredicate>
+ScalarEvolution::getMonotonicPredicate(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L) {
// If there is a loop-invariant, force it into the RHS, otherwise bail out.
+ bool Swapped = false;
if (!isLoopInvariant(RHS, L)) {
if (!isLoopInvariant(LHS, L))
return std::nullopt;
std::swap(LHS, RHS);
Pred = ICmpInst::getSwappedCmpPredicate(Pred);
+ Swapped = true;
}
- const SCEVAddRecExpr *ArLHS = dyn_cast<SCEVAddRecExpr>(LHS);
- if (!ArLHS || ArLHS->getLoop() != L)
+ const auto *AR = dyn_cast<SCEVAddRecExpr>(LHS);
+ if (!AR || AR->getLoop() != L)
return std::nullopt;
- auto MonotonicType = getMonotonicPredicateType(ArLHS, Pred);
- if (!MonotonicType)
+ auto Type = getMonotonicPredicateType(AR, Pred);
+ if (!Type)
+ return std::nullopt;
+ return MonotonicPredicate{Pred, AR, RHS, *Type, Swapped};
+}
+
+std::optional<ScalarEvolution::LoopInvariantPredicate>
+ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L,
+ const Instruction *CtxI) {
+ auto MP = getMonotonicPredicate(Pred, LHS, RHS, L);
+ if (!MP)
return std::nullopt;
+ Pred = MP->Pred;
+ const SCEVAddRecExpr *ArLHS = MP->AR;
+ LHS = ArLHS;
+ RHS = MP->RHS;
// If the predicate "ArLHS `Pred` RHS" monotonically increases from false to
// true as the loop iterates, and the backedge is control dependent on
// "ArLHS `Pred` RHS" == true then we can reason as follows:
@@ -11568,7 +11583,7 @@ ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
//
// A similar reasoning applies for a monotonically decreasing predicate, by
// replacing true with false and false with true in the above two bullets.
- bool Increasing = *MonotonicType == ScalarEvolution::MonotonicallyIncreasing;
+ bool Increasing = MP->Type == ScalarEvolution::MonotonicallyIncreasing;
auto P = Increasing ? Pred : ICmpInst::getInverseCmpPredicate(Pred);
if (isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
>From 4ba245df05be9ca4c8317a9ad59042889da1ab01 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Tue, 13 Oct 2026 22:30:55 +0000
Subject: [PATCH 5/5] [SCEV] Prove monotonic predicates from loop entry in
isKnownViaInduction
isKnownViaInduction requires the predicate to hold on loop entry and on
the backedge. The backedge check usually fails when the latch only
bounds the IV from above, so e.g. {s,+,1}<nsw> >=s 0 is not proven even
when s >=s 0 is known on entry.
In a loop guarded by "start <u len", this keeps IndVars from computing
a[i - 32] in i64.
A monotonically increasing predicate against a loop-invariant value can
only change from false to true, so holding on entry is enough.
Use this in isKnownOnEveryIteration when the backedge check fails.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 4 +-
llvm/lib/Analysis/ScalarEvolution.cpp | 17 +-
.../IndVarSimplify/eliminate-comparison.ll | 68 ++++-
.../IndVarSimplify/iv-widen-elim-ext.ll | 7 +-
.../monotonic-predicate-samesign.ll | 81 ++++++
.../IndVarSimplify/trivial-guard.ll | 7 +-
.../widen-zext-nneg-add-nsw-runtime-start.ll | 61 ++--
.../indvars-vectorize-runtime-start.ll | 32 +--
.../Analysis/ScalarEvolutionTest.cpp | 261 ++++++++++++++++++
9 files changed, 463 insertions(+), 75 deletions(-)
create mode 100644 llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index d00642aecd58a..d4e0fff494974 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2449,7 +2449,9 @@ class ScalarEvolution {
/// Test if the condition described by Pred, LHS, RHS is known to be true on
/// every iteration of \p L, by checking it on entry to \p L and on its
/// backedge. Unlike the public overload, LHS and RHS may be any expressions
- /// with recurrences of \p L and values available on entry to \p L.
+ /// with recurrences of \p L and values available on entry to \p L. If one
+ /// side is a recurrence of \p L, the other is invariant in \p L and the
+ /// predicate is monotonically increasing, checking it on entry is enough.
bool isKnownOnEveryIteration(CmpPredicate Pred, const SCEV *LHS,
const SCEV *RHS, const Loop *L);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index c97934dfcd43b..702955bc717a5 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11463,9 +11463,20 @@ bool ScalarEvolution::isKnownOnEveryIteration(CmpPredicate Pred,
// It seems backedge guard check is faster than entry one so in some cases
// it can speed up whole estimation by short circuit
- return isLoopBackedgeGuardedByCond(L, Pred, SplitLHS.second,
- SplitRHS.second) &&
- isLoopEntryGuardedByCond(L, Pred, SplitLHS.first, SplitRHS.first);
+ if (isLoopBackedgeGuardedByCond(L, Pred, SplitLHS.second, SplitRHS.second))
+ return isLoopEntryGuardedByCond(L, Pred, SplitLHS.first, SplitRHS.first);
+
+ // If one side is a recurrence of L and the other side is invariant in L,
+ // and the predicate can only change from false to true as the loop
+ // iterates, it holds on every iteration once it holds on entry. Drop
+ // samesign: monotonicity is computed for the plain predicate, so the entry
+ // check must use the same one.
+ auto MP = getMonotonicPredicate(Pred.dropSameSign(), LHS, RHS, L);
+ if (!MP || MP->Type != MonotonicallyIncreasing)
+ return false;
+ if (MP->Swapped)
+ std::swap(SplitLHS, SplitRHS);
+ return isLoopEntryGuardedByCond(L, MP->Pred, SplitLHS.first, SplitRHS.first);
}
std::optional<ScalarEvolution::MonotonicPredicateType>
diff --git a/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll b/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
index 08f9856ac603d..ecea132725329 100644
--- a/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
+++ b/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
@@ -569,6 +569,10 @@ leave:
ret void
}
+; The entry condition admits only %len s< INT_MIN + 5, i.e. a negative %len, so
+; the loop exits on its first iteration: %iv.2 is 0 there and 0 s< %len is
+; false. Both exits fold. @func_17_iterating covers the deduction for a loop
+; that iterates.
define void @func_17(ptr %len.ptr) {
; CHECK-LABEL: @func_17(
; CHECK-NEXT: entry:
@@ -579,18 +583,13 @@ define void @func_17(ptr %len.ptr) {
; CHECK-NEXT: [[ENTRY_COND:%.*]] = and i1 [[ENTRY_COND_0]], [[ENTRY_COND_1]]
; CHECK-NEXT: br i1 [[ENTRY_COND]], label [[LOOP_PREHEADER:%.*]], label [[LEAVE:%.*]]
; CHECK: loop.preheader:
-; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[LEN]], i32 0)
-; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[SMAX]], -5
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_INC:%.*]], [[BE:%.*]] ], [ -6, [[LOOP_PREHEADER]] ]
; CHECK-NEXT: call void @side_effect()
-; CHECK-NEXT: [[IV_INC]] = add nsw i32 [[IV]], 1
-; CHECK-NEXT: br i1 true, label [[BE]], label [[LEAVE_LOOPEXIT:%.*]]
+; CHECK-NEXT: br i1 true, label [[BE:%.*]], label [[LEAVE_LOOPEXIT:%.*]]
; CHECK: be:
; CHECK-NEXT: call void @side_effect()
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_INC]], [[TMP0]]
-; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LEAVE_LOOPEXIT]]
+; CHECK-NEXT: br i1 false, label [[LOOP]], label [[LEAVE_LOOPEXIT]]
; CHECK: leave.loopexit:
; CHECK-NEXT: br label [[LEAVE]]
; CHECK: leave:
@@ -611,9 +610,60 @@ loop:
%iv.inc = add i32 %iv, 1
%iv.2.inc = add i32 %iv.2, 1
%iv.cmp = icmp slt i32 %iv, %len.add.5
+ br i1 %iv.cmp, label %be, label %leave
+
+be:
+ call void @side_effect()
+ %be.cond = icmp slt i32 %iv.2, %len
+ br i1 %be.cond, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+; Deduces {INT_MAX - 4,+,1} s< (-5 + %len), the post-increment value of %iv,
+; from {INT_MIN,+,1} s< %len on the backedge, since %len s< INT_MIN - (-5)
+; from the entry condition. Unlike in @func_17, the loop can iterate.
+define void @func_17_iterating(ptr %len.ptr) {
+; CHECK-LABEL: @func_17_iterating(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_PTR:%.*]], align 4
+; CHECK-NEXT: [[LEN_ADD_5:%.*]] = add i32 [[LEN]], -5
+; CHECK-NEXT: [[ENTRY_COND_0:%.*]] = icmp slt i32 [[LEN]], -2147483643
+; CHECK-NEXT: [[ENTRY_COND_1:%.*]] = icmp slt i32 2147483642, [[LEN_ADD_5]]
+; CHECK-NEXT: [[ENTRY_COND:%.*]] = and i1 [[ENTRY_COND_0]], [[ENTRY_COND_1]]
+; CHECK-NEXT: br i1 [[ENTRY_COND]], label [[LOOP_PREHEADER:%.*]], label [[LEAVE:%.*]]
+; CHECK: loop.preheader:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_INC:%.*]], [[BE:%.*]] ], [ 2147483642, [[LOOP_PREHEADER]] ]
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: [[IV_INC]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: br i1 true, label [[BE]], label [[LEAVE_LOOPEXIT:%.*]]
+; CHECK: be:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_INC]], [[LEN_ADD_5]]
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LEAVE_LOOPEXIT]]
+; CHECK: leave.loopexit:
+; CHECK-NEXT: br label [[LEAVE]]
+; CHECK: leave:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.ptr
+ %len.add.5 = add i32 %len, -5
+ %entry.cond.0 = icmp slt i32 %len, 2147483653 ;; 2147483653 == INT_MIN - (-5)
+ %entry.cond.1 = icmp slt i32 2147483642, %len.add.5
+ %entry.cond = and i1 %entry.cond.0, %entry.cond.1
+ br i1 %entry.cond, label %loop, label %leave
-; Deduces {-5,+,1} s< (-5 + %len) from {0,+,1} < %len
-; since %len s< INT_MIN - (-5) from the entry condition
+loop:
+ %iv.2 = phi i32 [ -2147483648, %entry ], [ %iv.2.inc, %be ]
+ %iv = phi i32 [ 2147483642, %entry ], [ %iv.inc, %be ]
+ call void @side_effect()
+ %iv.inc = add i32 %iv, 1
+ %iv.2.inc = add i32 %iv.2, 1
+ %iv.cmp = icmp slt i32 %iv, %len.add.5
br i1 %iv.cmp, label %be, label %leave
be:
diff --git a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
index e1965b6b330c6..59e9bc986e20c 100644
--- a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
+++ b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
@@ -206,7 +206,8 @@ for.end: ; preds = %for.cond.for.end_cr
declare i32 @dummy(ptr, ptr)
-; A case where zext should not be eliminated when its operands could only be extended by sext.
+; The zext of %add3 is eliminated even though its operands are only extended
+; by sext: the guard 0 <s %M makes %add3 non-negative on every iteration.
define i32 @foo3(i32 %M) {
; CHECK-LABEL: @foo3(
; CHECK-NEXT: entry:
@@ -224,9 +225,7 @@ define i32 @foo3(i32 %M) {
; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[INDVARS_IV]], [[TMP0]]
-; CHECK-NEXT: [[TMP4:%.*]] = trunc nsw i64 [[TMP3]] to i32
-; CHECK-NEXT: [[IDXPROM4:%.*]] = zext i32 [[TMP4]] to i64
-; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[IDXPROM4]]
+; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[TMP3]]
; CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX5]], align 4
; CHECK-NEXT: br label [[FOR_INC]]
; CHECK: for.inc:
diff --git a/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll b/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll
new file mode 100644
index 0000000000000..f363a9ddb0194
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll
@@ -0,0 +1,81 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=indvars -S < %s | FileCheck %s
+
+; Only s >s inv is known on entry. %c is a samesign ugt of an nuw IV against
+; inv: it holds on entry, but it does not hold on every iteration. With s = 3
+; and inv = -5, %iv reaches 0x80000000 and %c is false at the exit. %c must
+; not be folded.
+define i32 @samesign_ugt_signed_guard(i32 %s, i32 %inv) {
+; CHECK-LABEL: define i32 @samesign_ugt_signed_guard(
+; CHECK-SAME: i32 [[S:%.*]], i32 [[INV:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[G:%.*]] = icmp sgt i32 [[S]], [[INV]]
+; CHECK-NEXT: br i1 [[G]], label %[[LOOP_PREHEADER:.*]], label %[[BAIL:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[S]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp samesign ugt i32 [[IV]], [[INV]]
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV]], -2147483648
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[C_LCSSA:%.*]] = phi i1 [ [[C]], %[[LOOP]] ]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[C_LCSSA]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+; CHECK: [[BAIL]]:
+; CHECK-NEXT: ret i32 7
+;
+entry:
+ %g = icmp sgt i32 %s, %inv
+ br i1 %g, label %loop, label %bail
+loop:
+ %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ]
+ %c = icmp samesign ugt i32 %iv, %inv
+ %iv.next = add nuw i32 %iv, 1
+ %ec = icmp eq i32 %iv, u0x80000000
+ br i1 %ec, label %exit, label %loop
+exit:
+ %r = zext i1 %c to i32
+ ret i32 %r
+bail:
+ ret i32 7
+}
+
+; s >u inv is known on entry and the IV is nuw, so %c holds on every
+; iteration, also as a samesign ugt.
+define i32 @samesign_ugt_unsigned_guard(i32 %s, i32 %inv) {
+; CHECK-LABEL: define i32 @samesign_ugt_unsigned_guard(
+; CHECK-SAME: i32 [[S:%.*]], i32 [[INV:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[G:%.*]] = icmp ugt i32 [[S]], [[INV]]
+; CHECK-NEXT: br i1 [[G]], label %[[LOOP_PREHEADER:.*]], label %[[BAIL:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[S]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV]], -2147483648
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[C_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[C_LCSSA]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+; CHECK: [[BAIL]]:
+; CHECK-NEXT: ret i32 7
+;
+entry:
+ %g = icmp ugt i32 %s, %inv
+ br i1 %g, label %loop, label %bail
+loop:
+ %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ]
+ %c = icmp samesign ugt i32 %iv, %inv
+ %iv.next = add nuw i32 %iv, 1
+ %ec = icmp eq i32 %iv, u0x80000000
+ br i1 %ec, label %exit, label %loop
+exit:
+ %r = zext i1 %c to i32
+ ret i32 %r
+bail:
+ ret i32 7
+}
diff --git a/llvm/test/Transforms/IndVarSimplify/trivial-guard.ll b/llvm/test/Transforms/IndVarSimplify/trivial-guard.ll
index c1c9a90d9831d..7533407be6f02 100644
--- a/llvm/test/Transforms/IndVarSimplify/trivial-guard.ll
+++ b/llvm/test/Transforms/IndVarSimplify/trivial-guard.ll
@@ -3,6 +3,8 @@
declare i1 @cond()
+; One of the two loops is unreachable. Its check may fold to either true or
+; false, since both are provable there.
define void @test_01(i32 %x) {
; CHECK-LABEL: @test_01(
; CHECK-NEXT: entry:
@@ -20,7 +22,7 @@ define void @test_01(i32 %x) {
; CHECK-NEXT: [[LOOP_COND_1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[LOOP_COND_1]], label [[LOOP_1]], label [[EXIT_LOOPEXIT:%.*]]
; CHECK: loop.2:
-; CHECK-NEXT: br i1 true, label [[GUARDED_2:%.*]], label [[FAIL_LOOPEXIT1:%.*]]
+; CHECK-NEXT: br i1 false, label [[GUARDED_2:%.*]], label [[FAIL_LOOPEXIT1:%.*]]
; CHECK: guarded.2:
; CHECK-NEXT: [[LOOP_COND_2:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[LOOP_COND_2]], label [[LOOP_2]], label [[EXIT_LOOPEXIT2:%.*]]
@@ -67,6 +69,7 @@ fail:
unreachable
}
+; Same as @test_01, with the other loop unreachable.
define void @test_02(i32 %x) {
; CHECK-LABEL: @test_02(
; CHECK-NEXT: entry:
@@ -76,7 +79,7 @@ define void @test_02(i32 %x) {
; CHECK: loop.1.preheader:
; CHECK-NEXT: br label [[LOOP_1:%.*]]
; CHECK: loop.1:
-; CHECK-NEXT: br i1 true, label [[GUARDED_1:%.*]], label [[FAIL_LOOPEXIT:%.*]]
+; CHECK-NEXT: br i1 false, label [[GUARDED_1:%.*]], label [[FAIL_LOOPEXIT:%.*]]
; CHECK: guarded.1:
; CHECK-NEXT: [[LOOP_COND_1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[LOOP_COND_1]], label [[LOOP_1]], label [[EXIT_LOOPEXIT:%.*]]
diff --git a/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll b/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
index bbb78084c1c0a..da4c9582ff7a5 100644
--- a/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
+++ b/llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll
@@ -9,8 +9,7 @@ target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
; Where start is known non-negative on loop entry, i - 32 is computed in i64.
; Where start may be negative, i - 32 is computed in i32.
-; ---- start is known non-negative: i - 32 should be widened ----
-; FIXME: Only @const_start is computed in i64.
+; ---- start is known non-negative: i - 32 is computed in i64 ----
; Compound guard: start and start - 32 are both <u len, len is an array length.
define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
@@ -32,17 +31,15 @@ define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP3]], -32
-; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP1]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP2]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], 10000
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
@@ -89,20 +86,20 @@ define void @ult_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[START]], i32 9999)
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[SMAX]], 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], [[TMP1]]
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
@@ -142,20 +139,20 @@ define void @ule_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[START]], i32 9999)
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[SMAX]], 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], [[TMP1]]
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
@@ -195,20 +192,20 @@ define void @ugt_guard_len_range(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[START]], i32 9999)
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[SMAX]], 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], [[TMP1]]
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
@@ -247,20 +244,20 @@ define void @sge_guard(ptr %a, i32 %start) {
; CHECK-NEXT: br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[START]], i32 9999)
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[SMAX]], 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], [[TMP1]]
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
@@ -300,20 +297,20 @@ define void @ult_guard_on_false_edge(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[LOOP_PREHEADER:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[START]], i32 9999)
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[SMAX]], 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP1]], -32
-; CHECK-NEXT: [[TMP2:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP2]]
; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[SRC]], align 1
; CHECK-NEXT: [[V1:%.*]] = add i8 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i8 [[V1]], ptr [[DST]], align 1
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP3:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[TMP3]], 9998
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], [[TMP1]]
; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
diff --git a/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll b/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
index 272ed1e5a3a1a..24237265a3003 100644
--- a/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
+++ b/llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll
@@ -7,7 +7,6 @@ target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
; Array indices are zext nneg, and i - 32 is nsw but not nuw. start is
; non-negative on loop entry, so the dependence distance between a[i] and
; a[i - 32] is known and no runtime alias check is needed.
-; FIXME: a runtime alias check is emitted.
define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
; CHECK-LABEL: define void @runtime_start(
; CHECK-SAME: ptr [[A:%.*]], i32 [[START:%.*]], ptr [[LEN_P:%.*]]) {
@@ -28,18 +27,7 @@ define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64
; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 1
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[TMP3]], 4
-; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_SCEVCHECK:.*]]
-; CHECK: [[VECTOR_SCEVCHECK]]:
-; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i32 9967, [[START_M32]]
-; CHECK-NEXT: br i1 [[TMP4]], label %[[SCALAR_PH]], label %[[VECTOR_MEMCHECK:.*]]
-; CHECK: [[VECTOR_MEMCHECK]]:
-; CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP0]], 2
-; CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[START_M32]] to i64
-; CHECK-NEXT: [[TMP7:%.*]] = shl nuw nsw i64 [[TMP6]], 2
-; CHECK-NEXT: [[TMP8:%.*]] = sub i64 [[TMP5]], [[TMP7]]
-; CHECK-NEXT: [[TMP9:%.*]] = sub i64 [[TMP8]], 1
-; CHECK-NEXT: [[DIFF_CHECK:%.*]] = icmp ult i64 [[TMP9]], 15
-; CHECK-NEXT: br i1 [[DIFF_CHECK]], label %[[SCALAR_PH]], label %[[VECTOR_PH:.*]]
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
; CHECK-NEXT: [[TMP10:%.*]] = and i64 [[TMP3]], 3
; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[TMP3]], [[TMP10]]
@@ -48,9 +36,7 @@ define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
; CHECK: [[VECTOR_BODY]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP12:%.*]] = add nuw i64 [[TMP0]], [[INDEX]]
-; CHECK-NEXT: [[TMP13:%.*]] = trunc nuw i64 [[TMP12]] to i32
-; CHECK-NEXT: [[TMP14:%.*]] = add nsw i32 [[TMP13]], -32
-; CHECK-NEXT: [[TMP15:%.*]] = zext nneg i32 [[TMP14]] to i64
+; CHECK-NEXT: [[TMP15:%.*]] = add nsw i64 [[TMP12]], -32
; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[TMP15]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <4 x i32>, ptr [[TMP16]], align 4
; CHECK-NEXT: [[TMP17:%.*]] = add <4 x i32> [[WIDE_LOAD]], splat (i32 1)
@@ -63,22 +49,20 @@ define void @runtime_start(ptr %a, i32 %start, ptr %len.p) {
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[TMP3]], [[N_VEC]]
; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT_LOOPEXIT:.*]], label %[[SCALAR_PH]]
; CHECK: [[SCALAR_PH]]:
-; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[TMP11]], %[[MIDDLE_BLOCK]] ], [ [[TMP0]], %[[LOOP_PREHEADER]] ], [ [[TMP0]], %[[VECTOR_SCEVCHECK]] ], [ [[TMP0]], %[[VECTOR_MEMCHECK]] ]
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[TMP11]], %[[MIDDLE_BLOCK]] ], [ [[TMP0]], %[[LOOP_PREHEADER]] ]
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[BC_RESUME_VAL]], %[[SCALAR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[TMP20:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[IDX:%.*]] = add nsw i32 [[TMP20]], -32
-; CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[IDX]] to i64
+; CHECK-NEXT: [[IDX_EXT:%.*]] = add nsw i64 [[INDVARS_IV]], -32
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[IDX_EXT]]
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[SRC]], align 4
; CHECK-NEXT: [[V1:%.*]] = add i32 [[V]], 1
; CHECK-NEXT: [[DST:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store i32 [[V1]], ptr [[DST]], align 4
; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT: [[TMP21:%.*]] = trunc nuw i64 [[INDVARS_IV]] to i32
-; CHECK-NEXT: [[DONE:%.*]] = icmp sgt i32 [[TMP21]], 9998
-; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT_LOOPEXIT]], label %[[LOOP]], !llvm.loop [[LOOP4:![0-9]+]]
+; CHECK-NEXT: [[LFTR_WIDEIV:%.*]] = trunc i64 [[INDVARS_IV_NEXT]] to i32
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[LFTR_WIDEIV]], 10000
+; CHECK-NEXT: br i1 [[EXITCOND]], label %[[EXIT_LOOPEXIT]], label %[[LOOP]], !llvm.loop [[LOOP4:![0-9]+]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
@@ -121,5 +105,5 @@ exit:
; CHECK: [[LOOP1]] = distinct !{[[LOOP1]], [[META2:![0-9]+]], [[META3:![0-9]+]]}
; CHECK: [[META2]] = !{!"llvm.loop.isvectorized", i32 1}
; CHECK: [[META3]] = !{!"llvm.loop.unroll.runtime.disable"}
-; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META2]]}
+; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META3]], [[META2]]}
;.
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 42e524b0b0556..5fe354896d3e0 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -74,6 +74,12 @@ static std::optional<APInt> computeConstantDifference(ScalarEvolution &SE,
const SCEV *FoundRHS) {
return SE.isImpliedCond(Pred, LHS, RHS, FoundPred, FoundLHS, FoundRHS);
}
+
+ static bool isKnownOnEveryIteration(ScalarEvolution &SE, CmpPredicate Pred,
+ const SCEV *LHS, const SCEV *RHS,
+ const Loop *L) {
+ return SE.isKnownOnEveryIteration(Pred, LHS, RHS, L);
+ }
};
TEST_F(ScalarEvolutionsTest, SCEVUnknownRAUW) {
@@ -1760,6 +1766,261 @@ TEST_F(ScalarEvolutionsTest, GuardedStartNonNegative) {
});
}
+// A predicate that can only change from false to true as the loop iterates
+// holds on every iteration if it holds on loop entry.
+TEST_F(ScalarEvolutionsTest, KnownOnEveryIterationMonotonic) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ // In each loop the start value is known only from the guard, and the latch
+ // bounds the IV from above only. Each IV is in its own loop, so that the
+ // IVs are distinct SCEVs with exactly the flags written here.
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "define void @nsw(i32 %s, i32 %n, i32 %inv) { "
+ "entry: "
+ " %g = icmp sge i32 %s, 0 "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nsw i32 %iv, 1 "
+ " %var = add i32 %inv, %iv "
+ " %ec = icmp slt i32 %iv.next, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ "define void @nsw_strict(i32 %s, i32 %n) { "
+ "entry: "
+ " %g = icmp sgt i32 %s, 0 "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nsw i32 %iv, 1 "
+ " %ec = icmp slt i32 %iv.next, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ "define void @dec(i32 %s, i32 %n) { "
+ "entry: "
+ " %g = icmp sge i32 %s, 0 "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nsw i32 %iv, -1 "
+ " %ec = icmp sgt i32 %iv.next, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ "define void @wrap(i32 %s, i32 %n) { "
+ "entry: "
+ " %g = icmp sge i32 %s, 0 "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add i32 %iv, 1 "
+ " %ec = icmp ne i32 %iv.next, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ "define void @unk_step(i32 %s, i32 %n, i32 %step) { "
+ "entry: "
+ " %g = icmp sge i32 %s, 0 "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nsw i32 %iv, %step "
+ " %ec = icmp slt i32 %iv.next, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ // An nuw IV, compared with a value that is known to be below it on
+ // entry only via a signed guard.
+ "define void @nuw(i32 %s, i32 %inv, i32 %n) { "
+ "entry: "
+ " %g = icmp sgt i32 %s, %inv "
+ " %g.u = icmp ugt i32 %s, %inv "
+ " %g2 = and i1 %g, %g.u "
+ " br i1 %g2, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nuw i32 %iv, 1 "
+ " %ec = icmp ne i32 %iv, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ "define void @nuw_signed_guard(i32 %s, i32 %inv, i32 %n) { "
+ "entry: "
+ " %g = icmp sgt i32 %s, %inv "
+ " br i1 %g, label %loop, label %exit "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nuw i32 %iv, 1 "
+ " %ec = icmp ne i32 %iv, %n "
+ " br i1 %ec, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ // The latch guarantees iv >=s 0 on the backedge, but nothing is known
+ // on entry.
+ "define void @no_entry(i32 %s) { "
+ "entry: "
+ " br label %loop "
+ "loop: "
+ " %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add nsw i32 %iv, 1 "
+ " %ec = icmp sge i32 %iv.next, 0 "
+ " %ec2 = icmp slt i32 %iv.next, 100 "
+ " %ec3 = and i1 %ec, %ec2 "
+ " br i1 %ec3, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "} "
+ // An outer-loop recurrence compared with an inner-loop one.
+ "define void @nest(i32 %s, i32 %n) { "
+ "entry: "
+ " %g = icmp sge i32 %s, 0 "
+ " br i1 %g, label %outer, label %exit "
+ "outer: "
+ " %o = phi i32 [ 0, %entry ], [ %o.next, %outer.latch ] "
+ " %og = icmp sge i32 %s, %o "
+ " br i1 %og, label %inner, label %outer.latch "
+ "inner: "
+ " %i = phi i32 [ %s, %outer ], [ %i.next, %inner ] "
+ " %i.next = add nsw i32 %i, 1 "
+ " %iec = icmp slt i32 %i.next, %n "
+ " br i1 %iec, label %inner, label %outer.latch "
+ "outer.latch: "
+ " %o.next = add nsw i32 %o, 1 "
+ " %oec = icmp slt i32 %o.next, %n "
+ " br i1 %oec, label %outer, label %exit "
+ "exit: "
+ " ret void "
+ "} ",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ const auto SGE = ICmpInst::ICMP_SGE, SGT = ICmpInst::ICMP_SGT;
+ const auto SLE = ICmpInst::ICMP_SLE, UGT = ICmpInst::ICMP_UGT;
+ // Runs Check(SE, IV, Loop, Arg) on the IV named "iv" in function Name.
+ auto WithIV = [&](StringRef Name,
+ function_ref<void(ScalarEvolution &, const SCEV *,
+ const Loop *, Function &)>
+ Check) {
+ runWithSE(*M, Name, [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ Instruction *IV = getInstructionByName(F, "iv");
+ Check(SE, SE.getSCEV(IV), LI.getLoopFor(IV->getParent()), F);
+ });
+ };
+
+ WithIV("nsw",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &F) {
+ const SCEV *Zero = SE.getZero(IV->getType());
+ const SCEV *MinusOne = SE.getMinusOne(IV->getType());
+ // {s,+,1}<nsw> >=s 0 holds on entry and can only become true.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SGE, IV, Zero, L));
+ // The same with the operands swapped.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SLE, Zero, IV, L));
+ // The same through the public query.
+ EXPECT_TRUE(SE.isKnownPredicate(SGE, IV, Zero));
+ // {s,+,1}<nsw> <=s -1 can become false.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SLE, IV, MinusOne, L));
+ // The other side varies in the loop.
+ const SCEV *Var = SE.getSCEV(getInstructionByName(F, "var"));
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV, Var, L));
+ // The predicate does not hold on entry.
+ const SCEV *Inv = SE.getSCEV(F.getArg(2));
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV, Inv, L));
+ });
+
+ WithIV("nsw_strict",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &) {
+ // A strict predicate: {s,+,1}<nsw> >s 0 with s >s 0 on entry.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SGT, IV,
+ SE.getZero(IV->getType()), L));
+ });
+
+ WithIV("dec",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &) {
+ // A negative step: {s,+,-1}<nsw> >=s 0 can become false.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV,
+ SE.getZero(IV->getType()), L));
+ });
+
+ WithIV("wrap",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &F) {
+ // No nsw or nuw: {s,+,1} may wrap.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV,
+ SE.getZero(IV->getType()), L));
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, ICmpInst::ICMP_UGE, IV,
+ SE.getSCEV(F.getArg(0)), L));
+ });
+
+ WithIV("unk_step",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &) {
+ // A step of unknown sign.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV,
+ SE.getZero(IV->getType()), L));
+ });
+
+ WithIV("nuw",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &F) {
+ const SCEV *Inv = SE.getSCEV(F.getArg(1));
+ // An unsigned predicate with nuw: {s,+,1}<nuw> >u inv.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, UGT, IV, Inv, L));
+ // The same as samesign: it is monotonic as a plain ugt, and s >u inv
+ // holds on entry.
+ EXPECT_TRUE(isKnownOnEveryIteration(
+ SE, CmpPredicate(UGT, /*HasSameSign=*/true), IV, Inv, L));
+ });
+
+ WithIV("nuw_signed_guard",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &F) {
+ const SCEV *Inv = SE.getSCEV(F.getArg(1));
+ // Only s >s inv is known on entry. As a samesign ugt the predicate
+ // holds on entry, but {s,+,1}<nuw> may become negative while inv is
+ // negative, and then samesign ugt is false: s = 3, inv = -5 at s + i
+ // = INT_MIN.
+ EXPECT_FALSE(isKnownOnEveryIteration(
+ SE, CmpPredicate(UGT, /*HasSameSign=*/true), IV, Inv, L));
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, UGT, IV, Inv, L));
+ });
+
+ WithIV("no_entry",
+ [&](ScalarEvolution &SE, const SCEV *IV, const Loop *L, Function &) {
+ // The backedge proves iv.next >=s 0, but the predicate is not known
+ // on entry.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, IV,
+ SE.getZero(IV->getType()), L));
+ });
+
+ runWithSE(*M, "nest", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *I = SE.getSCEV(getInstructionByName(F, "i"));
+ const SCEV *O = SE.getSCEV(getInstructionByName(F, "o"));
+ const Loop *Inner =
+ LI.getLoopFor(getInstructionByName(F, "i")->getParent());
+ const Loop *Outer =
+ LI.getLoopFor(getInstructionByName(F, "o")->getParent());
+ const SCEV *Zero = SE.getZero(I->getType());
+
+ // The inner recurrence against zero, for the inner loop.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SGE, I, Zero, Inner));
+ // The recurrence belongs to another loop.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SGE, I, Zero, Outer));
+ // The outer recurrence is invariant in the inner loop, and may be on
+ // either side. i >=s o holds on entry to the inner loop from the guard.
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SGE, I, O, Inner));
+ EXPECT_TRUE(isKnownOnEveryIteration(SE, SLE, O, I, Inner));
+ // Both sides vary in the outer loop.
+ EXPECT_FALSE(isKnownOnEveryIteration(SE, SLE, O, I, Outer));
+ });
+}
+
TEST_F(ScalarEvolutionsTest, MatchURem) {
LLVMContext C;
SMDiagnostic Err;
More information about the llvm-branch-commits
mailing list