[llvm] [DA] Check monotonicity for subscripts (PR #154527)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 22 09:30:30 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/3] [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/3] 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 ac9bbc6729d43e748b2c358324f34d98dad8ad7b 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/3] Fix format
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 3a0b4aca31521..a32324ba449d3 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3311,15 +3311,9 @@ 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.
+ 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.
@@ -3818,9 +3812,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)
More information about the llvm-commits
mailing list