This is an archive of the discontinued LLVM Phabricator instance.

[mlir][linalg] Enable propagation of pack/unpack ops through non-elementwise
ClosedPublic

Authored by qedawkins on Apr 4 2023, 12:46 AM.

Details

Summary

Allows pack propagation through non-elementwise generics as long as all
tiled dimensions have parallel iterator types and are only indexed with
affine dim expressions by any of the operands.

This enables unpack propagation cases where the result type is different
from the current unpack destination tensor and thus motivates a similar
helper as the for pack for creating a destination tensor based on
pack information.

Outer dim permutations are allowed to permute reduction dims, however
remains unsupported for non-affine dim indexing map results.
Additionally ops with gather semantics now explicitly prohibit propagation.

Pack/unpack propagation through reductions may not always be beneficial
so user control over propagation decisions is made available through
a control function similar to the one for fusion.

Diff Detail

Event Timeline

qedawkins created this revision.Apr 4 2023, 12:46 AM
qedawkins requested review of this revision.Apr 4 2023, 12:46 AM

I can split this up into smaller revisions if that helps for review (?)

  1. User control over propagation
  2. Unpack op change
  3. Propagation pattern changes

I didn't see any immediate use for the first two pieces without the propagation changes though hence why I have it in one.

Thank you. I left a couple of comments; I will have a more in-depth look tomorrow.

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

Why do we need a control function? It seems the one we have for testing always returns true.

mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
58–60

At this point I would pass the OpOperand and the genericOp. We can extract all the information we need within this method.

59

why SmallVector and not ArrayRef?

103

getResultPosition extract only the first position only. I think we need to scan all the results.

285

Why do we need to check outerDimsPerm. Checking innerDimPos for emptiness should be sufficient.

mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
3783

nit, remove commented lines.

3792

nit: add braces, as it spans multiple lines.

Thanks for the review! Addressing comments now.

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

The thinking is that once you allow pack propagation on reductions it becomes harder to reason about whether you actually want to pack the generic in all cases. The example I'm thinking of is drawing a distinction between data tiling and transposing where packing with a small tile might always be worth it, but transposing a large parallel dimension behind a reduction might not always be worth it.

That's my experience coming from IREE at least but I expect the user control here to be useful for all downstream users. Also it seems like a sensible way to enable padding propagation later on down the road.

I left the testing control function as always true because the tests should be verifying the propagation patterns but I can add a contrived filtered case as a test for the control function. Also it might be worth adding a default control function but I wasn't sure what it would be.

mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
285

Sorry I forgot about this but this needs a test (and can be a separate patch if needed). I found that if you specify an unpack with empty innerDimPos but non-empty outerDimsPerm it will spin forever due to not inserting a pack between the unpack and the generic (because it keeps pushing down the unpack).

hanchung requested changes to this revision.Apr 5 2023, 1:53 PM
hanchung added inline comments.
mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
58–60

+1, otherwise we should document indexingMap, indexingMaps; have assumption that they are all from the same generic op?

59

+1, they should be ArrayRef because we won't modify them. If we want to modify them, they can be SmallVectorImpl<...> &.

138

I think we can move the declaration of permutedDomainDims to here. We won't have to insert the element to both SmallVector and DenseSet. It's cleaner to me.

llvm::DenseSet<int64_t> permutedDomainDims(permutedOuterDims);
// or maybe llvm::DenseSet<int64_t> permutedDomainDims(permutedOuterDims. begin(), permutedOuterDims.end());
582–584

The TiedOpResult must have as same type as InitOperand by definition. We can cast the type of genericOp.getDpsInitOperand(0) here.

611–613

The check is part of pushDownUnPackOpThroughGenericOp. We don't have check it twice.

mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
3780–3782

I think we can declare a RankedTensorType srcType = .... Then we can use it to check dynamic size (i.e.,srcType.isDynamicDim) at l.3782 and src.getDimSize(i) at l.3785.

This revision now requires changes to proceed.Apr 5 2023, 1:53 PM
qedawkins updated this revision to Diff 511219.Apr 5 2023, 3:11 PM
  1. Get packing directly from generic
  2. Scan all map results
  3. Build domain dim set from list

+ nits

qedawkins updated this revision to Diff 511222.Apr 5 2023, 3:14 PM
qedawkins marked 3 inline comments as done.

remove unused var

qedawkins marked 6 inline comments as done.Apr 5 2023, 3:23 PM
qedawkins added inline comments.
mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
59

The helpers for getting the iterator types/indexing maps return them as SmallVectors and for whatever reason if I try to convert them to ArrayRefs, it ends up returning an array with garbage inside. If this is a problem I can figure out why.

qedawkins updated this revision to Diff 511231.Apr 5 2023, 4:34 PM

Add test for empty inner dims unpack propagation

qedawkins added inline comments.Apr 5 2023, 4:37 PM
mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
285

See the newly added test below (unpack_empty_inner_dims) which will hang without this check.

hanchung accepted this revision.Apr 10 2023, 3:00 PM

overall looks good to me, please also wait for @chelini 's review. Thanks!

mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
99

hmm, do we need a builder? It seems unused?

100–107

minor nit: based on the comment, I'd expect the tiledDimPos iteration be the outer most loop.

We can declare a lambda for the rest of logic, like auto areAllAffineDim = [&](int dim) { ... } and use it in the iteration. That would give us better readability, IMO, and save us some nested indents. Something like below

auto areAllAffineDimExpr = [&](int dim) {
  for (AffineMap map : indexing Maps) {
    if (llvm::any_of(map.getResults(), ...) return false;
  }
  return true;
};
for (int64_t i : packInfo.tiledDimsPos) 
  if (!areAllAffineDimExpr(i))
    return failure();
mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
3793–3797

nit, we can use structured binding and llvm::zip_equal here. I.e.,

for (auto [dimPos, tileSize] = llvm::zip_equal(...))
  mixedSizes ...
3799

nit: this can be srcType.getElementType()

This revision is now accepted and ready to land.Apr 10 2023, 3:00 PM
qedawkins added inline comments.Apr 10 2023, 3:08 PM
mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
99

oh, looks like it was leftover from when I used it to get affine expressions. I guess the compiler doesn't see this as unused...

qedawkins updated this revision to Diff 512326.Apr 10 2023, 8:44 PM

Address comments/cleanup

qedawkins marked 3 inline comments as done.Apr 10 2023, 8:46 PM
chelini accepted this revision.Apr 11 2023, 4:45 AM

Thank you!

mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
130

nit: I would write: permutedOuterDims.push_back(dimExpr.getPosition());. Looks we don't have other uses for dimPos.

qedawkins updated this revision to Diff 512431.Apr 11 2023, 6:48 AM

fold dimPos