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

Ryotaro Kasuga via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 22 09:27:54 PDT 2025


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

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

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

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

>From 75639b93edaf3a4ec7a0cf13bb64f89c550d80de Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 23 Aug 2025 01:27:13 +0900
Subject: [PATCH 2/2] 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.



More information about the llvm-commits mailing list