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

Ryotaro Kasuga via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 27 04:25:02 PDT 2025


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

>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 1/4] [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.

>From 75639b93edaf3a4ec7a0cf13bb64f89c550d80de Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 23 Aug 2025 01:27:13 +0900
Subject: [PATCH 2/4] Address review comments

---
 .../llvm/Analysis/DependenceAnalysis.h        |   7 -
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 450 +++++++++++++-----
 2 files changed, 343 insertions(+), 114 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DependenceAnalysis.h b/llvm/include/llvm/Analysis/DependenceAnalysis.h
index 7ea7f96b5c8e5..9d9b37ac197f7 100644
--- a/llvm/include/llvm/Analysis/DependenceAnalysis.h
+++ b/llvm/include/llvm/Analysis/DependenceAnalysis.h
@@ -922,13 +922,6 @@ class DependenceInfo {
   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 deb12c1026580..3a0b4aca31521 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3308,6 +3308,322 @@ void DependenceInfo::updateDirection(Dependence::DVEntry &Level,
     llvm_unreachable("constraint has unexpected kind");
 }
 
+namespace {
+
+enum class MonotonicityType {
+  MaySignedWrap, ///< The expression contains arithmetic operations that may
+                 ///< cause signed wrap.
+  Constant,      ///< The expression is constant. If a SCEV is classified as
+                 ///< Constant, it also implies that it doesn't contain any
+            ///< arithmetic operations that may cause signed wrap.
+  Monotonic, ///< The expression is monotonically increasing or decreasing. This
+             ///< is exclusive of Constant. That is, we say an SCEV is Monotonic
+             ///< iff it contains at least one AddRec where its step reccurence
+             ///< value is non-zero.
+};
+
+/// A visitor that checks the signed monotonicity of SCEVs.
+struct SCEVSignedMonotonicityChecker
+    : public SCEVVisitor<SCEVSignedMonotonicityChecker, MonotonicityType> {
+  /// \p Ptr is the pointer that the SCEV is associated with, if any. It may be
+  /// used for the inferrence.
+  SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
+                                const Value *Ptr = nullptr)
+      : SE(SE), OutermostLoop(OutermostLoop), Ptr(Ptr) {}
+
+  MonotonicityType visitAddExpr(const SCEVAddExpr *Expr);
+  MonotonicityType visitAddRecExpr(const SCEVAddRecExpr *Expr);
+  MonotonicityType visitMulExpr(const SCEVMulExpr *Expr);
+  MonotonicityType visitUnknown(const SCEVUnknown *Expr);
+  MonotonicityType visitZeroExtendExpr(const SCEVZeroExtendExpr *Expr);
+  MonotonicityType visitSignExtendExpr(const SCEVSignExtendExpr *Expr);
+
+  MonotonicityType visitConstant(const SCEVConstant *) {
+    return MonotonicityType::Constant;
+  }
+  MonotonicityType visitVScale(const SCEVVScale *) {
+    return MonotonicityType::Constant;
+  }
+
+  // TODO: Handle more cases.
+  MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitTruncateExpr(const SCEVTruncateExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitUDivExpr(const SCEVUDivExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitSMaxExpr(const SCEVSMaxExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitUMaxExpr(const SCEVUMaxExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitSMinExpr(const SCEVSMinExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitUMinExpr(const SCEVUMinExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+  MonotonicityType visitCouldNotCompute(const SCEVCouldNotCompute *) {
+    return MonotonicityType::MaySignedWrap;
+  }
+
+private:
+  ScalarEvolution *SE;
+  const Loop *OutermostLoop;
+  const Value *Ptr = nullptr;
+};
+
+using MinMaxType = std::pair<const SCEV *, const SCEV *>;
+const MinMaxType Bottom = {nullptr, nullptr};
+
+/// A visitor that calculates the possible minimum and maximum values of SCEVs.
+/// This class assumes that the given SCEV is constant or monotonic.
+struct SCEVMinMaxCalculator
+    : public SCEVVisitor<SCEVMinMaxCalculator, MinMaxType> {
+  SCEVMinMaxCalculator(ScalarEvolution *SE, const Loop *OutermostLoop)
+      : SE(SE), OutermostLoop(OutermostLoop) {}
+
+  MinMaxType visitAddExpr(const SCEVAddExpr *Expr);
+  MinMaxType visitAddRecExpr(const SCEVAddRecExpr *Expr);
+  MinMaxType visitMulExpr(const SCEVMulExpr *Expr);
+  MinMaxType visitSignExtendExpr(const SCEVSignExtendExpr *Expr);
+
+  MinMaxType visitUnknown(const SCEVUnknown *Expr) {
+    return constantHelper(Expr);
+  }
+  MinMaxType visitConstant(const SCEVConstant *Expr) {
+    return constantHelper(Expr);
+  }
+  MinMaxType visitVScale(const SCEVVScale *Expr) {
+    return constantHelper(Expr);
+  }
+
+  MinMaxType visitZeroExtendExpr(const SCEVZeroExtendExpr *) { return Bottom; }
+  MinMaxType visitPtrToIntExpr(const SCEVPtrToIntExpr *) { return Bottom; }
+  MinMaxType visitTruncateExpr(const SCEVTruncateExpr *) { return Bottom; }
+  MinMaxType visitUDivExpr(const SCEVUDivExpr *) { return Bottom; }
+  MinMaxType visitSMaxExpr(const SCEVSMaxExpr *) { return Bottom; }
+  MinMaxType visitUMaxExpr(const SCEVUMaxExpr *) { return Bottom; }
+  MinMaxType visitSMinExpr(const SCEVSMinExpr *) { return Bottom; }
+  MinMaxType visitUMinExpr(const SCEVUMinExpr *) { return Bottom; }
+  MinMaxType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *) {
+    return Bottom;
+  }
+  MinMaxType visitCouldNotCompute(const SCEVCouldNotCompute *) {
+    return Bottom;
+  }
+
+  MinMaxType constantHelper(const SCEV *C) { return MinMaxType(C, C); }
+
+private:
+  ScalarEvolution *SE;
+  const Loop *OutermostLoop;
+};
+
+} // anonymous namespace
+
+MonotonicityType
+SCEVSignedMonotonicityChecker::visitAddExpr(const SCEVAddExpr *Expr) {
+  if (!Expr->hasNoSignedWrap())
+    return MonotonicityType::MaySignedWrap;
+
+  MonotonicityType Result = MonotonicityType::Constant;
+  for (const SCEV *Op : Expr->operands()) {
+    switch (visit(Op)) {
+    case MonotonicityType::MaySignedWrap:
+      return MonotonicityType::MaySignedWrap;
+    case MonotonicityType::Constant:
+      break;
+    case MonotonicityType::Monotonic:
+      // Monotonic + Monotonic might be constant, so at the moment return
+      // MaySignedWrap.
+      // TODO: Should we separate Monotonically increasing and decreasing? Or
+      // SCEV is always simplified enough so that we don't have to consider such
+      // cases?
+      if (Result == MonotonicityType::Monotonic)
+        return MonotonicityType::MaySignedWrap;
+      Result = MonotonicityType::Constant;
+      break;
+    }
+  }
+  return Result;
+}
+
+MonotonicityType
+SCEVSignedMonotonicityChecker::visitMulExpr(const SCEVMulExpr *Expr) {
+  if (!Expr->hasNoSignedWrap())
+    return MonotonicityType::MaySignedWrap;
+
+  // Same as visitAddExpr.
+  MonotonicityType Result = MonotonicityType::Constant;
+  for (const SCEV *Op : Expr->operands()) {
+    switch (visit(Op)) {
+    case MonotonicityType::MaySignedWrap:
+      return MonotonicityType::MaySignedWrap;
+    case MonotonicityType::Constant:
+      break;
+    case MonotonicityType::Monotonic:
+      if (Result == MonotonicityType::Monotonic)
+        return MonotonicityType::MaySignedWrap;
+      Result = MonotonicityType::Constant;
+      break;
+    }
+  }
+  return Result;
+}
+
+MonotonicityType
+SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
+  if (!Expr->isAffine())
+    return MonotonicityType::MaySignedWrap;
+
+  const SCEV *Start = Expr->getStart();
+  const SCEV *Step = Expr->getStepRecurrence(*SE);
+
+  MonotonicityType StartRes = visit(Start);
+  if (StartRes == MonotonicityType::MaySignedWrap)
+    return MonotonicityType::MaySignedWrap;
+
+  MonotonicityType StepRes = visit(Step);
+  if (StepRes != MonotonicityType::Constant || !SE->isKnownNonZero(Step))
+    return MonotonicityType::MaySignedWrap;
+
+  bool IsNSW = [&] {
+    if (Expr->hasNoSignedWrap())
+      return true;
+
+    if (Ptr) {
+      // TODO: This seems incorrect. Maybe we should check the reachability from
+      // the GEP to the target instruction. E.g., in the following case, maybe
+      // no-wrap is not guaranteed:
+      //
+      //  entry:
+      //    ...
+      //    %gep = getelementptr inbounds i32, ptr %ptr, i32 %addrec
+      //    br i1 %cond, label %store, label %sink
+      //
+      //   store:
+      //     store i32 42, ptr %ptr
+      //     br label %sink
+      //
+      //   sink:
+      //     ...
+      //
+      auto *GEP = dyn_cast<GetElementPtrInst>(Ptr);
+      if (GEP && GEP->hasNoUnsignedSignedWrap())
+        return true;
+    }
+
+    return false;
+  }();
+
+  if (!IsNSW) {
+    if (!SE->isKnownNegative(Step))
+      // If the coefficient can be positive value, ensure that the AddRec is
+      // monotonically increasing.
+      if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SGE, Expr, Start))
+        return MonotonicityType::MaySignedWrap;
+
+    if (!SE->isKnownPositive(Step))
+      // If the coefficient can be positive value, ensure that the AddRec is
+      // monotonically decreasing.
+      if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SLE, Expr, Start))
+        return MonotonicityType::MaySignedWrap;
+  }
+
+  return MonotonicityType::Monotonic;
+}
+
+MonotonicityType SCEVSignedMonotonicityChecker::visitZeroExtendExpr(
+    const SCEVZeroExtendExpr *Expr) {
+  return visit(Expr->getOperand());
+}
+
+MonotonicityType SCEVSignedMonotonicityChecker::visitSignExtendExpr(
+    const SCEVSignExtendExpr *Expr) {
+  return visit(Expr->getOperand());
+}
+
+MonotonicityType
+SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
+  return SE->isLoopInvariant(Expr, OutermostLoop)
+             ? MonotonicityType::Constant
+             : MonotonicityType::MaySignedWrap;
+}
+
+MinMaxType SCEVMinMaxCalculator::visitAddExpr(const SCEVAddExpr *Expr) {
+  if (!Expr->hasNoSignedWrap())
+    return Bottom;
+
+  const SCEV *Min = SE->getZero(Expr->getType());
+  const SCEV *Max = SE->getZero(Expr->getType());
+  for (const SCEV *Op : Expr->operands()) {
+    auto [OpMin, OpMax] = visit(Op);
+    if (!OpMin || !OpMax)
+      return Bottom;
+    Min = SE->getAddExpr(Min, OpMin, Expr->getNoWrapFlags());
+    Max = SE->getAddExpr(Max, OpMax, Expr->getNoWrapFlags());
+  }
+  return MinMaxType(Min, Max);
+}
+
+MinMaxType SCEVMinMaxCalculator::visitMulExpr(const SCEVMulExpr *Expr) {
+  // TODO: Impl
+  return Bottom;
+}
+
+MinMaxType SCEVMinMaxCalculator::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
+  assert(Expr->isAffine() && "Expected affine AddRecExpr");
+  const SCEV *Start = Expr->getStart();
+  const SCEV *Step = Expr->getStepRecurrence(*SE);
+
+  const SCEV *BTC = SE->getBackedgeTakenCount(Expr->getLoop());
+  if (!BTC || !SE->isLoopInvariant(BTC, OutermostLoop))
+    return Bottom;
+
+  auto [StartMin, StartMax] = visit(Start);
+  if (!StartMin || !StartMax)
+    return Bottom;
+  assert(SE->isLoopInvariant(Step, OutermostLoop) &&
+         "Expected step to be loop invariant");
+
+  const SCEVAddRecExpr *MinAddRec = dyn_cast<SCEVAddRecExpr>(SE->getAddRecExpr(
+      StartMin, Step, Expr->getLoop(), Expr->getNoWrapFlags()));
+  const SCEVAddRecExpr *MaxAddRec = dyn_cast<SCEVAddRecExpr>(SE->getAddRecExpr(
+      StartMax, Step, Expr->getLoop(), Expr->getNoWrapFlags()));
+
+  const SCEV *MinLast = MinAddRec->evaluateAtIteration(BTC, *SE);
+  const SCEV *MaxLast = MaxAddRec->evaluateAtIteration(BTC, *SE);
+
+  // If the step value is positive, the AddRec is monotonically increasing,
+  if (SE->isKnownPositive(Step))
+    return MinMaxType(StartMin, MaxLast);
+
+  // If the step value is negative, the AddRec is monotonically decreasing,
+  if (SE->isKnownNegative(Step))
+    return MinMaxType(MinLast, StartMax);
+
+  const SCEV *Min = SE->getSMinExpr(StartMin, MinLast);
+  const SCEV *Max = SE->getSMaxExpr(StartMax, MaxLast);
+  return MinMaxType(Min, Max);
+}
+
+MinMaxType
+SCEVMinMaxCalculator::visitSignExtendExpr(const SCEVSignExtendExpr *Expr) {
+  auto [Min, Max] = visit(Expr->getOperand());
+  if (!Min || !Max)
+    return Bottom;
+  return MinMaxType(SE->getSignExtendExpr(Min, Expr->getType()),
+                    SE->getSignExtendExpr(Max, Expr->getType()));
+}
+
 /// Check if we can delinearize the subscripts. If the SCEVs representing the
 /// source and destination array references are recurrences on a nested loop,
 /// this function flattens the nested recurrences into separate recurrences
@@ -3506,25 +3822,41 @@ bool DependenceInfo::tryDelinearizeParametricSize(
       if (!Ty)
         return false;
 
-      const SCEV *SrcMin = nullptr, *SrcMax = nullptr;
-      if (!isMonotonicSCEV(SrcSubscripts[I], SrcMin, SrcMax, SE, OutermostLoop,
-                           Ty, SrcPtr))
+      MonotonicityType SrcMonotonicity =
+          SCEVSignedMonotonicityChecker(SE, OutermostLoop, SrcPtr)
+              .visit(SrcSubscripts[I]);
+      if (SrcMonotonicity == MonotonicityType::MaySignedWrap)
         return false;
-      if (!SE->isKnownNonNegative(SrcMin))
-        return false;
-      if (!SE->isKnownPredicate(CmpInst::ICMP_SLT, SrcMax, Sizes[I - 1]))
+
+      MonotonicityType DstMonotonicity =
+          SCEVSignedMonotonicityChecker(SE, OutermostLoop, DstPtr)
+              .visit(DstSubscripts[I]);
+      if (DstMonotonicity == MonotonicityType::MaySignedWrap)
         return false;
 
-      const SCEV *DstMin = nullptr, *DstMax = nullptr;
-      if (!isMonotonicSCEV(DstSubscripts[I], DstMin, DstMax, SE, OutermostLoop,
-                           Ty, DstPtr))
+      auto [SrcMin, SrcMax] =
+          SCEVMinMaxCalculator(SE, OutermostLoop).visit(SrcSubscripts[I]);
+      if (!SrcMin || !SrcMax)
         return false;
-      if (!SE->isKnownNonNegative(DstMin))
+      if (!SE->isKnownPositive(SrcMin) ||
+          !SE->isKnownPredicate(CmpInst::ICMP_SLT, SrcMax, Sizes[I - 1]))
+        return false;
+
+      auto [DstMin, DstMax] =
+          SCEVMinMaxCalculator(SE, OutermostLoop).visit(DstSubscripts[I]);
+      if (!DstMin || !DstMax)
         return false;
-      if (!SE->isKnownPredicate(CmpInst::ICMP_SLT, DstMax, Sizes[I - 1]))
+      if (!SE->isKnownPositive(DstMin) ||
+          !SE->isKnownPredicate(CmpInst::ICMP_SLT, DstMax, Sizes[I - 1]))
         return false;
     }
 
+  // TODO: Maybe we must check the the address calculation against delinearized
+  // result is safe. That is, the following calculation doesn't wrap, where Sub
+  // is either SrcSubscripts or DstSubscripts and Sz is Sizes:
+  //
+  //   Sub[0] + Sub[1]*Sz[0] + ... + Sub[N-1]*Sz[N-2]*Sz[N-3]*...*Sz[0]
+
   return true;
 }
 
@@ -3560,102 +3892,6 @@ 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.

>From 5063efc6290554fe900f8f2322426a7807cb449e Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 23 Aug 2025 01:30:17 +0900
Subject: [PATCH 3/4] Fix format

---
 llvm/lib/Analysis/DependenceAnalysis.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 3a0b4aca31521..ddced5acf1b14 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3315,7 +3315,7 @@ enum class MonotonicityType {
                  ///< cause signed wrap.
   Constant,      ///< The expression is constant. If a SCEV is classified as
                  ///< Constant, it also implies that it doesn't contain any
-            ///< arithmetic operations that may cause signed wrap.
+                 ///< arithmetic operations that may cause signed wrap.
   Monotonic, ///< The expression is monotonically increasing or decreasing. This
              ///< is exclusive of Constant. That is, we say an SCEV is Monotonic
              ///< iff it contains at least one AddRec where its step reccurence
@@ -3818,9 +3818,6 @@ bool DependenceInfo::tryDelinearizeParametricSize(
     for (size_t I = 1; I < Size; ++I) {
       const Loop *OutermostLoop =
           LI->getLoopFor(Src->getParent())->getOutermostLoop();
-      IntegerType *Ty = cast<IntegerType>(Sizes[I - 1]->getType());
-      if (!Ty)
-        return false;
 
       MonotonicityType SrcMonotonicity =
           SCEVSignedMonotonicityChecker(SE, OutermostLoop, SrcPtr)

>From 8986cd43ce09cb2e5c86c371f4dcd42883ee51fe Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Wed, 27 Aug 2025 11:11:12 +0000
Subject: [PATCH 4/4] Revise impl and add test

---
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 207 +++++++++++-------
 .../Analysis/DependenceAnalysis/monotonic.ll  | 173 +++++++++++++++
 2 files changed, 295 insertions(+), 85 deletions(-)
 create mode 100644 llvm/test/Analysis/DependenceAnalysis/monotonic.ll

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index ddced5acf1b14..dd9bd3e056b54 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3311,73 +3311,89 @@ void DependenceInfo::updateDirection(Dependence::DVEntry &Level,
 namespace {
 
 enum class MonotonicityType {
-  MaySignedWrap, ///< The expression contains arithmetic operations that may
-                 ///< cause signed wrap.
-  Constant,      ///< The expression is constant. If a SCEV is classified as
-                 ///< Constant, it also implies that it doesn't contain any
-                 ///< arithmetic operations that may cause signed wrap.
-  Monotonic, ///< The expression is monotonically increasing or decreasing. This
-             ///< is exclusive of Constant. That is, we say an SCEV is Monotonic
-             ///< iff it contains at least one AddRec where its step reccurence
-             ///< value is non-zero.
+  Unknown,   ///< The expression contains some non loop-invariant SCEVUnknown or
+             ///< arithmetic operations that may cause signed wrap.
+  Invariant, ///< The expression is a loop-invariant.
+  MultiMonotonic, ///< The expression is monotonically increasing or decreasing
+                  ///< with respect to each loop. This is exclusive of
+                  ///< Invariant. That is, we say an SCEV is MultiMonotonic only
+                  ///< if it contains at least one AddRec where its step
+                  ///< reccurence value is non-zero. Monotonicity is checked
+                  ///< independently for each loop. It is allowed to contain
+                  ///< both increasing and decreasing AddRecs.
 };
 
 /// A visitor that checks the signed monotonicity of SCEVs.
 struct SCEVSignedMonotonicityChecker
     : public SCEVVisitor<SCEVSignedMonotonicityChecker, MonotonicityType> {
+
   /// \p Ptr is the pointer that the SCEV is associated with, if any. It may be
   /// used for the inferrence.
-  SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
-                                const Value *Ptr = nullptr)
-      : SE(SE), OutermostLoop(OutermostLoop), Ptr(Ptr) {}
+  static MonotonicityType checkMonotonicity(ScalarEvolution *SE,
+                                            const SCEV *Expr,
+                                            const Loop *OutermostLoop,
+                                            const Value *Ptr = nullptr);
 
-  MonotonicityType visitAddExpr(const SCEVAddExpr *Expr);
   MonotonicityType visitAddRecExpr(const SCEVAddRecExpr *Expr);
-  MonotonicityType visitMulExpr(const SCEVMulExpr *Expr);
   MonotonicityType visitUnknown(const SCEVUnknown *Expr);
   MonotonicityType visitZeroExtendExpr(const SCEVZeroExtendExpr *Expr);
   MonotonicityType visitSignExtendExpr(const SCEVSignExtendExpr *Expr);
 
+  MonotonicityType visitAddExpr(const SCEVAddExpr *Expr) {
+    return visitNAryHelper(Expr);
+  }
+  MonotonicityType visitMulExpr(const SCEVMulExpr *Expr) {
+    return visitNAryHelper(Expr);
+  }
+
   MonotonicityType visitConstant(const SCEVConstant *) {
-    return MonotonicityType::Constant;
+    return MonotonicityType::Invariant;
   }
   MonotonicityType visitVScale(const SCEVVScale *) {
-    return MonotonicityType::Constant;
+    return MonotonicityType::Invariant;
   }
 
   // TODO: Handle more cases.
-  MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitTruncateExpr(const SCEVTruncateExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitTruncateExpr(const SCEVTruncateExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitUDivExpr(const SCEVUDivExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitUDivExpr(const SCEVUDivExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitSMaxExpr(const SCEVSMaxExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitSMaxExpr(const SCEVSMaxExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitUMaxExpr(const SCEVUMaxExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitUMaxExpr(const SCEVUMaxExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitSMinExpr(const SCEVSMinExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitSMinExpr(const SCEVSMinExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitUMinExpr(const SCEVUMinExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitUMinExpr(const SCEVUMinExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
+    return unknownMonotonicity(Expr);
   }
-  MonotonicityType visitCouldNotCompute(const SCEVCouldNotCompute *) {
-    return MonotonicityType::MaySignedWrap;
+  MonotonicityType visitCouldNotCompute(const SCEVCouldNotCompute *Expr) {
+    return unknownMonotonicity(Expr);
   }
 
 private:
   ScalarEvolution *SE;
   const Loop *OutermostLoop;
   const Value *Ptr = nullptr;
+
+  SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
+                                const Value *Ptr)
+      : SE(SE), OutermostLoop(OutermostLoop), Ptr(Ptr) {}
+
+  MonotonicityType visitNAryHelper(const SCEVNAryExpr *Expr);
+  MonotonicityType unknownMonotonicity(const SCEV *Expr);
+  bool isLoopInvariant(const SCEV *Expr) const;
 };
 
 using MinMaxType = std::pair<const SCEV *, const SCEV *>;
@@ -3429,71 +3445,81 @@ struct SCEVMinMaxCalculator
 
 } // anonymous namespace
 
-MonotonicityType
-SCEVSignedMonotonicityChecker::visitAddExpr(const SCEVAddExpr *Expr) {
-  if (!Expr->hasNoSignedWrap())
-    return MonotonicityType::MaySignedWrap;
+MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
+    ScalarEvolution *SE, const SCEV *Expr, const Loop *OutermostLoop,
+    const Value *Ptr) {
+  SCEVSignedMonotonicityChecker Checker(SE, OutermostLoop, Ptr);
+  MonotonicityType MT = Checker.visit(Expr);
 
-  MonotonicityType Result = MonotonicityType::Constant;
-  for (const SCEV *Op : Expr->operands()) {
-    switch (visit(Op)) {
-    case MonotonicityType::MaySignedWrap:
-      return MonotonicityType::MaySignedWrap;
-    case MonotonicityType::Constant:
-      break;
-    case MonotonicityType::Monotonic:
-      // Monotonic + Monotonic might be constant, so at the moment return
-      // MaySignedWrap.
-      // TODO: Should we separate Monotonically increasing and decreasing? Or
-      // SCEV is always simplified enough so that we don't have to consider such
-      // cases?
-      if (Result == MonotonicityType::Monotonic)
-        return MonotonicityType::MaySignedWrap;
-      Result = MonotonicityType::Constant;
-      break;
-    }
+#ifndef NDEBUG
+  switch (MT) {
+  case MonotonicityType::Unknown:
+    break;
+  case MonotonicityType::Invariant:
+    LLVM_DEBUG(dbgs() << "Invariant expr: " << *Expr << "\n");
+    break;
+  case MonotonicityType::MultiMonotonic:
+    LLVM_DEBUG(dbgs() << "Monotonic expr: " << *Expr << "\n");
+    break;
   }
-  return Result;
+#endif
+  return MT;
 }
 
 MonotonicityType
-SCEVSignedMonotonicityChecker::visitMulExpr(const SCEVMulExpr *Expr) {
+SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
+  if (isLoopInvariant(Expr))
+    return MonotonicityType::Invariant;
+
   if (!Expr->hasNoSignedWrap())
-    return MonotonicityType::MaySignedWrap;
+    return unknownMonotonicity(Expr);
 
-  // Same as visitAddExpr.
-  MonotonicityType Result = MonotonicityType::Constant;
+  MonotonicityType Result = MonotonicityType::Invariant;
   for (const SCEV *Op : Expr->operands()) {
     switch (visit(Op)) {
-    case MonotonicityType::MaySignedWrap:
-      return MonotonicityType::MaySignedWrap;
-    case MonotonicityType::Constant:
+    case MonotonicityType::Unknown:
+      return unknownMonotonicity(Expr);
+    case MonotonicityType::Invariant:
       break;
-    case MonotonicityType::Monotonic:
-      if (Result == MonotonicityType::Monotonic)
-        return MonotonicityType::MaySignedWrap;
-      Result = MonotonicityType::Constant;
+    case MonotonicityType::MultiMonotonic:
+      // Monotonic + Monotonic might be a loop invariant, e.g., {0,+,1}<%loop> +
+      // {0,+,-1}<%loop>.
+      // TODO: It would be better to record visited loops and return Unknown
+      // only when the same loop is visited multiple times.
+      if (Result == MonotonicityType::MultiMonotonic)
+        return unknownMonotonicity(Expr);
+      Result = MonotonicityType::MultiMonotonic;
       break;
     }
   }
   return Result;
 }
 
+MonotonicityType
+SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
+  LLVM_DEBUG(dbgs() << "Failed to prove monotonicity for: " << *Expr << "\n");
+  return MonotonicityType::Unknown;
+}
+
+bool SCEVSignedMonotonicityChecker::isLoopInvariant(const SCEV *Expr) const {
+  return !OutermostLoop || SE->isLoopInvariant(Expr, OutermostLoop);
+}
+
 MonotonicityType
 SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
   if (!Expr->isAffine())
-    return MonotonicityType::MaySignedWrap;
+    return unknownMonotonicity(Expr);
 
   const SCEV *Start = Expr->getStart();
   const SCEV *Step = Expr->getStepRecurrence(*SE);
 
   MonotonicityType StartRes = visit(Start);
-  if (StartRes == MonotonicityType::MaySignedWrap)
-    return MonotonicityType::MaySignedWrap;
+  if (StartRes == MonotonicityType::Unknown)
+    return unknownMonotonicity(Expr);
 
   MonotonicityType StepRes = visit(Step);
-  if (StepRes != MonotonicityType::Constant || !SE->isKnownNonZero(Step))
-    return MonotonicityType::MaySignedWrap;
+  if (StepRes != MonotonicityType::Invariant || !SE->isKnownNonZero(Step))
+    return unknownMonotonicity(Expr);
 
   bool IsNSW = [&] {
     if (Expr->hasNoSignedWrap())
@@ -3524,21 +3550,22 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
     return false;
   }();
 
+  // TODO: Is this additional check necessary?
   if (!IsNSW) {
     if (!SE->isKnownNegative(Step))
       // If the coefficient can be positive value, ensure that the AddRec is
       // monotonically increasing.
       if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SGE, Expr, Start))
-        return MonotonicityType::MaySignedWrap;
+        return unknownMonotonicity(Expr);
 
     if (!SE->isKnownPositive(Step))
       // If the coefficient can be positive value, ensure that the AddRec is
       // monotonically decreasing.
       if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SLE, Expr, Start))
-        return MonotonicityType::MaySignedWrap;
+        return unknownMonotonicity(Expr);
   }
 
-  return MonotonicityType::Monotonic;
+  return MonotonicityType::MultiMonotonic;
 }
 
 MonotonicityType SCEVSignedMonotonicityChecker::visitZeroExtendExpr(
@@ -3553,9 +3580,9 @@ MonotonicityType SCEVSignedMonotonicityChecker::visitSignExtendExpr(
 
 MonotonicityType
 SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
-  return SE->isLoopInvariant(Expr, OutermostLoop)
-             ? MonotonicityType::Constant
-             : MonotonicityType::MaySignedWrap;
+  if (!isLoopInvariant(Expr))
+    return unknownMonotonicity(Expr);
+  return MonotonicityType::Invariant;
 }
 
 MinMaxType SCEVMinMaxCalculator::visitAddExpr(const SCEVAddExpr *Expr) {
@@ -3820,15 +3847,15 @@ bool DependenceInfo::tryDelinearizeParametricSize(
           LI->getLoopFor(Src->getParent())->getOutermostLoop();
 
       MonotonicityType SrcMonotonicity =
-          SCEVSignedMonotonicityChecker(SE, OutermostLoop, SrcPtr)
-              .visit(SrcSubscripts[I]);
-      if (SrcMonotonicity == MonotonicityType::MaySignedWrap)
+          SCEVSignedMonotonicityChecker::checkMonotonicity(
+              SE, SrcSubscripts[I], OutermostLoop, SrcPtr);
+      if (SrcMonotonicity == MonotonicityType::Unknown)
         return false;
 
       MonotonicityType DstMonotonicity =
-          SCEVSignedMonotonicityChecker(SE, OutermostLoop, DstPtr)
-              .visit(DstSubscripts[I]);
-      if (DstMonotonicity == MonotonicityType::MaySignedWrap)
+          SCEVSignedMonotonicityChecker::checkMonotonicity(
+              SE, DstSubscripts[I], OutermostLoop, DstPtr);
+      if (DstMonotonicity == MonotonicityType::Unknown)
         return false;
 
       auto [SrcMin, SrcMax] =
@@ -4020,6 +4047,16 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
   Pair[0].Src = SrcSCEV;
   Pair[0].Dst = DstSCEV;
 
+  const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop() : nullptr;
+  if (SCEVSignedMonotonicityChecker::checkMonotonicity(
+          SE, SrcEv, OutermostLoop, SrcPtr) == MonotonicityType::Unknown)
+    return std::make_unique<Dependence>(Src, Dst,
+                                        SCEVUnionPredicate(Assume, *SE));
+  if (SCEVSignedMonotonicityChecker::checkMonotonicity(
+          SE, DstEv, OutermostLoop, DstPtr) == MonotonicityType::Unknown)
+    return std::make_unique<Dependence>(Src, Dst,
+                                        SCEVUnionPredicate(Assume, *SE));
+
   if (Delinearize) {
     if (tryDelinearize(Src, Dst, Pair)) {
       LLVM_DEBUG(dbgs() << "    delinearized\n");
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
new file mode 100644
index 0000000000000..0267cbd1fd04a
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
@@ -0,0 +1,173 @@
+; REQUIRES: asserts
+; RUN: opt < %s -disable-output -passes="print<da>" -debug-only=da 2>&1 | FileCheck %s
+
+; for (int i = 0; i < n; i++)
+;   a[i] = 0;
+;
+define void @f0(ptr %a, i64 %n) {
+; CHECK-LABEL: 'f0'
+; CHECK: Monotonic expr: {0,+,1}<nuw><nsw><%loop>
+;
+entry:
+  %guard = icmp sgt i64 %n, 0
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+  %idx = getelementptr inbounds i8, ptr %a, i64 %i
+  store i8 0, ptr %idx
+  %i.inc = add nsw i64 %i, 1
+  %exitcond = icmp eq i64 %i.inc, %n
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The purpose of the variable `begin` is to avoid violating the size limitation
+; of the allocated object in LLVM IR, which would cause UB.
+;
+; for (unsigned long long i = begin; i < end; i++)
+;   a[i] = 0;
+;
+define void @f1(ptr %a, i64 %begin, i64 %end) {
+; CHECK-LABEL: 'f1'
+; CHECK: Failed to prove monotonicity for: {%begin,+,1}<nuw><%loop>
+;
+entry:
+  %guard = icmp ult i64 %begin, %end
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %i = phi i64 [ %begin, %entry ], [ %i.inc, %loop ]
+  %idx = getelementptr i8, ptr %a, i64 %i
+  store i8 0, ptr %idx
+  %i.inc = add nuw i64 %i, 1
+  %exitcond = icmp eq i64 %i.inc, %end
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; for (int i = 0; i < n; i++)
+;   for (int j = 0; j < m; j++)
+;     a[i + j] = 0;
+;
+define void @f2(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'f2'
+; CHECK: Monotonic expr: {{\{}}{0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nuw><nsw><%loop.j>
+;
+entry:
+  %guard.i = icmp sgt i64 %n, 0
+  br i1 %guard.i, label %loop.i.header, label %exit
+
+loop.i.header:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop.i.latch ]
+  br label %loop.j.preheader
+
+loop.j.preheader:
+  %gurard.j = icmp sgt i64 %m, 0
+  br i1 %gurard.j, label %loop.j, label %loop.i.latch
+
+loop.j:
+  %j = phi i64 [ 0, %loop.j.preheader ], [ %j.inc, %loop.j ]
+  %offset = add nsw i64 %i, %j
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %j.inc = add nsw i64 %j, 1
+  %exitcond.j = icmp eq i64 %j.inc, %m
+  br i1 %exitcond.j, label %loop.i.latch, label %loop.j
+
+loop.i.latch:
+  %i.inc = add nsw i64 %i, 1
+  %exitcond.i = icmp eq i64 %i.inc, %n
+  br i1 %exitcond.i, label %exit, label %loop.i.header
+
+exit:
+  ret void
+}
+
+; for (int i = n - 1; i >= 0; i--)
+;   for (int j = 0; j < m; j++)
+;     a[i + j] = 0;
+define void @f3(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'f3'
+; CHECK: Monotonic expr: {{\{}}{(-1 + %n),+,-1}<nsw><%loop.i.header>,+,1}<nsw><%loop.j>
+;
+entry:
+  %guard.i = icmp sgt i64 %n, 0
+  br i1 %guard.i, label %loop.i.header, label %exit
+
+loop.i.header:
+  %i = phi i64 [ %n, %entry ], [ %i.dec, %loop.i.latch ]
+  %i.dec = add nsw i64 %i, -1
+  br label %loop.j.preheader
+
+loop.j.preheader:
+  %gurard.j = icmp sgt i64 %m, 0
+  br i1 %gurard.j, label %loop.j, label %loop.i.latch
+
+loop.j:
+  %j = phi i64 [ 0, %loop.j.preheader ], [ %j.inc, %loop.j ]
+  %offset = add nsw i64 %i.dec, %j
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %j.inc = add nsw i64 %j, 1
+  %exitcond.j = icmp eq i64 %j.inc, %m
+  br i1 %exitcond.j, label %loop.i.latch, label %loop.j
+
+loop.i.latch:
+  %exitcond.i = icmp eq i64 %i.dec, 0
+  br i1 %exitcond.i, label %exit, label %loop.i.header
+
+exit:
+  ret void
+}
+
+; for (int i = begin0; i < end0; i++)
+;   for (int j = begin1; j < end1; j++) {
+;     unsigned long long offset = (unsigned long long)i + (unsigned long long)j;
+;     a[offset] = 0;
+;   }
+define void @f4(ptr %a, i64 %begin0, i64 %end0, i64 %begin1, i64 %end1) {
+; CHECK-LABEL: 'f4'
+; CHECK: Failed to prove monotonicity for: {{\{}}{(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
+;
+entry:
+  %guard.i.0 = icmp slt i64 0, %begin0
+  %guard.i.1 = icmp slt i64 %begin0, %end0
+  %guard.i.2 = icmp slt i64 0, %end0
+  %and.i.0 = and i1 %guard.i.0, %guard.i.1
+  %and.i.1 = and i1 %and.i.0, %guard.i.2
+  br i1 %and.i.1, label %loop.i.header, label %exit
+
+loop.i.header:
+  %i = phi i64 [ %begin0, %entry ], [ %i.inc, %loop.i.latch ]
+  br label %loop.j.preheader
+
+loop.j.preheader:
+  %guard.j.0 = icmp slt i64 0, %begin1
+  %guard.j.1 = icmp slt i64 %begin1, %end1
+  %guard.j.2 = icmp slt i64 0, %end1
+  %and.j.0 = and i1 %guard.j.0, %guard.j.1
+  %and.j.1 = and i1 %and.j.0, %guard.j.2
+  br i1 %and.j.1, label %loop.j, label %loop.i.latch
+
+loop.j:
+  %j = phi i64 [ %begin1, %loop.j.preheader ], [ %j.inc, %loop.j ]
+  %offset = add nuw i64 %i, %j
+  %idx = getelementptr i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %j.inc = add nsw i64 %j, 1
+  %exitcond.j = icmp eq i64 %j.inc, %end1
+  br i1 %exitcond.j, label %loop.i.latch, label %loop.j
+
+loop.i.latch:
+  %i.inc = add nsw i64 %i, 1
+  %exitcond.i = icmp eq i64 %i.inc, %end0
+  br i1 %exitcond.i, label %exit, label %loop.i.header
+
+exit:
+  ret void
+}



More information about the llvm-commits mailing list