[llvm] [DA] Check monotonicity for subscripts (PR #154527)

Ryotaro Kasuga via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 20 05:48:36 PDT 2025


https://github.com/kasuga-fj created https://github.com/llvm/llvm-project/pull/154527

None

>From a0d05ec13ce11b3e895a502adfe50fc94aa54bbb Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Wed, 20 Aug 2025 12:42:06 +0000
Subject: [PATCH] [DA] Check monotonicity for subscripts

---
 .../llvm/Analysis/DependenceAnalysis.h        |   8 ++
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 118 +++++++++++++++++-
 2 files changed, 121 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DependenceAnalysis.h b/llvm/include/llvm/Analysis/DependenceAnalysis.h
index f66c79d915665..7ea7f96b5c8e5 100644
--- a/llvm/include/llvm/Analysis/DependenceAnalysis.h
+++ b/llvm/include/llvm/Analysis/DependenceAnalysis.h
@@ -921,6 +921,14 @@ class DependenceInfo {
   /// checkDstSubscript to avoid duplicate code
   bool checkSubscript(const SCEV *Expr, const Loop *LoopNest,
                       SmallBitVector &Loops, bool IsSrc);
+
+  /// Test whether \p Expr is monotonic or not. Return true if we can prove it
+  /// is monotonic. The term "monotonic" means that all AddRec Exprs in \p Expr
+  /// doesn't wrap in signed sense. When it is monotonic, the minimum and
+  /// maximum values of \p Expr are stored in \p Min and \p Max, respectively.
+  bool isMonotonicSCEV(const SCEV *Expr, const SCEV *&Min, const SCEV *&Max,
+                       ScalarEvolution *SE, const Loop *OutermostLoop,
+                       IntegerType *Ty, const Value *Ptr = nullptr) const;
 }; // class DependenceInfo
 
 /// AnalysisPass to compute dependence information in a function
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index f33e04e804e3d..deb12c1026580 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3500,16 +3500,28 @@ bool DependenceInfo::tryDelinearizeParametricSize(
   // to the dependency checks.
   if (!DisableDelinearizationChecks)
     for (size_t I = 1; I < Size; ++I) {
-      if (!isKnownNonNegative(SrcSubscripts[I], SrcPtr))
+      const Loop *OutermostLoop =
+          LI->getLoopFor(Src->getParent())->getOutermostLoop();
+      IntegerType *Ty = cast<IntegerType>(Sizes[I - 1]->getType());
+      if (!Ty)
         return false;
 
-      if (!isKnownLessThan(SrcSubscripts[I], Sizes[I - 1]))
+      const SCEV *SrcMin = nullptr, *SrcMax = nullptr;
+      if (!isMonotonicSCEV(SrcSubscripts[I], SrcMin, SrcMax, SE, OutermostLoop,
+                           Ty, SrcPtr))
         return false;
-
-      if (!isKnownNonNegative(DstSubscripts[I], DstPtr))
+      if (!SE->isKnownNonNegative(SrcMin))
+        return false;
+      if (!SE->isKnownPredicate(CmpInst::ICMP_SLT, SrcMax, Sizes[I - 1]))
         return false;
 
-      if (!isKnownLessThan(DstSubscripts[I], Sizes[I - 1]))
+      const SCEV *DstMin = nullptr, *DstMax = nullptr;
+      if (!isMonotonicSCEV(DstSubscripts[I], DstMin, DstMax, SE, OutermostLoop,
+                           Ty, DstPtr))
+        return false;
+      if (!SE->isKnownNonNegative(DstMin))
+        return false;
+      if (!SE->isKnownPredicate(CmpInst::ICMP_SLT, DstMax, Sizes[I - 1]))
         return false;
     }
 
@@ -3548,6 +3560,102 @@ SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const {
   return SCEVUnionPredicate(Assumptions, *SE);
 }
 
+bool DependenceInfo::isMonotonicSCEV(const SCEV *Expr, const SCEV *&Min,
+                                     const SCEV *&Max, ScalarEvolution *SE,
+                                     const Loop *OutermostLoop, IntegerType *Ty,
+                                     const Value *Ptr) const {
+  const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(Expr);
+  if (!AddRec) {
+    if (!SE->isLoopInvariant(Expr, OutermostLoop))
+      return false;
+    const SCEV *Init = Expr ? Expr : SE->getZero(Ty);
+    Min = Init;
+    Max = Init;
+    return true;
+  }
+
+  if (!AddRec->isAffine())
+    return false;
+
+  // TODO: Support cast?
+  auto *AddRecTy = dyn_cast<IntegerType>(AddRec->getType());
+  if (!AddRecTy || AddRecTy->getBitWidth() != Ty->getBitWidth())
+    return false;
+
+  if (!isMonotonicSCEV(AddRec->getStart(), Min, Max, SE, OutermostLoop, Ty))
+    return false;
+
+  const SCEV *Coeff = AddRec->getStepRecurrence(*SE);
+  const Loop *L = AddRec->getLoop();
+
+  // Bail out if the coefficient can be zero.
+  if (!SE->isKnownNonZero(Coeff))
+    return false;
+  if (!SE->isLoopInvariant(Coeff, OutermostLoop))
+    return false;
+
+  bool IsKnownCoeffPositive = SE->isKnownPositive(Coeff);
+  bool IsKnownCoeffNegative = SE->isKnownNegative(Coeff);
+
+  bool IsNoWrap = AddRec->hasNoUnsignedWrap();
+  if (Ptr) {
+    // TODO: This seems incorrect. Maybe we should check the reachability from
+    // the GEP to the target instruction.
+    auto *GEP = dyn_cast<GetElementPtrInst>(Ptr);
+    if (GEP && GEP->hasNoUnsignedSignedWrap())
+      IsNoWrap = true;
+  }
+
+  if (!IsNoWrap) {
+    if (!IsKnownCoeffNegative)
+      // If the coefficient can be positive value, ensure that the AddRec is
+      // monotonically increasing.
+      if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SGE, AddRec,
+                                       AddRec->getStart()))
+        return false;
+
+    if (!IsKnownCoeffPositive)
+      // If the coefficient can be positive value, ensure that the AddRec is
+      // monotonically decreasing.
+      if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SLE, AddRec,
+                                       AddRec->getStart()))
+        return false;
+  }
+
+  const SCEV *BTC = SE->getBackedgeTakenCount(L);
+  if (!BTC || !SE->isLoopInvariant(BTC, OutermostLoop))
+    return false;
+
+  const SCEVAddRecExpr *MinAddRec = dyn_cast<SCEVAddRecExpr>(
+      SE->getAddRecExpr(Min, Coeff, L, AddRec->getNoWrapFlags()));
+  if (!MinAddRec)
+    return false;
+
+  const SCEVAddRecExpr *MaxAddRec = dyn_cast<SCEVAddRecExpr>(
+      SE->getAddRecExpr(Max, Coeff, L, AddRec->getNoWrapFlags()));
+  if (!MaxAddRec)
+    return false;
+
+  // If we know the coefficient is positive, the maximum value of AddRec is
+  // MaxLast. Similarly, if we know the coefficient is negative, the minimum
+  // value of AddRec is MinLast. If we don't know the sign of the coefficient,
+  // use SMin/SMax.
+  const SCEV *MinLast = MinAddRec->evaluateAtIteration(BTC, *SE);
+  const SCEV *MaxLast = MaxAddRec->evaluateAtIteration(BTC, *SE);
+  if (IsKnownCoeffPositive) {
+    Max = MaxLast;
+  } else if (IsKnownCoeffNegative) {
+    Min = MinLast;
+  } else {
+    assert(!IsKnownCoeffPositive && !IsKnownCoeffNegative &&
+           "Unexpected coefficient sign");
+    Min = SE->getSMinExpr(Min, MinLast);
+    Max = SE->getSMaxExpr(Max, MaxLast);
+  }
+
+  return true;
+}
+
 // depends -
 // Returns NULL if there is no dependence.
 // Otherwise, return a Dependence with as many details as possible.



More information about the llvm-commits mailing list