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

Ryotaro Kasuga via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 8 02:42:12 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 01/11] [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 02/11] 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 03/11] 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 04/11] 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
+}

>From dcb3b1954d9e6619bb7d2e019db90e5ce5c8162c Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Thu, 28 Aug 2025 10:05:36 +0000
Subject: [PATCH 05/11] remove minmax calculator

---
 .../llvm/Analysis/DependenceAnalysis.h        |   1 -
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 139 +-----------------
 2 files changed, 8 insertions(+), 132 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DependenceAnalysis.h b/llvm/include/llvm/Analysis/DependenceAnalysis.h
index 9d9b37ac197f7..f66c79d915665 100644
--- a/llvm/include/llvm/Analysis/DependenceAnalysis.h
+++ b/llvm/include/llvm/Analysis/DependenceAnalysis.h
@@ -921,7 +921,6 @@ class DependenceInfo {
   /// checkDstSubscript to avoid duplicate code
   bool checkSubscript(const SCEV *Expr, const Loop *LoopNest,
                       SmallBitVector &Loops, bool IsSrc);
-
 }; // 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 dd9bd3e056b54..d6060e524be08 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3396,53 +3396,6 @@ struct SCEVSignedMonotonicityChecker
   bool isLoopInvariant(const SCEV *Expr) const;
 };
 
-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::checkMonotonicity(
@@ -3585,72 +3538,6 @@ SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
   return MonotonicityType::Invariant;
 }
 
-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
@@ -3852,35 +3739,25 @@ bool DependenceInfo::tryDelinearizeParametricSize(
       if (SrcMonotonicity == MonotonicityType::Unknown)
         return false;
 
+      if (!isKnownNonNegative(SrcSubscripts[I], SrcPtr))
+        return false;
+
+      if (!isKnownLessThan(SrcSubscripts[I], Sizes[I - 1]))
+        return false;
+
       MonotonicityType DstMonotonicity =
           SCEVSignedMonotonicityChecker::checkMonotonicity(
               SE, DstSubscripts[I], OutermostLoop, DstPtr);
       if (DstMonotonicity == MonotonicityType::Unknown)
         return false;
 
-      auto [SrcMin, SrcMax] =
-          SCEVMinMaxCalculator(SE, OutermostLoop).visit(SrcSubscripts[I]);
-      if (!SrcMin || !SrcMax)
-        return false;
-      if (!SE->isKnownPositive(SrcMin) ||
-          !SE->isKnownPredicate(CmpInst::ICMP_SLT, SrcMax, Sizes[I - 1]))
+      if (!isKnownNonNegative(DstSubscripts[I], DstPtr))
         return false;
 
-      auto [DstMin, DstMax] =
-          SCEVMinMaxCalculator(SE, OutermostLoop).visit(DstSubscripts[I]);
-      if (!DstMin || !DstMax)
-        return false;
-      if (!SE->isKnownPositive(DstMin) ||
-          !SE->isKnownPredicate(CmpInst::ICMP_SLT, DstMax, Sizes[I - 1]))
+      if (!isKnownLessThan(DstSubscripts[I], 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;
 }
 

>From 72b290266a8f4947284bc32f5b5bbd19cd15a010 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Fri, 29 Aug 2025 14:00:06 +0000
Subject: [PATCH 06/11] Introduce NoSignedWrap to MonotonicityType

---
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 168 +++++++++++-----
 .../Analysis/DependenceAnalysis/Banerjee.ll   |  30 +--
 .../Analysis/DependenceAnalysis/BasePtrBug.ll |   4 +-
 .../Analysis/DependenceAnalysis/DADelin.ll    |  12 +-
 .../Analysis/DependenceAnalysis/ExactRDIV.ll  |   8 +-
 llvm/test/Analysis/DependenceAnalysis/GCD.ll  |  12 +-
 .../Analysis/DependenceAnalysis/PR21585.ll    |   6 +-
 .../DependenceAnalysis/Preliminary.ll         |   8 +-
 .../DependenceAnalysis/Propagating.ll         |  20 +-
 .../DependenceAnalysis/Separability.ll        |   8 +-
 .../Analysis/DependenceAnalysis/StrongSIV.ll  |   4 +-
 .../DependenceAnalysis/SymbolicRDIV.ll        |  12 +-
 .../DependenceAnalysis/SymbolicSIV.ll         |  18 +-
 .../DependenceAnalysis/WeakCrossingSIV.ll     |   6 +-
 .../DependenceAnalysis/WeakZeroDstSIV.ll      |   4 +-
 .../DependenceAnalysis/WeakZeroSrcSIV.ll      |   4 +-
 .../Analysis/DependenceAnalysis/monotonic.ll  | 187 ++++++++++++++++--
 17 files changed, 368 insertions(+), 143 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index d6060e524be08..f31d4d02e517d 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3310,10 +3310,23 @@ void DependenceInfo::updateDirection(Dependence::DVEntry &Level,
 
 namespace {
 
+/// The type of signed monotonicity of a SCEV expression. This property is
+/// defined with respect to the outermost loop that DA is analyzing. Invariant
+/// and MultiMonotonic mutually exclusive, and both imply NoSignedWrap.
+///
+/// This is designed to classify the behavior of AddRec expressions, and does
+/// not care about other SCEVs. For example, given the two loop invariants `A`
+/// and `B`, `A + B` is treated as Invariant even if the addition may wrap. On
+/// the other hand, if either `A` or `B` is an AddRec and we cannot prove the
+/// addition doesn't wrap, the result is classified as Unknown.
 enum class MonotonicityType {
-  Unknown,   ///< The expression contains some non loop-invariant SCEVUnknown or
-             ///< arithmetic operations that may cause signed wrap.
-  Invariant, ///< The expression is a loop-invariant.
+  Unknown, ///< The expression contains some non loop-invariant SCEVUnknown or
+           ///< arithmetic operation that has some AddRec as its subexpression
+           ///< and may cause signed wrap.
+  NoSignedWrap, ///< The expression doesn't contain any AddRecs that may wrap.
+                ///< This is a weaker property than Invariant or MultiMonotonic.
+                ///< Invariant and MultiMonotonic imply NoSignedWrap.
+  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
@@ -3385,11 +3398,10 @@ struct SCEVSignedMonotonicityChecker
 private:
   ScalarEvolution *SE;
   const Loop *OutermostLoop;
-  const Value *Ptr = nullptr;
+  bool NoWrapFromGEP = false;
 
   SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
-                                const Value *Ptr)
-      : SE(SE), OutermostLoop(OutermostLoop), Ptr(Ptr) {}
+                                const Value *Ptr);
 
   MonotonicityType visitNAryHelper(const SCEVNAryExpr *Expr);
   MonotonicityType unknownMonotonicity(const SCEV *Expr);
@@ -3398,21 +3410,54 @@ struct SCEVSignedMonotonicityChecker
 
 } // anonymous namespace
 
+SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
+    ScalarEvolution *SE, const Loop *OutermostLoop, const Value *Ptr)
+    : SE(SE), OutermostLoop(OutermostLoop) {
+  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())
+      NoWrapFromGEP = true;
+  }
+}
+
 MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
     ScalarEvolution *SE, const SCEV *Expr, const Loop *OutermostLoop,
     const Value *Ptr) {
   SCEVSignedMonotonicityChecker Checker(SE, OutermostLoop, Ptr);
   MonotonicityType MT = Checker.visit(Expr);
+  if (MT == MonotonicityType::Unknown && Checker.NoWrapFromGEP)
+    MT = MonotonicityType::NoSignedWrap;
 
 #ifndef NDEBUG
   switch (MT) {
   case MonotonicityType::Unknown:
+    LLVM_DEBUG(dbgs() << "Monotonicity: Unknown expr: " << *Expr << "\n");
+    break;
+  case MonotonicityType::NoSignedWrap:
+    LLVM_DEBUG(dbgs() << "Monotonicity: No signed wrap expr: " << *Expr
+                      << "\n");
     break;
   case MonotonicityType::Invariant:
-    LLVM_DEBUG(dbgs() << "Invariant expr: " << *Expr << "\n");
+    LLVM_DEBUG(dbgs() << "Monotonicity: Invariant expr: " << *Expr << "\n");
     break;
   case MonotonicityType::MultiMonotonic:
-    LLVM_DEBUG(dbgs() << "Monotonic expr: " << *Expr << "\n");
+    LLVM_DEBUG(dbgs() << "Monotonicity: Monotonic expr: " << *Expr << "\n");
     break;
   }
 #endif
@@ -3421,28 +3466,49 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
 
 MonotonicityType
 SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
+  assert((isa<SCEVAddExpr>(Expr) || isa<SCEVMulExpr>(Expr)) &&
+         "Unexpected SCEV");
+
   if (isLoopInvariant(Expr))
     return MonotonicityType::Invariant;
 
-  if (!Expr->hasNoSignedWrap())
-    return unknownMonotonicity(Expr);
-
   MonotonicityType Result = MonotonicityType::Invariant;
   for (const SCEV *Op : Expr->operands()) {
+    assert(Result != MonotonicityType::Unknown && "Unexpected state");
     switch (visit(Op)) {
     case MonotonicityType::Unknown:
       return unknownMonotonicity(Expr);
-    case MonotonicityType::Invariant:
+    case MonotonicityType::NoSignedWrap:
+      Result = MonotonicityType::NoSignedWrap;
       break;
-    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;
+    case MonotonicityType::Invariant:
       break;
+    case MonotonicityType::MultiMonotonic: {
+      switch (Result) {
+      case MonotonicityType::Unknown:
+        llvm_unreachable("should have been handled above");
+      case MonotonicityType::NoSignedWrap:
+        break;
+      case MonotonicityType::Invariant:
+        if (!Expr->hasNoSignedWrap())
+          return unknownMonotonicity(Expr);
+        Result = MonotonicityType::MultiMonotonic;
+        break;
+      case MonotonicityType::MultiMonotonic:
+        if (!Expr->hasNoSignedWrap())
+          return unknownMonotonicity(Expr);
+        if (!isa<SCEVAddExpr>(Expr))
+          return unknownMonotonicity(Expr);
+        // Monotonic + Monotonic might be a loop invariant, e.g., the following
+        // SCEV:
+        //
+        //   {0,+,1}<%loop> + {0,+,-1}<%loop>
+        //
+        // In that case, relax the property to NoSignedWrap.
+        Result = MonotonicityType::NoSignedWrap;
+        break;
+      }
+    } break;
     }
   }
   return Result;
@@ -3466,44 +3532,17 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
   const SCEV *Start = Expr->getStart();
   const SCEV *Step = Expr->getStepRecurrence(*SE);
 
+  bool IsNSW = Expr->hasNoSignedWrap();
+
   MonotonicityType StartRes = visit(Start);
   if (StartRes == MonotonicityType::Unknown)
     return unknownMonotonicity(Expr);
 
   MonotonicityType StepRes = visit(Step);
-  if (StepRes != MonotonicityType::Invariant || !SE->isKnownNonZero(Step))
+  if (StepRes != MonotonicityType::Invariant)
     return unknownMonotonicity(Expr);
 
-  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;
-  }();
-
-  // TODO: Is this additional check necessary?
+  // TODO: Enhance the inference here.
   if (!IsNSW) {
     if (!SE->isKnownNegative(Step))
       // If the coefficient can be positive value, ensure that the AddRec is
@@ -3518,7 +3557,21 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
         return unknownMonotonicity(Expr);
   }
 
-  return MonotonicityType::MultiMonotonic;
+  bool IsKnownNonZero = SE->isKnownNonZero(Step);
+  switch (StartRes) {
+  case MonotonicityType::Unknown:
+    llvm_unreachable("should have been handled above");
+  case MonotonicityType::NoSignedWrap:
+    return MonotonicityType::NoSignedWrap;
+  case MonotonicityType::Invariant:
+    return IsKnownNonZero ? MonotonicityType::MultiMonotonic
+                          : MonotonicityType::NoSignedWrap;
+  case MonotonicityType::MultiMonotonic:
+    // TODO: Should handle SCEV like `{{0,+,-1}<%loop>,+,1}<%loop>`?
+    return IsKnownNonZero ? MonotonicityType::MultiMonotonic
+                          : MonotonicityType::NoSignedWrap;
+  }
+  llvm_unreachable("unhandled MonotonicityType");
 }
 
 MonotonicityType SCEVSignedMonotonicityChecker::visitZeroExtendExpr(
@@ -3733,6 +3786,15 @@ bool DependenceInfo::tryDelinearizeParametricSize(
       const Loop *OutermostLoop =
           LI->getLoopFor(Src->getParent())->getOutermostLoop();
 
+      // TODO: In general, reasoning about monotonicity of a subscript from the
+      // base pointer would not be allowed. Probably we need to check the loops
+      // associated with this subscript are disjoint from those associated with
+      // the other subscripts. The validation would be something like:
+      //
+      //   LoopsI = collectCommonLoops(SrcSubscripts[I])
+      //   LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
+      //   CanUsePtr = (LoopsI intersect LoopsOthers) is empty.
+      //
       MonotonicityType SrcMonotonicity =
           SCEVSignedMonotonicityChecker::checkMonotonicity(
               SE, SrcSubscripts[I], OutermostLoop, SrcPtr);
@@ -3939,6 +4001,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
       LLVM_DEBUG(dbgs() << "    delinearized\n");
       Pairs = Pair.size();
     }
+    // TODO: Check that the original offsets are monotonic when delinearization
+    // fails.
   }
 
   for (unsigned P = 0; P < Pairs; ++P) {
diff --git a/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll b/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
index e0def901d1759..c96f0dfa1f5d5 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
@@ -28,7 +28,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 ; NORMALIZE-LABEL: 'banerjee0'
 ; NORMALIZE-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -42,7 +42,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; NORMALIZE-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; NORMALIZE-NEXT:    da analyze - confused!
 ; NORMALIZE-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT:    da analyze - none!
+; NORMALIZE-NEXT:    da analyze - confused!
 ;
 ; DELIN-LABEL: 'banerjee0'
 ; DELIN-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -56,7 +56,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; DELIN-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; DELIN-NEXT:    da analyze - confused!
 ; DELIN-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT:    da analyze - none!
+; DELIN-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -810,7 +810,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 ; NORMALIZE-LABEL: 'banerjee9'
 ; NORMALIZE-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -824,7 +824,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; NORMALIZE-NEXT:  Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; NORMALIZE-NEXT:    da analyze - confused!
 ; NORMALIZE-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT:    da analyze - none!
+; NORMALIZE-NEXT:    da analyze - confused!
 ;
 ; DELIN-LABEL: 'banerjee9'
 ; DELIN-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -838,7 +838,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; DELIN-NEXT:  Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; DELIN-NEXT:    da analyze - confused!
 ; DELIN-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; DELIN-NEXT:    da analyze - none!
+; DELIN-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -896,7 +896,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 ; NORMALIZE-LABEL: 'banerjee10'
 ; NORMALIZE-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -910,7 +910,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; NORMALIZE-NEXT:  Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; NORMALIZE-NEXT:    da analyze - confused!
 ; NORMALIZE-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT:    da analyze - none!
+; NORMALIZE-NEXT:    da analyze - confused!
 ;
 ; DELIN-LABEL: 'banerjee10'
 ; DELIN-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -924,7 +924,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; DELIN-NEXT:  Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
 ; DELIN-NEXT:    da analyze - confused!
 ; DELIN-NEXT:  Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; DELIN-NEXT:    da analyze - none!
+; DELIN-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -981,7 +981,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 ; NORMALIZE-LABEL: 'banerjee11'
 ; NORMALIZE-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -995,7 +995,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; NORMALIZE-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; NORMALIZE-NEXT:    da analyze - confused!
 ; NORMALIZE-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT:    da analyze - none!
+; NORMALIZE-NEXT:    da analyze - confused!
 ;
 ; DELIN-LABEL: 'banerjee11'
 ; DELIN-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1009,7 +1009,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; DELIN-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; DELIN-NEXT:    da analyze - confused!
 ; DELIN-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT:    da analyze - none!
+; DELIN-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -1066,7 +1066,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 ; NORMALIZE-LABEL: 'banerjee12'
 ; NORMALIZE-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1080,7 +1080,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; NORMALIZE-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; NORMALIZE-NEXT:    da analyze - confused!
 ; NORMALIZE-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT:    da analyze - none!
+; NORMALIZE-NEXT:    da analyze - confused!
 ;
 ; DELIN-LABEL: 'banerjee12'
 ; DELIN-NEXT:  Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1094,7 +1094,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
 ; DELIN-NEXT:  Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
 ; DELIN-NEXT:    da analyze - confused!
 ; DELIN-NEXT:  Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT:    da analyze - none!
+; DELIN-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
index 81e461a5e092d..f51f1fa59f544 100644
--- a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
@@ -18,11 +18,11 @@ define void @test1(ptr nocapture %A, ptr nocapture %B, i32 %N) #0 {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %0 = load i32, ptr %gep.0, align 4
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4
-; CHECK-NEXT:    da analyze - input [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %gep.1, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %gep.1, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %add, ptr %gep.B, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 8f94a455d3724..174f105b666f3 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -646,11 +646,11 @@ exit:
 define void @coeff_may_negative(ptr %a, i32 %k) {
 ; CHECK-LABEL: 'coeff_may_negative'
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT:    da analyze - output [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %loop
@@ -685,11 +685,11 @@ exit:
 define void @coeff_positive(ptr %a, i32 %k) {
 ; CHECK-LABEL: 'coeff_positive'
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT:    da analyze - output [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %loop
diff --git a/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll b/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
index b5ece14121686..73c0c8b47d74f 100644
--- a/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
@@ -508,7 +508,7 @@ define void @rdiv9(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -564,7 +564,7 @@ define void @rdiv10(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -619,7 +619,7 @@ define void @rdiv11(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -674,7 +674,7 @@ define void @rdiv12(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 03343e7a98211..24cf95987df4d 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -25,7 +25,7 @@ define void @gcd0(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -84,7 +84,7 @@ define void @gcd1(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -144,7 +144,7 @@ define void @gcd2(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -204,7 +204,7 @@ define void @gcd3(ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -262,7 +262,7 @@ define void @gcd4(ptr %A, ptr %B, i64 %M, i64 %N) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx16, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -330,7 +330,7 @@ define void @gcd5(ptr %A, ptr %B, i64 %M, i64 %N) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx16, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
index 0d59e107b9c35..c234c59db9f17 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
@@ -70,11 +70,11 @@ for.end:
 define void @i16_wrap(ptr %a) {
 ; CHECK-LABEL: 'i16_wrap'
 ; CHECK-NEXT:  Src: %0 = load i64, ptr %idx, align 4 --> Dst: %0 = load i64, ptr %idx, align 4
-; CHECK-NEXT:    da analyze - input [*]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i64, ptr %idx, align 4 --> Dst: store i64 %1, ptr %idx, align 4
-; CHECK-NEXT:    da analyze - anti [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i64 %1, ptr %idx, align 4 --> Dst: store i64 %1, ptr %idx, align 4
-; CHECK-NEXT:    da analyze - output [*]!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.body
diff --git a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
index 8cb0e2ac770dc..e7449c6072846 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
@@ -448,7 +448,7 @@ define void @p4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp sgt i64 %n, 0
@@ -501,7 +501,7 @@ define void @p5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp sgt i64 %n, 0
@@ -729,9 +729,9 @@ define void @foo(ptr %s, i32 %size) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %0, align 4 --> Dst: %1 = load i32, ptr %0, align 4
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %0, align 4 --> Dst: store i32 %1, ptr %i.02, align 4
-; CHECK-NEXT:    da analyze - consistent anti [1]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %1, ptr %i.02, align 4 --> Dst: store i32 %1, ptr %i.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %idx.ext = zext i32 %size to i64
diff --git a/llvm/test/Analysis/DependenceAnalysis/Propagating.ll b/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
index 866f515baeafb..2cd393730096d 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
@@ -24,7 +24,7 @@ define void @prop0(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -81,7 +81,7 @@ define void @prop1(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx14, align 4 --> Dst: store i32 %0, ptr %B.addr.21, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.21, align 4 --> Dst: store i32 %0, ptr %B.addr.21, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -151,7 +151,7 @@ define void @prop2(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -208,7 +208,7 @@ define void @prop3(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -266,7 +266,7 @@ define void @prop4(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx10, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -325,7 +325,7 @@ define void @prop5(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -385,7 +385,7 @@ define void @prop6(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -445,7 +445,7 @@ define void @prop7(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx13, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -507,7 +507,7 @@ define void @prop8(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -565,7 +565,7 @@ define void @prop9(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/Separability.ll b/llvm/test/Analysis/DependenceAnalysis/Separability.ll
index 2ed9cca4d1fc0..3e886e65c5ab1 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Separability.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Separability.ll
@@ -26,7 +26,7 @@ define void @sep0(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx15, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -108,7 +108,7 @@ define void @sep1(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx15, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -190,7 +190,7 @@ define void @sep2(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx19, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
@@ -272,7 +272,7 @@ define void @sep3(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx20, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
index 44bd9b7727910..d6bd30ed9050f 100644
--- a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
@@ -122,7 +122,7 @@ define void @strong2(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -435,7 +435,7 @@ define void @strong9(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll b/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
index 8b9aa257a7c57..ba4f2101df77e 100644
--- a/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
@@ -25,7 +25,7 @@ define void @symbolicrdiv0(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
@@ -94,7 +94,7 @@ define void @symbolicrdiv1(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
@@ -165,7 +165,7 @@ define void @symbolicrdiv2(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
@@ -234,7 +234,7 @@ define void @symbolicrdiv3(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
@@ -301,7 +301,7 @@ define void @symbolicrdiv4(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
@@ -369,7 +369,7 @@ define void @symbolicrdiv5(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp4 = icmp eq i64 %n1, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
index cdfaec76fa892..b2b021cf41681 100644
--- a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
@@ -23,7 +23,7 @@ define void @symbolicsiv0(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -75,7 +75,7 @@ define void @symbolicsiv1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -129,7 +129,7 @@ define void @symbolicsiv2(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -181,7 +181,7 @@ define void @symbolicsiv3(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -234,7 +234,7 @@ define void @symbolicsiv4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -286,7 +286,7 @@ define void @symbolicsiv5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -341,7 +341,7 @@ define void @weaktest(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -392,7 +392,7 @@ define void @symbolicsiv6(ptr %A, ptr %B, i64 %n, i64 %N, i64 %M) nounwind uwtab
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -448,7 +448,7 @@ define void @symbolicsiv7(ptr %A, ptr %B, i64 %n, i64 %N, i64 %M) nounwind uwtab
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %1, ptr %B.addr.02, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
index cd044032e34f1..4884a34217a86 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
@@ -24,7 +24,7 @@ define void @weakcrossing0(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -77,7 +77,7 @@ define void @weakcrossing1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -254,7 +254,7 @@ define void @weakcrossing5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %1 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %1, ptr %B.addr.02, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
index 71a5ed62b8e21..3c7ab6075964b 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
@@ -100,7 +100,7 @@ define void @weakzerodst1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -318,7 +318,7 @@ define void @weakzerodst6(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
index 2ee1a37775f46..31fae73f0b652 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
@@ -98,7 +98,7 @@ define void @weakzerosrc1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
@@ -316,7 +316,7 @@ define void @weakzerosrc6(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT:    da analyze - none!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
index 0267cbd1fd04a..790c10bfbc91e 100644
--- a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
@@ -4,9 +4,9 @@
 ; 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>
+define void @single_loop_nsw(ptr %a, i64 %n) {
+; CHECK-LABEL: 'single_loop_nsw'
+; CHECK: Monotonicity: Monotonic expr: {0,+,1}<nuw><nsw><%loop>
 ;
 entry:
   %guard = icmp sgt i64 %n, 0
@@ -30,9 +30,10 @@ exit:
 ; for (unsigned long long i = begin; i < end; i++)
 ;   a[i] = 0;
 ;
-define void @f1(ptr %a, i64 %begin, i64 %end) {
-; CHECK-LABEL: 'f1'
+define void @single_loop_nuw(ptr %a, i64 %begin, i64 %end) {
+; CHECK-LABEL: 'single_loop_nuw'
 ; CHECK: Failed to prove monotonicity for: {%begin,+,1}<nuw><%loop>
+; CHECK: Monotonicity: Unknown expr: {%begin,+,1}<nuw><%loop>
 ;
 entry:
   %guard = icmp ult i64 %begin, %end
@@ -54,9 +55,9 @@ exit:
 ;   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>
+define void @nested_loop_nsw0(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw0'
+; CHECK: Monotonicity: Monotonic expr: {{\{}}{0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nuw><nsw><%loop.j>
 ;
 entry:
   %guard.i = icmp sgt i64 %n, 0
@@ -91,9 +92,10 @@ exit:
 ; 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>
+;
+define void @nested_loop_nsw1(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw1'
+; CHECK: Monotonicity: Monotonic expr: {{\{}}{(-1 + %n),+,-1}<nsw><%loop.i.header>,+,1}<nsw><%loop.j>
 ;
 entry:
   %guard.i = icmp sgt i64 %n, 0
@@ -130,9 +132,11 @@ exit:
 ;     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'
+;
+define void @nested_loop_nuw(ptr %a, i64 %begin0, i64 %end0, i64 %begin1, i64 %end1) {
+; CHECK-LABEL: 'nested_loop_nuw'
 ; CHECK: Failed to prove monotonicity for: {{\{}}{(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
+; CHECK: Monotonicity: Unknown expr: {{\{}}{(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
 ;
 entry:
   %guard.i.0 = icmp slt i64 0, %begin0
@@ -171,3 +175,160 @@ loop.i.latch:
 exit:
   ret void
 }
+
+; `step` can be zero, so `step*j` can be either Invariant or Monotonic. This
+; propagates to `i + step*j`.
+;
+; for (int i = 0; i < n; i++)
+;   for (int j = 0; j < m; j++)
+;     a[i + step*j] = 0;
+;
+define void @nested_loop_step(ptr %a, i64 %n, i64 %m, i64 %step) {
+; CHECK-LABEL: 'nested_loop_step'
+; CHECK: Monotonicity: No signed wrap expr: {{\{}}{0,+,1}<nuw><nsw><%loop.i.header>,+,%step}<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.j = phi i64 [ 0, %loop.j.preheader ], [ %offset.j.next, %loop.j ]
+  %offset = add nsw i64 %i, %offset.j
+  %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+  store i8 0, ptr %idx
+  %j.inc = add nsw i64 %j, 1
+  %offset.j.next = add nsw i64 %offset.j, %step
+  %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
+}
+
+; `offset` can be loop-invariant since `step` can be zero.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+;   a[sext(offset)] = 0;
+;
+define void @sext_nsw(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'sext_nsw'
+; CHECK: Monotonicity: No signed wrap expr: {(sext i8 %start to i64),+,(sext i8 %step to i64)}<nsw><%loop>
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+  %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+  %offset.sext = sext i8 %offset to i64
+  %idx = getelementptr i8, ptr %a, i64 %offset.sext
+  store i8 0, ptr %idx
+  %i.inc = add nsw i64 %i, 1
+  %offset.next = add nsw i8 %offset, %step
+  %exitcond = icmp eq i64 %i.inc, 100
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The addition for `%offset.next` can wrap, so we cannot prove monotonicity.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+;   a[sext(offset)] = 0;
+;
+define void @sext_may_wrap(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'sext_may_wrap'
+; CHECK: Failed to prove monotonicity for: {%start,+,%step}<%loop>
+; CHECK: Monotonicity: Unknown expr: (sext i8 {%start,+,%step}<%loop> to i64)
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+  %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+  %offset.sext = sext i8 %offset to i64
+  %idx = getelementptr i8, ptr %a, i64 %offset.sext
+  store i8 0, ptr %idx
+  %i.inc = add nsw i64 %i, 1
+  %offset.next = add i8 %offset, %step
+  %exitcond = icmp eq i64 %i.inc, 100
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; `offset` can be loop-invariant since `step` can be zero.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+;   a[zext(offset)] = 0;
+;
+define void @zext_nsw(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'zext_nsw'
+; CHECK: Monotonicity: No signed wrap expr: (zext i8 {%start,+,%step}<nsw><%loop> to i64)
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+  %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+  %offset.zext = zext i8 %offset to i64
+  %idx = getelementptr i8, ptr %a, i64 %offset.zext
+  store i8 0, ptr %idx
+  %i.inc = add nsw i64 %i, 1
+  %offset.next = add nsw i8 %offset, %step
+  %exitcond = icmp eq i64 %i.inc, 100
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; The addition for `%offset.next` can wrap, so we cannot prove monotonicity.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+;   a[zext(offset)] = 0;
+;
+define void @zext_may_wrap(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'zext_may_wrap'
+; CHECK: Failed to prove monotonicity for: {%start,+,%step}<%loop>
+; CHECK: Monotonicity: Unknown expr: (zext i8 {%start,+,%step}<%loop> to i64)
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+  %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+  %offset.zext = zext i8 %offset to i64
+  %idx = getelementptr i8, ptr %a, i64 %offset.zext
+  store i8 0, ptr %idx
+  %i.inc = add nsw i64 %i, 1
+  %offset.next = add i8 %offset, %step
+  %exitcond = icmp eq i64 %i.inc, 100
+  br i1 %exitcond, label %exit, label %loop
+
+exit:
+  ret void
+}

>From b5ca793172e604255cb4d57ab13e232cc712228a Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 1 Sep 2025 12:05:56 +0000
Subject: [PATCH 07/11] fix gep-based inferrence

---
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 34 +++++++++-----
 llvm/test/Analysis/DependenceAnalysis/GCD.ll  | 12 ++---
 .../Analysis/DependenceAnalysis/monotonic.ll  | 45 +++++++++++++++++++
 3 files changed, 74 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index f31d4d02e517d..75a59c9553e18 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3414,9 +3414,24 @@ SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
     ScalarEvolution *SE, const Loop *OutermostLoop, const Value *Ptr)
     : SE(SE), OutermostLoop(OutermostLoop) {
   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:
+    // Perform reasoning similar to LoopAccessAnalysis. If an AddRec would wrap
+    // and the GEP would have nusw, the wrapped memory location would become
+    // like as follows (in the mathmatical sense, assuming the step recurrence
+    // is positive):
+    //
+    //   (previously accessed location) + (step recurrence) - 2^N
+    //
+    // where N is the size of the pointer index type. Since the value of step
+    // recurrence is less than 2^(N-1), the distance between the previously
+    // accessed location and the wrapped location will be greater than 2^(N-1),
+    // which is larger than half the pointer index type space. The size of
+    // allocated object must not exceed the largest signed integer that fits
+    // into the index type, so the GEP value would be poison and any memory
+    // access using it would be immediate UB when executed.
+    //
+    // TODO: We don't check if the result of the GEP is always used. Maybe we
+    // should check the reachability from the GEP to the target instruction.
+    // E.g., in the following case, no-wrap would not trigger immediate UB:
     //
     //  entry:
     //    ...
@@ -3441,8 +3456,6 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
     const Value *Ptr) {
   SCEVSignedMonotonicityChecker Checker(SE, OutermostLoop, Ptr);
   MonotonicityType MT = Checker.visit(Expr);
-  if (MT == MonotonicityType::Unknown && Checker.NoWrapFromGEP)
-    MT = MonotonicityType::NoSignedWrap;
 
 #ifndef NDEBUG
   switch (MT) {
@@ -3532,8 +3545,6 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
   const SCEV *Start = Expr->getStart();
   const SCEV *Step = Expr->getStepRecurrence(*SE);
 
-  bool IsNSW = Expr->hasNoSignedWrap();
-
   MonotonicityType StartRes = visit(Start);
   if (StartRes == MonotonicityType::Unknown)
     return unknownMonotonicity(Expr);
@@ -3543,7 +3554,7 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
     return unknownMonotonicity(Expr);
 
   // TODO: Enhance the inference here.
-  if (!IsNSW) {
+  if (!Expr->hasNoSignedWrap() && !NoWrapFromGEP) {
     if (!SE->isKnownNegative(Step))
       // If the coefficient can be positive value, ensure that the AddRec is
       // monotonically increasing.
@@ -3787,9 +3798,10 @@ bool DependenceInfo::tryDelinearizeParametricSize(
           LI->getLoopFor(Src->getParent())->getOutermostLoop();
 
       // TODO: In general, reasoning about monotonicity of a subscript from the
-      // base pointer would not be allowed. Probably we need to check the loops
-      // associated with this subscript are disjoint from those associated with
-      // the other subscripts. The validation would be something like:
+      // base pointer would lead incorrect result. Probably we need to check
+      // the loops associated with this subscript are disjoint from those
+      // associated with the other subscripts. The validation would be
+      // something like:
       //
       //   LoopsI = collectCommonLoops(SrcSubscripts[I])
       //   LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 24cf95987df4d..93a2f07e7df21 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -465,13 +465,13 @@ for.end12:                                        ; preds = %for.end12.loopexit,
 define void @gcd7(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-LABEL: 'gcd7'
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %7, ptr %arrayidx6, align 4
-; CHECK-NEXT:    da analyze - output [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - flow [* *|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - input [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %11, ptr %B.addr.12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
@@ -640,13 +640,13 @@ for.end15:                                        ; preds = %for.end15.loopexit,
 define void @gcd9(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-LABEL: 'gcd9'
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %7, ptr %arrayidx6, align 4
-; CHECK-NEXT:    da analyze - output [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - flow [* *|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - input [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %11, ptr %B.addr.12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
index 790c10bfbc91e..4d80772e5ed24 100644
--- a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
@@ -332,3 +332,48 @@ loop:
 exit:
   ret void
 }
+
+; The value of step reccurence is not invariant with respect to the outer most
+; loop (the i-loop).
+;
+; offset_i = 0;
+; for (int i = 0; i < 100; i++) {
+;   for (int j = 0; j < 100; j++)
+;     a[offset_i + j] = 0;
+;   offset_i += (i % 2 == 0) ? 0 : 3;
+; }
+;
+define void @step_is_variant(ptr %a) {
+; CHECK-LABEL: 'step_is_variant'
+; CHECK: Failed to prove monotonicity for: %offset.i
+; CHECK: Failed to prove monotonicity for: {%offset.i,+,1}<nuw><nsw><%loop.j>
+; CHECK: Monotonicity: Unknown expr: {%offset.i,+,1}<nuw><nsw><%loop.j>
+;
+entry:
+  br label %loop.i.header
+
+loop.i.header:
+  %i = phi i64 [ 0, %entry ], [ %i.inc, %loop.i.latch ]
+  %offset.i = phi i64 [ 0, %entry ], [ %offset.i.next, %loop.i.latch ]
+  %step.i.0 = phi i64 [ 0, %entry ], [ %step.i.1, %loop.i.latch ]
+  %step.i.1 = phi i64 [ 3, %entry ], [ %step.i.0, %loop.i.latch ]
+  br label %loop.j
+
+loop.j:
+  %j = phi i64 [ 0, %loop.i.header ], [ %j.inc, %loop.j ]
+  %offset = add nsw i64 %offset.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, 100
+  br i1 %exitcond.j, label %loop.i.latch, label %loop.j
+
+loop.i.latch:
+  %i.inc = add nsw i64 %i, 1
+  %offset.i.next = add nsw i64 %offset.i, %step.i.0
+  %exitcond.i = icmp eq i64 %i.inc, 100
+  br i1 %exitcond.i, label %exit, label %loop.i.header
+
+exit:
+  ret void
+}

>From 137c193ecbbab179ae4fbc1fea4768fa3d0ae463 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 6 Sep 2025 04:49:40 +0900
Subject: [PATCH 08/11] fix logic for SCEVNArayExpr

---
 llvm/lib/Analysis/DependenceAnalysis.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 4f62a17af6041..183107cf247d5 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3497,19 +3497,17 @@ SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
     case MonotonicityType::Invariant:
       break;
     case MonotonicityType::MultiMonotonic: {
+      if (!Expr->hasNoSignedWrap())
+        return unknownMonotonicity(Expr);
       switch (Result) {
       case MonotonicityType::Unknown:
         llvm_unreachable("should have been handled above");
       case MonotonicityType::NoSignedWrap:
         break;
       case MonotonicityType::Invariant:
-        if (!Expr->hasNoSignedWrap())
-          return unknownMonotonicity(Expr);
         Result = MonotonicityType::MultiMonotonic;
         break;
       case MonotonicityType::MultiMonotonic:
-        if (!Expr->hasNoSignedWrap())
-          return unknownMonotonicity(Expr);
         if (!isa<SCEVAddExpr>(Expr))
           return unknownMonotonicity(Expr);
         // Monotonic + Monotonic might be a loop invariant, e.g., the following

>From f9b2a4e12b3236778180b3fa9b03a92d916d5b68 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 6 Sep 2025 05:31:14 +0900
Subject: [PATCH 09/11] adjust comments

---
 llvm/lib/Analysis/DependenceAnalysis.cpp | 61 ++++++++++++++----------
 1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 183107cf247d5..9172d31119ac0 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3429,22 +3429,10 @@ SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
     // into the index type, so the GEP value would be poison and any memory
     // access using it would be immediate UB when executed.
     //
-    // TODO: We don't check if the result of the GEP is always used. Maybe we
-    // should check the reachability from the GEP to the target instruction.
-    // E.g., in the following case, no-wrap would not trigger immediate UB:
-    //
-    //  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:
-    //     ...
-    //
+    // TODO: The monotonicity check ensures that the given SCEV does not wrap
+    // in "any" iteration. Thus, inference from nusw should be valid only if
+    // the GEP is executed and its result is used in every iteration of the
+    // loop.
     auto *GEP = dyn_cast<GetElementPtrInst>(Ptr);
     if (GEP && GEP->hasNoUnsignedSignedWrap())
       NoWrapFromGEP = true;
@@ -3807,16 +3795,25 @@ bool DependenceInfo::tryDelinearizeParametricSize(
       const Loop *OutermostLoop =
           LI->getLoopFor(Src->getParent())->getOutermostLoop();
 
-      // TODO: In general, reasoning about monotonicity of a subscript from the
-      // base pointer would lead incorrect result. Probably we need to check
-      // the loops associated with this subscript are disjoint from those
-      // associated with the other subscripts. The validation would be
-      // something like:
+      // TODO: Inferring a subscript's monotonicity from the base pointer can
+      // lead to incorrect results. Consider the following code:
+      //
+      //   %offset = ...
+      //   %gep = getelementptr nusw i8, ptr %base, i64 %offset
       //
-      //   LoopsI = collectCommonLoops(SrcSubscripts[I])
-      //   LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
-      //   CanUsePtr = (LoopsI intersect LoopsOthers) is empty.
+      // We might infer the monotonicity of %offset from nusw on the GEP (see
+      // the implementation of checkMonotonicity for details). This inference
+      // may be valid, but the same does not necessarily hold for each
+      // subscript. For example, assume %offset is {0,+,(%m * %n)}<%loop> where
+      // %m and %n are loop invariants. Delinearization can "decompose" this
+      // SCEV into something like:
       //
+      //   Size: [UnknownSize][%m]
+      //   Subscripts: [{0,+,%n}][{0,+,1}]
+      //
+      // Here, if (%m * %n) wraps, %n can be larger than (%m * %n). Hence even
+      // if we know {0,+,(%m * %n)} doesn't wrap, we cannot conclude the same
+      // for {0,+,%n}.
       MonotonicityType SrcMonotonicity =
           SCEVSignedMonotonicityChecker::checkMonotonicity(
               SE, SrcSubscripts[I], OutermostLoop, SrcPtr);
@@ -3852,6 +3849,22 @@ bool DependenceInfo::tryDelinearizeParametricSize(
       return false;
     }
 
+  // TODO: Probably we need to prove that the "offset calculation" doesn't
+  // wrap. Here the offset calculation is:
+  //
+  //   Offset =
+  //     Subscripts[0] +
+  //     Subscripts[1]*Sizes[0] +
+  //     Subscripts[2]*Sizes[0]*Sizes[1] +
+  //     ...
+  //     Subscripts[N-1]*Sizes[0]*Sizes[1]*...*Sizes[N-2]
+  //
+  // where N is the number of dimensions. The subsequent dependence tests assume
+  // that different subscript values result in different offset values. If the
+  // above calculation wraps, this assumption is violated. Note that if every
+  // element of Subscripts is positive, the situation would be simple. However,
+  // the subscript for the outermost dimension (Subscripts[0]) can be negative.
+
   return true;
 }
 

>From e83fdb8723f45a53ea4400dcff6e0dddee0c3106 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 8 Sep 2025 09:35:50 +0000
Subject: [PATCH 10/11] remove handlings for add/mul

---
 llvm/lib/Analysis/DependenceAnalysis.cpp      | 62 +++----------------
 llvm/test/Analysis/DependenceAnalysis/AA.ll   |  4 +-
 .../Analysis/DependenceAnalysis/DADelin.ll    |  4 +-
 llvm/test/Analysis/DependenceAnalysis/GCD.ll  |  6 +-
 .../Analysis/DependenceAnalysis/PR21585.ll    |  2 +-
 .../Analysis/DependenceAnalysis/PR31848.ll    |  2 +-
 .../DependenceAnalysis/Preliminary.ll         | 12 ++--
 7 files changed, 22 insertions(+), 70 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 9172d31119ac0..c7ea2349d8100 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3352,13 +3352,6 @@ struct SCEVSignedMonotonicityChecker
   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::Invariant;
   }
@@ -3367,6 +3360,12 @@ struct SCEVSignedMonotonicityChecker
   }
 
   // TODO: Handle more cases.
+  MonotonicityType visitAddExpr(const SCEVAddExpr *Expr) {
+    return unknownMonotonicity(Expr);
+  }
+  MonotonicityType visitMulExpr(const SCEVMulExpr *Expr) {
+    return unknownMonotonicity(Expr);
+  }
   MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
     return unknownMonotonicity(Expr);
   }
@@ -3403,7 +3402,6 @@ struct SCEVSignedMonotonicityChecker
   SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
                                 const Value *Ptr);
 
-  MonotonicityType visitNAryHelper(const SCEVNAryExpr *Expr);
   MonotonicityType unknownMonotonicity(const SCEV *Expr);
   bool isLoopInvariant(const SCEV *Expr) const;
 };
@@ -3466,55 +3464,9 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
 }
 
 MonotonicityType
-SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
-  assert((isa<SCEVAddExpr>(Expr) || isa<SCEVMulExpr>(Expr)) &&
-         "Unexpected SCEV");
-
+SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
   if (isLoopInvariant(Expr))
     return MonotonicityType::Invariant;
-
-  MonotonicityType Result = MonotonicityType::Invariant;
-  for (const SCEV *Op : Expr->operands()) {
-    assert(Result != MonotonicityType::Unknown && "Unexpected state");
-    switch (visit(Op)) {
-    case MonotonicityType::Unknown:
-      return unknownMonotonicity(Expr);
-    case MonotonicityType::NoSignedWrap:
-      Result = MonotonicityType::NoSignedWrap;
-      break;
-    case MonotonicityType::Invariant:
-      break;
-    case MonotonicityType::MultiMonotonic: {
-      if (!Expr->hasNoSignedWrap())
-        return unknownMonotonicity(Expr);
-      switch (Result) {
-      case MonotonicityType::Unknown:
-        llvm_unreachable("should have been handled above");
-      case MonotonicityType::NoSignedWrap:
-        break;
-      case MonotonicityType::Invariant:
-        Result = MonotonicityType::MultiMonotonic;
-        break;
-      case MonotonicityType::MultiMonotonic:
-        if (!isa<SCEVAddExpr>(Expr))
-          return unknownMonotonicity(Expr);
-        // Monotonic + Monotonic might be a loop invariant, e.g., the following
-        // SCEV:
-        //
-        //   {0,+,1}<%loop> + {0,+,-1}<%loop>
-        //
-        // In that case, relax the property to NoSignedWrap.
-        Result = MonotonicityType::NoSignedWrap;
-        break;
-      }
-    } break;
-    }
-  }
-  return Result;
-}
-
-MonotonicityType
-SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
   LLVM_DEBUG(dbgs() << "Failed to prove monotonicity for: " << *Expr << "\n");
   return MonotonicityType::Unknown;
 }
diff --git a/llvm/test/Analysis/DependenceAnalysis/AA.ll b/llvm/test/Analysis/DependenceAnalysis/AA.ll
index 173744a07ef96..b03f82ed1b084 100644
--- a/llvm/test/Analysis/DependenceAnalysis/AA.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/AA.ll
@@ -128,11 +128,11 @@ define void @test_tbaa_diff(ptr %A, ptr %B) {
 define void @tbaa_loop(i32 %I, i32 %J, ptr nocapture %A, ptr nocapture readonly %B) {
 ; CHECK-LABEL: 'tbaa_loop'
 ; CHECK-NEXT:  Src: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0 --> Dst: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0
-; CHECK-NEXT:    da analyze - input [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0 --> Dst: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4 --> Dst: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4
-; CHECK-NEXT:    da analyze - output [*]!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp = icmp ne i32 %J, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 174f105b666f3..eb05b7dd3e9a8 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -547,9 +547,9 @@ define double @test_sizes(i16 %h, i16 %N, ptr nocapture %array) {
 ; CHECK-NEXT:  Src: %2 = load i16, ptr %arrayidx, align 4 --> Dst: %2 = load i16, ptr %arrayidx, align 4
 ; CHECK-NEXT:    da analyze - consistent input [0 S]!
 ; CHECK-NEXT:  Src: %2 = load i16, ptr %arrayidx, align 4 --> Dst: store i16 %add6, ptr %arrayidx8, align 4
-; CHECK-NEXT:    da analyze - anti [* *|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i16 %add6, ptr %arrayidx8, align 4 --> Dst: store i16 %add6, ptr %arrayidx8, align 4
-; CHECK-NEXT:    da analyze - output [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp28 = icmp sgt i16 %N, 1
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 93a2f07e7df21..98026629ce4e4 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -556,13 +556,13 @@ for.end15:                                        ; preds = %for.end15.loopexit,
 define void @gcd8(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
 ; CHECK-LABEL: 'gcd8'
 ; CHECK-NEXT:  Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: store i32 %i.06, ptr %arrayidx, align 4
-; CHECK-NEXT:    da analyze - output [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: %5 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - flow [* *|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %5 = load i32, ptr %arrayidx12, align 4 --> Dst: %5 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT:    da analyze - input [* *]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %5 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %5, ptr %B.addr.12, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
index c234c59db9f17..fe7a6135e23b5 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
@@ -47,7 +47,7 @@ define void @t(ptr noalias %a, i32 %n) nounwind {
 ; CHECK-NEXT:  Src: %0 = load i32, ptr @g, align 4 --> Dst: store i32 %0, ptr %arrayidx, align 4
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %arrayidx, align 4
-; CHECK-NEXT:    da analyze - output [*]!
+; CHECK-NEXT:    da analyze - confused!
 ;
 entry:
   %cmp1 = icmp eq i32 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR31848.ll b/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
index 8ee1224a18ad1..db64d2cab2fed 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
@@ -8,7 +8,7 @@
 define void @barney(ptr nocapture %arg, i32 %arg1) {
 ; CHECK-LABEL: 'barney'
 ; CHECK-NEXT:  Src: store i32 7, ptr %getelementptr, align 4 --> Dst: store i32 7, ptr %getelementptr, align 4
-; CHECK-NEXT:    da analyze - output [* * * *]!
+; CHECK-NEXT:    da analyze - confused!
 ;
 bb:
   %icmp = icmp sgt i32 %arg1, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
index e7449c6072846..3fac79957dfab 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
@@ -438,13 +438,13 @@ for.end26:                                        ; preds = %for.end26.loopexit,
 define void @p4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-LABEL: 'p4'
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %conv2, ptr %arrayidx, align 4
-; CHECK-NEXT:    da analyze - output [*]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT:    da analyze - flow [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT:    da analyze - input [*]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
@@ -491,13 +491,13 @@ for.end:                                          ; preds = %for.end.loopexit, %
 define void @p5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
 ; CHECK-LABEL: 'p5'
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %conv2, ptr %arrayidx, align 4
-; CHECK-NEXT:    da analyze - output [*]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT:    da analyze - flow [*|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT:    da analyze - input [*]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
 ; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4

>From a0987435560700b6dff04f866910b942956286e2 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 8 Sep 2025 09:37:59 +0000
Subject: [PATCH 11/11] rename function name and add comments

---
 llvm/lib/Analysis/DependenceAnalysis.cpp | 42 +++++++++++++-----------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index c7ea2349d8100..95d1286414841 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3361,37 +3361,37 @@ struct SCEVSignedMonotonicityChecker
 
   // TODO: Handle more cases.
   MonotonicityType visitAddExpr(const SCEVAddExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitMulExpr(const SCEVMulExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitTruncateExpr(const SCEVTruncateExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitUDivExpr(const SCEVUDivExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitSMaxExpr(const SCEVSMaxExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitUMaxExpr(const SCEVUMaxExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitSMinExpr(const SCEVSMinExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitUMinExpr(const SCEVUMinExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
   MonotonicityType visitCouldNotCompute(const SCEVCouldNotCompute *Expr) {
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   }
 
 private:
@@ -3402,7 +3402,11 @@ struct SCEVSignedMonotonicityChecker
   SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
                                 const Value *Ptr);
 
-  MonotonicityType unknownMonotonicity(const SCEV *Expr);
+  /// A helper to classify \p Expr as either Invariant or Unknown.
+  MonotonicityType checkInvarianceOnly(const SCEV *Expr);
+
+  /// Return true if \p Expr is loop-invariant with respect to the outermost
+  /// loop.
   bool isLoopInvariant(const SCEV *Expr) const;
 };
 
@@ -3464,7 +3468,7 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
 }
 
 MonotonicityType
-SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
+SCEVSignedMonotonicityChecker::checkInvarianceOnly(const SCEV *Expr) {
   if (isLoopInvariant(Expr))
     return MonotonicityType::Invariant;
   LLVM_DEBUG(dbgs() << "Failed to prove monotonicity for: " << *Expr << "\n");
@@ -3478,18 +3482,18 @@ bool SCEVSignedMonotonicityChecker::isLoopInvariant(const SCEV *Expr) const {
 MonotonicityType
 SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
   if (!Expr->isAffine())
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
 
   const SCEV *Start = Expr->getStart();
   const SCEV *Step = Expr->getStepRecurrence(*SE);
 
   MonotonicityType StartRes = visit(Start);
   if (StartRes == MonotonicityType::Unknown)
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
 
   MonotonicityType StepRes = visit(Step);
   if (StepRes != MonotonicityType::Invariant)
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
 
   // TODO: Enhance the inference here.
   if (!Expr->hasNoSignedWrap() && !NoWrapFromGEP) {
@@ -3497,13 +3501,13 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
       // If the coefficient can be positive value, ensure that the AddRec is
       // monotonically increasing.
       if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SGE, Expr, Start))
-        return unknownMonotonicity(Expr);
+        return checkInvarianceOnly(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 unknownMonotonicity(Expr);
+        return checkInvarianceOnly(Expr);
   }
 
   bool IsKnownNonZero = SE->isKnownNonZero(Step);
@@ -3536,7 +3540,7 @@ MonotonicityType SCEVSignedMonotonicityChecker::visitSignExtendExpr(
 MonotonicityType
 SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
   if (!isLoopInvariant(Expr))
-    return unknownMonotonicity(Expr);
+    return checkInvarianceOnly(Expr);
   return MonotonicityType::Invariant;
 }
 



More information about the llvm-commits mailing list