[llvm] [SCEV] Look thru more expressions in isKnownMultipleOf (PR #219951)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 17 00:32:13 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/219951

>From 5ceef5d24f0d48646039f3b33c7616c7c061e52f Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 31 Aug 2026 12:18:58 +0100
Subject: [PATCH 1/2] [SCEV] Look thru commutative in isKnownMultipleOf

Generalize the existing logic to look through AddRecs to look through
commutative expressions in isKnownMultipleOf, noting that this simply
increases precision of added predicates, allowing us to add more
fine-grained predicates.
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 25 ++++++++++++-------
 .../DependenceAnalysis/DifferentOffsets.ll    | 10 +++++---
 2 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 30b5adfa266a2..146d413616f18 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11297,21 +11297,12 @@ bool ScalarEvolution::isKnownMultipleOf(
   if (M == 1)
     return true;
 
-  // Recursively check AddRec operands. An AddRecExpr S is a multiple of M if S
-  // starts with a multiple of M and at every iteration step S only adds
-  // multiples of M.
-  if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(S))
-    return isKnownMultipleOf(AddRec->getStart(), M, Predicates) &&
-           isKnownMultipleOf(AddRec->getStepRecurrence(*this), M, Predicates);
-
   // For a constant, check that "S % M == 0".
   if (auto *Cst = dyn_cast<SCEVConstant>(S)) {
     APInt C = Cst->getAPInt();
     return C.urem(M) == 0;
   }
 
-  // TODO: Also check other SCEV expressions, i.e., SCEVAddRecExpr, etc.
-
   // Basic tests have failed.
   // Check "S % M == 0" at compile time and record runtime Assumptions.
   auto *STy = dyn_cast<IntegerType>(S->getType());
@@ -11330,6 +11321,22 @@ bool ScalarEvolution::isKnownMultipleOf(
   if (!Predicates)
     return false;
 
+  // Look through AddRec expressions to improve the precision of added
+  // predicates. An AddRecExpr S is a multiple of M if S starts with a multiple
+  // of M and at every iteration step S only adds multiples of M.
+  if (auto *AR = dyn_cast<SCEVAddRecExpr>(S))
+    if (isKnownMultipleOf(AR->getStart(), M, Predicates) &&
+        isKnownMultipleOf(AR->getStepRecurrence(*this), M, Predicates))
+      return true;
+
+  // Similarly, look through commutative expressions to improve the precision of
+  // added predicates.
+  if (isa<SCEVCommutativeExpr>(S))
+    if (all_of(S->operands(), [&](SCEVUse Op) {
+          return isKnownMultipleOf(Op, M, Predicates);
+        }))
+      return true;
+
   const SCEVPredicate *P = getComparePredicate(ICmpInst::ICMP_EQ, SmodM, Zero);
 
   // Detect redundant predicates.
diff --git a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
index 077d5bde44eac..1a53da55abad8 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
@@ -49,11 +49,13 @@ define i32 @alias_with_parametric_expr(ptr nocapture %A, i64 %n, i64 %m) {
 ; CHECK-NEXT:  Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
 ; CHECK-NEXT:    da analyze - output []!
 ; CHECK-NEXT:    Runtime Assumptions:
-; CHECK-NEXT:    Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
+; CHECK-NEXT:    Equal predicate: (2 * (zext i1 (trunc i64 %n to i1) to i64))<nuw><nsw> == 0
+; CHECK-NEXT:    Equal predicate: (zext i2 (trunc i64 %m to i2) to i64) == 0
 ; CHECK-NEXT:  Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
 ; CHECK-NEXT:    da analyze - flow [|<]!
 ; CHECK-NEXT:    Runtime Assumptions:
-; CHECK-NEXT:    Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
+; CHECK-NEXT:    Equal predicate: (2 * (zext i1 (trunc i64 %n to i1) to i64))<nuw><nsw> == 0
+; CHECK-NEXT:    Equal predicate: (zext i2 (trunc i64 %m to i2) to i64) == 0
 ; CHECK-NEXT:    Equal predicate: (zext i2 (-2 + (trunc i64 %m to i2)) to i64) == 0
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
 ; CHECK-NEXT:    da analyze - input []!
@@ -211,7 +213,9 @@ end:
 define void @multidim_accesses2(ptr %A) {
 ; CHECK-LABEL: 'multidim_accesses2'
 ; CHECK-NEXT:  Src: store i64 1, ptr %idx, align 4 --> Dst: store i64 1, ptr %idx, align 4
-; CHECK-NEXT:    da analyze - confused!
+; CHECK-NEXT:    da analyze - output [* * *]!
+; CHECK-NEXT:    Runtime Assumptions:
+; CHECK-NEXT:    Equal predicate: (zext i3 {0,+,-4}<%for.k> to i64) == 0
 ;
 entry:
   br label %for.i

>From 8e7dd84d7191d45795544a5ba424285d5a31546a Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 4 Sep 2026 10:46:33 +0100
Subject: [PATCH 2/2] [SCEV] Fix underlying bug, redo extension logic

---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 30 ++++++++++++++-----
 .../DependenceAnalysis/DifferentOffsets.ll    |  6 ++--
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 146d413616f18..9ea40fa71f1e8 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11322,16 +11322,30 @@ bool ScalarEvolution::isKnownMultipleOf(
     return false;
 
   // Look through AddRec expressions to improve the precision of added
-  // predicates. An AddRecExpr S is a multiple of M if S starts with a multiple
-  // of M and at every iteration step S only adds multiples of M.
-  if (auto *AR = dyn_cast<SCEVAddRecExpr>(S))
-    if (isKnownMultipleOf(AR->getStart(), M, Predicates) &&
-        isKnownMultipleOf(AR->getStepRecurrence(*this), M, Predicates))
+  // predicates. S is a multiple of M if S starts with a multiple of M and at
+  // every iteration step S only adds multiples of M.
+  if (isa<SCEVAddRecExpr>(S) && all_of(S->operands(), [&](SCEVUse Op) {
+        return isKnownMultipleOf(Op, M, Predicates);
+      }))
+    return true;
+
+  // Similarly, look through Adds with nuw.
+  if (auto *Add = dyn_cast<SCEVAddExpr>(S))
+    if (Add->hasNoUnsignedWrap() && all_of(S->operands(), [&](SCEVUse Op) {
+          return isKnownMultipleOf(Op, M, Predicates);
+        }))
+      return true;
+
+  // Similarly, look through Muls with nuw, and any operand being a
+  // known-multiple is sufficient.
+  if (auto *Mul = dyn_cast<SCEVMulExpr>(S))
+    if (Mul->hasNoUnsignedWrap() && any_of(S->operands(), [&](SCEVUse Op) {
+          return isKnownMultipleOf(Op, M, Predicates);
+        }))
       return true;
 
-  // Similarly, look through commutative expressions to improve the precision of
-  // added predicates.
-  if (isa<SCEVCommutativeExpr>(S))
+  // Similarly, look through UMinMax, with no wrapping arithmetic to consider.
+  if (isa<SCEVUMinExpr, SCEVUMaxExpr>(S))
     if (all_of(S->operands(), [&](SCEVUse Op) {
           return isKnownMultipleOf(Op, M, Predicates);
         }))
diff --git a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
index 1a53da55abad8..f201400be1cbc 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
@@ -49,13 +49,11 @@ define i32 @alias_with_parametric_expr(ptr nocapture %A, i64 %n, i64 %m) {
 ; CHECK-NEXT:  Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
 ; CHECK-NEXT:    da analyze - output []!
 ; CHECK-NEXT:    Runtime Assumptions:
-; CHECK-NEXT:    Equal predicate: (2 * (zext i1 (trunc i64 %n to i1) to i64))<nuw><nsw> == 0
-; CHECK-NEXT:    Equal predicate: (zext i2 (trunc i64 %m to i2) to i64) == 0
+; CHECK-NEXT:    Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
 ; CHECK-NEXT:  Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
 ; CHECK-NEXT:    da analyze - flow [|<]!
 ; CHECK-NEXT:    Runtime Assumptions:
-; CHECK-NEXT:    Equal predicate: (2 * (zext i1 (trunc i64 %n to i1) to i64))<nuw><nsw> == 0
-; CHECK-NEXT:    Equal predicate: (zext i2 (trunc i64 %m to i2) to i64) == 0
+; CHECK-NEXT:    Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
 ; CHECK-NEXT:    Equal predicate: (zext i2 (-2 + (trunc i64 %m to i2)) to i64) == 0
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
 ; CHECK-NEXT:    da analyze - input []!



More information about the llvm-commits mailing list