[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