[llvm] ab789be - [DA] Add initial support for monotonicity check (#162280)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 21 02:11:14 PDT 2025
Author: Ryotaro Kasuga
Date: 2025-10-21T09:11:09Z
New Revision: ab789beffdf68c65722203250d3094c15466459f
URL: https://github.com/llvm/llvm-project/commit/ab789beffdf68c65722203250d3094c15466459f
DIFF: https://github.com/llvm/llvm-project/commit/ab789beffdf68c65722203250d3094c15466459f.diff
LOG: [DA] Add initial support for monotonicity check (#162280)
The dependence testing functions in DA assume that the analyzed AddRec
does not wrap over the entire iteration space. For AddRecs that may
wrap, DA should conservatively return unknown dependence. However, no
validation is currently performed to ensure that this condition holds,
which can lead to incorrect results in some cases.
This patch introduces the notion of *monotonicity* and a validation
logic to check whether a SCEV is monotonic. The monotonicity check
classifies the SCEV into one of the following categories:
- Unknown: Nothing is known about the monotonicity of the SCEV.
- Invariant: The SCEV is loop-invariant.
- MultivariateSignedMonotonic: The SCEV doesn't wrap in a signed sense
for any iteration of the loops in the loop nest.
The current validation logic basically searches an affine AddRec
recursively and checks whether the `nsw` flag is present. Notably, it is
still unclear whether we should also have a category for unsigned
monotonicity.
The monotonicity check is still under development and disabled by
default for now. Since such a check is necessary to make DA sound, it
should be enabled by default once the functionality is sufficient.
Split off from #154527.
Added:
llvm/test/Analysis/DependenceAnalysis/monotonicity-cast.ll
llvm/test/Analysis/DependenceAnalysis/monotonicity-delinearize.ll
llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll
llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll
llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll
Modified:
llvm/lib/Analysis/DependenceAnalysis.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 805b6820e1e1c..0a8c2f82e162f 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -128,6 +128,18 @@ static cl::opt<bool> RunSIVRoutinesOnly(
"The purpose is mainly to exclude the influence of those routines "
"in regression tests for SIV routines."));
+// TODO: This flag is disabled by default because it is still under development.
+// Enable it or delete this flag when the feature is ready.
+static cl::opt<bool> EnableMonotonicityCheck(
+ "da-enable-monotonicity-check", cl::init(false), cl::Hidden,
+ cl::desc("Check if the subscripts are monotonic. If it's not, dependence "
+ "is reported as unknown."));
+
+static cl::opt<bool> DumpMonotonicityReport(
+ "da-dump-monotonicity-report", cl::init(false), cl::Hidden,
+ cl::desc(
+ "When printing analysis, dump the results of monotonicity checks."));
+
//===----------------------------------------------------------------------===//
// basics
@@ -177,13 +189,196 @@ void DependenceAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequiredTransitive<LoopInfoWrapperPass>();
}
+namespace {
+
+/// The property of monotonicity of a SCEV. To define the monotonicity, assume
+/// a SCEV defined within N-nested loops. Let i_k denote the iteration number
+/// of the k-th loop. Then we can regard the SCEV as an N-ary function:
+///
+/// F(i_1, i_2, ..., i_N)
+///
+/// The domain of i_k is the closed range [0, BTC_k], where BTC_k is the
+/// backedge-taken count of the k-th loop.
+///
+/// A function F is said to be "monotonically increasing with respect to the
+/// k-th loop" if x <= y implies the following condition:
+///
+/// F(i_1, ..., i_{k-1}, x, i_{k+1}, ..., i_N) <=
+/// F(i_1, ..., i_{k-1}, y, i_{k+1}, ..., i_N)
+///
+/// where i_1, ..., i_{k-1}, i_{k+1}, ..., i_N, x, and y are elements of their
+/// respective domains.
+///
+/// Likewise F is "monotonically decreasing with respect to the k-th loop"
+/// if x <= y implies
+///
+/// F(i_1, ..., i_{k-1}, x, i_{k+1}, ..., i_N) >=
+/// F(i_1, ..., i_{k-1}, y, i_{k+1}, ..., i_N)
+///
+/// A function F that is monotonically increasing or decreasing with respect to
+/// the k-th loop is simply called "monotonic with respect to k-th loop".
+///
+/// A function F is said to be "multivariate monotonic" when it is monotonic
+/// with respect to all of the N loops.
+///
+/// Since integer comparison can be either signed or unsigned, we need to
+/// distinguish monotonicity in the signed sense from that in the unsigned
+/// sense. Note that the inequality "x <= y" merely indicates loop progression
+/// and is not affected by the
diff erence between signed and unsigned order.
+///
+/// Currently we only consider monotonicity in a signed sense.
+enum class SCEVMonotonicityType {
+ /// We don't know anything about the monotonicity of the SCEV.
+ Unknown,
+
+ /// The SCEV is loop-invariant with respect to the outermost loop. In other
+ /// words, the function F corresponding to the SCEV is a constant function.
+ Invariant,
+
+ /// The function F corresponding to the SCEV is multivariate monotonic in a
+ /// signed sense. Note that the multivariate monotonic function may also be a
+ /// constant function. The order employed in the definition of monotonicity
+ /// is not strict order.
+ MultivariateSignedMonotonic,
+};
+
+struct SCEVMonotonicity {
+ SCEVMonotonicity(SCEVMonotonicityType Type,
+ const SCEV *FailurePoint = nullptr);
+
+ SCEVMonotonicityType getType() const { return Type; }
+
+ const SCEV *getFailurePoint() const { return FailurePoint; }
+
+ bool isUnknown() const { return Type == SCEVMonotonicityType::Unknown; }
+
+ void print(raw_ostream &OS, unsigned Depth) const;
+
+private:
+ SCEVMonotonicityType Type;
+
+ /// The subexpression that caused Unknown. Mainly for debugging purpose.
+ const SCEV *FailurePoint;
+};
+
+/// Check the monotonicity of a SCEV. Since dependence tests (SIV, MIV, etc.)
+/// assume that subscript expressions are (multivariate) monotonic, we need to
+/// verify this property before applying those tests. Violating this assumption
+/// may cause them to produce incorrect results.
+struct SCEVMonotonicityChecker
+ : public SCEVVisitor<SCEVMonotonicityChecker, SCEVMonotonicity> {
+
+ SCEVMonotonicityChecker(ScalarEvolution *SE) : SE(SE) {}
+
+ /// Check the monotonicity of \p Expr. \p Expr must be integer type. If \p
+ /// OutermostLoop is not null, \p Expr must be defined in \p OutermostLoop or
+ /// one of its nested loops.
+ SCEVMonotonicity checkMonotonicity(const SCEV *Expr,
+ const Loop *OutermostLoop);
+
+private:
+ ScalarEvolution *SE;
+
+ /// The outermost loop that DA is analyzing.
+ const Loop *OutermostLoop;
+
+ /// A helper to classify \p Expr as either Invariant or Unknown.
+ SCEVMonotonicity invariantOrUnknown(const SCEV *Expr);
+
+ /// Return true if \p Expr is loop-invariant with respect to the outermost
+ /// loop.
+ bool isLoopInvariant(const SCEV *Expr) const;
+
+ /// A helper to create an Unknown SCEVMonotonicity.
+ SCEVMonotonicity createUnknown(const SCEV *FailurePoint) {
+ return SCEVMonotonicity(SCEVMonotonicityType::Unknown, FailurePoint);
+ }
+
+ SCEVMonotonicity visitAddRecExpr(const SCEVAddRecExpr *Expr);
+
+ SCEVMonotonicity visitConstant(const SCEVConstant *) {
+ return SCEVMonotonicity(SCEVMonotonicityType::Invariant);
+ }
+ SCEVMonotonicity visitVScale(const SCEVVScale *) {
+ return SCEVMonotonicity(SCEVMonotonicityType::Invariant);
+ }
+
+ // TODO: Handle more cases.
+ SCEVMonotonicity visitZeroExtendExpr(const SCEVZeroExtendExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitSignExtendExpr(const SCEVSignExtendExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitAddExpr(const SCEVAddExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitMulExpr(const SCEVMulExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitTruncateExpr(const SCEVTruncateExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitUDivExpr(const SCEVUDivExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitSMaxExpr(const SCEVSMaxExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitUMaxExpr(const SCEVUMaxExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitSMinExpr(const SCEVSMinExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitUMinExpr(const SCEVUMinExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitUnknown(const SCEVUnknown *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+ SCEVMonotonicity visitCouldNotCompute(const SCEVCouldNotCompute *Expr) {
+ return invariantOrUnknown(Expr);
+ }
+
+ friend struct SCEVVisitor<SCEVMonotonicityChecker, SCEVMonotonicity>;
+};
+
+} // anonymous namespace
+
// Used to test the dependence analyzer.
// Looks through the function, noting instructions that may access memory.
// Calls depends() on every possible pair and prints out the result.
// Ignores all other instructions.
static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
- ScalarEvolution &SE, bool NormalizeResults) {
+ ScalarEvolution &SE, LoopInfo &LI,
+ bool NormalizeResults) {
auto *F = DA->getFunction();
+
+ if (DumpMonotonicityReport) {
+ SCEVMonotonicityChecker Checker(&SE);
+ OS << "Monotonicity check:\n";
+ for (Instruction &Inst : instructions(F)) {
+ if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
+ continue;
+ Value *Ptr = getLoadStorePointerOperand(&Inst);
+ const Loop *L = LI.getLoopFor(Inst.getParent());
+ const SCEV *PtrSCEV = SE.getSCEVAtScope(Ptr, L);
+ const SCEV *AccessFn = SE.removePointerBase(PtrSCEV);
+ SCEVMonotonicity Mon = Checker.checkMonotonicity(AccessFn, L);
+ OS.indent(2) << "Inst: " << Inst << "\n";
+ OS.indent(4) << "Expr: " << *AccessFn << "\n";
+ Mon.print(OS, 4);
+ }
+ OS << "\n";
+ }
+
for (inst_iterator SrcI = inst_begin(F), SrcE = inst_end(F); SrcI != SrcE;
++SrcI) {
if (SrcI->mayReadOrWriteMemory()) {
@@ -235,7 +430,8 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
void DependenceAnalysisWrapperPass::print(raw_ostream &OS,
const Module *) const {
dumpExampleDependence(
- OS, info.get(), getAnalysis<ScalarEvolutionWrapperPass>().getSE(), false);
+ OS, info.get(), getAnalysis<ScalarEvolutionWrapperPass>().getSE(),
+ getAnalysis<LoopInfoWrapperPass>().getLoopInfo(), false);
}
PreservedAnalyses
@@ -244,7 +440,7 @@ DependenceAnalysisPrinterPass::run(Function &F, FunctionAnalysisManager &FAM) {
<< "':\n";
dumpExampleDependence(OS, &FAM.getResult<DependenceAnalysis>(F),
FAM.getResult<ScalarEvolutionAnalysis>(F),
- NormalizeResults);
+ FAM.getResult<LoopAnalysis>(F), NormalizeResults);
return PreservedAnalyses::all();
}
@@ -670,6 +866,81 @@ bool DependenceInfo::intersectConstraints(Constraint *X, const Constraint *Y) {
return false;
}
+//===----------------------------------------------------------------------===//
+// SCEVMonotonicity
+
+SCEVMonotonicity::SCEVMonotonicity(SCEVMonotonicityType Type,
+ const SCEV *FailurePoint)
+ : Type(Type), FailurePoint(FailurePoint) {
+ assert(
+ ((Type == SCEVMonotonicityType::Unknown) == (FailurePoint != nullptr)) &&
+ "FailurePoint must be provided iff Type is Unknown");
+}
+
+void SCEVMonotonicity::print(raw_ostream &OS, unsigned Depth) const {
+ OS.indent(Depth) << "Monotonicity: ";
+ switch (Type) {
+ case SCEVMonotonicityType::Unknown:
+ assert(FailurePoint && "FailurePoint must be provided for Unknown");
+ OS << "Unknown\n";
+ OS.indent(Depth) << "Reason: " << *FailurePoint << "\n";
+ break;
+ case SCEVMonotonicityType::Invariant:
+ OS << "Invariant\n";
+ break;
+ case SCEVMonotonicityType::MultivariateSignedMonotonic:
+ OS << "MultivariateSignedMonotonic\n";
+ break;
+ }
+}
+
+bool SCEVMonotonicityChecker::isLoopInvariant(const SCEV *Expr) const {
+ return !OutermostLoop || SE->isLoopInvariant(Expr, OutermostLoop);
+}
+
+SCEVMonotonicity SCEVMonotonicityChecker::invariantOrUnknown(const SCEV *Expr) {
+ if (isLoopInvariant(Expr))
+ return SCEVMonotonicity(SCEVMonotonicityType::Invariant);
+ return createUnknown(Expr);
+}
+
+SCEVMonotonicity
+SCEVMonotonicityChecker::checkMonotonicity(const SCEV *Expr,
+ const Loop *OutermostLoop) {
+ assert(Expr->getType()->isIntegerTy() && "Expr must be integer type");
+ this->OutermostLoop = OutermostLoop;
+ return visit(Expr);
+}
+
+/// We only care about an affine AddRec at the moment. For an affine AddRec,
+/// the monotonicity can be inferred from its nowrap property. For example, let
+/// X and Y be loop-invariant, and assume Y is non-negative. An AddRec
+/// {X,+.Y}<nsw> implies:
+///
+/// X <=s (X + Y) <=s ((X + Y) + Y) <=s ...
+///
+/// Thus, we can conclude that the AddRec is monotonically increasing with
+/// respect to the associated loop in a signed sense. The similar reasoning
+/// applies when Y is non-positive, leading to a monotonically decreasing
+/// AddRec.
+SCEVMonotonicity
+SCEVMonotonicityChecker::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
+ if (!Expr->isAffine() || !Expr->hasNoSignedWrap())
+ return createUnknown(Expr);
+
+ const SCEV *Start = Expr->getStart();
+ const SCEV *Step = Expr->getStepRecurrence(*SE);
+
+ SCEVMonotonicity StartMon = visit(Start);
+ if (StartMon.isUnknown())
+ return StartMon;
+
+ if (!isLoopInvariant(Step))
+ return createUnknown(Expr);
+
+ return SCEVMonotonicity(SCEVMonotonicityType::MultivariateSignedMonotonic);
+}
+
//===----------------------------------------------------------------------===//
// DependenceInfo methods
@@ -3488,10 +3759,19 @@ bool DependenceInfo::tryDelinearize(Instruction *Src, Instruction *Dst,
// resize Pair to contain as many pairs of subscripts as the delinearization
// has found, and then initialize the pairs following the delinearization.
Pair.resize(Size);
+ SCEVMonotonicityChecker MonChecker(SE);
+ const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop() : nullptr;
for (int I = 0; I < Size; ++I) {
Pair[I].Src = SrcSubscripts[I];
Pair[I].Dst = DstSubscripts[I];
unifySubscriptType(&Pair[I]);
+
+ if (EnableMonotonicityCheck) {
+ if (MonChecker.checkMonotonicity(Pair[I].Src, OutermostLoop).isUnknown())
+ return false;
+ if (MonChecker.checkMonotonicity(Pair[I].Dst, OutermostLoop).isUnknown())
+ return false;
+ }
}
return true;
@@ -3824,6 +4104,14 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
Pair[0].Src = SrcEv;
Pair[0].Dst = DstEv;
+ SCEVMonotonicityChecker MonChecker(SE);
+ const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop() : nullptr;
+ if (EnableMonotonicityCheck)
+ if (MonChecker.checkMonotonicity(Pair[0].Src, OutermostLoop).isUnknown() ||
+ MonChecker.checkMonotonicity(Pair[0].Dst, OutermostLoop).isUnknown())
+ 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/monotonicity-cast.ll b/llvm/test/Analysis/DependenceAnalysis/monotonicity-cast.ll
new file mode 100644
index 0000000000000..e43d00d0bf651
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-cast.ll
@@ -0,0 +1,207 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -disable-output -passes="print<da>" -da-dump-monotonicity-report \
+; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s
+
+; 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-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {(sext i8 %start to i64),+,(sext i8 %step to i64)}<nsw><%loop>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - none!
+;
+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-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: (sext i8 {%start,+,%step}<%loop> to i64)
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: (sext i8 {%start,+,%step}<%loop> to i64)
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+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
+}
+
+; for (int8_t i = 0; i < 100; i++)
+; a[zext(offset)] = 0;
+;
+define void @zext_pos(ptr %a) {
+; CHECK-LABEL: 'zext_pos'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - none!
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset.zext = zext nneg i8 %i to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.zext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i8 %i, 1
+ %exitcond = icmp eq i8 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The zero-extened value of `offset` is no longer monotonic. In fact, the
+; values of `offset` in each iteration are:
+;
+; iteration | 0 | 1 | 2 | ...
+; -------------|-----|---|---|---------
+; offset | -1 | 0 | 1 | ...
+; zext(offset) | 255 | 0 | 1 | ...
+;
+;
+; for (int8_t i = -1; i < 100; i++)
+; a[zext(offset)] = 0;
+;
+define void @zext_cross_zero(ptr %a) {
+; CHECK-LABEL: 'zext_cross_zero'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: (zext i8 {-1,+,1}<nsw><%loop> to i64)
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: (zext i8 {-1,+,1}<nsw><%loop> to i64)
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i8 [ -1, %entry ], [ %i.inc, %loop ]
+ %offset.zext = zext nneg i8 %i to i64
+ %idx = getelementptr i8, ptr %a, i64 %offset.zext
+ store i8 0, ptr %idx
+ %i.inc = add nsw i8 %i, 1
+ %exitcond = icmp eq i8 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; In principle, we can prove that `zext(offset)` is monotonic since we know
+; that `offset` is non-negative.
+;
+; int8_t offset = 0;
+; for (int i = 0; i < 100; i++, offset += step)
+; a[zext(offset)] = 0;
+;
+define void @zext_nneg_nsw(ptr %a, i8 %step) {
+; CHECK-LABEL: 'zext_nneg_nsw'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: (zext i8 {0,+,%step}<nsw><%loop> to i64)
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: (zext i8 {0,+,%step}<nsw><%loop> to i64)
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i8 [ 0, %entry ], [ %offset.next, %loop ]
+ %offset.zext = zext nneg 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
+}
+
+; SCEV handles `i & 1` as an i1 addrec. Ensure that the monotonicity analysis
+; properly analyzes it.
+;
+; for (i = 0; i < 100; i++)
+; a[i & 1] = 0;
+;
+define void @offset_truncated_to_i1(ptr %a) {
+; CHECK-LABEL: 'offset_truncated_to_i1'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: (zext i1 {false,+,true}<%loop> to i64)
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: (zext i1 {false,+,true}<%loop> to i64)
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %and = and i64 %i, 1
+ %idx = getelementptr inbounds i8, ptr %a, i64 %and
+ store i8 0, ptr %idx
+ %i.inc = add nsw i64 %i, 1
+ %exitcond = icmp eq i64 %i.inc, 100
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonicity-delinearize.ll b/llvm/test/Analysis/DependenceAnalysis/monotonicity-delinearize.ll
new file mode 100644
index 0000000000000..71ea4e95059a0
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-delinearize.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -disable-output -passes="print<da>" -da-dump-monotonicity-report \
+; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s
+
+; The offset SCEV will be delinearized into a 2D array access, like as follows:
+;
+; - Outer subscript: {0,+,1}<nuw><nsw><%loop.i.header>
+; - Inner subscript: {0,+,1}<nuw><nsw><%loop.j.header>
+;
+; These subscripts are both monotonic, but we also need to check the
+; monotonicity of the original addrec.
+;
+; char A[...][32];
+; for (i = 0; i < 1ll << 62; i++)
+; for (j = 0; j < 32; j++)
+; if (i < (1ll << 57))
+; A[i][j] = 0;
+;
+define void @linearized_offset_wrap(ptr %a) {
+; CHECK-LABEL: 'linearized_offset_wrap'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %gep, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,32}<%loop.i.header>,+,1}<nw><%loop.j.header>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {{\{\{}}0,+,32}<%loop.i.header>,+,1}<nw><%loop.j.header>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %gep, align 1 --> Dst: store i8 0, ptr %gep, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ br label %loop.i.header
+
+loop.i.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop.i.latch ]
+ br label %loop.j.header
+
+loop.j.header:
+ %j = phi i64 [ 0, %loop.i.header ], [ %j.inc, %loop.j.latch ]
+ %cond = icmp slt i64 %i, 144115188075855872 ; 2^57
+ br i1 %cond, label %if.then, label %loop.j.latch
+
+if.then:
+ %gep = getelementptr inbounds [32 x i8], ptr %a, i64 %i, i64 %j
+ store i8 0, ptr %gep
+ br label %loop.j.latch
+
+loop.j.latch:
+ %j.inc = add nuw nsw i64 %j, 1
+ %ec.j = icmp eq i64 %j.inc, 32
+ br i1 %ec.j, label %loop.i.latch, label %loop.j.header
+
+loop.i.latch:
+ %i.inc = add nuw nsw i64 %i, 1
+ %ec.i = icmp eq i64 %i.inc, 4611686018427387904 ; 2^62
+ br i1 %ec.i, label %exit, label %loop.i.header
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll b/llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll
new file mode 100644
index 0000000000000..e5b6ddbaca6fe
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll
@@ -0,0 +1,150 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -disable-output -passes="print<da>" -da-dump-monotonicity-report \
+; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s
+
+; for (int i = 0; i < n; i++)
+; a[x] = 0;
+define void @single_loop_invariant(ptr %a, i64 %x, i64 %n) {
+; CHECK-LABEL: 'single_loop_invariant'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: %x
+; CHECK-NEXT: Monotonicity: Invariant
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - consistent output [S]!
+;
+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 %x
+ 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
+}
+
+; for (int i = 0; i < n; i++)
+; a[(i % 2 == 0 ? x : y)] = 0;
+define void @single_loop_variant(ptr %a, i64 %x, i64 %y, i64 %n) {
+; CHECK-LABEL: 'single_loop_variant'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: %offset
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: %offset
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ %guard = icmp sgt i64 %n, 0
+ br i1 %guard, label %loop, label %exit
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
+ %offset = phi i64 [ %x, %entry ], [ %offset.next, %loop ]
+ %offset.next = phi i64 [ %y, %entry ], [ %offset, %loop ]
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ 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
+}
+
+; for (int i = 0; i < n; i++)
+; for (int j = 0; j < m; j++)
+; a[x + i] = 0;
+define void @invariant_plus_monotonic0(ptr %a, i64 %x, i64 %n, i64 %m) {
+; CHECK-LABEL: 'invariant_plus_monotonic0'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {%x,+,1}<nsw><%loop.i.header>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - consistent output [0 S]!
+;
+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 ]
+ %offset = phi i64 [ %x, %entry ], [ %offset.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 ]
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ store i8 0, ptr %idx
+ %j.inc = add nuw 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
+ %offset.inc = add nsw i64 %offset, 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 = 0; i < n; i++)
+; for (int j = 0; j < m; j++)
+; a[x + j] = 0;
+define void @invariant_plus_monotonic1(ptr %a, i64 %x, i64 %n, i64 %m) {
+; CHECK-LABEL: 'invariant_plus_monotonic1'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {%x,+,1}<nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - consistent output [S 0]!
+;
+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 = phi i64 [ %x, %loop.j.preheader ], [ %offset.inc, %loop.j ]
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ store i8 0, ptr %idx
+ %j.inc = add nuw nsw i64 %j, 1
+ %offset.inc = add nsw i64 %offset, 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
+}
diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll b/llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll
new file mode 100644
index 0000000000000..7411dc9f5c053
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll
@@ -0,0 +1,519 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -disable-output -passes="print<da>" -da-dump-monotonicity-report \
+; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s
+
+; for (int i = 0; i < n; i++)
+; a[i] = 0;
+;
+define void @single_loop_nsw(ptr %a, i64 %n) {
+; CHECK-LABEL: 'single_loop_nsw'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - none!
+;
+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 @single_loop_nuw(ptr %a, i64 %begin, i64 %end) {
+; CHECK-LABEL: 'single_loop_nuw'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {%begin,+,1}<nuw><%loop>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {%begin,+,1}<nuw><%loop>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+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 @nested_loop_nsw0(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw0'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nuw><nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - output [* *]!
+;
+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 @nested_loop_nsw1(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw1'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}(-1 + %n),+,-1}<nsw><%loop.i.header>,+,1}<nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - output [* *]!
+;
+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 = 0; i < n; i--)
+; for (int j = 0; j < m; j++)
+; a[i - j] = 0;
+;
+define void @nested_loop_nsw2(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_nsw2'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,-1}<nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - output [* *]!
+;
+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 = sub 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 = 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 @nested_loop_nuw(ptr %a, i64 %begin0, i64 %end0, i64 %begin1, i64 %end1) {
+; CHECK-LABEL: 'nested_loop_nuw'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {{\{\{}}(%begin0 + %begin1),+,1}<nw><%loop.i.header>,+,1}<nw><%loop.j>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+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
+}
+
+; 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-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,%step}<nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - output [* *]!
+;
+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
+}
+
+; 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-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {%offset.i,+,1}<nuw><nsw><%loop.j>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+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
+}
+
+; The AddRec doesn't have nsw flag for the j-loop, since the store may not be
+; executed.
+;
+; for (int i = 0; i < n; i++)
+; for (int j = 0; j < m; j++)
+; if (cond)
+; a[i + j] = 0;
+;
+define void @conditional_store0(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'conditional_store0'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nw><%loop.j.header>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nw><%loop.j.header>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+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.header, label %loop.i.latch
+
+loop.j.header:
+ %j = phi i64 [ 0, %loop.j.preheader ], [ %j.inc, %loop.j.latch ]
+ %offset = add nsw i64 %i, %j
+ %cond = freeze i1 poison
+ br i1 %cond, label %if.then, label %loop.j.latch
+
+if.then:
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ store i8 0, ptr %idx
+ br label %loop.j.latch
+
+loop.j.latch:
+ %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.header
+
+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
+}
+
+; Similar to the @conditional_store0, but the definition of the `%offset` is
+;
diff erent from it and we can infer `nsw` in this case.
+;
+; for (int i = 0; i < n; i++)
+; for (int j = 0; j < m; j++)
+; if (cond)
+; a[i + j] = 0;
+;
+define void @conditional_store1(ptr %a, i64 %n, i64 %m) {
+; CHECK-LABEL: 'conditional_store1'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: Expr: {{\{\{}}0,+,1}<nuw><nsw><%loop.i.header>,+,1}<nuw><nsw><%loop.j.header>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
+; CHECK-NEXT: da analyze - output [* *]!
+;
+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.header, label %loop.i.latch
+
+loop.j.header:
+ %j = phi i64 [ 0, %loop.j.preheader ], [ %j.inc, %loop.j.latch ]
+ %offset = phi i64 [ %i, %loop.j.preheader ], [ %offset.next, %loop.j.latch ]
+ %cond = freeze i1 poison
+ br i1 %cond, label %if.then, label %loop.j.latch
+
+if.then:
+ %idx = getelementptr inbounds i8, ptr %a, i64 %offset
+ store i8 0, ptr %idx
+ br label %loop.j.latch
+
+loop.j.latch:
+ %j.inc = add nsw i64 %j, 1
+ %offset.next = add nsw i64 %offset, 1
+ %exitcond.j = icmp eq i64 %j.inc, %m
+ br i1 %exitcond.j, label %loop.i.latch, label %loop.j.header
+
+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
+}
+
+; In the following case, the computation `offset = offset_i + j` will not wrap,
+; but `offset_i += 1024` will wrap both in a signed sense and an unsigned
+; sense. We cannot prove the monotonicity in this case.
+;
+; offset_i = (1ULL << 63) - 256;
+; for (i = 0; i < (1ULL << 62); i++, offset_i += 1024)
+; for (j = 0; j < 32; j++) {
+; offset = offset_i + j;
+;
+; // The value of `offset` is positive in a signed sense.
+; if (offset < (1ULL << 63))
+; a[offset] = 0;
+; }
+;
+define void @outer_loop_may_wrap(ptr %a) {
+; CHECK-LABEL: 'outer_loop_may_wrap'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 0, ptr %gep, align 1
+; CHECK-NEXT: Expr: {{\{\{}}9223372036854775552,+,1024}<%loop.i.header>,+,1}<nuw><nsw><%loop.j.header>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {9223372036854775552,+,1024}<%loop.i.header>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 0, ptr %gep, align 1 --> Dst: store i8 0, ptr %gep, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+entry:
+ br label %loop.i.header
+
+loop.i.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %loop.i.latch ]
+ %subscript.i = phi i64 [ 9223372036854775552, %entry ], [ %subscript.i.next, %loop.i.latch ] ; The initial value is 2^63 - 256
+ br label %loop.j.header
+
+loop.j.header:
+ %j = phi i64 [ 0, %loop.i.header ], [ %j.inc, %loop.j.latch ]
+ %subscript = phi i64 [ %subscript.i, %loop.i.header ], [ %subscript.next, %loop.j.latch ]
+ %cond = icmp sge i64 %subscript, 0
+ br i1 %cond, label %if.then, label %loop.j.latch
+
+if.then:
+ %gep = getelementptr inbounds i8, ptr %a, i64 %subscript
+ store i8 0, ptr %gep
+ br label %loop.j.latch
+
+loop.j.latch:
+ %j.inc = add nuw nsw i64 %j, 1
+ %subscript.next = add nuw nsw i64 %subscript, 1
+ %ec.j = icmp eq i64 %j.inc, 32
+ br i1 %ec.j, label %loop.i.latch, label %loop.j.header
+
+loop.i.latch:
+ %i.inc = add nuw nsw i64 %i, 1
+ %subscript.i.next = add i64 %subscript.i, 1024
+ %ec.i = icmp eq i64 %i.inc, 4611686018427387904 ; 2^62
+ br i1 %ec.i, label %exit, label %loop.i.header
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll
new file mode 100644
index 0000000000000..6247336456d2c
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll
@@ -0,0 +1,77 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -disable-output -passes="print<da>" -da-dump-monotonicity-report \
+; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s
+; RUN: opt < %s -disable-output -passes="print<da>" 2>&1 | FileCheck %s -check-prefix=DISABLE-CHECK
+
+;
+; for (i = 0; i < (1ULL << 60); i++) {
+; A[i] = 1;
+;
+; unsigned long long offset = i * 32 + (1ULL << 62);
+; // offset is positive when interpreted as a signed value.
+; // To prevent violating the size limitation for an allocated object.
+; if (offset < (1ULL << 63))
+; A[offset] = 2;
+; }
+;
+; -----------------------------------------------------------------------------
+;
+; There is a dependency between the two stores. To detect it, we need to check
+; the monotonicity and bail out the analysis since `offset` is not monotonic.
+;
+; memory location | first store (A[i]) | second store (A[offset])
+; ------------------|--------------------|----------------------------
+; A[0] | i = 0 | i = 2^59 - 2^57
+; A[2^60 - 32] | i = 2^60 - 32 | i = 2^59 - 2^57 + 2^55 - 1
+;
+define void @f(ptr %A) {
+; CHECK-LABEL: 'f'
+; CHECK-NEXT: Monotonicity check:
+; CHECK-NEXT: Inst: store i8 1, ptr %idx.0, align 1
+; CHECK-NEXT: Expr: {0,+,1}<nuw><nsw><%loop.header>
+; CHECK-NEXT: Monotonicity: MultivariateSignedMonotonic
+; CHECK-NEXT: Inst: store i8 2, ptr %idx.1, align 1
+; CHECK-NEXT: Expr: {4611686018427387904,+,32}<%loop.header>
+; CHECK-NEXT: Monotonicity: Unknown
+; CHECK-NEXT: Reason: {4611686018427387904,+,32}<%loop.header>
+; CHECK-EMPTY:
+; CHECK-NEXT: Src: store i8 1, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.0, align 1
+; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: Src: store i8 1, ptr %idx.0, align 1 --> Dst: store i8 2, ptr %idx.1, align 1
+; CHECK-NEXT: da analyze - confused!
+; CHECK-NEXT: Src: store i8 2, ptr %idx.1, align 1 --> Dst: store i8 2, ptr %idx.1, align 1
+; CHECK-NEXT: da analyze - confused!
+;
+; DISABLE-CHECK-LABEL: 'f'
+; DISABLE-CHECK-NEXT: Src: store i8 1, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.0, align 1
+; DISABLE-CHECK-NEXT: da analyze - none!
+; DISABLE-CHECK-NEXT: Src: store i8 1, ptr %idx.0, align 1 --> Dst: store i8 2, ptr %idx.1, align 1
+; DISABLE-CHECK-NEXT: da analyze - none!
+; DISABLE-CHECK-NEXT: Src: store i8 2, ptr %idx.1, align 1 --> Dst: store i8 2, ptr %idx.1, align 1
+; DISABLE-CHECK-NEXT: da analyze - none!
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop.latch ]
+ %idx.0 = getelementptr inbounds i8, ptr %A, i64 %i
+ store i8 1, ptr %idx.0
+ %offset.tmp = mul i64 %i, 32
+ %offset = add i64 %offset.tmp, 4611686018427387904 ; 1ULL << 62
+ %if.cond = icmp sge i64 %offset, 0
+ br i1 %if.cond, label %if.then, label %loop.latch
+
+if.then:
+ %idx.1 = getelementptr inbounds i8, ptr %A, i64 %offset
+ store i8 2, ptr %idx.1
+ br label %loop.latch
+
+loop.latch:
+ %i.next = add nuw nsw i64 %i, 1
+ %exit.cond = icmp eq i64 %i.next, 1152921504606846976 ; 1ULL << 60
+ br i1 %exit.cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list