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

Sushant Gokhale via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 25 02:59:54 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;
+    }
----------------
sushgokh wrote:

>First of all, probably the name is misleading. MaySignedWrap should be renamed to something like Unknown

ok, please change to Unknown or CouldNotCompute. `MaySignedWrap ` is confusing

>example like {0,+,%m}<%loop> + {0,+,%n}<%loop>

If the entire expression is nuw/nsw then individual SCEVs must follow the same pattern but vice-versa cant be true(this may wrap).

>that this form is always folded into something like {0,+,(%m+%n)}<%loop>

this is not true because when its split form, each AddRed can have different values . But with (%m+%n) , every itr is multiple of (%m+%n)

>{0,+,1}<%loop> + {0,+,-1}<%loop>

this expr can have values 0(=0+0), -1(=0-1), 1(=1+0), 0(=1-1). This is definitely not a constant. So, this should be `Unknown`

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


More information about the llvm-commits mailing list