[llvm] [SCEV] Check whether the start is non-zero in `ScalarEvolution::howFarToZero` (PR #131522)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 16 07:47:00 PDT 2025


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/131522

https://github.com/llvm/llvm-project/pull/94525 assumes that the loop will be infinite when the stride is zero. However, it doesn't hold when the start value of addrec is also zero.

Closes https://github.com/llvm/llvm-project/issues/131465.


>From ddb05e7e0d4428711e6ab48dddef04058dfa47c4 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 16 Mar 2025 21:38:22 +0800
Subject: [PATCH 1/2] [SCEV][LoopUnroll] Add pre-commit tests. NFC.

---
 .../trip-count-unknown-stride.ll              | 21 +++++++
 llvm/test/Transforms/LoopUnroll/pr131465.ll   | 57 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopUnroll/pr131465.ll

diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
index 2d02cb6194f4c..ee3586de037ba 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
@@ -493,6 +493,27 @@ for.end:                                          ; preds = %for.body, %entry
   ret void
 }
 
+define i32 @pr131465(i1 %x) mustprogress {
+; CHECK-LABEL: 'pr131465'
+; CHECK-NEXT:  Determining loop execution counts for: @pr131465
+; CHECK-NEXT:  Loop %for.body: backedge-taken count is ((-1 * (zext i1 %x to i32))<nuw><nsw> /u (zext i1 %x to i32))
+; CHECK-NEXT:  Loop %for.body: constant max backedge-taken count is i32 -1
+; CHECK-NEXT:  Loop %for.body: symbolic max backedge-taken count is ((-1 * (zext i1 %x to i32))<nuw><nsw> /u (zext i1 %x to i32))
+; CHECK-NEXT:  Loop %for.body: Trip multiple is 1
+;
+entry:
+  %inc = zext i1 %x to i32
+  br label %for.body
+
+for.body:
+  %indvar = phi i32 [ 2, %entry ], [ %next, %for.body ]
+  %next = add nsw i32 %indvar, %inc
+  %exitcond = icmp eq i32 %next, 2
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:
+  ret i32 0
+}
 
 declare void @llvm.assume(i1)
 
diff --git a/llvm/test/Transforms/LoopUnroll/pr131465.ll b/llvm/test/Transforms/LoopUnroll/pr131465.ll
new file mode 100644
index 0000000000000..25bc377016345
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/pr131465.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=loop-unroll -unroll-runtime %s | FileCheck %s
+
+define i32 @pr131465(i1 %x) mustprogress {
+; CHECK-LABEL: define i32 @pr131465(
+; CHECK-SAME: i1 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[INC:%.*]] = zext i1 [[X]] to i32
+; CHECK-NEXT:    [[TMP0:%.*]] = sub i32 0, [[INC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = udiv i32 [[TMP0]], [[INC]]
+; CHECK-NEXT:    [[TMP2:%.*]] = add i32 [[TMP1]], 1
+; CHECK-NEXT:    [[XTRAITER:%.*]] = and i32 [[TMP2]], 1
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ult i32 [[TMP1]], 1
+; CHECK-NEXT:    br i1 [[TMP3]], label %[[FOR_END_UNR_LCSSA:.*]], label %[[ENTRY_NEW:.*]]
+; CHECK:       [[ENTRY_NEW]]:
+; CHECK-NEXT:    [[UNROLL_ITER:%.*]] = sub i32 [[TMP2]], [[XTRAITER]]
+; CHECK-NEXT:    br label %[[FOR_BODY:.*]]
+; CHECK:       [[FOR_BODY]]:
+; CHECK-NEXT:    [[INDVAR:%.*]] = phi i32 [ 2, %[[ENTRY_NEW]] ], [ [[NEXT_1:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT:    [[NITER:%.*]] = phi i32 [ 0, %[[ENTRY_NEW]] ], [ [[NITER_NEXT_1:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT:    [[NEXT:%.*]] = add nsw i32 [[INDVAR]], [[INC]]
+; CHECK-NEXT:    [[NEXT_1]] = add nsw i32 [[NEXT]], [[INC]]
+; CHECK-NEXT:    [[NITER_NEXT_1]] = add i32 [[NITER]], 2
+; CHECK-NEXT:    [[NITER_NCMP_1:%.*]] = icmp eq i32 [[NITER_NEXT_1]], [[UNROLL_ITER]]
+; CHECK-NEXT:    br i1 [[NITER_NCMP_1]], label %[[FOR_END_UNR_LCSSA_LOOPEXIT:.*]], label %[[FOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK:       [[FOR_END_UNR_LCSSA_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[FOR_END_UNR_LCSSA]]
+; CHECK:       [[FOR_END_UNR_LCSSA]]:
+; CHECK-NEXT:    [[LCMP_MOD:%.*]] = icmp ne i32 [[XTRAITER]], 0
+; CHECK-NEXT:    br i1 [[LCMP_MOD]], label %[[FOR_BODY_EPIL_PREHEADER:.*]], label %[[FOR_END:.*]]
+; CHECK:       [[FOR_BODY_EPIL_PREHEADER]]:
+; CHECK-NEXT:    br label %[[FOR_BODY_EPIL:.*]]
+; CHECK:       [[FOR_BODY_EPIL]]:
+; CHECK-NEXT:    br label %[[FOR_END]]
+; CHECK:       [[FOR_END]]:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %inc = zext i1 %x to i32
+  br label %for.body
+
+for.body:
+  %indvar = phi i32 [ 2, %entry ], [ %next, %for.body ]
+  %next = add nsw i32 %indvar, %inc
+  %exitcond = icmp eq i32 %next, 2
+  br i1 %exitcond, label %for.end, label %for.body, !llvm.loop !0
+
+for.end:
+  ret i32 0
+}
+
+; Force runtime unrolling.
+!0 = !{!0, !{!"llvm.loop.unroll.count", i32 2}}
+;.
+; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
+;.

>From 3282a055d87466785c5f1f0392217827b1b161da Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 16 Mar 2025 22:23:53 +0800
Subject: [PATCH 2/2] [SCEV] Check whether the start is non-zero in
 `ScalarEvolution::howFarToZero`

---
 llvm/lib/Analysis/ScalarEvolution.cpp         |  9 ++---
 .../trip-count-unknown-stride.ll              | 21 +++++-------
 llvm/test/Transforms/LoopUnroll/pr131465.ll   | 34 ++++++-------------
 3 files changed, 24 insertions(+), 40 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 8f74c1c398ced..314baa7c7aee1 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -10635,10 +10635,11 @@ ScalarEvolution::ExitLimit ScalarEvolution::howFarToZero(const SCEV *V,
   if (ControlsOnlyExit && AddRec->hasNoSelfWrap() &&
       loopHasNoAbnormalExits(AddRec->getLoop())) {
 
-    // If the stride is zero, the loop must be infinite.  In C++, most loops
-    // are finite by assumption, in which case the step being zero implies
-    // UB must execute if the loop is entered.
-    if (!loopIsFiniteByAssumption(L) && !isKnownNonZero(StepWLG))
+    // If the stride is zero and the start is non-zero, the loop must be
+    // infinite. In C++, most loops are finite by assumption, in which case the
+    // step being zero implies UB must execute if the loop is entered.
+    if (!(loopIsFiniteByAssumption(L) && isKnownNonZero(Start)) &&
+        !isKnownNonZero(StepWLG))
       return getCouldNotCompute();
 
     const SCEV *Exact =
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
index ee3586de037ba..1f08a620b2e15 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-unknown-stride.ll
@@ -329,10 +329,9 @@ define void @ne_nsw_nonneg_step(ptr nocapture %A, i32 %n, i32 %s) mustprogress {
 ;
 ; CHECK-LABEL: 'ne_nsw_nonneg_step'
 ; CHECK-NEXT:  Determining loop execution counts for: @ne_nsw_nonneg_step
-; CHECK-NEXT:  Loop %for.body: backedge-taken count is (((-1 * %s) + %n) /u %s)
-; CHECK-NEXT:  Loop %for.body: constant max backedge-taken count is i32 -1
-; CHECK-NEXT:  Loop %for.body: symbolic max backedge-taken count is (((-1 * %s) + %n) /u %s)
-; CHECK-NEXT:  Loop %for.body: Trip multiple is 1
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable symbolic max backedge-taken count.
 ;
 entry:
   %nonneg_step = icmp sge i32 %s, 0
@@ -442,10 +441,9 @@ define void @ne_nuw_nonneg_step(ptr nocapture %A, i32 %n, i32 %s) mustprogress {
 ;
 ; CHECK-LABEL: 'ne_nuw_nonneg_step'
 ; CHECK-NEXT:  Determining loop execution counts for: @ne_nuw_nonneg_step
-; CHECK-NEXT:  Loop %for.body: backedge-taken count is (((-1 * %s) + %n) /u %s)
-; CHECK-NEXT:  Loop %for.body: constant max backedge-taken count is i32 -1
-; CHECK-NEXT:  Loop %for.body: symbolic max backedge-taken count is (((-1 * %s) + %n) /u %s)
-; CHECK-NEXT:  Loop %for.body: Trip multiple is 1
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable symbolic max backedge-taken count.
 ;
 entry:
   %nonneg_step = icmp sge i32 %s, 0
@@ -496,10 +494,9 @@ for.end:                                          ; preds = %for.body, %entry
 define i32 @pr131465(i1 %x) mustprogress {
 ; CHECK-LABEL: 'pr131465'
 ; CHECK-NEXT:  Determining loop execution counts for: @pr131465
-; CHECK-NEXT:  Loop %for.body: backedge-taken count is ((-1 * (zext i1 %x to i32))<nuw><nsw> /u (zext i1 %x to i32))
-; CHECK-NEXT:  Loop %for.body: constant max backedge-taken count is i32 -1
-; CHECK-NEXT:  Loop %for.body: symbolic max backedge-taken count is ((-1 * (zext i1 %x to i32))<nuw><nsw> /u (zext i1 %x to i32))
-; CHECK-NEXT:  Loop %for.body: Trip multiple is 1
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable symbolic max backedge-taken count.
 ;
 entry:
   %inc = zext i1 %x to i32
diff --git a/llvm/test/Transforms/LoopUnroll/pr131465.ll b/llvm/test/Transforms/LoopUnroll/pr131465.ll
index 25bc377016345..643b020c6c110 100644
--- a/llvm/test/Transforms/LoopUnroll/pr131465.ll
+++ b/llvm/test/Transforms/LoopUnroll/pr131465.ll
@@ -4,34 +4,18 @@
 define i32 @pr131465(i1 %x) mustprogress {
 ; CHECK-LABEL: define i32 @pr131465(
 ; CHECK-SAME: i1 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    [[INC:%.*]] = zext i1 [[X]] to i32
-; CHECK-NEXT:    [[TMP0:%.*]] = sub i32 0, [[INC]]
-; CHECK-NEXT:    [[TMP1:%.*]] = udiv i32 [[TMP0]], [[INC]]
-; CHECK-NEXT:    [[TMP2:%.*]] = add i32 [[TMP1]], 1
-; CHECK-NEXT:    [[XTRAITER:%.*]] = and i32 [[TMP2]], 1
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp ult i32 [[TMP1]], 1
-; CHECK-NEXT:    br i1 [[TMP3]], label %[[FOR_END_UNR_LCSSA:.*]], label %[[ENTRY_NEW:.*]]
-; CHECK:       [[ENTRY_NEW]]:
-; CHECK-NEXT:    [[UNROLL_ITER:%.*]] = sub i32 [[TMP2]], [[XTRAITER]]
 ; CHECK-NEXT:    br label %[[FOR_BODY:.*]]
 ; CHECK:       [[FOR_BODY]]:
-; CHECK-NEXT:    [[INDVAR:%.*]] = phi i32 [ 2, %[[ENTRY_NEW]] ], [ [[NEXT_1:%.*]], %[[FOR_BODY]] ]
-; CHECK-NEXT:    [[NITER:%.*]] = phi i32 [ 0, %[[ENTRY_NEW]] ], [ [[NITER_NEXT_1:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT:    [[INDVAR:%.*]] = phi i32 [ 2, %[[ENTRY]] ], [ [[NEXT_1:%.*]], %[[FOR_BODY_1:.*]] ]
 ; CHECK-NEXT:    [[NEXT:%.*]] = add nsw i32 [[INDVAR]], [[INC]]
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i32 [[NEXT]], 2
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[FOR_END:.*]], label %[[FOR_BODY_1]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK:       [[FOR_BODY_1]]:
 ; CHECK-NEXT:    [[NEXT_1]] = add nsw i32 [[NEXT]], [[INC]]
-; CHECK-NEXT:    [[NITER_NEXT_1]] = add i32 [[NITER]], 2
-; CHECK-NEXT:    [[NITER_NCMP_1:%.*]] = icmp eq i32 [[NITER_NEXT_1]], [[UNROLL_ITER]]
-; CHECK-NEXT:    br i1 [[NITER_NCMP_1]], label %[[FOR_END_UNR_LCSSA_LOOPEXIT:.*]], label %[[FOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
-; CHECK:       [[FOR_END_UNR_LCSSA_LOOPEXIT]]:
-; CHECK-NEXT:    br label %[[FOR_END_UNR_LCSSA]]
-; CHECK:       [[FOR_END_UNR_LCSSA]]:
-; CHECK-NEXT:    [[LCMP_MOD:%.*]] = icmp ne i32 [[XTRAITER]], 0
-; CHECK-NEXT:    br i1 [[LCMP_MOD]], label %[[FOR_BODY_EPIL_PREHEADER:.*]], label %[[FOR_END:.*]]
-; CHECK:       [[FOR_BODY_EPIL_PREHEADER]]:
-; CHECK-NEXT:    br label %[[FOR_BODY_EPIL:.*]]
-; CHECK:       [[FOR_BODY_EPIL]]:
-; CHECK-NEXT:    br label %[[FOR_END]]
+; CHECK-NEXT:    [[EXITCOND_1:%.*]] = icmp eq i32 [[NEXT_1]], 2
+; CHECK-NEXT:    br i1 [[EXITCOND_1]], label %[[FOR_END]], label %[[FOR_BODY]], !llvm.loop [[LOOP2:![0-9]+]]
 ; CHECK:       [[FOR_END]]:
 ; CHECK-NEXT:    ret i32 0
 ;
@@ -53,5 +37,7 @@ for.end:
 !0 = !{!0, !{!"llvm.loop.unroll.count", i32 2}}
 ;.
 ; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
-; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.count", i32 2}
+; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{!"llvm.loop.unroll.disable"}
 ;.



More information about the llvm-commits mailing list