[llvm] e698d03 - [SCEVDivision] Prevent propagating nowrap flags when numerator is an addrec (#154745)

via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 16 01:15:02 PST 2026


Author: Ryotaro Kasuga
Date: 2026-02-16T09:14:57Z
New Revision: e698d03ee24d93f4c316daf218f04fdbae72f0bd

URL: https://github.com/llvm/llvm-project/commit/e698d03ee24d93f4c316daf218f04fdbae72f0bd
DIFF: https://github.com/llvm/llvm-project/commit/e698d03ee24d93f4c316daf218f04fdbae72f0bd.diff

LOG: [SCEVDivision] Prevent propagating nowrap flags when numerator is an addrec (#154745)

In ScalarEvolutionDivision, when the numerator is an addrec, its nowrap
flags are unconditionally propagated to the quotient and remainder.
However, this is not always correct.
This pass is only used by Delinearization, and just dropping these flags
doesn't affect Delinearization's accuracy in almost all cases.
Therefore, this patch conservatively stop propagating the nowrap flags
from the numerator to the quotient and remainder.

Fix #152566

Added: 
    llvm/test/Analysis/Delinearization/wraps.ll

Modified: 
    llvm/lib/Analysis/ScalarEvolutionDivision.cpp
    llvm/test/Analysis/Delinearization/a.ll
    llvm/test/Analysis/Delinearization/divide_by_one.ll
    llvm/test/Analysis/Delinearization/fixed_size_array.ll
    llvm/test/Analysis/Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll
    llvm/test/Analysis/DependenceAnalysis/DADelin.ll
    llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
index 4e422539ff9f6..52d82785f6b9c 100644
--- a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
+++ b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
@@ -141,10 +141,11 @@ void SCEVDivision::visitAddRecExpr(const SCEVAddRecExpr *Numerator) {
   if (Ty != StartQ->getType() || Ty != StartR->getType() ||
       Ty != StepQ->getType() || Ty != StepR->getType())
     return cannotDivide(Numerator);
+
   Quotient = SE.getAddRecExpr(StartQ, StepQ, Numerator->getLoop(),
-                              Numerator->getNoWrapFlags());
+                              SCEV::NoWrapFlags::FlagAnyWrap);
   Remainder = SE.getAddRecExpr(StartR, StepR, Numerator->getLoop(),
-                               Numerator->getNoWrapFlags());
+                               SCEV::NoWrapFlags::FlagAnyWrap);
 }
 
 void SCEVDivision::visitAddExpr(const SCEVAddExpr *Numerator) {

diff  --git a/llvm/test/Analysis/Delinearization/a.ll b/llvm/test/Analysis/Delinearization/a.ll
index 5d2d4dc29206e..d2e0fa5020d73 100644
--- a/llvm/test/Analysis/Delinearization/a.ll
+++ b/llvm/test/Analysis/Delinearization/a.ll
@@ -14,7 +14,7 @@ define void @foo(i64 %n, i64 %m, i64 %o, ptr nocapture %A) #0 {
 ; CHECK-NEXT:  AccessFunction: {{\{\{\{}}(28 + (4 * (-4 + (3 * %m)) * %o)),+,(8 * %m * %o)}<%for.i>,+,(12 * %o)}<%for.j>,+,20}<%for.k>
 ; CHECK-NEXT:  Base offset: %A
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][%m][%o] with elements of 4 bytes.
-; CHECK-NEXT:  ArrayRef[{3,+,2}<nuw><%for.i>][{-4,+,3}<nw><%for.j>][{7,+,5}<nw><%for.k>]
+; CHECK-NEXT:  ArrayRef[{3,+,2}<nuw><%for.i>][{-4,+,3}<%for.j>][{7,+,5}<%for.k>]
 ; CHECK-NEXT:  Delinearization validation: Failed
 ;
 entry:

diff  --git a/llvm/test/Analysis/Delinearization/divide_by_one.ll b/llvm/test/Analysis/Delinearization/divide_by_one.ll
index 3d8e55984291e..fa7f4cead2b00 100644
--- a/llvm/test/Analysis/Delinearization/divide_by_one.ll
+++ b/llvm/test/Analysis/Delinearization/divide_by_one.ll
@@ -17,7 +17,7 @@ define void @test(ptr nocapture %dst, i32 %stride, i32 %bs) {
 ; CHECK-NEXT:  AccessFunction: {{\{\{}}(-1 + ((1 + %bs) * %stride)),+,(-1 * %stride)}<%for.cond1.preheader>,+,1}<nw><%for.body3>
 ; CHECK-NEXT:  Base offset: %dst
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][%stride] with elements of 1 bytes.
-; CHECK-NEXT:  ArrayRef[{(1 + %bs),+,-1}<nw><%for.cond1.preheader>][{-1,+,1}<nw><%for.body3>]
+; CHECK-NEXT:  ArrayRef[{(1 + %bs),+,-1}<nw><%for.cond1.preheader>][{-1,+,1}<%for.body3>]
 ; CHECK-NEXT:  Delinearization validation: Failed
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  Inst: store i8 %0, ptr %arrayidx7, align 1

diff  --git a/llvm/test/Analysis/Delinearization/fixed_size_array.ll b/llvm/test/Analysis/Delinearization/fixed_size_array.ll
index 59e0cffdd0878..7cd90f2bfb61c 100644
--- a/llvm/test/Analysis/Delinearization/fixed_size_array.ll
+++ b/llvm/test/Analysis/Delinearization/fixed_size_array.ll
@@ -203,7 +203,7 @@ define void @a_i_2j1_k(ptr %a) {
 ; CHECK-NEXT:  AccessFunction: {{\{\{\{}}128,+,1024}<nuw><nsw><%for.i.header>,+,256}<nw><%for.j.header>,+,4}<nw><%for.k>
 ; CHECK-NEXT:  Base offset: %a
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][4][64] with elements of 4 bytes.
-; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><nsw><%for.i.header>][{0,+,1}<nuw><%for.j.header>][{32,+,1}<nw><%for.k>]
+; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><nsw><%for.i.header>][{0,+,1}<%for.j.header>][{32,+,1}<%for.k>]
 ; CHECK-NEXT:  Delinearization validation: Succeeded
 ;
 ; OFF-LABEL: 'a_i_2j1_k'

diff  --git a/llvm/test/Analysis/Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll b/llvm/test/Analysis/Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll
index 96ea88df56a9f..479ce6f1e1649 100644
--- a/llvm/test/Analysis/Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll
+++ b/llvm/test/Analysis/Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll
@@ -15,7 +15,7 @@ define void @foo(i64 %n, i64 %m, i64 %o, i64 %p, ptr nocapture %A) nounwind uwta
 ; CHECK-NEXT:  AccessFunction: {{\{\{\{}}(56 + (8 * (-4 + (3 * %m)) * (%o + %p))),+,(8 * (%o + %p) * %m)}<%for.cond4.preheader.lr.ph.us>,+,(8 * (%o + %p))}<%for.body6.lr.ph.us.us>,+,8}<%for.body6.us.us>
 ; CHECK-NEXT:  Base offset: %A
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][%m][(%o + %p)] with elements of 8 bytes.
-; CHECK-NEXT:  ArrayRef[{3,+,1}<nuw><%for.cond4.preheader.lr.ph.us>][{-4,+,1}<nw><%for.body6.lr.ph.us.us>][{7,+,1}<nw><%for.body6.us.us>]
+; CHECK-NEXT:  ArrayRef[{3,+,1}<nuw><%for.cond4.preheader.lr.ph.us>][{-4,+,1}<%for.body6.lr.ph.us.us>][{7,+,1}<%for.body6.us.us>]
 ; CHECK-NEXT:  Delinearization validation: Failed
 ;
 entry:

diff  --git a/llvm/test/Analysis/Delinearization/wraps.ll b/llvm/test/Analysis/Delinearization/wraps.ll
new file mode 100644
index 0000000000000..f57334ea61320
--- /dev/null
+++ b/llvm/test/Analysis/Delinearization/wraps.ll
@@ -0,0 +1,145 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='print<delinearization>' -disable-output 2>&1 | FileCheck %s
+
+; for (i = 0; i < btc; i++)
+;   a[i * (m + 42)] = 0;
+;
+; We don't know the concret value of `m`, so we cannot deduce no-wrap flags for
+; the quotient/remainder divided by `m`.
+;
+define void @divide_by_m0(ptr %a, i64 %m, i64 %btc) {
+; CHECK-LABEL: 'divide_by_m0'
+; CHECK-NEXT:  Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT:  AccessFunction: {0,+,(42 + %m)}<nuw><nsw><%loop>
+; CHECK-NEXT:  Base offset: %a
+; CHECK-NEXT:  ArrayDecl[UnknownSize][%m] with elements of 1 bytes.
+; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><nsw><%loop>][{0,+,42}<%loop>]
+; CHECK-NEXT:  Delinearization validation: Failed
+;
+entry:
+  %stride = add nsw nuw i64 %m, 42
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %offset = phi i64 [ 0, %entry ], [ %offset.next, %loop ]
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %i.next = add nsw nuw i64 %i, 1
+  %offset.next = add nsw nuw i64 %offset, %stride
+  %cond = icmp eq i64 %i.next, %btc
+  br i1 %cond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; for (int i = 0; i < btc; i++)
+;   a[i * (2 * m + 42)] = 0;
+;
+; We don't know the concret value of `m`, so we cannot deduce no-wrap flags for
+; the quotient/remainder divided by `m`.
+;
+define void @divide_by_m1(ptr %a, i64 %m, i64 %btc) {
+; CHECK-LABEL: 'divide_by_m1'
+; CHECK-NEXT:  Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT:  AccessFunction: {0,+,(42 + (2 * %m))}<nuw><nsw><%loop>
+; CHECK-NEXT:  Base offset: %a
+; CHECK-NEXT:  ArrayDecl[UnknownSize][%m] with elements of 1 bytes.
+; CHECK-NEXT:  ArrayRef[{0,+,2}<%loop>][{0,+,42}<%loop>]
+; CHECK-NEXT:  Delinearization validation: Failed
+;
+entry:
+  %m2 = add nsw nuw i64 %m, %m
+  %stride = add nsw nuw i64 %m2, 42
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %offset = phi i64 [ 0, %entry ], [ %offset.next, %loop ]
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %i.next = add nsw nuw i64 %i, 1
+  %offset.next = add nsw nuw i64 %offset, %stride
+  %cond = icmp eq i64 %i.next, %btc
+  br i1 %cond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; if (d == 0)
+;   for (unsigned long long i = 0; i != UINT64_MAX; i++)
+;     a[i * 2 * d] = 42;
+;
+; `i * 2 * d` is always zero, so it's nsw and nuw. However, the quotient
+; divided by `d` is neither nsw nor nuw.
+;
+define void @divide_by_zero(ptr %a, i64 %d) {
+; CHECK-LABEL: 'divide_by_zero'
+; CHECK-NEXT:  Inst: store i8 42, ptr %idx, align 1
+; CHECK-NEXT:  AccessFunction: {0,+,(2 * %d)}<nuw><nsw><%loop>
+; CHECK-NEXT:  Base offset: %a
+; CHECK-NEXT:  ArrayDecl[UnknownSize][%d] with elements of 1 bytes.
+; CHECK-NEXT:  ArrayRef[{0,+,2}<%loop>][0]
+; CHECK-NEXT:  Delinearization validation: Failed
+;
+entry:
+  %guard = icmp eq i64 %d, 0
+  br i1 %guard, label %loop.preheader, label %exit
+
+loop.preheader:
+  %stride = mul nsw nuw i64 %d, 2  ; since %d is 0, %stride is also 0
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %loop.preheader ], [ %i.next, %loop ]
+  %offset = phi i64 [ 0, %loop.preheader ], [ %offset.next, %loop ]
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 42, ptr %idx
+  %i.next = add nuw i64 %i, 1
+  %offset.next = add nsw nuw i64 %offset, %stride
+  %cond = icmp eq i64 %i.next, -1
+  br i1 %cond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; if (d == UINT64_MAX)
+;   for (unsigned long long i = 0; i != d; i++)
+;     a[i * (d + 1)] = 42;
+;
+; `i * (d + 1)` is always zero, so it's nsw and nuw. However, the
+; quotient/remainder divided by `d` is not nsw.
+;
+define void @divide_by_minus_one(ptr %a, i64 %d) {
+; CHECK-LABEL: 'divide_by_minus_one'
+; CHECK-NEXT:  Inst: store i8 42, ptr %idx, align 1
+; CHECK-NEXT:  AccessFunction: {0,+,(1 + %d)}<nuw><nsw><%loop>
+; CHECK-NEXT:  Base offset: %a
+; CHECK-NEXT:  ArrayDecl[UnknownSize][%d] with elements of 1 bytes.
+; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><%loop>][{0,+,1}<nuw><%loop>]
+; CHECK-NEXT:  Delinearization validation: Failed
+;
+entry:
+  %guard = icmp eq i64 %d, -1
+  br i1 %guard, label %loop.preheader, label %exit
+
+loop.preheader:
+  %stride = add nsw i64 %d, 1  ; since %d is -1, %stride is 0
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %loop.preheader ], [ %i.next, %loop ]
+  %offset = phi i64 [ 0, %loop.preheader ], [ %offset.next, %loop ]
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 42, ptr %idx
+  %i.next = add nuw i64 %i, 1
+  %offset.next = add nsw nuw i64 %offset, %stride
+  %cond = icmp eq i64 %i.next, %d
+  br i1 %cond, label %exit, label %loop
+
+exit:
+  ret void
+}

diff  --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 429e37de0a453..2f43f8d705cb2 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -479,6 +479,8 @@ for.cond.cleanup:                                 ; preds = %for.cond.cleanup3,
 ;;    for (int k = 1; k < o; k++)
 ;;      = A[i*m*o + j*o + k]
 ;;     A[i*m*o + j*o + k - 1] =
+;;
+;; FIXME: Currently fails to infer nsw for the SCEV `{0,+,1}<for.body8>`
 define void @t8(i32 %n, i32 %m, i32 %o, ptr nocapture %A) {
 ; CHECK-LABEL: 't8'
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx, align 4

diff  --git a/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
index e3cf15eb63534..e7a78dca2d2cb 100644
--- a/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
+++ b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
@@ -91,7 +91,7 @@ define void @addrec_step0(i8 %n, i8 %step) {
 ; CHECK-NEXT:  Instruction: %div = sdiv i8 %num, %step
 ; CHECK-NEXT:    Numerator: {0,+,%step}<nuw><nsw><%loop>
 ; CHECK-NEXT:    Denominator: %step
-; CHECK-NEXT:    Quotient: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT:    Quotient: {0,+,1}<%loop>
 ; CHECK-NEXT:    Remainder: 0
 ;
 entry:
@@ -115,14 +115,14 @@ exit:
 ;   if (cond)
 ;     div = (step * i) / step;
 ;
-; FIXME: The quotient can cause signed wrap, e.g., when %step is 0 and %n is
-; larger than 127.
+; The quotient can cause signed wrap, e.g., when %step is 0 and %n is larger
+; than 127.
 define void @addrec_step1(i8 %n, i8 %step) {
 ; CHECK-LABEL: 'addrec_step1'
 ; CHECK-NEXT:  Instruction: %div = sdiv i8 %num, %step
 ; CHECK-NEXT:    Numerator: {0,+,%step}<nuw><nsw><%loop.header>
 ; CHECK-NEXT:    Denominator: %step
-; CHECK-NEXT:    Quotient: {0,+,1}<nuw><nsw><%loop.header>
+; CHECK-NEXT:    Quotient: {0,+,1}<%loop.header>
 ; CHECK-NEXT:    Remainder: 0
 ;
 entry:
@@ -152,14 +152,14 @@ exit:
 ; for (i = 0; i < n; i++)
 ;   div = (a + b) * i / a;
 ;
-; FIXME: Both the quotient and the remainder can cause signed/unsigned wrap,
-; e.g., when %a + %b = 0 && %a != 0.
+; Both the quotient and the remainder can cause signed/unsigned wrap, e.g.,
+; when %a + %b = 0 && %a != 0.
 define void @addrec_a_b(i8 %n, i8 %a, i8 %b) {
 ; CHECK-LABEL: 'addrec_a_b'
 ; CHECK-NEXT:  Instruction: %div = sdiv i8 %num, %step
 ; CHECK-NEXT:    Numerator: {0,+,(%a + %b)}<nuw><nsw><%loop>
 ; CHECK-NEXT:    Denominator: (%a + %b)
-; CHECK-NEXT:    Quotient: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT:    Quotient: {0,+,1}<%loop>
 ; CHECK-NEXT:    Remainder: 0
 ;
 entry:


        


More information about the llvm-commits mailing list