This is an archive of the discontinued LLVM Phabricator instance.

[mlir] Introduce AffineMinSCF folding as a pattern
ClosedPublic

Authored by nicolasvasilache on Jun 17 2020, 5:49 AM.

Details

Summary

This revision adds a folding pattern to replace affine.min ops by the actual min value, when it can be determined statically from the strides and bounds of enclosing scf loop .

This matches the type of expressions that Linalg produces during tiling and simplifies boundary checks. For now Linalg depends both on Affine and SCF but they do not depend on each other, so the pattern is added there.
In the future this will move to a more appropriate place when it is determined.

The canonicalization of AffineMinOp operations in the context of enclosing scf.for and scf.parallel proceeds by:

  1. building an affine map where uses of the induction variable of a loop are replaced by %lb + %step * floordiv(%iv - %lb, %step) expressions.
  2. checking if any of the results of this affine map divides all the other results (in which case it is also guaranteed to be the min).
  3. replacing the AffineMinOp by the result of (2).

The algorithm is functional in simple parametric tiling cases by using semi-affine maps. However simplifications of such semi-affine maps are not yet available and the canonicalization does not succeed yet.

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald Transcript
rriddle added inline comments.Jun 17 2020, 10:12 AM
mlir/include/mlir/IR/AffineExpr.h
18

I don't think this is necessary.

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
357

nit: Please move the lambda out of the if, it is making it difficult to see the control flow structure.

mlir/test/lib/Transforms/TestLinalgTransforms.cpp
309

Please run clang-format on this.

bondhugula requested changes to this revision.Jun 17 2020, 10:43 AM
bondhugula added a subscriber: bondhugula.

Can you rewrite the summary a bit? 'canonicalize' is a general term - you are rather folding/simplifying; replacing canonicalize/canonicalization will greatly clarify comments / commit summary. Condition (1) isn't fully spelt out - just replacing IVs would be something totally different. The choice for (2) (divides the results as opposed to min of the results isn't clear either - it is for catching a simple subset?).

Based on the commit summary, this is all about folding it to one of the results. Then, implementing it in the fold hook on AffineMinOp would be the right thing. You'll be able to do faster/more simplification via the rewriter.

mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h
506

Nit: by the result of (2).

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
240–242

Could you rephrase this? As written, it doesn't parse right. It's not scf.for %iv = %lb to %ub step %step that's being substituted!

mlir/test/Dialect/Linalg/fold-affine-min-scf.mlir
33–34

Can you add a TODO or better file a feature request?

34

It's a 2-d map. How could it evaluate to 0? Did you just mean for the first result dimension?

106

Likewise.

This revision now requires changes to proceed.Jun 17 2020, 10:43 AM

Looks like this pattern could just work on LoopLikeOpInterface?

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
268–271

Looks like this whole pattern can just be registered on LoopLikeOpInterface? - by just making the interface return lb + step * iv.floorDiv(step) expressions with the necessary operands (dims + symbols). If it can't, because the lower bound is a max, etc. for a AffineForOp, just fail to match.

270

I'm missing something - did you mean lb + step * (iv - lb).floorDiv(step)?

nicolasvasilache marked 13 inline comments as done.

Address review comments.

Changed the commit message.

Re implementing it in the fold hook on AffineMinOp would be the right thing. You'll be able to do faster/more simplification via the rewriter.,
I actually need to control it programmatically for now and add it as part of: getLinalgTilingCanonicalizationPatterns.
I do not see how to get folding hooks exposed programmatically at this point: they only activate as part of a call to the canonicalization pass.

Lastly, for the day this moves into Affine, it would make Affine depend on SCF. Are you ok with that ?

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
268–271

I wouldn't think so based on today's needs: the part about deciding between constant, symbol and dimension and their position is quite specific to this particular rewrite.
At the very best I could see a refactoring if we ever see a second user for this functionality with similar specifics.
Tentatively marking as solved.

270

Very true, thank you for catching!

mlir/test/Dialect/Linalg/fold-affine-min-scf.mlir
33–34

https://bugs.llvm.org/show_bug.cgi?id=46367 for the missing canonicalization.

nicolasvasilache retitled this revision from [mlir] Introduce AffineMinSCF canonicalization pattern to [mlir] Introduce AffineMinSCF folding as a pattern.Jun 17 2020, 1:28 PM
nicolasvasilache edited the summary of this revision. (Show Details)
nicolasvasilache edited the summary of this revision. (Show Details)
nicolasvasilache edited the summary of this revision. (Show Details)
ftynse added inline comments.Jun 18 2020, 5:06 AM
mlir/include/mlir/IR/AffineExpr.h
121

I don't understand what "sparse" means in this context. Does this function replace any occurrence of the expr subtree within this with replacement?

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
240

Nit: corresponds

268–271

Looks like this whole pattern can just be registered on LoopLikeOpInterface?

We don't (yet) have patterns that can work on interfaces. That being said, I had a similar thought -- this transformation is useful for any kind of loops. There is another pending patch that could benefit from if lb/ub/step are constants. That being said, this refactoring may be out of scope for this patch.

275

Nit: "instead in place", chose one

334

llvm::to_vector<4>(minOp.getAffineMap().getResults()) ?

368

Nit: please use symmetric braces

mlir/test/Dialect/Linalg/fold-affine-min-scf.mlir
13

I'm puzzled. This seems to check for a store of an _index_ constant into a memref<i64>, since it explicitly checks for the absence of index_cast. This looks incorrect.

nicolasvasilache marked an inline comment as done.Jun 18 2020, 5:22 AM
nicolasvasilache added inline comments.
mlir/test/Dialect/Linalg/fold-affine-min-scf.mlir
13

nice catch !

The issue is that the test pattern application is done via greedily applying the pattern which triggers foldings.
There is also a constant 2: i64 that is the one being used and it happens that its SSA name is a prefix of the one for constant 2: index.
The CHECK only matches that prefix.

Rewriting to ensure the folding does not happen which will keep the index_cast.

nicolasvasilache marked 8 inline comments as done.Jun 18 2020, 6:40 AM
nicolasvasilache added inline comments.
mlir/include/mlir/IR/AffineExpr.h
121

Yes, added more doc to clarify.

nicolasvasilache marked an inline comment as done.

Address review.

ftynse accepted this revision.Jun 18 2020, 6:41 AM

@bondhugula I believe I addressed your comments?

bondhugula added a comment.EditedJun 18 2020, 7:59 AM

Changed the commit message.

Re implementing it in the fold hook on AffineMinOp would be the right thing. You'll be able to do faster/more simplification via the rewriter.,
I actually need to control it programmatically for now and add it as part of: getLinalgTilingCanonicalizationPatterns.
I do not see how to get folding hooks exposed programmatically at this point: they only activate as part of a call to the canonicalization pass.

Oh that's straightforward :) The folding pattern will automatically get called when you run applyPatternsAndFoldGreedily, which you are anyway calling. There wouldn't be anything to add to patterns for it. The only thing is that: with it being in the folder, there wouldn't be a way to prevent it from being run currently.

Changed the commit message.

Lastly, for the day this moves into Affine, it would make Affine depend on SCF. Are you ok with that ?

I didn't understand this part. What moves into affine? Affine already depends on LoopLikeOpInterface, and so when this pattern is moved to LoopLikeOpInterface, it should be usable with any more deps.

@bondhugula I believe I addressed your comments?

Just added a few more comments on the folding part. But do finally rely on @ftynse's approval for going ahead with the commit.

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
270

Is this covered via test cases now?

Upon deeper testing, some incorrect assumptions were made when using modulos.
Switch to an implementation in which we explicitly build out the min expression.

ftynse accepted this revision.Aug 7 2020, 8:59 AM

Please add a test where the constant is not the first dimension in the map.

mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
249

Nit: return positivePath ? min : max and drop the outer braces?

278

Typo: matchine

279

I don't see lb, ub and step in the argument list. Did you mean lbVal and co? In this case, how do you store AffineDimExpr into a Value?

339

Consider bailing out early if exprs.empty() or documenting the assumption

368

Can substitute just return the resulting map? It deconstructs the map into the vector-of-expressions, and here the map is reconstructed back...

394

Shouldn't this be continue? Otherwise you stop after the first expression.

402

If the map is known to be symbolic (the check at the entry of the loop), it shouldn't have any dims, so we can just spare them here. I don't know if further simplification is posisble.

mlir/test/Dialect/Linalg/fold-affine-min-scf.mlir
60

I think you actually want to check for affine.min, index_cast will always be there

76

Idem

nicolasvasilache marked 10 inline comments as done.Aug 7 2020, 11:27 AM
nicolasvasilache added inline comments.
mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
394

Thanks for catching, added a new test.

nicolasvasilache marked an inline comment as done.

Address review comments.

This revision was not accepted when it landed; it landed in state Needs Review.Aug 7 2020, 11:33 AM
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.