@@ -128,6 +128,18 @@ static cl::opt<bool> RunSIVRoutinesOnly(
128
128
" The purpose is mainly to exclude the influence of those routines "
129
129
" in regression tests for SIV routines." ));
130
130
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
+
131
143
// ===----------------------------------------------------------------------===//
132
144
// basics
133
145
@@ -177,13 +189,189 @@ void DependenceAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
177
189
AU.addRequiredTransitive <LoopInfoWrapperPass>();
178
190
}
179
191
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
+
180
348
// Used to test the dependence analyzer.
181
349
// Looks through the function, noting instructions that may access memory.
182
350
// Calls depends() on every possible pair and prints out the result.
183
351
// Ignores all other instructions.
184
352
static void dumpExampleDependence (raw_ostream &OS, DependenceInfo *DA,
185
- ScalarEvolution &SE, bool NormalizeResults) {
353
+ ScalarEvolution &SE, LoopInfo &LI,
354
+ bool NormalizeResults) {
186
355
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
+
187
375
for (inst_iterator SrcI = inst_begin (F), SrcE = inst_end (F); SrcI != SrcE;
188
376
++SrcI) {
189
377
if (SrcI->mayReadOrWriteMemory ()) {
@@ -235,7 +423,8 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
235
423
void DependenceAnalysisWrapperPass::print (raw_ostream &OS,
236
424
const Module *) const {
237
425
dumpExampleDependence (
238
- OS, info.get (), getAnalysis<ScalarEvolutionWrapperPass>().getSE (), false );
426
+ OS, info.get (), getAnalysis<ScalarEvolutionWrapperPass>().getSE (),
427
+ getAnalysis<LoopInfoWrapperPass>().getLoopInfo (), false );
239
428
}
240
429
241
430
PreservedAnalyses
@@ -244,7 +433,7 @@ DependenceAnalysisPrinterPass::run(Function &F, FunctionAnalysisManager &FAM) {
244
433
<< " ':\n " ;
245
434
dumpExampleDependence (OS, &FAM.getResult <DependenceAnalysis>(F),
246
435
FAM.getResult <ScalarEvolutionAnalysis>(F),
247
- NormalizeResults);
436
+ FAM. getResult <LoopAnalysis>(F), NormalizeResults);
248
437
return PreservedAnalyses::all ();
249
438
}
250
439
@@ -670,6 +859,70 @@ bool DependenceInfo::intersectConstraints(Constraint *X, const Constraint *Y) {
670
859
return false ;
671
860
}
672
861
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
+
673
926
// ===----------------------------------------------------------------------===//
674
927
// DependenceInfo methods
675
928
@@ -3479,10 +3732,19 @@ bool DependenceInfo::tryDelinearize(Instruction *Src, Instruction *Dst,
3479
3732
// resize Pair to contain as many pairs of subscripts as the delinearization
3480
3733
// has found, and then initialize the pairs following the delinearization.
3481
3734
Pair.resize (Size);
3735
+ SCEVMonotonicityChecker MonChecker (SE);
3736
+ const Loop *OutermostLoop = SrcLoop ? SrcLoop->getOutermostLoop () : nullptr ;
3482
3737
for (int I = 0 ; I < Size; ++I) {
3483
3738
Pair[I].Src = SrcSubscripts[I];
3484
3739
Pair[I].Dst = DstSubscripts[I];
3485
3740
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
+ }
3486
3748
}
3487
3749
3488
3750
return true ;
@@ -3815,6 +4077,14 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
3815
4077
Pair[0 ].Src = SrcEv;
3816
4078
Pair[0 ].Dst = DstEv;
3817
4079
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
+
3818
4088
if (Delinearize) {
3819
4089
if (tryDelinearize (Src, Dst, Pair)) {
3820
4090
LLVM_DEBUG (dbgs () << " delinearized\n " );
0 commit comments