Page MenuHomePhabricator

[mlir] Add std.tensor_to_memref op and teach the infra about it
ClosedPublic

Authored by silvas on Oct 14 2020, 6:39 PM.

Details

Summary

The opposite of tensor_to_memref is tensor_load.

  • Add some basic tensor_load/tensor_to_memref folding.
  • Add source/target materializations to BufferizeTypeConverter.
  • Add an example std bufferization pattern/pass that shows how the materialiations work together (more std bufferization patterns to come in subsequent commits).
    • In coming commits, I'll document how to write composable bufferization passes/patterns and update the other in-tree bufferization passes to match this convention. The populate* functions will of course continue to be exposed for power users.

The naming on tensor_load/tensor_to_memref and their pretty forms are
not very intuitive. I'm open to any suggestions here. One key
observation is that the memref type must always be the one specified in
the pretty form, since the tensor type can be inferred from the memref
type but not vice-versa.

With this, I've been able to replace all my custom bufferization type
converters in npcomp with BufferizeTypeConverter!

Part of the plan discussed in:
https://llvm.discourse.group/t/what-is-the-strategy-for-tensor-memref-conversion-bufferization/1938/17

Diff Detail

Event Timeline

silvas created this revision.Oct 14 2020, 6:39 PM
silvas requested review of this revision.Oct 14 2020, 6:39 PM
silvas updated this revision to Diff 298273.Oct 14 2020, 6:40 PM

update description

silvas edited the summary of this revision. (Show Details)Oct 14 2020, 6:41 PM
herhut accepted this revision.Oct 15 2020, 8:42 AM
herhut added inline comments.
mlir/include/mlir/Transforms/Bufferize.h
16

Should we make this distinction here? Why not just have all derive from BufferizeConversionPattern? I am not strongly opposed, just wondering what the motivation is.

18

nit: is sufficient

This revision is now accepted and ready to land.Oct 15 2020, 8:42 AM
jurahul added inline comments.Oct 15 2020, 11:21 AM
mlir/lib/Dialect/StandardOps/IR/Ops.cpp
3619

More of a question than a comment: this seems like a canonicalization rather than constant folding. Should it be expressed that way (as a canonicalization pattern?)

3630

Same comment as above: should this be a canonicalization?

mehdi_amini added inline comments.Oct 15 2020, 11:27 AM
mlir/lib/Dialect/StandardOps/IR/Ops.cpp
3619

Folding isn't about "constant folding", but when you can infer a value from the existing IR, i.e. the main difference between canonicalization and folding is that folding cannot create new IR.

jurahul added inline comments.Oct 15 2020, 11:36 AM
mlir/lib/Dialect/StandardOps/IR/Ops.cpp
3619

Make sense. Thanks!

This revision was automatically updated to reflect the committed changes.
silvas marked an inline comment as done.
silvas added inline comments.Oct 15 2020, 12:22 PM
mlir/include/mlir/Transforms/Bufferize.h
16

Good point! I will be writing more docs elaborating on this soon (I felt the need to put this advice in this patch since I started applying it).

One fundamental reason to avoid BufferizationTypeConverter is that many type conversions that are useful for bufferization also apply to any type. E.g. the patterns for select (with scalar predicate) or scf.if. These are often "structural" ops whose operands/results behave much as a block argument, where we structurally know that it is a pure passthrough from some other SSA value, so any type conversion is ok (e.g. index->i64). A recent example of this is https://reviews.llvm.org/D88083 in which we inadvertently specialized the shape.assuming type conversion to bufferization, when it need not be limited to that (and I will be refactoring that soon).

There are a couple more "stylistic" reasons as well:

  • a principle of least requirements -- if you are writing an STL algorithm that only use ++ on an iterator, it's overkill to require a RandomAccessIterator. There is both an cover-more-use-cases advantage and a clarity-for-reader advantage to know that the less-powerful ForwardIterator abstraction is sufficient for the algorithm. This is similar to how one would use a RewritePattern instead of a ConversionRewritePattern if the pattern does not need type conversions.
  • a (very?) small minority of bufferization conversions actually require BufferizeTypeConverter's extra methods. So using a regular ConversionPattern helps to avoid introducing new concepts for readers.

Actually, I have been considering making BufferizeTypeConverter not inherit from TypeConverter at all. I think it could be split into a free function populateBufferizeTypeConversions(TypeConverter &) + a new BufferizeConversionPolicy with all the extra methods. BufferizeConversionPattern would then change to hold a BufferizeConversionPolicy, and any type conversions would happen through the TypeConverter obtained from getTypeConverter().

herhut added inline comments.Oct 16 2020, 1:51 AM
mlir/include/mlir/Transforms/Bufferize.h
16

Thanks for clarifying. Splitting into general _type conversion_ and _bufferize_ patterns seems useful. I also like the idea of splitting the bufferization policy out form type conversion, as thinking about it they are separate things.