[llvm] [LSR] Return null from getExactSDiv for a zero divisor (PR #226703)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 26 08:14:57 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Akash Manna (akash-manna-sky)
<details>
<summary>Changes</summary>
Fixes #<!-- -->226063
`getExactSDiv` in LSR assumed its divisor is never zero, which holds for the strides SCEV hands out directly since `{X,+,0}` folds to `X`. But `CollectInterestingTypesAndFactors` sign-extends the strides of narrower IVs before pairing them with wider ones, and that extension can fold to a constant zero when SCEV knows the narrow stride is a multiple of the narrow type's modulus. In the reproducer the i64 IV steps by `-4 * %v` where `%v` is known to be `2^30`, and a truncated i32 copy of the same value steps by `4 * trunc(%v)`. SCEV keeps that symbolic in i32, but once widened it re-truncates `%v` to i30, sees only zero bits, and folds the whole stride to `0`, so `getExactSDiv` recursed into the constant of the i64 stride and did `-4 srem 0`. Nothing in LSR changed here recently, by the way; the bug is old, trunk just started handing LSR the unfolded constant chain that exposes it.
`getExactSDiv` now returns null when the divisor is zero, the same answer it gives for any other quotient it cannot determine, so the callers simply treat the pair as having no common factor. `OptimizeLoopTermCond` widens strides the same way before dividing, so putting the check in the helper covers that path too.
---
Full diff: https://github.com/llvm/llvm-project/pull/226703.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp (+4)
- (added) llvm/test/Transforms/LoopStrengthReduce/pr226063.ll (+45)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 08a4a2f95033e..b2961f80226af 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -825,6 +825,10 @@ static bool isMulSExtable(const SCEVMulExpr *M, ScalarEvolution &SE) {
static const SCEV *getExactSDiv(const SCEV *LHS, const SCEV *RHS,
ScalarEvolution &SE,
bool IgnoreSignificantBits = false) {
+ // A stride can fold to zero once the callers sign-extend it.
+ if (RHS->isZero())
+ return nullptr;
+
// Handle the trivial case, which works for any SCEV type.
if (LHS == RHS)
return SE.getConstant(LHS->getType(), 1);
diff --git a/llvm/test/Transforms/LoopStrengthReduce/pr226063.ll b/llvm/test/Transforms/LoopStrengthReduce/pr226063.ll
new file mode 100644
index 0000000000000..84697c6aa873d
--- /dev/null
+++ b/llvm/test/Transforms/LoopStrengthReduce/pr226063.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=loop-reduce < %s | FileCheck %s
+
+; %x has 30 known trailing zeros, so the i32 IV stride 4 * trunc(%x) is not a
+; constant but folds to 0 once sign-extended to i64. Make sure LSR does not
+; divide the i64 stride by it while collecting factors.
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+declare void @use(i32)
+
+define void @test(i64 range(i64 1073741824, 1073741825) %x) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: i64 range(i64 1073741824, 1073741825) [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[STEP:%.*]] = shl i64 [[X]], 2
+; CHECK-NEXT: [[TMP0:%.*]] = trunc i64 [[X]] to i32
+; CHECK-NEXT: [[TMP1:%.*]] = shl i32 [[TMP0]], 2
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[LSR_IV1:%.*]] = phi i64 [ [[LSR_IV_NEXT2:%.*]], %[[LOOP]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: [[LSR_IV:%.*]] = phi i32 [ [[LSR_IV_NEXT:%.*]], %[[LOOP]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: call void @use(i32 [[LSR_IV]])
+; CHECK-NEXT: [[LSR_IV_NEXT]] = add i32 [[LSR_IV]], [[TMP1]]
+; CHECK-NEXT: [[LSR_IV_NEXT2]] = sub i64 [[LSR_IV1]], [[STEP]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[LSR_IV_NEXT2]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %step = shl i64 %x, 2
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %iv.next = add i64 %iv, %step
+ %t = trunc i64 %iv to i32
+ call void @use(i32 %t)
+ %cmp = icmp eq i64 %iv.next, 0
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret void
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/226703
More information about the llvm-commits
mailing list