[llvm] [DA] Check monotonicity for subscripts (PR #154527)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 27 04:25:02 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/4] [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/4] 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 3/4] 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 4/4] 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
+}
More information about the llvm-commits
mailing list