[llvm] [SCEVDivision] Prevent propagating nowrap flags when numerator is an addrec (PR #154745)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 16 00:46:22 PST 2026
https://github.com/kasuga-fj updated https://github.com/llvm/llvm-project/pull/154745
>From 1e2b6d4c5631b1d228fbe3fecc5134de76c7be7a Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Wed, 20 Aug 2025 10:11:43 +0000
Subject: [PATCH 1/3] [SCEVDivision] Prevent propagation of incorrect no-wrap
flags
---
llvm/lib/Analysis/ScalarEvolutionDivision.cpp | 22 ++-
.../Delinearization/fixed_size_array.ll | 2 +-
llvm/test/Analysis/Delinearization/wraps.ll | 130 ++++++++++++++++++
.../Analysis/DependenceAnalysis/DADelin.ll | 6 +-
4 files changed, 154 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/Analysis/Delinearization/wraps.ll
diff --git a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
index d03930d9e2d99..c374d328c984a 100644
--- a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
+++ b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
@@ -141,10 +141,26 @@ void SCEVDivision::visitAddRecExpr(const SCEVAddRecExpr *Numerator) {
if (Ty != StartQ->getType() || Ty != StartR->getType() ||
Ty != StepQ->getType() || Ty != StepR->getType())
return cannotDivide(Numerator);
+
+ // Infer no-wrap flags for Remainder.
+ // TODO: Catch more cases.
+ SCEV::NoWrapFlags NumFlags = Numerator->getNoWrapFlags();
+ SCEV::NoWrapFlags RemFlags = SCEV::NoWrapFlags::FlagAnyWrap;
+ const SCEV *StepNumAbs =
+ SE.getAbsExpr(Numerator->getStepRecurrence(SE), /*IsNSW=*/false);
+ const SCEV *StepRAbs = SE.getAbsExpr(StepR, /*IsNSW=*/false);
+ const Loop *L = Numerator->getLoop();
+
+ // If abs(StepR) <=u abs(StepNumAbs) and both are loop invariant, propagate
+ // the <NW> from Numerator to Remainder.
+ if (ScalarEvolution::hasFlags(NumFlags, SCEV::NoWrapFlags::FlagNW) &&
+ SE.isLoopInvariant(StepNumAbs, L) && SE.isLoopInvariant(StepRAbs, L) &&
+ SE.isKnownPredicate(ICmpInst::ICMP_ULE, StepRAbs, StepNumAbs))
+ RemFlags = ScalarEvolution::setFlags(RemFlags, SCEV::NoWrapFlags::FlagNW);
+
Quotient = SE.getAddRecExpr(StartQ, StepQ, Numerator->getLoop(),
- Numerator->getNoWrapFlags());
- Remainder = SE.getAddRecExpr(StartR, StepR, Numerator->getLoop(),
- Numerator->getNoWrapFlags());
+ SCEV::NoWrapFlags::FlagAnyWrap);
+ Remainder = SE.getAddRecExpr(StartR, StepR, Numerator->getLoop(), RemFlags);
}
void SCEVDivision::visitAddExpr(const SCEVAddExpr *Numerator) {
diff --git a/llvm/test/Analysis/Delinearization/fixed_size_array.ll b/llvm/test/Analysis/Delinearization/fixed_size_array.ll
index 0512044990163..e77fd79a49b35 100644
--- a/llvm/test/Analysis/Delinearization/fixed_size_array.ll
+++ b/llvm/test/Analysis/Delinearization/fixed_size_array.ll
@@ -163,7 +163,7 @@ exit:
; CHECK: Delinearization on function a_i_2j1_k:
; CHECK: 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>]
define void @a_i_2j1_k(ptr %a) {
entry:
br label %for.i.header
diff --git a/llvm/test/Analysis/Delinearization/wraps.ll b/llvm/test/Analysis/Delinearization/wraps.ll
new file mode 100644
index 0000000000000..fc4935bad9939
--- /dev/null
+++ b/llvm/test/Analysis/Delinearization/wraps.ll
@@ -0,0 +1,130 @@
+; RUN: opt < %s -passes='print<delinearization>' -disable-output 2>&1 | FileCheck %s
+
+; In the following case, we don't know the concret value of `m`, so we cannot
+; deduce no-wrap behavior for the quotient/remainder divided by `m`. However,
+; we can infer `{0,+,1}<%loop>` is nuw and nsw from the induction variable.
+;
+; for (int i = 0; i < btc; i++)
+; a[i * (m + 42)] = 0;
+
+; CHECK: 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>]
+define void @divide_by_m0(ptr %a, i64 %m, i64 %btc) {
+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
+}
+
+; In the following case, we don't know the concret value of `m`, so we cannot
+; deduce no-wrap behavior for the quotient/remainder divided by `m`. Also, we
+; don't infer nsw/nuw from the induction variable in this case.
+;
+; for (int i = 0; i < btc; i++)
+; a[i * (2 * m + 42)] = 0;
+
+; CHECK: 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>]
+define void @divide_by_m2(ptr %a, i64 %m, i64 %btc) {
+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
+}
+
+; In the following case, the `i * 2 * d` is always zero, so it's nsw and nuw.
+; However, the quotient divided by `d` is neither nsw nor nuw.
+;
+; if (d == 0)
+; for (unsigned long long i = 0; i != UINT64_MAX; i++)
+; a[i * 2 * d] = 42;
+
+; CHECK: 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]
+define void @divide_by_zero(ptr %a, i64 %d) {
+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
+}
+
+; In the following case, the `i * (d + 1)` is always zero, so it's nsw and nuw.
+; However, the quotient/remainder divided by `d` is not nsw.
+;
+; if (d == UINT64_MAX)
+; for (unsigned long long i = 0; i != d; i++)
+; a[i * (d + 1)] = 42;
+
+; CHECK: 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>]
+define void @divide_by_minus_one(ptr %a, i64 %d) {
+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 8f94a455d3724..f670136aed750 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -479,14 +479,16 @@ 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
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx, align 4 --> Dst: store i32 %add12, ptr %arrayidx2, align 4
-; CHECK-NEXT: da analyze - consistent anti [0 0 1]!
+; CHECK-NEXT: da analyze - anti [* * *|<]!
; CHECK-NEXT: Src: store i32 %add12, ptr %arrayidx2, align 4 --> Dst: store i32 %add12, ptr %arrayidx2, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - output [* * *]!
;
entry:
%cmp49 = icmp sgt i32 %n, 0
>From 01321161a192f48ad01ea14a740bbc6fc977976a Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 16 Feb 2026 08:09:12 +0000
Subject: [PATCH 2/3] [SCEVDivision] Drop nowrap flags for addrecs
---
llvm/lib/Analysis/ScalarEvolutionDivision.cpp | 19 +---
.../Delinearization/fixed_size_array.ll | 2 +-
llvm/test/Analysis/Delinearization/wraps.ll | 87 +++++++++++--------
.../Analysis/ScalarEvolutionDivision/sdiv.ll | 14 +--
4 files changed, 61 insertions(+), 61 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
index f45b2e0196c23..52d82785f6b9c 100644
--- a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
+++ b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
@@ -142,25 +142,10 @@ void SCEVDivision::visitAddRecExpr(const SCEVAddRecExpr *Numerator) {
Ty != StepQ->getType() || Ty != StepR->getType())
return cannotDivide(Numerator);
- // Infer no-wrap flags for Remainder.
- // TODO: Catch more cases.
- SCEV::NoWrapFlags NumFlags = Numerator->getNoWrapFlags();
- SCEV::NoWrapFlags RemFlags = SCEV::NoWrapFlags::FlagAnyWrap;
- const SCEV *StepNumAbs =
- SE.getAbsExpr(Numerator->getStepRecurrence(SE), /*IsNSW=*/false);
- const SCEV *StepRAbs = SE.getAbsExpr(StepR, /*IsNSW=*/false);
- const Loop *L = Numerator->getLoop();
-
- // If abs(StepR) <=u abs(StepNumAbs) and both are loop invariant, propagate
- // the <NW> from Numerator to Remainder.
- if (ScalarEvolution::hasFlags(NumFlags, SCEV::NoWrapFlags::FlagNW) &&
- SE.isLoopInvariant(StepNumAbs, L) && SE.isLoopInvariant(StepRAbs, L) &&
- SE.isKnownPredicate(ICmpInst::ICMP_ULE, StepRAbs, StepNumAbs))
- RemFlags = ScalarEvolution::setFlags(RemFlags, SCEV::NoWrapFlags::FlagNW);
-
Quotient = SE.getAddRecExpr(StartQ, StepQ, Numerator->getLoop(),
SCEV::NoWrapFlags::FlagAnyWrap);
- Remainder = SE.getAddRecExpr(StartR, StepR, Numerator->getLoop(), RemFlags);
+ Remainder = SE.getAddRecExpr(StartR, StepR, Numerator->getLoop(),
+ SCEV::NoWrapFlags::FlagAnyWrap);
}
void SCEVDivision::visitAddExpr(const SCEVAddExpr *Numerator) {
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/wraps.ll b/llvm/test/Analysis/Delinearization/wraps.ll
index fc4935bad9939..f57334ea61320 100644
--- a/llvm/test/Analysis/Delinearization/wraps.ll
+++ b/llvm/test/Analysis/Delinearization/wraps.ll
@@ -1,17 +1,21 @@
+; 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
-; In the following case, we don't know the concret value of `m`, so we cannot
-; deduce no-wrap behavior for the quotient/remainder divided by `m`. However,
-; we can infer `{0,+,1}<%loop>` is nuw and nsw from the induction variable.
-;
-; for (int i = 0; i < btc; i++)
+; for (i = 0; i < btc; i++)
; a[i * (m + 42)] = 0;
-
-; CHECK: 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>]
+;
+; 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
@@ -30,18 +34,21 @@ exit:
ret void
}
-; In the following case, we don't know the concret value of `m`, so we cannot
-; deduce no-wrap behavior for the quotient/remainder divided by `m`. Also, we
-; don't infer nsw/nuw from the induction variable in this case.
-;
; for (int i = 0; i < btc; i++)
; a[i * (2 * m + 42)] = 0;
-
-; CHECK: 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>]
-define void @divide_by_m2(ptr %a, i64 %m, i64 %btc) {
+;
+; 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
@@ -61,18 +68,22 @@ exit:
ret void
}
-; In the following case, the `i * 2 * d` is always zero, so it's nsw and nuw.
-; However, the quotient divided by `d` is neither nsw nor nuw.
-;
; if (d == 0)
; for (unsigned long long i = 0; i != UINT64_MAX; i++)
; a[i * 2 * d] = 42;
-
-; CHECK: 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]
+;
+; `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
@@ -95,18 +106,22 @@ exit:
ret void
}
-; In the following case, the `i * (d + 1)` is always zero, so it's nsw and nuw.
-; However, the quotient/remainder divided by `d` is not nsw.
-;
; if (d == UINT64_MAX)
; for (unsigned long long i = 0; i != d; i++)
; a[i * (d + 1)] = 42;
-
-; CHECK: 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>]
+;
+; `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
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:
>From bb607a0b086f7d9c4d3252b441f85df762737f1e Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 16 Feb 2026 08:45:56 +0000
Subject: [PATCH 3/3] forgot to add test changes...
---
llvm/test/Analysis/Delinearization/a.ll | 2 +-
llvm/test/Analysis/Delinearization/divide_by_one.ll | 2 +-
.../Delinearization/multidim_ivs_and_integer_offsets_nts_3d.ll | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
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/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:
More information about the llvm-commits
mailing list