This is an archive of the discontinued LLVM Phabricator instance.

[mlir][transform] Add support for expressing scalable tile sizes
ClosedPublic

Authored by awarzynski on May 19 2023, 1:44 AM.

Details

Summary

This patch enables specifying scalable tile sizes when using the
Transform dialect to drive tiling, e.g.:

%1, %loop = transform.structured.tile %0 [[4]]

This is implemented by extending the TileOp with a dedicated attribute
for "scalability" and by updating various parsing hooks.

This change is a part of larger effort to enable scalable vectorisation
in Linalg. See this RFC for more context:

Diff Detail

Event Timeline

awarzynski created this revision.May 19 2023, 1:44 AM
Herald added a project: Restricted Project. · View Herald Transcript
awarzynski requested review of this revision.May 19 2023, 1:44 AM

I'm new to most of this code, so wanted to send this for early feedback to check whether I'm heading in the right direction.

I do realise that I am yet to test/cater for the following 2 cases (as discussed in the RFC):

  • using SSA values for tile sizes,
  • multi-dim tiling (e.g. [4, [8]]

The former should just work, the latter will require tweaking the parser.

Thank you for taking a look!

ftynse requested changes to this revision.May 19 2023, 4:16 AM

The extension itself is fine, but I have a suggestion on how to rework the op parser to avoid this change being intrusive into core IR parser and other dialects.

mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td
1532

Do we assume that the scalable dimension is always the last? If not, I'd rather model this as an optional integer attribute that indicates the position of the scalable dimension (and is absent when no dimensions are scalable).

mlir/include/mlir/IR/OpImplementation.h
701 ↗(On Diff #523693)

I don't think we want to change the core IR parser to support what feels like a special case. It looks possible to craft a parseElementFn that will do what is needed here along the following lines:

bool isScalable= false;
if (parseOptionalLBracket().succeeded())
  isScalable = true;
// do the usual parsing with optional SSA value and integer
if (isScalable && parseRBracket().failed())
  return failure();
mlir/lib/AsmParser/Parser.cpp
122 ↗(On Diff #523693)

This function is not limited to sizes, it's an arbitrary comma-separated list that may be applied to SSA values, value/type pairs, etc.

This revision now requires changes to proceed.May 19 2023, 4:16 AM
Matt added a subscriber: Matt.May 19 2023, 8:25 AM

Thank you for taking a look, @ftynse !

mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td
1532

Do we assume that the scalable dimension is always the last?

Good question. Looking at scalable vectors in MLIR, it would always be the trailing dim:

he scalable dimensions in a vector are indicated between square brackets ([ ]), and all fixed-length dimensions, if present, must precede the set of scalable dimensions. That is, a vector<2x[4]xf32> is valid, but vector<[4]x2xf32> is not.

However, it doesn't have to be only 1 dim. So, I'd limit this to the trailing dimensions. Personally I'd focus on 1 dim to begin with, but don't mind much.

mlir/include/mlir/IR/OpImplementation.h
701 ↗(On Diff #523693)

I don't think we want to change the core IR parser to support what feels like a special case.

Good point, but we still need a way to communicate "scalability" all the way to TileOp::parse. Updating parseElementFnalone won't be enough. We'll still need to tweak parseDynamicIndexList, but I guess that would already be less intrusive.

Let me demonstrate first and then we can discuss whether that's OK or whether to refine more.

Revert changes in parseCommaSeparatedList

ftynse added inline comments.May 22 2023, 2:47 AM
mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td
1532

Ok. I would suggest renaming the attribute to something like last_dim_scalable for clarity.

mlir/include/mlir/Interfaces/ViewLikeInterface.h
71–82

Please update the documentation to reflect the new argument.

mlir/test/Dialect/Linalg/transform-op-tile.mlir
170–182

How much of this do we actually need to test the new functionality? The test looks like the entire IR with SSA values changed to captures. We prefer more minimalist tests to avoid them being spuriously broken by, e.g., changes in the tensor.dim pretty-printer syntax.

184

Don't hardcode generated alias names such as #map1, capture them instead of necessary.

191

Ditto.

205–207

Let's not use !pdl.operation anymore, prefer !transform.any_op or a more specific type.

208

It would be nice to also have a test with at least one fixed-length dimension and a scalable dimension.

tblah added a subscriber: tblah.May 30 2023, 3:55 AM

Addressing comments from Alex (thank you!)

  • Renamed the attribute (scalable --> last_tile_size_scalable)
  • Trimmed the test and added another case with a fixed-length tile size
  • Added documentation
  • Refined transform::TileOp::apply to make sure that only the trailing dim is made scalable
awarzynski marked 6 inline comments as done.May 30 2023, 5:28 AM
awarzynski added inline comments.
mlir/test/Dialect/Linalg/transform-op-tile.mlir
170–182

Good point, this has now been trimmed.

ftynse accepted this revision.May 30 2023, 7:28 AM

One small change in tests is needed, otherwise good to go.

mlir/test/Dialect/Linalg/transform-op-tile.mlir
178

This (and below) still hardcodes #map, #map2`, etc. These alias names are similar to SSA value names and have no guarantee of being preserved. You can use -mlir-print-local-scope to print maps inline or capture them with more CHECKs.

This revision is now accepted and ready to land.May 30 2023, 7:28 AM
c-rhodes added inline comments.May 30 2023, 8:57 AM
mlir/include/mlir/Interfaces/ViewLikeInterface.h
78–79

nit: scalable indices are not allowed.

mlir/lib/Interfaces/ViewLikeInterface.cpp
128–174

should this check it's the last index that's scalable? It will currently parse:

transform.sequence failures(propagate) {
^bb0(%arg1: !transform.any_op):
  %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op
  %1, %loops:3 = transform.structured.tile %0 [4, [4], [4]] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op)
}

or

transform.sequence failures(propagate) {
^bb0(%arg1: !transform.any_op):
  %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op
  %1, %loops:3 = transform.structured.tile %0 [[4], [4], [4]] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op)
}

are there any negative tests for the parser for things like this?

awarzynski added inline comments.May 31 2023, 1:45 AM
mlir/include/mlir/Interfaces/ViewLikeInterface.h
78–79

Thank you :)

mlir/lib/Interfaces/ViewLikeInterface.cpp
128–174

Thanks, good catch! I will be sending an update shortly that addresses both your comments.

mlir/test/Dialect/Linalg/transform-op-tile.mlir
178

Argh, sorry about that! I wasn't aware of -mlir-print-local-scope and it looks like a perfect solution, thank you!

I will ignore #map2 below as that's far less relevant to "scalability" than this one (i.e. I'll replace it with a generic regex).

Address the remaining comments

  • Make sure that only the trailing tile size can be scalable (and add a negative test that verifies this)
  • Refine the tests (remove references to #map)
c-rhodes accepted this revision.May 31 2023, 2:38 AM

One minor nit but otherwise LGTM, nice work!

mlir/test/Dialect/Linalg/transform-op-tile.mlir
226

nit: for specifying