From 9bfa9d5d0a2c05665861f808b4b0a11f746101f0 Mon Sep 17 00:00:00 2001 From: Ryotaro Kasuga Date: Tue, 7 Oct 2025 12:35:16 +0000 Subject: [PATCH 1/2] [DA] Add initial support for monotonicity check --- llvm/lib/Analysis/DependenceAnalysis.cpp | 276 ++++++++++- .../DependenceAnalysis/monotonicity-cast.ll | 174 +++++++ .../monotonicity-invariant.ll | 150 ++++++ .../monotonicity-no-wrap-flags.ll | 459 ++++++++++++++++++ 4 files changed, 1056 insertions(+), 3 deletions(-) create mode 100644 llvm/test/Analysis/DependenceAnalysis/monotonicity-cast.ll create mode 100644 llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll create mode 100644 llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp index 1f0da8d1830d3..a3134f8571481 100644 --- a/llvm/lib/Analysis/DependenceAnalysis.cpp +++ b/llvm/lib/Analysis/DependenceAnalysis.cpp @@ -128,6 +128,18 @@ static cl::opt 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 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 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,189 @@ void DependenceAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequiredTransitive(); } +namespace { + +/// The type of monotonicity of a SCEV. This property is defined with respect to +/// the outermost loop that DA is analyzing. +/// +/// This is designed to classify the behavior of AddRec expressions, and does +/// not care about other SCEVs. For example, given the two loop-invariant values +/// `A` and `B`, `A + B` is treated as Invariant even if the addition wraps. +enum class SCEVMonotonicityType { + /// The expression is neither loop-invariant nor monotonic (or we fail to + /// prove it). + Unknown, + + /// The expression is loop-invariant with respect to the outermost loop. + Invariant, + + /// The expression is a (nested) affine AddRec and is monotonically increasing + /// or decreasing in a signed sense with respect to each loop. Monotonicity is + /// checked independently for each loop, and the expression is classified as + /// MultiSignedMonotonic if all AddRecs are nsw. For example, in the following + /// loop: + /// + /// for (i = 0; i < 100; i++) + /// for (j = 0; j < 100; j++) + /// A[i + j] = ...; + /// + /// The SCEV for `i + j` is classified as MultiSignedMonotonic. On the other + /// hand, in the following loop: + /// + /// for (i = 0; i < 100; i++) + /// for (j = 0; j <= (1ULL << 63); j++) + /// A[i + j] = ...; + /// + /// The SCEV for `i + j` is NOT classified as MultiMonotonic, because the + /// AddRec for `j` wraps in a signed sense. We don't consider the "direction" + /// of each AddRec. For example, in the following loop: + /// + /// for (int i = 0; i < 100; i++) + /// for (int j = 0; j < 100; j++) + /// A[i - j] = ...; + /// + /// The SCEV for `i - j` is classified as MultiSignedMonotonic, even though it + /// contains both increasing and decreasing AddRecs. + /// + /// Note that we don't check if the step recurrence can be zero. For + /// example,an AddRec `{0,+,%a} is classifed as Monotonic if `%a` can be + /// zero. That is, the expression can be Invariant. + MultiSignedMonotonic, +}; + +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; +}; + +struct SCEVMonotonicityChecker + : public SCEVVisitor { + + 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; +}; + +} // 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(Inst) && !isa(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 +423,8 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA, void DependenceAnalysisWrapperPass::print(raw_ostream &OS, const Module *) const { dumpExampleDependence( - OS, info.get(), getAnalysis().getSE(), false); + OS, info.get(), getAnalysis().getSE(), + getAnalysis().getLoopInfo(), false); } PreservedAnalyses @@ -244,7 +433,7 @@ DependenceAnalysisPrinterPass::run(Function &F, FunctionAnalysisManager &FAM) { << "':\n"; dumpExampleDependence(OS, &FAM.getResult(F), FAM.getResult(F), - NormalizeResults); + FAM.getResult(F), NormalizeResults); return PreservedAnalyses::all(); } @@ -670,6 +859,70 @@ 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::MultiSignedMonotonic: + OS << "MultiSignedMonotonic\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); +} + +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::MultiSignedMonotonic); +} + //===----------------------------------------------------------------------===// // DependenceInfo methods @@ -3479,10 +3732,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; @@ -3815,6 +4077,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(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..7a72755bcaf2f --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-cast.ll @@ -0,0 +1,174 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -disable-output -passes="print" -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)}<%loop> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop> to i64) +; CHECK-NEXT: Monotonicity: Unknown +; CHECK-NEXT: Reason: (zext i8 {-1,+,1}<%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}<%loop> to i64) +; CHECK-NEXT: Monotonicity: Unknown +; CHECK-NEXT: Reason: (zext i8 {0,+,%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 [ 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 +} diff --git a/llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll b/llvm/test/Analysis/DependenceAnalysis/monotonicity-invariant.ll new file mode 100644 index 0000000000000..8f45dfa3af5dd --- /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-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}<%loop.i.header> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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..83ea15bf76682 --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/monotonicity-no-wrap-flags.ll @@ -0,0 +1,459 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -disable-output -passes="print" -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}<%loop> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop> +; CHECK-NEXT: Monotonicity: Unknown +; CHECK-NEXT: Reason: {%begin,+,1}<%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}<%loop.i.header>,+,1}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.i.header>,+,1}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.i.header>,+,-1}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.i.header>,+,1}<%loop.j> +; CHECK-NEXT: Monotonicity: Unknown +; CHECK-NEXT: Reason: {{\{\{}}(%begin0 + %begin1),+,1}<%loop.i.header>,+,1}<%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}<%loop.i.header>,+,%step}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.j> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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}<%loop.i.header>,+,1}<%loop.j.header> +; CHECK-NEXT: Monotonicity: Unknown +; CHECK-NEXT: Reason: {{\{\{}}0,+,1}<%loop.i.header>,+,1}<%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 +; different 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}<%loop.i.header>,+,1}<%loop.j.header> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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 +} From d088dbe94b2c0e0a71af76f5237009e5ff7b3dbe Mon Sep 17 00:00:00 2001 From: Ryotaro Kasuga Date: Thu, 9 Oct 2025 11:29:37 +0000 Subject: [PATCH 2/2] add another test --- .../DependenceAnalysis/non-monotonic.ll | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll diff --git a/llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll b/llvm/test/Analysis/DependenceAnalysis/non-monotonic.ll new file mode 100644 index 0000000000000..04db865f8b1ee --- /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-dump-monotonicity-report \ +; RUN: -da-enable-monotonicity-check 2>&1 | FileCheck %s +; RUN: opt < %s -disable-output -passes="print" 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}<%loop.header> +; CHECK-NEXT: Monotonicity: MultiSignedMonotonic +; 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 +}