[llvm] [SCEV] Compute predicated backedge-taken count for non-unit-stride loops (PR #208581)
Bavly Peter via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 00:18:37 PDT 2026
https://github.com/bavly-bob updated https://github.com/llvm/llvm-project/pull/208581
>From 9d2e6318c6b74e64381955c111ce3f05efe1a55c Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Thu, 9 Jul 2026 23:15:49 +0300
Subject: [PATCH 1/4] feat: Scev predicated overflow btc and testing
---
llvm/lib/Analysis/ScalarEvolution.cpp | 22 +++++++--
.../ScalarEvolution/fwrapv-assume-safety.ll | 34 ++++++++++++++
.../LoopVectorize/fwrapv-assume-safety.ll | 45 +++++++++++++++++++
.../Analysis/ScalarEvolutionTest.cpp | 32 +++++++++++++
4 files changed, 130 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
create mode 100644 llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ea2d72db97fb08..69f0b9c315a301 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13567,11 +13567,27 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
Stride = getUMaxExpr(Stride, getOne(Stride->getType()));
}
}
- } else if (!NoWrap) {
+ }else if (!NoWrap) {
// Avoid proven overflow cases: this will ensure that the backedge taken
// count will not generate any unsigned overflow.
- if (canIVOverflowOnLT(RHS, Stride, IsSigned))
- return getCouldNotCompute();
+ if (canIVOverflowOnLT(RHS, Stride, IsSigned)) {
+ if (!AllowPredicates)
+ return getCouldNotCompute();
+
+ unsigned BitWidth = getTypeSizeInBits(RHS->getType());
+ const SCEV *One = getOne(Stride->getType());
+ const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
+
+ APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
+ : getUnsignedRangeMax(StrideMinusOne);
+ APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
+ : APInt::getMaxValue(BitWidth)) -
+ MaxStrideMinusOne;
+
+ Predicates.push_back(getComparePredicate(
+ IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE, RHS,
+ getConstant(Limit)));
+ }
}
// On all paths just preceeding, we established the following invariant:
diff --git a/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll b/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
new file mode 100644
index 00000000000000..f8f7778cda5073
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
@@ -0,0 +1,34 @@
+; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" 2>&1 | FileCheck %s
+
+; Verify that ScalarEvolution can compute a predicated backedge-taken count
+; for a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
+; induction variable (e.g. from compilation under -fwrapv).
+; Without the predicate, the backedge-taken count is unpredictable.
+
+define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: Determining loop execution counts for: @fwrapv_stride3
+; CHECK-NEXT: Loop %loop.body: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Predicated backedge-taken count is
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %u sle) 2147483645
+
+entry:
+ %cmp1 = icmp slt i32 %l, %u
+ br i1 %cmp1, label %loop.body, label %exit
+
+loop.body:
+ %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+ %idxprom = sext i32 %i to i64
+ %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+ %val = load i32, ptr %arrayidx, align 4
+ %inc = add nsw i32 %val, 1
+ store i32 %inc, ptr %arrayidx, align 4
+ %i.next = add i32 %i, 3
+ %cmp = icmp slt i32 %i.next, %u
+ br i1 %cmp, label %loop.body, label %exit
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
new file mode 100644
index 00000000000000..6987c4ba662014
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
@@ -0,0 +1,45 @@
+; RUN: opt -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -S < %s | FileCheck %s
+
+; Verify that a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
+; induction variable (e.g. from compilation with -fwrapv) can be vectorized
+; by generating a runtime SCEV check to verify the lack of overflow/wrapping.
+
+define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: @fwrapv_stride3(
+; CHECK: vector.scevcheck:
+; CHECK: %ident.check = icmp sgt i32 %u, 2147483645
+; CHECK: %mul = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 3, i32 {{.*}})
+; CHECK: %mul.result = extractvalue { i32, i1 } %mul, 0
+; CHECK: %mul.overflow = extractvalue { i32, i1 } %mul, 1
+; CHECK: [[IDENT:%.*]] = add i32 %l, %mul.result
+; CHECK: [[OVERFLOW:%.*]] = icmp slt i32 [[IDENT]], %l
+; CHECK: [[OVERFLOW2:%.*]] = or i1 [[OVERFLOW]], %mul.overflow
+; CHECK: [[CHECK:%.*]] = or i1 %ident.check, [[OVERFLOW2]]
+; CHECK: br i1 [[CHECK]], label %scalar.ph, label %vector.ph
+; CHECK: vector.body:
+; CHECK: br i1 {{.*}}, label %middle.block, label %vector.body
+; CHECK: scalar.ph:
+; CHECK: [[RESUME_PHI:%.*]] = phi i32
+; CHECK: br label %loop.body
+
+entry:
+ %cmp1 = icmp slt i32 %l, %u
+ br i1 %cmp1, label %loop.body, label %exit
+
+loop.body:
+ %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+ %idxprom = sext i32 %i to i64
+ %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+ %val = load i32, ptr %arrayidx, align 4
+ %inc = add nsw i32 %val, 1
+ store i32 %inc, ptr %arrayidx, align 4
+ %i.next = add i32 %i, 3
+ %cmp = icmp slt i32 %i.next, %u
+ br i1 %cmp, label %loop.body, label %exit, !llvm.loop !0
+
+exit:
+ ret void
+}
+
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.vectorize.enable", i1 true}
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 4bd4463dda787f..849a7fec8fe510 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1495,6 +1495,38 @@ TEST_F(ScalarEvolutionsTest, ImpliedCond) {
});
}
+TEST_F(ScalarEvolutionsTest, PredicatedBackedgeTakenCountForOverflowingStride) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "define void @foo(i32 %n) { "
+ "entry: "
+ " br label %loop "
+ "loop: "
+ " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add i32 %iv, 3 "
+ " %cmp = icmp slt i32 %iv, %n "
+ " br i1 %cmp, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "}",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const Loop *L = LI.begin() != LI.end() ? *LI.begin() : nullptr;
+ ASSERT_NE(L, nullptr);
+
+ SmallVector<const SCEVPredicate *, 4> Predicates;
+ const SCEV *BTC = SE.getPredicatedBackedgeTakenCount(L, Predicates);
+
+ EXPECT_FALSE(isa<SCEVCouldNotCompute>(BTC));
+ EXPECT_FALSE(Predicates.empty());
+ });
+}
+
TEST_F(ScalarEvolutionsTest, MatchURem) {
LLVMContext C;
SMDiagnostic Err;
>From c726c54a679e176bb2b9104eb5663a797154a313 Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Fri, 10 Jul 2026 17:12:07 +0300
Subject: [PATCH 2/4] [ScalarEvolution][test] Update expected test output
---
.../addrec-may-wrap-udiv-canonicalize.ll | 45 +++++++
.../ScalarEvolution/max-trip-count.ll | 23 ++++
.../trip-count-non-unit-stride.ll | 112 ++++++++++++++++++
3 files changed, 180 insertions(+)
diff --git a/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll b/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
index e041c963717626..1132e7478c82a8 100644
--- a/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
+++ b/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
@@ -28,6 +28,15 @@ define void @test_step2_div4(i64 %n) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is ((1 + (0 smax %n))<nuw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387903
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is ((1 + (0 smax %n))<nuw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
;
entry:
br label %loop
@@ -78,6 +87,15 @@ define void @test_step3_div6(i64 %n) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is ((((-1 * (1 umin (0 smax %n)))<nuw><nsw> + (0 smax %n)) /u 3) + (1 umin (0 smax %n)))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775805
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 3074457345618258602
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775805
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is ((((-1 * (1 umin (0 smax %n)))<nuw><nsw> + (0 smax %n)) /u 3) + (1 umin (0 smax %n)))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775805
;
entry:
br label %loop
@@ -137,6 +155,15 @@ define void @test_step4_div4(i64 %n) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is ((3 + (0 smax %n))<nuw> /u 4)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775804
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 2305843009213693951
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775804
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is ((3 + (0 smax %n))<nuw> /u 4)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775804
;
entry:
br label %loop
@@ -219,6 +246,15 @@ define void @test_step2_start_outer_add_rec_step_16(i64 %n, i64 %m) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (({1,+,-16}<%outer.header> + ({0,+,16}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775807
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (({1,+,-16}<%outer.header> + ({0,+,16}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
; CHECK-NEXT: Loop %outer.header: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %outer.header: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %outer.header: Unpredictable symbolic max backedge-taken count.
@@ -335,6 +371,15 @@ define void @test_step2_div4_start_outer_add_rec_step_2(i64 %n, i64 %m) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (({1,+,-2}<%outer.header> + ({0,+,2}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775807
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (({1,+,-2}<%outer.header> + ({0,+,2}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 9223372036854775806
; CHECK-NEXT: Loop %outer.header: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %outer.header: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %outer.header: Unpredictable symbolic max backedge-taken count.
diff --git a/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll b/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
index 501aa963f9294d..49051471297d06 100644
--- a/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
+++ b/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
@@ -446,6 +446,12 @@ define void @changing_end_bound4(i32 %start, ptr %n_addr, ptr %addr) {
; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i32 1073741823
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 2147483644
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is i32 1073741823
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 2147483644
;
entry:
br label %loop
@@ -573,10 +579,27 @@ define void @max_overflow_me(i8 %n) mustprogress {
; CHECK-NEXT: Loop %loop: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for loop: i8 1
; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for latch: i8 0
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 65
+; CHECK-EMPTY:
; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 1
; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is i8 1
; CHECK-NEXT: symbolic max exit count for loop: i8 1
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for latch: i8 0
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 65
+; CHECK-EMPTY:
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is i8 0
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 65
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i8 0
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 65
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is i8 0
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %n sle) 65
;
entry:
br label %loop
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
index b52b4a984296c1..ef4d9a3ac43f65 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
@@ -14,11 +14,39 @@ define void @test_preinc_ult(i64 %len) {
; CHECK-NEXT: Determining loop execution counts for: @test_preinc_ult
; CHECK-NEXT: Loop %loop: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for loop: ((1 + %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for loop: ((1 + %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (((1 + %len) /u 2) umin ((-1 + (2 umax %len)) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775806
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (((1 + %len) /u 2) umin ((-1 + (2 umax %len)) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
;
start:
br label %loop
@@ -49,11 +77,39 @@ define void @test_postinc_ult(i64 %len) {
; CHECK-NEXT: Determining loop execution counts for: @test_postinc_ult
; CHECK-NEXT: Loop %loop: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for loop: ((1 umax %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for loop: ((1 umax %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-EMPTY:
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (((-1 + (2 umax %len)) /u 2) umin ((1 umax %len) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775806
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (((-1 + (2 umax %len)) /u 2) umin ((1 umax %len) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len ule) -2
+; CHECK-NEXT: Compare predicate: %len ule) -2
;
start:
br label %loop
@@ -83,11 +139,39 @@ define void @test_preinc_slt(i64 %len) {
; CHECK-NEXT: Determining loop execution counts for: @test_preinc_slt
; CHECK-NEXT: Loop %loop: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for loop: ((1 + (0 smax %len))<nuw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for loop: ((1 + (0 smax %len))<nuw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (((1 + (0 smax %len))<nuw> /u 2) umin ((-1 + (2 smax %len))<nsw> /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387902
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (((1 + (0 smax %len))<nuw> /u 2) umin ((-1 + (2 smax %len))<nsw> /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
;
start:
br label %loop
@@ -118,11 +202,39 @@ define void @test_postinc_slt(i64 %len) {
; CHECK-NEXT: Determining loop execution counts for: @test_postinc_slt
; CHECK-NEXT: Loop %loop: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for loop: ((1 smax %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for loop: ((1 smax %len) /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: predicated symbolic max exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (((-1 + (2 smax %len))<nsw> /u 2) umin ((1 smax %len) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387902
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Loop %loop: Predicated symbolic max backedge-taken count is (((-1 + (2 smax %len))<nsw> /u 2) umin ((1 smax %len) /u 2))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT: Compare predicate: %len sle) 9223372036854775806
;
start:
br label %loop
>From 3fa46258ed3ac2f0decd92026c49484ae124ac69 Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Wed, 2 Sep 2026 17:40:56 +0300
Subject: [PATCH 3/4] [SCEV] Avoid creating compare predicates before pointer
IV normalization howManyLessThans() can receive pointer-typed induction
variables. Creating a compare predicate before converting the pointer SCEV to
an integer SCEV violates SCEVComparePredicate's type equality invariant. Move
predicate creation after pointer normalization so both operands have matching
integer types.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 85 ++++++++++---------
.../LoopVectorize/fwrapv-assume-safety.ll | 2 +-
.../Analysis/ScalarEvolutionTest.cpp | 26 +++---
3 files changed, 61 insertions(+), 52 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index b06bc2512f7357..9c9471147a8b7a 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13463,8 +13463,27 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
ICmpInst::Predicate Cond = IsSigned ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT;
const SCEV *Stride = IV->getStepRecurrence(*this);
+ const SCEV *Start = IV->getStart();
+
+ // Preserve pointer-typed Start/RHS to pass to isLoopEntryGuardedByCond.
+ // If we convert to integers, isLoopEntryGuardedByCond will miss some cases.
+ // Use integer-typed versions for actual computation; we can't subtract
+ // pointers in general.
+ const SCEV *OrigStart = Start;
+ const SCEV *OrigRHS = RHS;
+ if (Start->getType()->isPointerTy()) {
+ Start = getPtrToAddrExpr(Start);
+ if (isa<SCEVCouldNotCompute>(Start))
+ return Start;
+ }
+ if (RHS->getType()->isPointerTy()) {
+ RHS = getPtrToAddrExpr(RHS);
+ if (isa<SCEVCouldNotCompute>(RHS))
+ return RHS;
+ }
bool PositiveStride = isKnownPositive(Stride);
+ bool IVcanOverFlowOnLT = false;
// Avoid negative or zero stride values.
if (!PositiveStride) {
@@ -13535,27 +13554,14 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
Stride = getUMaxExpr(Stride, getOne(Stride->getType()));
}
}
- }else if (!NoWrap) {
- // Avoid proven overflow cases: this will ensure that the backedge taken
- // count will not generate any unsigned overflow.
- if (canIVOverflowOnLT(RHS, Stride, IsSigned)) {
- if (!AllowPredicates)
- return getCouldNotCompute();
-
- unsigned BitWidth = getTypeSizeInBits(RHS->getType());
- const SCEV *One = getOne(Stride->getType());
- const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
-
- APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
- : getUnsignedRangeMax(StrideMinusOne);
- APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
- : APInt::getMaxValue(BitWidth)) -
- MaxStrideMinusOne;
-
- Predicates.push_back(getComparePredicate(
- IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE, RHS,
- getConstant(Limit)));
- }
+ } else if (!NoWrap) {
+ // Avoid proven overflow cases: this will ensure that the backedge taken
+ // count will not generate any unsigned overflow.
+ IVcanOverFlowOnLT = canIVOverflowOnLT(RHS, Stride, IsSigned);
+ if (IVcanOverFlowOnLT) {
+ if (!AllowPredicates)
+ return getCouldNotCompute();
+ }
}
// On all paths just preceeding, we established the following invariant:
@@ -13567,23 +13573,26 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
// before any possible exit.
// Note that we have not yet proved RHS invariant (in general).
- const SCEV *Start = IV->getStart();
-
- // Preserve pointer-typed Start/RHS to pass to isLoopEntryGuardedByCond.
- // If we convert to integers, isLoopEntryGuardedByCond will miss some cases.
- // Use integer-typed versions for actual computation; we can't subtract
- // pointers in general.
- const SCEV *OrigStart = Start;
- const SCEV *OrigRHS = RHS;
- if (Start->getType()->isPointerTy()) {
- Start = getPtrToAddrExpr(Start);
- if (isa<SCEVCouldNotCompute>(Start))
- return Start;
- }
- if (RHS->getType()->isPointerTy()) {
- RHS = getPtrToAddrExpr(RHS);
- if (isa<SCEVCouldNotCompute>(RHS))
- return RHS;
+ // RHS has been normalized to an integer type above (converted from a
+ // pointer type via getPtrToAddrExpr, if it started out as one), so
+ // RHS->getType() is guaranteed to be an integer type here. Combined with
+ // getConstant(Limit) also producing an integer type of the same BitWidth,
+ // this ensures RHS and getConstant(Limit) have identical LLVM types, as
+ // SCEVComparePredicate requires.
+ if (!NoWrap && IVcanOverFlowOnLT) {
+ unsigned BitWidth = getTypeSizeInBits(RHS->getType());
+ const SCEV *One = getOne(Stride->getType());
+ const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
+
+ APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
+ : getUnsignedRangeMax(StrideMinusOne);
+ APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
+ : APInt::getMaxValue(BitWidth)) -
+ MaxStrideMinusOne;
+
+ Predicates.push_back(
+ getComparePredicate(IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE,
+ RHS, getConstant(Limit)));
}
const SCEV *End = nullptr, *BECount = nullptr,
diff --git a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
index 6987c4ba662014..d34caf59e7ddcc 100644
--- a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
+++ b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
@@ -42,4 +42,4 @@ exit:
}
!0 = distinct !{!0, !1}
-!1 = !{!"llvm.loop.vectorize.enable", i1 true}
+!1 = !{!"llvm.loop.vectorize.enable"}
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index cf048408baf8d5..caa50513423d6b 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1538,19 +1538,19 @@ TEST_F(ScalarEvolutionsTest, ImpliedCond) {
TEST_F(ScalarEvolutionsTest, PredicatedBackedgeTakenCountForOverflowingStride) {
LLVMContext C;
SMDiagnostic Err;
- std::unique_ptr<Module> M = parseAssemblyString(
- "define void @foo(i32 %n) { "
- "entry: "
- " br label %loop "
- "loop: "
- " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] "
- " %iv.next = add i32 %iv, 3 "
- " %cmp = icmp slt i32 %iv, %n "
- " br i1 %cmp, label %loop, label %exit "
- "exit: "
- " ret void "
- "}",
- Err, C);
+ std::unique_ptr<Module> M =
+ parseAssemblyString("define void @foo(i32 %n) { "
+ "entry: "
+ " br label %loop "
+ "loop: "
+ " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] "
+ " %iv.next = add i32 %iv, 3 "
+ " %cmp = icmp slt i32 %iv, %n "
+ " br i1 %cmp, label %loop, label %exit "
+ "exit: "
+ " ret void "
+ "}",
+ Err, C);
ASSERT_TRUE(M && "Could not parse module?");
ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
>From b8758493b90b1e4fbe762f0881054c130171e279 Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Fri, 11 Sep 2026 16:27:59 +0300
Subject: [PATCH 4/4] [SCEV] Fix indentation and simplify predicate comment in
howManyLessThans()
The else-if (!NoWrap) body was over-indented by two spaces. Fix it to
match the surrounding style.
Replace a now-redundant comment about pointer-to-integer normalization
(handled by the prior commit) with a short description of what the
predicate enforces.
Remove the PredicatedBackedgeTakenCountForOverflowingStride unit test;
its coverage is superseded by the lit test in
trip-count-non-unit-stride.ll.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 22 ++--
.../ScalarEvolution/fwrapv-assume-safety.ll | 34 -----
.../trip-count-non-unit-stride.ll | 53 ++++++++
.../LoopVectorize/fwrapv-assume-safety.ll | 124 +++++++++++++++---
.../Analysis/ScalarEvolutionTest.cpp | 32 -----
5 files changed, 169 insertions(+), 96 deletions(-)
delete mode 100644 llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index de29d046b34e1c..a99c67ac26b207 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13522,13 +13522,13 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
}
}
} else if (!NoWrap) {
- // Avoid proven overflow cases: this will ensure that the backedge taken
- // count will not generate any unsigned overflow.
- IVcanOverFlowOnLT = canIVOverflowOnLT(RHS, Stride, IsSigned);
- if (IVcanOverFlowOnLT) {
- if (!AllowPredicates)
- return getCouldNotCompute();
- }
+ // Avoid proven overflow cases: this will ensure that the backedge taken
+ // count will not generate any unsigned overflow.
+ IVcanOverFlowOnLT = canIVOverflowOnLT(RHS, Stride, IsSigned);
+ if (IVcanOverFlowOnLT) {
+ if (!AllowPredicates)
+ return getCouldNotCompute();
+ }
}
// On all paths just preceeding, we established the following invariant:
@@ -13540,12 +13540,8 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
// before any possible exit.
// Note that we have not yet proved RHS invariant (in general).
- // RHS has been normalized to an integer type above (converted from a
- // pointer type via getPtrToAddrExpr, if it started out as one), so
- // RHS->getType() is guaranteed to be an integer type here. Combined with
- // getConstant(Limit) also producing an integer type of the same BitWidth,
- // this ensures RHS and getConstant(Limit) have identical LLVM types, as
- // SCEVComparePredicate requires.
+ // Add a predicate to ensure RHS does not exceed the maximum value
+ // that can be represented without overflow, given the stride.
if (!NoWrap && IVcanOverFlowOnLT) {
unsigned BitWidth = getTypeSizeInBits(RHS->getType());
const SCEV *One = getOne(Stride->getType());
diff --git a/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll b/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
deleted file mode 100644
index f8f7778cda5073..00000000000000
--- a/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
+++ /dev/null
@@ -1,34 +0,0 @@
-; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" 2>&1 | FileCheck %s
-
-; Verify that ScalarEvolution can compute a predicated backedge-taken count
-; for a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
-; induction variable (e.g. from compilation under -fwrapv).
-; Without the predicate, the backedge-taken count is unpredictable.
-
-define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
-; CHECK-LABEL: Determining loop execution counts for: @fwrapv_stride3
-; CHECK-NEXT: Loop %loop.body: Unpredictable backedge-taken count.
-; CHECK-NEXT: Loop %loop.body: Unpredictable constant max backedge-taken count.
-; CHECK-NEXT: Loop %loop.body: Unpredictable symbolic max backedge-taken count.
-; CHECK-NEXT: Loop %loop.body: Predicated backedge-taken count is
-; CHECK-NEXT: Predicates:
-; CHECK-NEXT: Compare predicate: %u sle) 2147483645
-
-entry:
- %cmp1 = icmp slt i32 %l, %u
- br i1 %cmp1, label %loop.body, label %exit
-
-loop.body:
- %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
- %idxprom = sext i32 %i to i64
- %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
- %val = load i32, ptr %arrayidx, align 4
- %inc = add nsw i32 %val, 1
- store i32 %inc, ptr %arrayidx, align 4
- %i.next = add i32 %i, 3
- %cmp = icmp slt i32 %i.next, %u
- br i1 %cmp, label %loop.body, label %exit
-
-exit:
- ret void
-}
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
index ef4d9a3ac43f65..d5bc87d1e696fa 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
@@ -322,3 +322,56 @@ latch:
exit:
ret void
}
+
+; Verify that ScalarEvolution can compute a predicated backedge-taken count
+; for a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
+; induction variable.
+; Without the predicate, the backedge-taken count is unpredictable.
+
+define void @stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: 'stride3'
+; CHECK-NEXT: Classifying expressions for: @stride3
+; CHECK-NEXT: %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+; CHECK-NEXT: --> {%l,+,3}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
+; CHECK-NEXT: %idxprom = sext i32 %i to i64
+; CHECK-NEXT: --> (sext i32 {%l,+,3}<%loop.body> to i64) U: [-2147483648,2147483648) S: [-2147483648,2147483648) Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
+; CHECK-NEXT: %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+; CHECK-NEXT: --> ((4 * (sext i32 {%l,+,3}<%loop.body> to i64))<nsw> + %x) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
+; CHECK-NEXT: %val = load i32, ptr %arrayidx, align 4
+; CHECK-NEXT: --> %val U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Variant }
+; CHECK-NEXT: %inc = add nsw i32 %val, 1
+; CHECK-NEXT: --> (1 + %val) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Variant }
+; CHECK-NEXT: %i.next = add i32 %i, 3
+; CHECK-NEXT: --> {(3 + %l),+,3}<%loop.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop.body: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @stride3
+; CHECK-NEXT: Loop %loop.body: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %loop.body: Predicated backedge-taken count is (((-3 + (-1 * (1 umin (-3 + (-1 * %l) + ((3 + %l) smax %u))))<nuw><nsw> + (-1 * %l) + ((3 + %l) smax %u)) /u 3) + (1 umin (-3 + (-1 * %l) + ((3 + %l) smax %u))))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %u sle) 2147483645
+; CHECK-NEXT: Loop %loop.body: Predicated constant max backedge-taken count is i32 1431655765
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %u sle) 2147483645
+; CHECK-NEXT: Loop %loop.body: Predicated symbolic max backedge-taken count is (((-3 + (-1 * (1 umin (-3 + (-1 * %l) + ((3 + %l) smax %u))))<nuw><nsw> + (-1 * %l) + ((3 + %l) smax %u)) /u 3) + (1 umin (-3 + (-1 * %l) + ((3 + %l) smax %u))))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: Compare predicate: %u sle) 2147483645
+;
+entry:
+ %cmp1 = icmp slt i32 %l, %u
+ br i1 %cmp1, label %loop.body, label %exit
+
+loop.body:
+ %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+ %idxprom = sext i32 %i to i64
+ %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+ %val = load i32, ptr %arrayidx, align 4
+ %inc = add nsw i32 %val, 1
+ store i32 %inc, ptr %arrayidx, align 4
+ %i.next = add i32 %i, 3
+ %cmp = icmp slt i32 %i.next, %u
+ br i1 %cmp, label %loop.body, label %exit
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
index d34caf59e7ddcc..ddfe21597ed42b 100644
--- a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
+++ b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
@@ -1,26 +1,110 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -S < %s | FileCheck %s
; Verify that a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
; induction variable (e.g. from compilation with -fwrapv) can be vectorized
; by generating a runtime SCEV check to verify the lack of overflow/wrapping.
-define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
-; CHECK-LABEL: @fwrapv_stride3(
-; CHECK: vector.scevcheck:
-; CHECK: %ident.check = icmp sgt i32 %u, 2147483645
-; CHECK: %mul = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 3, i32 {{.*}})
-; CHECK: %mul.result = extractvalue { i32, i1 } %mul, 0
-; CHECK: %mul.overflow = extractvalue { i32, i1 } %mul, 1
-; CHECK: [[IDENT:%.*]] = add i32 %l, %mul.result
-; CHECK: [[OVERFLOW:%.*]] = icmp slt i32 [[IDENT]], %l
-; CHECK: [[OVERFLOW2:%.*]] = or i1 [[OVERFLOW]], %mul.overflow
-; CHECK: [[CHECK:%.*]] = or i1 %ident.check, [[OVERFLOW2]]
-; CHECK: br i1 [[CHECK]], label %scalar.ph, label %vector.ph
-; CHECK: vector.body:
-; CHECK: br i1 {{.*}}, label %middle.block, label %vector.body
-; CHECK: scalar.ph:
-; CHECK: [[RESUME_PHI:%.*]] = phi i32
-; CHECK: br label %loop.body
+define void @stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: define void @stride3(
+; CHECK-SAME: ptr noalias [[X:%.*]], i32 [[L:%.*]], i32 [[U:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[L]], [[U]]
+; CHECK-NEXT: br i1 [[CMP1]], label %[[LOOP_BODY_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_BODY_PREHEADER]]:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[L]], 3
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smax.i32(i32 [[U]], i32 [[TMP0]])
+; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[TMP1]], [[L]]
+; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], -3
+; CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.umin.i32(i32 [[TMP3]], i32 1)
+; CHECK-NEXT: [[TMP5:%.*]] = sub i32 [[TMP2]], [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[TMP5]], -3
+; CHECK-NEXT: [[TMP7:%.*]] = udiv i32 [[TMP6]], 3
+; CHECK-NEXT: [[TMP8:%.*]] = add i32 [[TMP4]], [[TMP7]]
+; CHECK-NEXT: [[TMP9:%.*]] = add i32 [[TMP8]], 1
+; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP9]], 4
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_SCEVCHECK:.*]]
+; CHECK: [[VECTOR_SCEVCHECK]]:
+; CHECK-NEXT: [[IDENT_CHECK:%.*]] = icmp sgt i32 [[U]], 2147483645
+; CHECK-NEXT: [[TMP10:%.*]] = add i32 [[L]], 3
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[U]], i32 [[TMP10]])
+; CHECK-NEXT: [[TMP11:%.*]] = add i32 [[SMAX]], -3
+; CHECK-NEXT: [[TMP12:%.*]] = sub i32 [[TMP11]], [[L]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[TMP12]], i32 1)
+; CHECK-NEXT: [[TMP13:%.*]] = sub i32 [[TMP12]], [[UMIN]]
+; CHECK-NEXT: [[TMP14:%.*]] = udiv i32 [[TMP13]], 3
+; CHECK-NEXT: [[TMP15:%.*]] = add i32 [[UMIN]], [[TMP14]]
+; CHECK-NEXT: [[MUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 3, i32 [[TMP15]])
+; CHECK-NEXT: [[MUL_RESULT:%.*]] = extractvalue { i32, i1 } [[MUL]], 0
+; CHECK-NEXT: [[MUL_OVERFLOW:%.*]] = extractvalue { i32, i1 } [[MUL]], 1
+; CHECK-NEXT: [[TMP16:%.*]] = add i32 [[L]], [[MUL_RESULT]]
+; CHECK-NEXT: [[TMP17:%.*]] = icmp slt i32 [[TMP16]], [[L]]
+; CHECK-NEXT: [[TMP18:%.*]] = or i1 [[TMP17]], [[MUL_OVERFLOW]]
+; CHECK-NEXT: [[TMP19:%.*]] = or i1 [[IDENT_CHECK]], [[TMP18]]
+; CHECK-NEXT: br i1 [[TMP19]], label %[[SCALAR_PH]], label %[[VECTOR_PH:.*]]
+; CHECK: [[VECTOR_PH]]:
+; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP9]], 4
+; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP9]], [[N_MOD_VF]]
+; CHECK-NEXT: [[TMP20:%.*]] = mul i32 [[N_VEC]], 3
+; CHECK-NEXT: [[TMP21:%.*]] = add i32 [[L]], [[TMP20]]
+; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <4 x i32> poison, i32 [[L]], i64 0
+; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <4 x i32> [[BROADCAST_SPLATINSERT]], <4 x i32> poison, <4 x i32> zeroinitializer
+; CHECK-NEXT: [[INDUCTION:%.*]] = add <4 x i32> [[BROADCAST_SPLAT]], <i32 0, i32 3, i32 6, i32 9>
+; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
+; CHECK: [[VECTOR_BODY]]:
+; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[VEC_IND:%.*]] = phi <4 x i32> [ [[INDUCTION]], %[[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[TMP22:%.*]] = sext <4 x i32> [[VEC_IND]] to <4 x i64>
+; CHECK-NEXT: [[TMP23:%.*]] = extractelement <4 x i64> [[TMP22]], i64 0
+; CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds i32, ptr [[X]], i64 [[TMP23]]
+; CHECK-NEXT: [[TMP25:%.*]] = extractelement <4 x i64> [[TMP22]], i64 1
+; CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds i32, ptr [[X]], i64 [[TMP25]]
+; CHECK-NEXT: [[TMP27:%.*]] = extractelement <4 x i64> [[TMP22]], i64 2
+; CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds i32, ptr [[X]], i64 [[TMP27]]
+; CHECK-NEXT: [[TMP29:%.*]] = extractelement <4 x i64> [[TMP22]], i64 3
+; CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds i32, ptr [[X]], i64 [[TMP29]]
+; CHECK-NEXT: [[TMP31:%.*]] = load i32, ptr [[TMP24]], align 4
+; CHECK-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP26]], align 4
+; CHECK-NEXT: [[TMP33:%.*]] = load i32, ptr [[TMP28]], align 4
+; CHECK-NEXT: [[TMP34:%.*]] = load i32, ptr [[TMP30]], align 4
+; CHECK-NEXT: [[TMP35:%.*]] = insertelement <4 x i32> poison, i32 [[TMP31]], i32 0
+; CHECK-NEXT: [[TMP36:%.*]] = insertelement <4 x i32> [[TMP35]], i32 [[TMP32]], i32 1
+; CHECK-NEXT: [[TMP37:%.*]] = insertelement <4 x i32> [[TMP36]], i32 [[TMP33]], i32 2
+; CHECK-NEXT: [[TMP38:%.*]] = insertelement <4 x i32> [[TMP37]], i32 [[TMP34]], i32 3
+; CHECK-NEXT: [[TMP39:%.*]] = add nsw <4 x i32> [[TMP38]], splat (i32 1)
+; CHECK-NEXT: [[TMP40:%.*]] = extractelement <4 x i32> [[TMP39]], i64 0
+; CHECK-NEXT: store i32 [[TMP40]], ptr [[TMP24]], align 4
+; CHECK-NEXT: [[TMP41:%.*]] = extractelement <4 x i32> [[TMP39]], i64 1
+; CHECK-NEXT: store i32 [[TMP41]], ptr [[TMP26]], align 4
+; CHECK-NEXT: [[TMP42:%.*]] = extractelement <4 x i32> [[TMP39]], i64 2
+; CHECK-NEXT: store i32 [[TMP42]], ptr [[TMP28]], align 4
+; CHECK-NEXT: [[TMP43:%.*]] = extractelement <4 x i32> [[TMP39]], i64 3
+; CHECK-NEXT: store i32 [[TMP43]], ptr [[TMP30]], align 4
+; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 4
+; CHECK-NEXT: [[VEC_IND_NEXT]] = add <4 x i32> [[VEC_IND]], splat (i32 12)
+; CHECK-NEXT: [[TMP44:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[TMP44]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK: [[MIDDLE_BLOCK]]:
+; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP9]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT_LOOPEXIT:.*]], label %[[SCALAR_PH]]
+; CHECK: [[SCALAR_PH]]:
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i32 [ [[TMP21]], %[[MIDDLE_BLOCK]] ], [ [[L]], %[[LOOP_BODY_PREHEADER]] ], [ [[L]], %[[VECTOR_SCEVCHECK]] ]
+; CHECK-NEXT: br label %[[LOOP_BODY:.*]]
+; CHECK: [[LOOP_BODY]]:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[BC_RESUME_VAL]], %[[SCALAR_PH]] ]
+; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
+; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[X]], i64 [[IDXPROM]]
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[VAL]], 1
+; CHECK-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT: [[I_NEXT]] = add i32 [[I]], 3
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_NEXT]], [[U]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP_BODY]], label %[[EXIT_LOOPEXIT]], !llvm.loop [[LOOP3:![0-9]+]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
entry:
%cmp1 = icmp slt i32 %l, %u
@@ -43,3 +127,9 @@ exit:
!0 = distinct !{!0, !1}
!1 = !{!"llvm.loop.vectorize.enable"}
+;.
+; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]], [[META2:![0-9]+]]}
+; CHECK: [[META1]] = !{!"llvm.loop.isvectorized", i32 1}
+; CHECK: [[META2]] = !{!"llvm.loop.unroll.runtime.disable"}
+; CHECK: [[LOOP3]] = distinct !{[[LOOP3]], [[META1]]}
+;.
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index e75549c5d399a4..a50566cb640939 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1597,38 +1597,6 @@ TEST_F(ScalarEvolutionsTest, ImpliedCond) {
});
}
-TEST_F(ScalarEvolutionsTest, PredicatedBackedgeTakenCountForOverflowingStride) {
- LLVMContext C;
- SMDiagnostic Err;
- std::unique_ptr<Module> M =
- parseAssemblyString("define void @foo(i32 %n) { "
- "entry: "
- " br label %loop "
- "loop: "
- " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] "
- " %iv.next = add i32 %iv, 3 "
- " %cmp = icmp slt i32 %iv, %n "
- " br i1 %cmp, label %loop, label %exit "
- "exit: "
- " ret void "
- "}",
- Err, C);
-
- ASSERT_TRUE(M && "Could not parse module?");
- ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
-
- runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
- const Loop *L = LI.begin() != LI.end() ? *LI.begin() : nullptr;
- ASSERT_NE(L, nullptr);
-
- SmallVector<const SCEVPredicate *, 4> Predicates;
- const SCEV *BTC = SE.getPredicatedBackedgeTakenCount(L, Predicates);
-
- EXPECT_FALSE(isa<SCEVCouldNotCompute>(BTC));
- EXPECT_FALSE(Predicates.empty());
- });
-}
-
TEST_F(ScalarEvolutionsTest, MatchURem) {
LLVMContext C;
SMDiagnostic Err;
More information about the llvm-commits
mailing list