[llvm] [DA] Check monotonicity for subscripts (PR #154527)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 25 01:56:12 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())
+ 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;
----------------
kasuga-fj wrote:
Just to align with the current behavior.
https://github.com/llvm/llvm-project/blob/db024764c18413a3b2d3cebe2bb06a09eeef507d/llvm/lib/Analysis/DependenceAnalysis.cpp#L854-L867
But I'm not sure whether checking invariance under the current loop is sufficient or not.
https://github.com/llvm/llvm-project/pull/154527
More information about the llvm-commits
mailing list