[PATCH] D110973: [DA] Handle mismatching loop levels by considering them non-linear

Michael Kruse via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Wed May 18 15:01:11 PDT 2022


Meinersbur added a comment.

As mentioned I would experimented a bit more. Unfortunately I found more problems.

The following crashes even with this patch: F23108829: da-variant-crash.ll <https://reviews.llvm.org/F23108829>

  void foo(int n, double * restrict A, double * restrict B) {
      for (int i = 0; i < n; ++i) {
          int s = 0;
          for (; s*s < n*n; ++s) { } // incomputable AddRec
           for (int k = 0; k < n; ++k) 
              A[s] = 0; // Invariant in innermost loop
           
          A[i] = 1; // do create a dependency pair
      }
  }

because the the index of `A[s]` is invariant to its innermost surrounding loop `k` is invariant and hence passes the additional test.

Even more concerning is something like this, even though it does not crash directly:

     int s = 0;
     for (; s*s < n*n; ++s) ; // incomputable AddRec
  
  for (int i = 0; i < n; ++i) {
       for (int k = 0; k < n; ++k) 
           for (int j = 0; j < n; ++j) 
              A[s + k] = 0; // Invariant: j; Computable: k; Variant: s
       
      A[i] = 1; // do create a dependency pair
  }

ScalarEvolution's LoopDisposition test skips `s` because it is nested inside an AddRec that is invariant because its of an outer loop (`((8 * (sext i32 {{0,+,1}<nuw><nsw><%for.cond>,+,1}<nuw><%for.cond5> to i64))<nsw> + %A)`) and hence returns the entire thing as invariant even though it contains an SCEVAddRecExpr outside the loop nest. I again tend towards to scanning the SCEVExpr for illegal SCEVAddRecExpr. See [the only important remark].



================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:798-799
                                      const Loop *LoopNest) const {
   if (!LoopNest)
     return true;
   return SE->isLoopInvariant(Expression, LoopNest) &&
----------------
This seems in conflict with handling within ScalarEvolution:
```
    // Instructions are never considered invariant in the function body
    // (null loop) because they are defined within the "loop".
```

Suggested alternative comments/non-recursive implementation:
```
  // Outside any loop, an expression will not vary between executions (there is only one) and we consider it invariant. In contrast to ScalarEvolution::isLoopInvariant, we only consider expression evalution at a specific position, the array access, not accross the function.
  if (!LoopNest)
    return true;

  // If the expression is invariant in the outermost loop of the loop nest, it is invariant anywhere in the loop nest. 
 const  Loop * OutermostLoop = LoopNest;
  while (OutermostLoop->getParentLoop())
      OutermostLoop = OutermostLoop->getParentLoop();
  return SE->isLoopInvariant(Expression, OutermostLoop);
```


================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:892
 
 // Examine the scev and return true iff it's linear.
 // Collect any loops mentioned in the set of "Loops".
----------------
We allow for non-zero Start value, hence it is affine, not linear.


================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:900
   const SCEV *Start = AddRec->getStart();
   const SCEV *Step = AddRec->getStepRecurrence(*SE);
   const SCEV *UB = SE->getBackedgeTakenCount(AddRec->getLoop());
----------------
[suggestion] `SCEVAddRecExpr` has a member `isAffine` that could be queried directly.


================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:921-924
   if (IsSrc)
     Loops.set(mapSrcLoop(AddRec->getLoop()));
   else
     Loops.set(mapDstLoop(AddRec->getLoop()));
----------------
`mapSrcLoop`/`mapDstLoop` may already be out-of-range because `AddRec->getLoop()` is not surrounding the SCEVExpr. 

One could include a sanity check inside `mapSrcLoop`/`mapDstLoop`:
```
    const Loop* L = LoopNest;
    while (L && AddRec->getLoop() != L) 
        L = L->getParentLoop();
    assert (L && "Must be in loop nest, otherwise mapSrcLoop/mapDstLoop will be out-of-range");
```


================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:925
     Loops.set(mapDstLoop(AddRec->getLoop()));
   return checkSubscript(Start, LoopNest, Loops, IsSrc);
 }
----------------
This recursive call may also return false, in which case `Loops.set` has already been executed. (This may be irrelevant because when returning false, `Loops` is probably be ignored anyway).


================
Comment at: llvm/lib/Analysis/DependenceAnalysis.cpp:917
+  DispositionTy Disposition = SE->getLoopDisposition(AddRec, LoopNest);
+  if (Disposition != DispositionTy::LoopInvariant &&
+      Disposition != DispositionTy::LoopComputable)
----------------
congzhe wrote:
> bmahjour wrote:
> > congzhe wrote:
> > > congzhe wrote:
> > > > Thanks for the change, It looks good to me. I don't know if Micheal has further comments?
> > > > 
> > > > [nit] I'm wondering if it would be better to simplify the code like this?
> > > > ```
> > > > if (SE->getLoopDisposition(AddRec, LoopNest) == ScalarEvolution::LoopDisposition::LoopVariant )
> > > >   return false;
> > > > ```
> > > > Thanks for the change, It looks good to me. I don't know if Micheal has further comments?
> > > > 
> > > > [nit] I'm wondering if it would be better to simplify the code like this?
> > > > ```
> > > > if (SE->getLoopDisposition(AddRec, LoopNest) == ScalarEvolution::LoopDisposition::LoopVariant )
> > > >   return false;
> > > > ```
> > > 
> > > Actually could be futher simplified to 
> > > ```
> > > if (SE->getLoopDisposition(AddRec, LoopNest) == ScalarEvolution::LoopVariant )
> > >   return false;
> > > ```
> > > 
> > I thought about that too, but decided to check for `LoopComputable` and `LoopInvariant` specifically, in case new enums are added to `LoopDisposition` in the future. The logic also makes a bit more sense when I think about it as neither invariant nor computable. LoopVariant isn't the best wording for what it really means.
> Sure, makes sense to me.
[the only important remark] I suggest to use this check instead to fix the additional crash:
```
    // The AddRec must depend on one of the surrounding loops. Otherwise, mapSrcLoop and mapDstLoop return indices outside the intended range. This can happen when a subscript in one loop references an IV from a sibling loop that could not be replaced with a concrete exit value by getSCEVAtScope. 
    const Loop* L = LoopNest;
    while (L && AddRec->getLoop() != L) 
        L = L->getParentLoop();
    if (!L)
        return false;
```
Currently I am too unsure whether we can make computeLoopDisposition work reliably.

This ignores that there could be `SCEVAddRecExpr` in other SCEV subexpressions, but I think DA doesn't try to reach them anyway. Maybe add a comment about this somewhere.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D110973/new/

https://reviews.llvm.org/D110973



More information about the llvm-commits mailing list