Skip to content

Commit 9bfa9d5

Browse files
committed
[DA] Add initial support for monotonicity check
1 parent f9b371d commit 9bfa9d5

File tree

4 files changed

+1056
-3
lines changed

4 files changed

+1056
-3
lines changed

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 273 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
184352
static 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,
235423
void 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

241430
PreservedAnalyses
@@ -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

Comments
 (0)