@@ -128,6 +128,18 @@ static cl::opt<bool> RunSIVRoutinesOnly(
128128 " The purpose is mainly to exclude the influence of those routines "
129129 " in regression tests for SIV routines." ));
130130
131+ // TODO: This flag is disabled by default because it is still under development.
132+ // Enable it or delete this flag when the feature is ready.
133+ static cl::opt<bool > EnableMonotonicityCheck (
134+ " da-enable-monotonicity-check" , cl::init(false ), cl::Hidden,
135+ cl::desc(" Check if the subscripts are monotonic. If it's not, dependence "
136+ " is reported as unknown." ));
137+
138+ static cl::opt<bool > DumpMonotonicityReport (
139+ " da-dump-monotonicity-report" , cl::init(false ), cl::Hidden,
140+ cl::desc(
141+ " When printing analysis, dump the results of monotonicity checks." ));
142+
131143// ===----------------------------------------------------------------------===//
132144// basics
133145
@@ -177,13 +189,189 @@ void DependenceAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
177189 AU.addRequiredTransitive <LoopInfoWrapperPass>();
178190}
179191
192+ namespace {
193+
194+ // / The type of monotonicity of a SCEV. This property is defined with respect to
195+ // / the outermost loop that DA is analyzing.
196+ // /
197+ // / This is designed to classify the behavior of AddRec expressions, and does
198+ // / not care about other SCEVs. For example, given the two loop-invariant values
199+ // / `A` and `B`, `A + B` is treated as Invariant even if the addition wraps.
200+ enum class SCEVMonotonicityType {
201+ // / The expression is neither loop-invariant nor monotonic (or we fail to
202+ // / prove it).
203+ Unknown,
204+
205+ // / The expression is loop-invariant with respect to the outermost loop.
206+ Invariant,
207+
208+ // / The expression is a (nested) affine AddRec and is monotonically increasing
209+ // / or decreasing in a signed sense with respect to each loop. Monotonicity is
210+ // / checked independently for each loop, and the expression is classified as
211+ // / MultiSignedMonotonic if all AddRecs are nsw. For example, in the following
212+ // / loop:
213+ // /
214+ // / for (i = 0; i < 100; i++)
215+ // / for (j = 0; j < 100; j++)
216+ // / A[i + j] = ...;
217+ // /
218+ // / The SCEV for `i + j` is classified as MultiSignedMonotonic. On the other
219+ // / hand, in the following loop:
220+ // /
221+ // / for (i = 0; i < 100; i++)
222+ // / for (j = 0; j <= (1ULL << 63); j++)
223+ // / A[i + j] = ...;
224+ // /
225+ // / The SCEV for `i + j` is NOT classified as MultiMonotonic, because the
226+ // / AddRec for `j` wraps in a signed sense. We don't consider the "direction"
227+ // / of each AddRec. For example, in the following loop:
228+ // /
229+ // / for (int i = 0; i < 100; i++)
230+ // / for (int j = 0; j < 100; j++)
231+ // / A[i - j] = ...;
232+ // /
233+ // / The SCEV for `i - j` is classified as MultiSignedMonotonic, even though it
234+ // / contains both increasing and decreasing AddRecs.
235+ // /
236+ // / Note that we don't check if the step recurrence can be zero. For
237+ // / example,an AddRec `{0,+,%a}<nsw> is classifed as Monotonic if `%a` can be
238+ // / zero. That is, the expression can be Invariant.
239+ MultiSignedMonotonic,
240+ };
241+
242+ struct SCEVMonotonicity {
243+ SCEVMonotonicity (SCEVMonotonicityType Type,
244+ const SCEV *FailurePoint = nullptr );
245+
246+ SCEVMonotonicityType getType () const { return Type; }
247+
248+ const SCEV *getFailurePoint () const { return FailurePoint; }
249+
250+ bool isUnknown () const { return Type == SCEVMonotonicityType::Unknown; }
251+
252+ void print (raw_ostream &OS, unsigned Depth) const ;
253+
254+ private:
255+ SCEVMonotonicityType Type;
256+
257+ // / The subexpression that caused Unknown. Mainly for debugging purpose.
258+ const SCEV *FailurePoint;
259+ };
260+
261+ struct SCEVMonotonicityChecker
262+ : public SCEVVisitor<SCEVMonotonicityChecker, SCEVMonotonicity> {
263+
264+ SCEVMonotonicityChecker (ScalarEvolution *SE) : SE(SE) {}
265+
266+ // / Check the monotonicity of \p Expr. \p Expr must be integer type. If \p
267+ // / OutermostLoop is not null, \p Expr must be defined in \p OutermostLoop or
268+ // / one of its nested loops.
269+ SCEVMonotonicity checkMonotonicity (const SCEV *Expr,
270+ const Loop *OutermostLoop);
271+
272+ private:
273+ ScalarEvolution *SE;
274+
275+ // / The outermost loop that DA is analyzing.
276+ const Loop *OutermostLoop;
277+
278+ // / A helper to classify \p Expr as either Invariant or Unknown.
279+ SCEVMonotonicity invariantOrUnknown (const SCEV *Expr);
280+
281+ // / Return true if \p Expr is loop-invariant with respect to the outermost
282+ // / loop.
283+ bool isLoopInvariant (const SCEV *Expr) const ;
284+
285+ // / A helper to create an Unknown SCEVMonotonicity.
286+ SCEVMonotonicity createUnknown (const SCEV *FailurePoint) {
287+ return SCEVMonotonicity (SCEVMonotonicityType::Unknown, FailurePoint);
288+ }
289+
290+ SCEVMonotonicity visitAddRecExpr (const SCEVAddRecExpr *Expr);
291+
292+ SCEVMonotonicity visitConstant (const SCEVConstant *) {
293+ return SCEVMonotonicity (SCEVMonotonicityType::Invariant);
294+ }
295+ SCEVMonotonicity visitVScale (const SCEVVScale *) {
296+ return SCEVMonotonicity (SCEVMonotonicityType::Invariant);
297+ }
298+
299+ // TODO: Handle more cases.
300+ SCEVMonotonicity visitZeroExtendExpr (const SCEVZeroExtendExpr *Expr) {
301+ return invariantOrUnknown (Expr);
302+ }
303+ SCEVMonotonicity visitSignExtendExpr (const SCEVSignExtendExpr *Expr) {
304+ return invariantOrUnknown (Expr);
305+ }
306+ SCEVMonotonicity visitAddExpr (const SCEVAddExpr *Expr) {
307+ return invariantOrUnknown (Expr);
308+ }
309+ SCEVMonotonicity visitMulExpr (const SCEVMulExpr *Expr) {
310+ return invariantOrUnknown (Expr);
311+ }
312+ SCEVMonotonicity visitPtrToIntExpr (const SCEVPtrToIntExpr *Expr) {
313+ return invariantOrUnknown (Expr);
314+ }
315+ SCEVMonotonicity visitTruncateExpr (const SCEVTruncateExpr *Expr) {
316+ return invariantOrUnknown (Expr);
317+ }
318+ SCEVMonotonicity visitUDivExpr (const SCEVUDivExpr *Expr) {
319+ return invariantOrUnknown (Expr);
320+ }
321+ SCEVMonotonicity visitSMaxExpr (const SCEVSMaxExpr *Expr) {
322+ return invariantOrUnknown (Expr);
323+ }
324+ SCEVMonotonicity visitUMaxExpr (const SCEVUMaxExpr *Expr) {
325+ return invariantOrUnknown (Expr);
326+ }
327+ SCEVMonotonicity visitSMinExpr (const SCEVSMinExpr *Expr) {
328+ return invariantOrUnknown (Expr);
329+ }
330+ SCEVMonotonicity visitUMinExpr (const SCEVUMinExpr *Expr) {
331+ return invariantOrUnknown (Expr);
332+ }
333+ SCEVMonotonicity visitSequentialUMinExpr (const SCEVSequentialUMinExpr *Expr) {
334+ return invariantOrUnknown (Expr);
335+ }
336+ SCEVMonotonicity visitUnknown (const SCEVUnknown *Expr) {
337+ return invariantOrUnknown (Expr);
338+ }
339+ SCEVMonotonicity visitCouldNotCompute (const SCEVCouldNotCompute *Expr) {
340+ return invariantOrUnknown (Expr);
341+ }
342+
343+ friend struct SCEVVisitor <SCEVMonotonicityChecker, SCEVMonotonicity>;
344+ };
345+
346+ } // anonymous namespace
347+
180348// Used to test the dependence analyzer.
181349// Looks through the function, noting instructions that may access memory.
182350// Calls depends() on every possible pair and prints out the result.
183351// Ignores all other instructions.
184352static void dumpExampleDependence (raw_ostream &OS, DependenceInfo *DA,
185- ScalarEvolution &SE, bool NormalizeResults) {
353+ ScalarEvolution &SE, LoopInfo &LI,
354+ bool NormalizeResults) {
186355 auto *F = DA->getFunction ();
356+
357+ if (DumpMonotonicityReport) {
358+ SCEVMonotonicityChecker Checker (&SE);
359+ OS << " Monotonicity check:\n " ;
360+ for (Instruction &Inst : instructions (F)) {
361+ if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
362+ continue ;
363+ Value *Ptr = getLoadStorePointerOperand (&Inst);
364+ const Loop *L = LI.getLoopFor (Inst.getParent ());
365+ const SCEV *PtrSCEV = SE.getSCEVAtScope (Ptr, L);
366+ const SCEV *AccessFn = SE.removePointerBase (PtrSCEV);
367+ SCEVMonotonicity Mon = Checker.checkMonotonicity (AccessFn, L);
368+ OS.indent (2 ) << " Inst: " << Inst << " \n " ;
369+ OS.indent (4 ) << " Expr: " << *AccessFn << " \n " ;
370+ Mon.print (OS, 4 );
371+ }
372+ OS << " \n " ;
373+ }
374+
187375 for (inst_iterator SrcI = inst_begin (F), SrcE = inst_end (F); SrcI != SrcE;
188376 ++SrcI) {
189377 if (SrcI->mayReadOrWriteMemory ()) {
@@ -235,7 +423,8 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
235423void DependenceAnalysisWrapperPass::print (raw_ostream &OS,
236424 const Module *) const {
237425 dumpExampleDependence (
238- OS, info.get (), getAnalysis<ScalarEvolutionWrapperPass>().getSE (), false );
426+ OS, info.get (), getAnalysis<ScalarEvolutionWrapperPass>().getSE (),
427+ getAnalysis<LoopInfoWrapperPass>().getLoopInfo (), false );
239428}
240429
241430PreservedAnalyses
@@ -244,7 +433,7 @@ DependenceAnalysisPrinterPass::run(Function &F, FunctionAnalysisManager &FAM) {
244433 << " ':\n " ;
245434 dumpExampleDependence (OS, &FAM.getResult <DependenceAnalysis>(F),
246435 FAM.getResult <ScalarEvolutionAnalysis>(F),
247- NormalizeResults);
436+ FAM. getResult <LoopAnalysis>(F), NormalizeResults);
248437 return PreservedAnalyses::all ();
249438}
250439
@@ -670,6 +859,70 @@ bool DependenceInfo::intersectConstraints(Constraint *X, const Constraint *Y) {
670859 return false ;
671860}
672861
862+ // ===----------------------------------------------------------------------===//
863+ // SCEVMonotonicity
864+
865+ SCEVMonotonicity::SCEVMonotonicity (SCEVMonotonicityType Type,
866+ const SCEV *FailurePoint)
867+ : Type(Type), FailurePoint(FailurePoint) {
868+ assert (
869+ ((Type == SCEVMonotonicityType::Unknown) == (FailurePoint != nullptr )) &&
870+ " FailurePoint must be provided iff Type is Unknown" );
871+ }
872+
873+ void SCEVMonotonicity::print (raw_ostream &OS, unsigned Depth) const {
874+ OS.indent (Depth) << " Monotonicity: " ;
875+ switch (Type) {
876+ case SCEVMonotonicityType::Unknown:
877+ assert (FailurePoint && " FailurePoint must be provided for Unknown" );
878+ OS << " Unknown\n " ;
879+ OS.indent (Depth) << " Reason: " << *FailurePoint << " \n " ;
880+ break ;
881+ case SCEVMonotonicityType::Invariant:
882+ OS << " Invariant\n " ;
883+ break ;
884+ case SCEVMonotonicityType::MultiSignedMonotonic:
885+ OS << " MultiSignedMonotonic\n " ;
886+ break ;
887+ }
888+ }
889+
890+ bool SCEVMonotonicityChecker::isLoopInvariant (const SCEV *Expr) const {
891+ return !OutermostLoop || SE->isLoopInvariant (Expr, OutermostLoop);
892+ }
893+
894+ SCEVMonotonicity SCEVMonotonicityChecker::invariantOrUnknown (const SCEV *Expr) {
895+ if (isLoopInvariant (Expr))
896+ return SCEVMonotonicity (SCEVMonotonicityType::Invariant);
897+ return createUnknown (Expr);
898+ }
899+
900+ SCEVMonotonicity
901+ SCEVMonotonicityChecker::checkMonotonicity (const SCEV *Expr,
902+ const Loop *OutermostLoop) {
903+ assert (Expr->getType ()->isIntegerTy () && " Expr must be integer type" );
904+ this ->OutermostLoop = OutermostLoop;
905+ return visit (Expr);
906+ }
907+
908+ SCEVMonotonicity
909+ SCEVMonotonicityChecker::visitAddRecExpr (const SCEVAddRecExpr *Expr) {
910+ if (!Expr->isAffine () || !Expr->hasNoSignedWrap ())
911+ return createUnknown (Expr);
912+
913+ const SCEV *Start = Expr->getStart ();
914+ const SCEV *Step = Expr->getStepRecurrence (*SE);
915+
916+ SCEVMonotonicity StartMon = visit (Start);
917+ if (StartMon.isUnknown ())
918+ return StartMon;
919+
920+ if (!isLoopInvariant (Step))
921+ return createUnknown (Expr);
922+
923+ return SCEVMonotonicity (SCEVMonotonicityType::MultiSignedMonotonic);
924+ }
925+
673926// ===----------------------------------------------------------------------===//
674927// DependenceInfo methods
675928
@@ -3479,10 +3732,19 @@ bool DependenceInfo::tryDelinearize(Instruction *Src, Instruction *Dst,
34793732 // resize Pair to contain as many pairs of subscripts as the delinearization
34803733 // has found, and then initialize the pairs following the delinearization.
34813734 Pair.resize (Size);
3735+ SCEVMonotonicityChecker MonChecker (SE);
3736+ const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop () : nullptr ;
34823737 for (int I = 0 ; I < Size; ++I) {
34833738 Pair[I].Src = SrcSubscripts[I];
34843739 Pair[I].Dst = DstSubscripts[I];
34853740 unifySubscriptType (&Pair[I]);
3741+
3742+ if (EnableMonotonicityCheck) {
3743+ if (MonChecker.checkMonotonicity (Pair[I].Src , OutermostLoop).isUnknown ())
3744+ return false ;
3745+ if (MonChecker.checkMonotonicity (Pair[I].Dst , OutermostLoop).isUnknown ())
3746+ return false ;
3747+ }
34863748 }
34873749
34883750 return true ;
@@ -3815,6 +4077,14 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38154077 Pair[0 ].Src = SrcEv;
38164078 Pair[0 ].Dst = DstEv;
38174079
4080+ SCEVMonotonicityChecker MonChecker (SE);
4081+ const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop () : nullptr ;
4082+ if (EnableMonotonicityCheck)
4083+ if (MonChecker.checkMonotonicity (Pair[0 ].Src , OutermostLoop).isUnknown () ||
4084+ MonChecker.checkMonotonicity (Pair[0 ].Dst , OutermostLoop).isUnknown ())
4085+ return std::make_unique<Dependence>(Src, Dst,
4086+ SCEVUnionPredicate (Assume, *SE));
4087+
38184088 if (Delinearize) {
38194089 if (tryDelinearize (Src, Dst, Pair)) {
38204090 LLVM_DEBUG (dbgs () << " delinearized\n " );
0 commit comments