[llvm] [DA] Check monotonicity for subscripts (PR #154527)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 8 02:42:12 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 01/11] [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 02/11] 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 03/11] 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 04/11] 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
+}
>From dcb3b1954d9e6619bb7d2e019db90e5ce5c8162c Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Thu, 28 Aug 2025 10:05:36 +0000
Subject: [PATCH 05/11] remove minmax calculator
---
.../llvm/Analysis/DependenceAnalysis.h | 1 -
llvm/lib/Analysis/DependenceAnalysis.cpp | 139 +-----------------
2 files changed, 8 insertions(+), 132 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DependenceAnalysis.h b/llvm/include/llvm/Analysis/DependenceAnalysis.h
index 9d9b37ac197f7..f66c79d915665 100644
--- a/llvm/include/llvm/Analysis/DependenceAnalysis.h
+++ b/llvm/include/llvm/Analysis/DependenceAnalysis.h
@@ -921,7 +921,6 @@ class DependenceInfo {
/// checkDstSubscript to avoid duplicate code
bool checkSubscript(const SCEV *Expr, const Loop *LoopNest,
SmallBitVector &Loops, bool IsSrc);
-
}; // 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 dd9bd3e056b54..d6060e524be08 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3396,53 +3396,6 @@ struct SCEVSignedMonotonicityChecker
bool isLoopInvariant(const SCEV *Expr) const;
};
-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::checkMonotonicity(
@@ -3585,72 +3538,6 @@ SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
return MonotonicityType::Invariant;
}
-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
@@ -3852,35 +3739,25 @@ bool DependenceInfo::tryDelinearizeParametricSize(
if (SrcMonotonicity == MonotonicityType::Unknown)
return false;
+ if (!isKnownNonNegative(SrcSubscripts[I], SrcPtr))
+ return false;
+
+ if (!isKnownLessThan(SrcSubscripts[I], Sizes[I - 1]))
+ return false;
+
MonotonicityType DstMonotonicity =
SCEVSignedMonotonicityChecker::checkMonotonicity(
SE, DstSubscripts[I], OutermostLoop, DstPtr);
if (DstMonotonicity == MonotonicityType::Unknown)
return false;
- auto [SrcMin, SrcMax] =
- SCEVMinMaxCalculator(SE, OutermostLoop).visit(SrcSubscripts[I]);
- if (!SrcMin || !SrcMax)
- return false;
- if (!SE->isKnownPositive(SrcMin) ||
- !SE->isKnownPredicate(CmpInst::ICMP_SLT, SrcMax, Sizes[I - 1]))
+ if (!isKnownNonNegative(DstSubscripts[I], DstPtr))
return false;
- auto [DstMin, DstMax] =
- SCEVMinMaxCalculator(SE, OutermostLoop).visit(DstSubscripts[I]);
- if (!DstMin || !DstMax)
- return false;
- if (!SE->isKnownPositive(DstMin) ||
- !SE->isKnownPredicate(CmpInst::ICMP_SLT, DstMax, Sizes[I - 1]))
+ if (!isKnownLessThan(DstSubscripts[I], 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;
}
>From 72b290266a8f4947284bc32f5b5bbd19cd15a010 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Fri, 29 Aug 2025 14:00:06 +0000
Subject: [PATCH 06/11] Introduce NoSignedWrap to MonotonicityType
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 168 +++++++++++-----
.../Analysis/DependenceAnalysis/Banerjee.ll | 30 +--
.../Analysis/DependenceAnalysis/BasePtrBug.ll | 4 +-
.../Analysis/DependenceAnalysis/DADelin.ll | 12 +-
.../Analysis/DependenceAnalysis/ExactRDIV.ll | 8 +-
llvm/test/Analysis/DependenceAnalysis/GCD.ll | 12 +-
.../Analysis/DependenceAnalysis/PR21585.ll | 6 +-
.../DependenceAnalysis/Preliminary.ll | 8 +-
.../DependenceAnalysis/Propagating.ll | 20 +-
.../DependenceAnalysis/Separability.ll | 8 +-
.../Analysis/DependenceAnalysis/StrongSIV.ll | 4 +-
.../DependenceAnalysis/SymbolicRDIV.ll | 12 +-
.../DependenceAnalysis/SymbolicSIV.ll | 18 +-
.../DependenceAnalysis/WeakCrossingSIV.ll | 6 +-
.../DependenceAnalysis/WeakZeroDstSIV.ll | 4 +-
.../DependenceAnalysis/WeakZeroSrcSIV.ll | 4 +-
.../Analysis/DependenceAnalysis/monotonic.ll | 187 ++++++++++++++++--
17 files changed, 368 insertions(+), 143 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index d6060e524be08..f31d4d02e517d 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3310,10 +3310,23 @@ void DependenceInfo::updateDirection(Dependence::DVEntry &Level,
namespace {
+/// The type of signed monotonicity of a SCEV expression. This property is
+/// defined with respect to the outermost loop that DA is analyzing. Invariant
+/// and MultiMonotonic mutually exclusive, and both imply NoSignedWrap.
+///
+/// This is designed to classify the behavior of AddRec expressions, and does
+/// not care about other SCEVs. For example, given the two loop invariants `A`
+/// and `B`, `A + B` is treated as Invariant even if the addition may wrap. On
+/// the other hand, if either `A` or `B` is an AddRec and we cannot prove the
+/// addition doesn't wrap, the result is classified as Unknown.
enum class MonotonicityType {
- Unknown, ///< The expression contains some non loop-invariant SCEVUnknown or
- ///< arithmetic operations that may cause signed wrap.
- Invariant, ///< The expression is a loop-invariant.
+ Unknown, ///< The expression contains some non loop-invariant SCEVUnknown or
+ ///< arithmetic operation that has some AddRec as its subexpression
+ ///< and may cause signed wrap.
+ NoSignedWrap, ///< The expression doesn't contain any AddRecs that may wrap.
+ ///< This is a weaker property than Invariant or MultiMonotonic.
+ ///< Invariant and MultiMonotonic imply NoSignedWrap.
+ 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
@@ -3385,11 +3398,10 @@ struct SCEVSignedMonotonicityChecker
private:
ScalarEvolution *SE;
const Loop *OutermostLoop;
- const Value *Ptr = nullptr;
+ bool NoWrapFromGEP = false;
SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
- const Value *Ptr)
- : SE(SE), OutermostLoop(OutermostLoop), Ptr(Ptr) {}
+ const Value *Ptr);
MonotonicityType visitNAryHelper(const SCEVNAryExpr *Expr);
MonotonicityType unknownMonotonicity(const SCEV *Expr);
@@ -3398,21 +3410,54 @@ struct SCEVSignedMonotonicityChecker
} // anonymous namespace
+SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
+ ScalarEvolution *SE, const Loop *OutermostLoop, const Value *Ptr)
+ : SE(SE), OutermostLoop(OutermostLoop) {
+ 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())
+ NoWrapFromGEP = true;
+ }
+}
+
MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
ScalarEvolution *SE, const SCEV *Expr, const Loop *OutermostLoop,
const Value *Ptr) {
SCEVSignedMonotonicityChecker Checker(SE, OutermostLoop, Ptr);
MonotonicityType MT = Checker.visit(Expr);
+ if (MT == MonotonicityType::Unknown && Checker.NoWrapFromGEP)
+ MT = MonotonicityType::NoSignedWrap;
#ifndef NDEBUG
switch (MT) {
case MonotonicityType::Unknown:
+ LLVM_DEBUG(dbgs() << "Monotonicity: Unknown expr: " << *Expr << "\n");
+ break;
+ case MonotonicityType::NoSignedWrap:
+ LLVM_DEBUG(dbgs() << "Monotonicity: No signed wrap expr: " << *Expr
+ << "\n");
break;
case MonotonicityType::Invariant:
- LLVM_DEBUG(dbgs() << "Invariant expr: " << *Expr << "\n");
+ LLVM_DEBUG(dbgs() << "Monotonicity: Invariant expr: " << *Expr << "\n");
break;
case MonotonicityType::MultiMonotonic:
- LLVM_DEBUG(dbgs() << "Monotonic expr: " << *Expr << "\n");
+ LLVM_DEBUG(dbgs() << "Monotonicity: Monotonic expr: " << *Expr << "\n");
break;
}
#endif
@@ -3421,28 +3466,49 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
MonotonicityType
SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
+ assert((isa<SCEVAddExpr>(Expr) || isa<SCEVMulExpr>(Expr)) &&
+ "Unexpected SCEV");
+
if (isLoopInvariant(Expr))
return MonotonicityType::Invariant;
- if (!Expr->hasNoSignedWrap())
- return unknownMonotonicity(Expr);
-
MonotonicityType Result = MonotonicityType::Invariant;
for (const SCEV *Op : Expr->operands()) {
+ assert(Result != MonotonicityType::Unknown && "Unexpected state");
switch (visit(Op)) {
case MonotonicityType::Unknown:
return unknownMonotonicity(Expr);
- case MonotonicityType::Invariant:
+ case MonotonicityType::NoSignedWrap:
+ Result = MonotonicityType::NoSignedWrap;
break;
- 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;
+ case MonotonicityType::Invariant:
break;
+ case MonotonicityType::MultiMonotonic: {
+ switch (Result) {
+ case MonotonicityType::Unknown:
+ llvm_unreachable("should have been handled above");
+ case MonotonicityType::NoSignedWrap:
+ break;
+ case MonotonicityType::Invariant:
+ if (!Expr->hasNoSignedWrap())
+ return unknownMonotonicity(Expr);
+ Result = MonotonicityType::MultiMonotonic;
+ break;
+ case MonotonicityType::MultiMonotonic:
+ if (!Expr->hasNoSignedWrap())
+ return unknownMonotonicity(Expr);
+ if (!isa<SCEVAddExpr>(Expr))
+ return unknownMonotonicity(Expr);
+ // Monotonic + Monotonic might be a loop invariant, e.g., the following
+ // SCEV:
+ //
+ // {0,+,1}<%loop> + {0,+,-1}<%loop>
+ //
+ // In that case, relax the property to NoSignedWrap.
+ Result = MonotonicityType::NoSignedWrap;
+ break;
+ }
+ } break;
}
}
return Result;
@@ -3466,44 +3532,17 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
const SCEV *Start = Expr->getStart();
const SCEV *Step = Expr->getStepRecurrence(*SE);
+ bool IsNSW = Expr->hasNoSignedWrap();
+
MonotonicityType StartRes = visit(Start);
if (StartRes == MonotonicityType::Unknown)
return unknownMonotonicity(Expr);
MonotonicityType StepRes = visit(Step);
- if (StepRes != MonotonicityType::Invariant || !SE->isKnownNonZero(Step))
+ if (StepRes != MonotonicityType::Invariant)
return unknownMonotonicity(Expr);
- 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;
- }();
-
- // TODO: Is this additional check necessary?
+ // TODO: Enhance the inference here.
if (!IsNSW) {
if (!SE->isKnownNegative(Step))
// If the coefficient can be positive value, ensure that the AddRec is
@@ -3518,7 +3557,21 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
return unknownMonotonicity(Expr);
}
- return MonotonicityType::MultiMonotonic;
+ bool IsKnownNonZero = SE->isKnownNonZero(Step);
+ switch (StartRes) {
+ case MonotonicityType::Unknown:
+ llvm_unreachable("should have been handled above");
+ case MonotonicityType::NoSignedWrap:
+ return MonotonicityType::NoSignedWrap;
+ case MonotonicityType::Invariant:
+ return IsKnownNonZero ? MonotonicityType::MultiMonotonic
+ : MonotonicityType::NoSignedWrap;
+ case MonotonicityType::MultiMonotonic:
+ // TODO: Should handle SCEV like `{{0,+,-1}<%loop>,+,1}<%loop>`?
+ return IsKnownNonZero ? MonotonicityType::MultiMonotonic
+ : MonotonicityType::NoSignedWrap;
+ }
+ llvm_unreachable("unhandled MonotonicityType");
}
MonotonicityType SCEVSignedMonotonicityChecker::visitZeroExtendExpr(
@@ -3733,6 +3786,15 @@ bool DependenceInfo::tryDelinearizeParametricSize(
const Loop *OutermostLoop =
LI->getLoopFor(Src->getParent())->getOutermostLoop();
+ // TODO: In general, reasoning about monotonicity of a subscript from the
+ // base pointer would not be allowed. Probably we need to check the loops
+ // associated with this subscript are disjoint from those associated with
+ // the other subscripts. The validation would be something like:
+ //
+ // LoopsI = collectCommonLoops(SrcSubscripts[I])
+ // LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
+ // CanUsePtr = (LoopsI intersect LoopsOthers) is empty.
+ //
MonotonicityType SrcMonotonicity =
SCEVSignedMonotonicityChecker::checkMonotonicity(
SE, SrcSubscripts[I], OutermostLoop, SrcPtr);
@@ -3939,6 +4001,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
LLVM_DEBUG(dbgs() << " delinearized\n");
Pairs = Pair.size();
}
+ // TODO: Check that the original offsets are monotonic when delinearization
+ // fails.
}
for (unsigned P = 0; P < Pairs; ++P) {
diff --git a/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll b/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
index e0def901d1759..c96f0dfa1f5d5 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Banerjee.ll
@@ -28,7 +28,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
; NORMALIZE-LABEL: 'banerjee0'
; NORMALIZE-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -42,7 +42,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; NORMALIZE-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; NORMALIZE-NEXT: da analyze - confused!
; NORMALIZE-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT: da analyze - none!
+; NORMALIZE-NEXT: da analyze - confused!
;
; DELIN-LABEL: 'banerjee0'
; DELIN-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -56,7 +56,7 @@ define void @banerjee0(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; DELIN-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; DELIN-NEXT: da analyze - confused!
; DELIN-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT: da analyze - none!
+; DELIN-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -810,7 +810,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
; NORMALIZE-LABEL: 'banerjee9'
; NORMALIZE-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -824,7 +824,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; NORMALIZE-NEXT: Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; NORMALIZE-NEXT: da analyze - confused!
; NORMALIZE-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT: da analyze - none!
+; NORMALIZE-NEXT: da analyze - confused!
;
; DELIN-LABEL: 'banerjee9'
; DELIN-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -838,7 +838,7 @@ define void @banerjee9(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; DELIN-NEXT: Src: %1 = load i64, ptr %arrayidx7, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; DELIN-NEXT: da analyze - confused!
; DELIN-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; DELIN-NEXT: da analyze - none!
+; DELIN-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -896,7 +896,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
; NORMALIZE-LABEL: 'banerjee10'
; NORMALIZE-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -910,7 +910,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; NORMALIZE-NEXT: Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; NORMALIZE-NEXT: da analyze - confused!
; NORMALIZE-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT: da analyze - none!
+; NORMALIZE-NEXT: da analyze - confused!
;
; DELIN-LABEL: 'banerjee10'
; DELIN-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -924,7 +924,7 @@ define void @banerjee10(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; DELIN-NEXT: Src: %1 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
; DELIN-NEXT: da analyze - confused!
; DELIN-NEXT: Src: store i64 %1, ptr %B.addr.11, align 8 --> Dst: store i64 %1, ptr %B.addr.11, align 8
-; DELIN-NEXT: da analyze - none!
+; DELIN-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -981,7 +981,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
; NORMALIZE-LABEL: 'banerjee11'
; NORMALIZE-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -995,7 +995,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; NORMALIZE-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; NORMALIZE-NEXT: da analyze - confused!
; NORMALIZE-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT: da analyze - none!
+; NORMALIZE-NEXT: da analyze - confused!
;
; DELIN-LABEL: 'banerjee11'
; DELIN-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1009,7 +1009,7 @@ define void @banerjee11(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; DELIN-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; DELIN-NEXT: da analyze - confused!
; DELIN-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT: da analyze - none!
+; DELIN-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -1066,7 +1066,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
; NORMALIZE-LABEL: 'banerjee12'
; NORMALIZE-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1080,7 +1080,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; NORMALIZE-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; NORMALIZE-NEXT: da analyze - confused!
; NORMALIZE-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; NORMALIZE-NEXT: da analyze - none!
+; NORMALIZE-NEXT: da analyze - confused!
;
; DELIN-LABEL: 'banerjee12'
; DELIN-NEXT: Src: store i64 0, ptr %arrayidx, align 8 --> Dst: store i64 0, ptr %arrayidx, align 8
@@ -1094,7 +1094,7 @@ define void @banerjee12(ptr %A, ptr %B, i64 %m, i64 %n) nounwind uwtable ssp {
; DELIN-NEXT: Src: %0 = load i64, ptr %arrayidx6, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
; DELIN-NEXT: da analyze - confused!
; DELIN-NEXT: Src: store i64 %0, ptr %B.addr.11, align 8 --> Dst: store i64 %0, ptr %B.addr.11, align 8
-; DELIN-NEXT: da analyze - none!
+; DELIN-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
index 81e461a5e092d..f51f1fa59f544 100644
--- a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll
@@ -18,11 +18,11 @@ define void @test1(ptr nocapture %A, ptr nocapture %B, i32 %N) #0 {
; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %0 = load i32, ptr %gep.0, align 4
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4
-; CHECK-NEXT: da analyze - input [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %1 = load i32, ptr %gep.1, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %1 = load i32, ptr %gep.1, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %add, ptr %gep.B, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 8f94a455d3724..174f105b666f3 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -646,11 +646,11 @@ exit:
define void @coeff_may_negative(ptr %a, i32 %k) {
; CHECK-LABEL: 'coeff_may_negative'
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT: da analyze - output [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %loop
@@ -685,11 +685,11 @@ exit:
define void @coeff_positive(ptr %a, i32 %k) {
; CHECK-LABEL: 'coeff_positive'
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT: da analyze - output [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %loop
diff --git a/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll b/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
index b5ece14121686..73c0c8b47d74f 100644
--- a/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/ExactRDIV.ll
@@ -508,7 +508,7 @@ define void @rdiv9(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -564,7 +564,7 @@ define void @rdiv10(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -619,7 +619,7 @@ define void @rdiv11(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -674,7 +674,7 @@ define void @rdiv12(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 03343e7a98211..24cf95987df4d 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -25,7 +25,7 @@ define void @gcd0(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -84,7 +84,7 @@ define void @gcd1(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -144,7 +144,7 @@ define void @gcd2(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -204,7 +204,7 @@ define void @gcd3(ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -262,7 +262,7 @@ define void @gcd4(ptr %A, ptr %B, i64 %M, i64 %N) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx16, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -330,7 +330,7 @@ define void @gcd5(ptr %A, ptr %B, i64 %M, i64 %N) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx16, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
index 0d59e107b9c35..c234c59db9f17 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
@@ -70,11 +70,11 @@ for.end:
define void @i16_wrap(ptr %a) {
; CHECK-LABEL: 'i16_wrap'
; CHECK-NEXT: Src: %0 = load i64, ptr %idx, align 4 --> Dst: %0 = load i64, ptr %idx, align 4
-; CHECK-NEXT: da analyze - input [*]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i64, ptr %idx, align 4 --> Dst: store i64 %1, ptr %idx, align 4
-; CHECK-NEXT: da analyze - anti [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i64 %1, ptr %idx, align 4 --> Dst: store i64 %1, ptr %idx, align 4
-; CHECK-NEXT: da analyze - output [*]!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.body
diff --git a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
index 8cb0e2ac770dc..e7449c6072846 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
@@ -448,7 +448,7 @@ define void @p4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp sgt i64 %n, 0
@@ -501,7 +501,7 @@ define void @p5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp sgt i64 %n, 0
@@ -729,9 +729,9 @@ define void @foo(ptr %s, i32 %size) nounwind uwtable ssp {
; CHECK-NEXT: Src: %1 = load i32, ptr %0, align 4 --> Dst: %1 = load i32, ptr %0, align 4
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: %1 = load i32, ptr %0, align 4 --> Dst: store i32 %1, ptr %i.02, align 4
-; CHECK-NEXT: da analyze - consistent anti [1]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %1, ptr %i.02, align 4 --> Dst: store i32 %1, ptr %i.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%idx.ext = zext i32 %size to i64
diff --git a/llvm/test/Analysis/DependenceAnalysis/Propagating.ll b/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
index 866f515baeafb..2cd393730096d 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Propagating.ll
@@ -24,7 +24,7 @@ define void @prop0(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -81,7 +81,7 @@ define void @prop1(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx14, align 4 --> Dst: store i32 %0, ptr %B.addr.21, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.21, align 4 --> Dst: store i32 %0, ptr %B.addr.21, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -151,7 +151,7 @@ define void @prop2(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -208,7 +208,7 @@ define void @prop3(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -266,7 +266,7 @@ define void @prop4(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx10, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -325,7 +325,7 @@ define void @prop5(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -385,7 +385,7 @@ define void @prop6(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -445,7 +445,7 @@ define void @prop7(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx13, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -507,7 +507,7 @@ define void @prop8(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -565,7 +565,7 @@ define void @prop9(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.11, align 4 --> Dst: store i32 %0, ptr %B.addr.11, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/Separability.ll b/llvm/test/Analysis/DependenceAnalysis/Separability.ll
index 2ed9cca4d1fc0..3e886e65c5ab1 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Separability.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Separability.ll
@@ -26,7 +26,7 @@ define void @sep0(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx15, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -108,7 +108,7 @@ define void @sep1(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx15, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -190,7 +190,7 @@ define void @sep2(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx19, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
@@ -272,7 +272,7 @@ define void @sep3(ptr %A, ptr %B, i32 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx20, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.31, align 4 --> Dst: store i32 %0, ptr %B.addr.31, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
br label %for.cond1.preheader
diff --git a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
index 44bd9b7727910..d6bd30ed9050f 100644
--- a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll
@@ -122,7 +122,7 @@ define void @strong2(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -435,7 +435,7 @@ define void @strong9(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll b/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
index 8b9aa257a7c57..ba4f2101df77e 100644
--- a/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/SymbolicRDIV.ll
@@ -25,7 +25,7 @@ define void @symbolicrdiv0(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx8, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
@@ -94,7 +94,7 @@ define void @symbolicrdiv1(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx9, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
@@ -165,7 +165,7 @@ define void @symbolicrdiv2(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
@@ -234,7 +234,7 @@ define void @symbolicrdiv3(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
@@ -301,7 +301,7 @@ define void @symbolicrdiv4(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
@@ -369,7 +369,7 @@ define void @symbolicrdiv5(ptr %A, ptr %B, i64 %n1, i64 %n2) nounwind uwtable ss
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp4 = icmp eq i64 %n1, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
index cdfaec76fa892..b2b021cf41681 100644
--- a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll
@@ -23,7 +23,7 @@ define void @symbolicsiv0(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -75,7 +75,7 @@ define void @symbolicsiv1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -129,7 +129,7 @@ define void @symbolicsiv2(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -181,7 +181,7 @@ define void @symbolicsiv3(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -234,7 +234,7 @@ define void @symbolicsiv4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx3, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -286,7 +286,7 @@ define void @symbolicsiv5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx4, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -341,7 +341,7 @@ define void @weaktest(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -392,7 +392,7 @@ define void @symbolicsiv6(ptr %A, ptr %B, i64 %n, i64 %N, i64 %M) nounwind uwtab
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx7, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -448,7 +448,7 @@ define void @symbolicsiv7(ptr %A, ptr %B, i64 %n, i64 %N, i64 %M) nounwind uwtab
; CHECK-NEXT: Src: %1 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %1, ptr %B.addr.02, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
index cd044032e34f1..4884a34217a86 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakCrossingSIV.ll
@@ -24,7 +24,7 @@ define void @weakcrossing0(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -77,7 +77,7 @@ define void @weakcrossing1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -254,7 +254,7 @@ define void @weakcrossing5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %1 = load i32, ptr %arrayidx2, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %1, ptr %B.addr.02, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
index 71a5ed62b8e21..3c7ab6075964b 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakZeroDstSIV.ll
@@ -100,7 +100,7 @@ define void @weakzerodst1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -318,7 +318,7 @@ define void @weakzerodst6(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll b/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
index 2ee1a37775f46..31fae73f0b652 100644
--- a/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/WeakZeroSrcSIV.ll
@@ -98,7 +98,7 @@ define void @weakzerosrc1(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
@@ -316,7 +316,7 @@ define void @weakzerosrc6(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
-; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i64 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
index 0267cbd1fd04a..790c10bfbc91e 100644
--- a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
@@ -4,9 +4,9 @@
; 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>
+define void @single_loop_nsw(ptr %a, i64 %n) {
+; CHECK-LABEL: 'single_loop_nsw'
+; CHECK: Monotonicity: Monotonic expr: {0,+,1}<nuw><nsw><%loop>
;
entry:
%guard = icmp sgt i64 %n, 0
@@ -30,9 +30,10 @@ exit:
; for (unsigned long long i = begin; i < end; i++)
; a[i] = 0;
;
-define void @f1(ptr %a, i64 %begin, i64 %end) {
-; CHECK-LABEL: 'f1'
+define void @single_loop_nuw(ptr %a, i64 %begin, i64 %end) {
+; CHECK-LABEL: 'single_loop_nuw'
; CHECK: Failed to prove monotonicity for: {%begin,+,1}<nuw><%loop>
+; CHECK: Monotonicity: Unknown expr: {%begin,+,1}<nuw><%loop>
;
entry:
%guard = icmp ult i64 %begin, %end
@@ -54,9 +55,9 @@ exit:
; 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>
+define void @nested_loop_nsw0(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw0'
+; CHECK: Monotonicity: Monotonic expr: {{\{}}{0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nuw><nsw><%loop.j>
;
entry:
%guard.i = icmp sgt i64 %n, 0
@@ -91,9 +92,10 @@ exit:
; 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>
+;
+define void @nested_loop_nsw1(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw1'
+; CHECK: Monotonicity: Monotonic expr: {{\{}}{(-1 + %n),+,-1}<nsw><%loop.i.header>,+,1}<nsw><%loop.j>
;
entry:
%guard.i = icmp sgt i64 %n, 0
@@ -130,9 +132,11 @@ exit:
; 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'
+;
+define void @nested_loop_nuw(ptr %a, i64 %begin0, i64 %end0, i64 %begin1, i64 %end1) {
+; CHECK-LABEL: 'nested_loop_nuw'
; CHECK: Failed to prove monotonicity for: {{\{}}{(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
+; CHECK: Monotonicity: Unknown expr: {{\{}}{(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
;
entry:
%guard.i.0 = icmp slt i64 0, %begin0
@@ -171,3 +175,160 @@ loop.i.latch:
exit:
ret void
}
+
+; `step` can be zero, so `step*j` can be either Invariant or Monotonic. This
+; propagates to `i + step*j`.
+;
+; for (int i = 0; i < n; i++)
+; for (int j = 0; j < m; j++)
+; a[i + step*j] = 0;
+;
+define void @nested_loop_step(ptr %a, i64 %n, i64 %m, i64 %step) {
+; CHECK-LABEL: 'nested_loop_step'
+; CHECK: Monotonicity: No signed wrap expr: {{\{}}{0,+,1}<nuw><nsw><%loop.i.header>,+,%step}<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.j = phi i64 [ 0, %loop.j.preheader ], [ %offset.j.next, %loop.j ]
+ %offset = add nsw i64 %i, %offset.j
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ store i8 0, ptr %idx
+ %j.inc = add nsw i64 %j, 1
+ %offset.j.next = add nsw i64 %offset.j, %step
+ %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
+}
+
+; `offset` can be loop-invariant since `step` can be zero.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+; a[sext(offset)] = 0;
+;
+define void @sext_nsw(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'sext_nsw'
+; CHECK: Monotonicity: No signed wrap expr: {(sext i8 %start to i64),+,(sext i8 %step to i64)}<nsw><%loop>
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+ %offset.sext = sext i8 %offset to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.sext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i64 %i, 1
+ %offset.next = add nsw i8 %offset, %step
+ %exitcond = icmp eq i64 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The addition for `%offset.next` can wrap, so we cannot prove monotonicity.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+; a[sext(offset)] = 0;
+;
+define void @sext_may_wrap(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'sext_may_wrap'
+; CHECK: Failed to prove monotonicity for: {%start,+,%step}<%loop>
+; CHECK: Monotonicity: Unknown expr: (sext i8 {%start,+,%step}<%loop> to i64)
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+ %offset.sext = sext i8 %offset to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.sext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i64 %i, 1
+ %offset.next = add i8 %offset, %step
+ %exitcond = icmp eq i64 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; `offset` can be loop-invariant since `step` can be zero.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+; a[zext(offset)] = 0;
+;
+define void @zext_nsw(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'zext_nsw'
+; CHECK: Monotonicity: No signed wrap expr: (zext i8 {%start,+,%step}<nsw><%loop> to i64)
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+ %offset.zext = zext i8 %offset to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.zext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i64 %i, 1
+ %offset.next = add nsw i8 %offset, %step
+ %exitcond = icmp eq i64 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The addition for `%offset.next` can wrap, so we cannot prove monotonicity.
+;
+; int8_t offset = start;
+; for (int i = 0; i < 100; i++, offset += step)
+; a[zext(offset)] = 0;
+;
+define void @zext_may_wrap(ptr %a, i8 %start, i8 %step) {
+; CHECK-LABEL: 'zext_may_wrap'
+; CHECK: Failed to prove monotonicity for: {%start,+,%step}<%loop>
+; CHECK: Monotonicity: Unknown expr: (zext i8 {%start,+,%step}<%loop> to i64)
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i8 [ %start, %entry ], [ %offset.next, %loop ]
+ %offset.zext = zext i8 %offset to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.zext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i64 %i, 1
+ %offset.next = add i8 %offset, %step
+ %exitcond = icmp eq i64 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
>From b5ca793172e604255cb4d57ab13e232cc712228a Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 1 Sep 2025 12:05:56 +0000
Subject: [PATCH 07/11] fix gep-based inferrence
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 34 +++++++++-----
llvm/test/Analysis/DependenceAnalysis/GCD.ll | 12 ++---
.../Analysis/DependenceAnalysis/monotonic.ll | 45 +++++++++++++++++++
3 files changed, 74 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index f31d4d02e517d..75a59c9553e18 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3414,9 +3414,24 @@ SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
ScalarEvolution *SE, const Loop *OutermostLoop, const Value *Ptr)
: SE(SE), OutermostLoop(OutermostLoop) {
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:
+ // Perform reasoning similar to LoopAccessAnalysis. If an AddRec would wrap
+ // and the GEP would have nusw, the wrapped memory location would become
+ // like as follows (in the mathmatical sense, assuming the step recurrence
+ // is positive):
+ //
+ // (previously accessed location) + (step recurrence) - 2^N
+ //
+ // where N is the size of the pointer index type. Since the value of step
+ // recurrence is less than 2^(N-1), the distance between the previously
+ // accessed location and the wrapped location will be greater than 2^(N-1),
+ // which is larger than half the pointer index type space. The size of
+ // allocated object must not exceed the largest signed integer that fits
+ // into the index type, so the GEP value would be poison and any memory
+ // access using it would be immediate UB when executed.
+ //
+ // TODO: We don't check if the result of the GEP is always used. Maybe we
+ // should check the reachability from the GEP to the target instruction.
+ // E.g., in the following case, no-wrap would not trigger immediate UB:
//
// entry:
// ...
@@ -3441,8 +3456,6 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
const Value *Ptr) {
SCEVSignedMonotonicityChecker Checker(SE, OutermostLoop, Ptr);
MonotonicityType MT = Checker.visit(Expr);
- if (MT == MonotonicityType::Unknown && Checker.NoWrapFromGEP)
- MT = MonotonicityType::NoSignedWrap;
#ifndef NDEBUG
switch (MT) {
@@ -3532,8 +3545,6 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
const SCEV *Start = Expr->getStart();
const SCEV *Step = Expr->getStepRecurrence(*SE);
- bool IsNSW = Expr->hasNoSignedWrap();
-
MonotonicityType StartRes = visit(Start);
if (StartRes == MonotonicityType::Unknown)
return unknownMonotonicity(Expr);
@@ -3543,7 +3554,7 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
return unknownMonotonicity(Expr);
// TODO: Enhance the inference here.
- if (!IsNSW) {
+ if (!Expr->hasNoSignedWrap() && !NoWrapFromGEP) {
if (!SE->isKnownNegative(Step))
// If the coefficient can be positive value, ensure that the AddRec is
// monotonically increasing.
@@ -3787,9 +3798,10 @@ bool DependenceInfo::tryDelinearizeParametricSize(
LI->getLoopFor(Src->getParent())->getOutermostLoop();
// TODO: In general, reasoning about monotonicity of a subscript from the
- // base pointer would not be allowed. Probably we need to check the loops
- // associated with this subscript are disjoint from those associated with
- // the other subscripts. The validation would be something like:
+ // base pointer would lead incorrect result. Probably we need to check
+ // the loops associated with this subscript are disjoint from those
+ // associated with the other subscripts. The validation would be
+ // something like:
//
// LoopsI = collectCommonLoops(SrcSubscripts[I])
// LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 24cf95987df4d..93a2f07e7df21 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -465,13 +465,13 @@ for.end12: ; preds = %for.end12.loopexit,
define void @gcd7(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-LABEL: 'gcd7'
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %7, ptr %arrayidx6, align 4
-; CHECK-NEXT: da analyze - output [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - flow [* *|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - input [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %11, ptr %B.addr.12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
@@ -640,13 +640,13 @@ for.end15: ; preds = %for.end15.loopexit,
define void @gcd9(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-LABEL: 'gcd9'
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %7, ptr %arrayidx6, align 4
-; CHECK-NEXT: da analyze - output [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - flow [* *|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %7, ptr %arrayidx6, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: %11 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - input [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %11 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %11, ptr %B.addr.12, align 4 --> Dst: store i32 %11, ptr %B.addr.12, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
index 790c10bfbc91e..4d80772e5ed24 100644
--- a/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonic.ll
@@ -332,3 +332,48 @@ loop:
exit:
ret void
}
+
+; The value of step reccurence is not invariant with respect to the outer most
+; loop (the i-loop).
+;
+; offset_i = 0;
+; for (int i = 0; i < 100; i++) {
+; for (int j = 0; j < 100; j++)
+; a[offset_i + j] = 0;
+; offset_i += (i % 2 == 0) ? 0 : 3;
+; }
+;
+define void @step_is_variant(ptr %a) {
+; CHECK-LABEL: 'step_is_variant'
+; CHECK: Failed to prove monotonicity for: %offset.i
+; CHECK: Failed to prove monotonicity for: {%offset.i,+,1}<nuw><nsw><%loop.j>
+; CHECK: Monotonicity: Unknown expr: {%offset.i,+,1}<nuw><nsw><%loop.j>
+;
+entry:
+ br label %loop.i.header
+
+loop.i.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop.i.latch ]
+ %offset.i = phi i64 [ 0, %entry ], [ %offset.i.next, %loop.i.latch ]
+ %step.i.0 = phi i64 [ 0, %entry ], [ %step.i.1, %loop.i.latch ]
+ %step.i.1 = phi i64 [ 3, %entry ], [ %step.i.0, %loop.i.latch ]
+ br label %loop.j
+
+loop.j:
+ %j = phi i64 [ 0, %loop.i.header ], [ %j.inc, %loop.j ]
+ %offset = add nsw i64 %offset.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, 100
+ br i1 %exitcond.j, label %loop.i.latch, label %loop.j
+
+loop.i.latch:
+ %i.inc = add nsw i64 %i, 1
+ %offset.i.next = add nsw i64 %offset.i, %step.i.0
+ %exitcond.i = icmp eq i64 %i.inc, 100
+ br i1 %exitcond.i, label %exit, label %loop.i.header
+
+exit:
+ ret void
+}
>From 137c193ecbbab179ae4fbc1fea4768fa3d0ae463 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 6 Sep 2025 04:49:40 +0900
Subject: [PATCH 08/11] fix logic for SCEVNArayExpr
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 4f62a17af6041..183107cf247d5 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3497,19 +3497,17 @@ SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
case MonotonicityType::Invariant:
break;
case MonotonicityType::MultiMonotonic: {
+ if (!Expr->hasNoSignedWrap())
+ return unknownMonotonicity(Expr);
switch (Result) {
case MonotonicityType::Unknown:
llvm_unreachable("should have been handled above");
case MonotonicityType::NoSignedWrap:
break;
case MonotonicityType::Invariant:
- if (!Expr->hasNoSignedWrap())
- return unknownMonotonicity(Expr);
Result = MonotonicityType::MultiMonotonic;
break;
case MonotonicityType::MultiMonotonic:
- if (!Expr->hasNoSignedWrap())
- return unknownMonotonicity(Expr);
if (!isa<SCEVAddExpr>(Expr))
return unknownMonotonicity(Expr);
// Monotonic + Monotonic might be a loop invariant, e.g., the following
>From f9b2a4e12b3236778180b3fa9b03a92d916d5b68 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 6 Sep 2025 05:31:14 +0900
Subject: [PATCH 09/11] adjust comments
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 61 ++++++++++++++----------
1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 183107cf247d5..9172d31119ac0 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3429,22 +3429,10 @@ SCEVSignedMonotonicityChecker::SCEVSignedMonotonicityChecker(
// into the index type, so the GEP value would be poison and any memory
// access using it would be immediate UB when executed.
//
- // TODO: We don't check if the result of the GEP is always used. Maybe we
- // should check the reachability from the GEP to the target instruction.
- // E.g., in the following case, no-wrap would not trigger immediate UB:
- //
- // 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:
- // ...
- //
+ // TODO: The monotonicity check ensures that the given SCEV does not wrap
+ // in "any" iteration. Thus, inference from nusw should be valid only if
+ // the GEP is executed and its result is used in every iteration of the
+ // loop.
auto *GEP = dyn_cast<GetElementPtrInst>(Ptr);
if (GEP && GEP->hasNoUnsignedSignedWrap())
NoWrapFromGEP = true;
@@ -3807,16 +3795,25 @@ bool DependenceInfo::tryDelinearizeParametricSize(
const Loop *OutermostLoop =
LI->getLoopFor(Src->getParent())->getOutermostLoop();
- // TODO: In general, reasoning about monotonicity of a subscript from the
- // base pointer would lead incorrect result. Probably we need to check
- // the loops associated with this subscript are disjoint from those
- // associated with the other subscripts. The validation would be
- // something like:
+ // TODO: Inferring a subscript's monotonicity from the base pointer can
+ // lead to incorrect results. Consider the following code:
+ //
+ // %offset = ...
+ // %gep = getelementptr nusw i8, ptr %base, i64 %offset
//
- // LoopsI = collectCommonLoops(SrcSubscripts[I])
- // LoopsOthers = collectCommonLoops(SrcSCEV - SrcSubscripts[I])
- // CanUsePtr = (LoopsI intersect LoopsOthers) is empty.
+ // We might infer the monotonicity of %offset from nusw on the GEP (see
+ // the implementation of checkMonotonicity for details). This inference
+ // may be valid, but the same does not necessarily hold for each
+ // subscript. For example, assume %offset is {0,+,(%m * %n)}<%loop> where
+ // %m and %n are loop invariants. Delinearization can "decompose" this
+ // SCEV into something like:
//
+ // Size: [UnknownSize][%m]
+ // Subscripts: [{0,+,%n}][{0,+,1}]
+ //
+ // Here, if (%m * %n) wraps, %n can be larger than (%m * %n). Hence even
+ // if we know {0,+,(%m * %n)} doesn't wrap, we cannot conclude the same
+ // for {0,+,%n}.
MonotonicityType SrcMonotonicity =
SCEVSignedMonotonicityChecker::checkMonotonicity(
SE, SrcSubscripts[I], OutermostLoop, SrcPtr);
@@ -3852,6 +3849,22 @@ bool DependenceInfo::tryDelinearizeParametricSize(
return false;
}
+ // TODO: Probably we need to prove that the "offset calculation" doesn't
+ // wrap. Here the offset calculation is:
+ //
+ // Offset =
+ // Subscripts[0] +
+ // Subscripts[1]*Sizes[0] +
+ // Subscripts[2]*Sizes[0]*Sizes[1] +
+ // ...
+ // Subscripts[N-1]*Sizes[0]*Sizes[1]*...*Sizes[N-2]
+ //
+ // where N is the number of dimensions. The subsequent dependence tests assume
+ // that different subscript values result in different offset values. If the
+ // above calculation wraps, this assumption is violated. Note that if every
+ // element of Subscripts is positive, the situation would be simple. However,
+ // the subscript for the outermost dimension (Subscripts[0]) can be negative.
+
return true;
}
>From e83fdb8723f45a53ea4400dcff6e0dddee0c3106 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 8 Sep 2025 09:35:50 +0000
Subject: [PATCH 10/11] remove handlings for add/mul
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 62 +++----------------
llvm/test/Analysis/DependenceAnalysis/AA.ll | 4 +-
.../Analysis/DependenceAnalysis/DADelin.ll | 4 +-
llvm/test/Analysis/DependenceAnalysis/GCD.ll | 6 +-
.../Analysis/DependenceAnalysis/PR21585.ll | 2 +-
.../Analysis/DependenceAnalysis/PR31848.ll | 2 +-
.../DependenceAnalysis/Preliminary.ll | 12 ++--
7 files changed, 22 insertions(+), 70 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 9172d31119ac0..c7ea2349d8100 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3352,13 +3352,6 @@ struct SCEVSignedMonotonicityChecker
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::Invariant;
}
@@ -3367,6 +3360,12 @@ struct SCEVSignedMonotonicityChecker
}
// TODO: Handle more cases.
+ MonotonicityType visitAddExpr(const SCEVAddExpr *Expr) {
+ return unknownMonotonicity(Expr);
+ }
+ MonotonicityType visitMulExpr(const SCEVMulExpr *Expr) {
+ return unknownMonotonicity(Expr);
+ }
MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
return unknownMonotonicity(Expr);
}
@@ -3403,7 +3402,6 @@ struct SCEVSignedMonotonicityChecker
SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
const Value *Ptr);
- MonotonicityType visitNAryHelper(const SCEVNAryExpr *Expr);
MonotonicityType unknownMonotonicity(const SCEV *Expr);
bool isLoopInvariant(const SCEV *Expr) const;
};
@@ -3466,55 +3464,9 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
}
MonotonicityType
-SCEVSignedMonotonicityChecker::visitNAryHelper(const SCEVNAryExpr *Expr) {
- assert((isa<SCEVAddExpr>(Expr) || isa<SCEVMulExpr>(Expr)) &&
- "Unexpected SCEV");
-
+SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
if (isLoopInvariant(Expr))
return MonotonicityType::Invariant;
-
- MonotonicityType Result = MonotonicityType::Invariant;
- for (const SCEV *Op : Expr->operands()) {
- assert(Result != MonotonicityType::Unknown && "Unexpected state");
- switch (visit(Op)) {
- case MonotonicityType::Unknown:
- return unknownMonotonicity(Expr);
- case MonotonicityType::NoSignedWrap:
- Result = MonotonicityType::NoSignedWrap;
- break;
- case MonotonicityType::Invariant:
- break;
- case MonotonicityType::MultiMonotonic: {
- if (!Expr->hasNoSignedWrap())
- return unknownMonotonicity(Expr);
- switch (Result) {
- case MonotonicityType::Unknown:
- llvm_unreachable("should have been handled above");
- case MonotonicityType::NoSignedWrap:
- break;
- case MonotonicityType::Invariant:
- Result = MonotonicityType::MultiMonotonic;
- break;
- case MonotonicityType::MultiMonotonic:
- if (!isa<SCEVAddExpr>(Expr))
- return unknownMonotonicity(Expr);
- // Monotonic + Monotonic might be a loop invariant, e.g., the following
- // SCEV:
- //
- // {0,+,1}<%loop> + {0,+,-1}<%loop>
- //
- // In that case, relax the property to NoSignedWrap.
- Result = MonotonicityType::NoSignedWrap;
- break;
- }
- } break;
- }
- }
- return Result;
-}
-
-MonotonicityType
-SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
LLVM_DEBUG(dbgs() << "Failed to prove monotonicity for: " << *Expr << "\n");
return MonotonicityType::Unknown;
}
diff --git a/llvm/test/Analysis/DependenceAnalysis/AA.ll b/llvm/test/Analysis/DependenceAnalysis/AA.ll
index 173744a07ef96..b03f82ed1b084 100644
--- a/llvm/test/Analysis/DependenceAnalysis/AA.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/AA.ll
@@ -128,11 +128,11 @@ define void @test_tbaa_diff(ptr %A, ptr %B) {
define void @tbaa_loop(i32 %I, i32 %J, ptr nocapture %A, ptr nocapture readonly %B) {
; CHECK-LABEL: 'tbaa_loop'
; CHECK-NEXT: Src: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0 --> Dst: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0
-; CHECK-NEXT: da analyze - input [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i16, ptr %arrayidx.us, align 4, !tbaa !0 --> Dst: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4 --> Dst: store i32 %add.us.lcssa, ptr %arrayidx6.us, align 4, !tbaa !4
-; CHECK-NEXT: da analyze - output [*]!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp = icmp ne i32 %J, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 174f105b666f3..eb05b7dd3e9a8 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -547,9 +547,9 @@ define double @test_sizes(i16 %h, i16 %N, ptr nocapture %array) {
; CHECK-NEXT: Src: %2 = load i16, ptr %arrayidx, align 4 --> Dst: %2 = load i16, ptr %arrayidx, align 4
; CHECK-NEXT: da analyze - consistent input [0 S]!
; CHECK-NEXT: Src: %2 = load i16, ptr %arrayidx, align 4 --> Dst: store i16 %add6, ptr %arrayidx8, align 4
-; CHECK-NEXT: da analyze - anti [* *|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i16 %add6, ptr %arrayidx8, align 4 --> Dst: store i16 %add6, ptr %arrayidx8, align 4
-; CHECK-NEXT: da analyze - output [* *]!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp28 = icmp sgt i16 %N, 1
diff --git a/llvm/test/Analysis/DependenceAnalysis/GCD.ll b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
index 93a2f07e7df21..98026629ce4e4 100644
--- a/llvm/test/Analysis/DependenceAnalysis/GCD.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/GCD.ll
@@ -556,13 +556,13 @@ for.end15: ; preds = %for.end15.loopexit,
define void @gcd8(i32 %n, ptr %A, ptr %B) nounwind uwtable ssp {
; CHECK-LABEL: 'gcd8'
; CHECK-NEXT: Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: store i32 %i.06, ptr %arrayidx, align 4
-; CHECK-NEXT: da analyze - output [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: %5 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - flow [* *|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %i.06, ptr %arrayidx, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %5 = load i32, ptr %arrayidx12, align 4 --> Dst: %5 = load i32, ptr %arrayidx12, align 4
-; CHECK-NEXT: da analyze - input [* *]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %5 = load i32, ptr %arrayidx12, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %5, ptr %B.addr.12, align 4 --> Dst: store i32 %5, ptr %B.addr.12, align 4
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
index c234c59db9f17..fe7a6135e23b5 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR21585.ll
@@ -47,7 +47,7 @@ define void @t(ptr noalias %a, i32 %n) nounwind {
; CHECK-NEXT: Src: %0 = load i32, ptr @g, align 4 --> Dst: store i32 %0, ptr %arrayidx, align 4
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: store i32 %0, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %arrayidx, align 4
-; CHECK-NEXT: da analyze - output [*]!
+; CHECK-NEXT: da analyze - confused!
;
entry:
%cmp1 = icmp eq i32 %n, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/PR31848.ll b/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
index 8ee1224a18ad1..db64d2cab2fed 100644
--- a/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/PR31848.ll
@@ -8,7 +8,7 @@
define void @barney(ptr nocapture %arg, i32 %arg1) {
; CHECK-LABEL: 'barney'
; CHECK-NEXT: Src: store i32 7, ptr %getelementptr, align 4 --> Dst: store i32 7, ptr %getelementptr, align 4
-; CHECK-NEXT: da analyze - output [* * * *]!
+; CHECK-NEXT: da analyze - confused!
;
bb:
%icmp = icmp sgt i32 %arg1, 0
diff --git a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
index e7449c6072846..3fac79957dfab 100644
--- a/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/Preliminary.ll
@@ -438,13 +438,13 @@ for.end26: ; preds = %for.end26.loopexit,
define void @p4(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-LABEL: 'p4'
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %conv2, ptr %arrayidx, align 4
-; CHECK-NEXT: da analyze - output [*]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT: da analyze - flow [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT: da analyze - input [*]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
@@ -491,13 +491,13 @@ for.end: ; preds = %for.end.loopexit, %
define void @p5(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp {
; CHECK-LABEL: 'p5'
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %conv2, ptr %arrayidx, align 4
-; CHECK-NEXT: da analyze - output [*]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT: da analyze - flow [*|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %conv2, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: %0 = load i32, ptr %arrayidx5, align 4
-; CHECK-NEXT: da analyze - input [*]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx5, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: store i32 %0, ptr %B.addr.02, align 4 --> Dst: store i32 %0, ptr %B.addr.02, align 4
>From a0987435560700b6dff04f866910b942956286e2 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Mon, 8 Sep 2025 09:37:59 +0000
Subject: [PATCH 11/11] rename function name and add comments
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 42 +++++++++++++-----------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index c7ea2349d8100..95d1286414841 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3361,37 +3361,37 @@ struct SCEVSignedMonotonicityChecker
// TODO: Handle more cases.
MonotonicityType visitAddExpr(const SCEVAddExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitMulExpr(const SCEVMulExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitTruncateExpr(const SCEVTruncateExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitUDivExpr(const SCEVUDivExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitSMaxExpr(const SCEVSMaxExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitUMaxExpr(const SCEVUMaxExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitSMinExpr(const SCEVSMinExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitUMinExpr(const SCEVUMinExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
MonotonicityType visitCouldNotCompute(const SCEVCouldNotCompute *Expr) {
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
private:
@@ -3402,7 +3402,11 @@ struct SCEVSignedMonotonicityChecker
SCEVSignedMonotonicityChecker(ScalarEvolution *SE, const Loop *OutermostLoop,
const Value *Ptr);
- MonotonicityType unknownMonotonicity(const SCEV *Expr);
+ /// A helper to classify \p Expr as either Invariant or Unknown.
+ MonotonicityType checkInvarianceOnly(const SCEV *Expr);
+
+ /// Return true if \p Expr is loop-invariant with respect to the outermost
+ /// loop.
bool isLoopInvariant(const SCEV *Expr) const;
};
@@ -3464,7 +3468,7 @@ MonotonicityType SCEVSignedMonotonicityChecker::checkMonotonicity(
}
MonotonicityType
-SCEVSignedMonotonicityChecker::unknownMonotonicity(const SCEV *Expr) {
+SCEVSignedMonotonicityChecker::checkInvarianceOnly(const SCEV *Expr) {
if (isLoopInvariant(Expr))
return MonotonicityType::Invariant;
LLVM_DEBUG(dbgs() << "Failed to prove monotonicity for: " << *Expr << "\n");
@@ -3478,18 +3482,18 @@ bool SCEVSignedMonotonicityChecker::isLoopInvariant(const SCEV *Expr) const {
MonotonicityType
SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
if (!Expr->isAffine())
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
const SCEV *Start = Expr->getStart();
const SCEV *Step = Expr->getStepRecurrence(*SE);
MonotonicityType StartRes = visit(Start);
if (StartRes == MonotonicityType::Unknown)
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
MonotonicityType StepRes = visit(Step);
if (StepRes != MonotonicityType::Invariant)
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
// TODO: Enhance the inference here.
if (!Expr->hasNoSignedWrap() && !NoWrapFromGEP) {
@@ -3497,13 +3501,13 @@ SCEVSignedMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
// If the coefficient can be positive value, ensure that the AddRec is
// monotonically increasing.
if (!SE->isKnownOnEveryIteration(ICmpInst::ICMP_SGE, Expr, Start))
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(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 unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
}
bool IsKnownNonZero = SE->isKnownNonZero(Step);
@@ -3536,7 +3540,7 @@ MonotonicityType SCEVSignedMonotonicityChecker::visitSignExtendExpr(
MonotonicityType
SCEVSignedMonotonicityChecker::visitUnknown(const SCEVUnknown *Expr) {
if (!isLoopInvariant(Expr))
- return unknownMonotonicity(Expr);
+ return checkInvarianceOnly(Expr);
return MonotonicityType::Invariant;
}
More information about the llvm-commits
mailing list