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

Ryotaro Kasuga via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 26 01:12:27 PDT 2025


================
@@ -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())
----------------
kasuga-fj wrote:

Yes, and the expected behavior is to detect such expressions and bail out of the analysis. The subsequent checks attempt to prove properties similar to nsw when it's not explicitly attached, although I'm not sure they're truly necessary (I've not tested enough yet).

https://github.com/llvm/llvm-project/pull/154527


More information about the llvm-commits mailing list