[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