This is an archive of the discontinued LLVM Phabricator instance.

[mlir][scf] Canonicalize scf.for last tensor iteration result.
ClosedPublic

Authored by nicolasvasilache on Mar 4 2021, 7:52 AM.

Details

Summary

Canonicalize the iter_args of an scf::ForOp that involve a tensor_load and
for which only the last loop iteration is actually visible outside of the
loop. The canonicalization looks for a pattern such as:

%t0 = ... : tensor_type
%0 = scf.for ... iter_args(%bb0 : %t0) -> (tensor_type) {
  ...
  // %m is either tensor_to_memref(%bb00) or defined above the loop
  %m... : memref_type
  ... // uses of %m with potential inplace updates
  %new_tensor = tensor_load %m : memref_type
  ...
  scf.yield %new_tensor : tensor_type
}

%bb0 may have either 0 or 1 use. If it has 1 use it must be exactly a
%m = tensor_to_memref %bb0 op that feeds into the yielded tensor_load
op.

If no aliasing write of %new_tensor occurs between tensor_load and yield
then the value %0 visible outside of the loop is the last tensor_load
produced in the loop.

For now, we approximate the absence of aliasing by only supporting the case
when the tensor_load is the operation immediately preceding the yield.

The canonicalization rewrites the pattern as:

// %m is either a tensor_to_memref or defined above
%m... : memref_type
scf.for ... { // no iter_args
  ... // uses of %m with potential inplace updates
}
%0 = tensor_load %m : memref_type

This revision also exhibits the incorrect behavior of the folding:
tensor_load(tensor_to_memref(x)) -> x which ignores aliasing.

The test is altered to pass despite the erroneous folding, but should be revisited once the folding is hardened.

Diff Detail

Event Timeline

nicolasvasilache requested review of this revision.Mar 4 2021, 7:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 4 2021, 7:52 AM
ftynse requested changes to this revision.Mar 4 2021, 8:22 AM
ftynse added inline comments.
mlir/lib/Dialect/SCF/SCF.cpp
605–607

This should never happen, I'd assert instead.

641

Code motion is not undoable by "root update", only modifications to the op state are, you'll have to recreate the op at the new position.

647

Same

659–662

I'm confused by this comment. You want to replace a subset of forOp results with values produced by hoisted ops. I don't see how side-effects are relevant here. Furthermore, the documentation above and the test show that iter_args are removed, but I don't see the corresponding bbargs removed from the loop, it is just cloned. I expect something weird to happen if the bbargs that correspond to hoisted tensor updates are not trailing in the list.

This revision now requires changes to proceed.Mar 4 2021, 8:22 AM
nicolasvasilache marked 4 inline comments as done.Mar 4 2021, 11:27 AM
nicolasvasilache added inline comments.
mlir/lib/Dialect/SCF/SCF.cpp
641

thanks!

659–662

I'll rephrase but basically rewriter only has a replaceOp method that unconditionally deletes the op.
So I I replace forOp it just gets dropped, even it has side effects.

rriddle added inline comments.Mar 4 2021, 11:35 AM
mlir/lib/Dialect/SCF/SCF.cpp
659–662

Can't you use replaceOpWithIf for this? Or provide an easier entry point to that function for what you want to do here? It isn't supported in dialect conversion ATM, but should work for canonicalization patterns.

silvas added inline comments.Mar 4 2021, 12:12 PM
mlir/test/Dialect/SCF/canonicalize.mlir
374

is this needed after the recent patch you landed?

nicolasvasilache marked 4 inline comments as done.Mar 4 2021, 12:31 PM
nicolasvasilache added inline comments.
mlir/lib/Dialect/SCF/SCF.cpp
659–662

Actually, I can just do nothin and let other canonicalizations kick in when appropriate...

mlir/test/Dialect/SCF/canonicalize.mlir
374

I reverted it locally so when I update it'll go away.

nicolasvasilache marked 2 inline comments as done.

Address review.

rriddle added inline comments.Mar 4 2021, 12:40 PM
mlir/lib/Dialect/SCF/SCF.cpp
605–607

Why assert at all? Isn't this already verified by the op?

nicolasvasilache marked an inline comment as done.

Dropp redundant assert.

mlir/lib/Dialect/SCF/SCF.cpp
605–607

fair enough.

rriddle added inline comments.Mar 4 2021, 12:43 PM
mlir/lib/Dialect/SCF/SCF.cpp
600

This pattern looks like something that would be better handled by LICM+a simpler canonicalization that forwarded yield results that are from outside of the loop.

LGTM conceptually. Please wait for River/Alex LGTM for the rewrite mechanics since they already started looking at it.

mlir/lib/Dialect/SCF/SCF.cpp
584

it is a bit strange to talk about aliasing of a tensor. can you reword?

mlir/test/Dialect/SCF/canonicalize.mlir
344

add a test case where the safety check fires and the pattern fails to apply?

rriddle added inline comments.Mar 4 2021, 12:45 PM
mlir/lib/Dialect/SCF/SCF.cpp
600

Wait, nvm. Missed the iteration handling here.

nicolasvasilache marked an inline comment as done.Mar 4 2021, 12:49 PM
nicolasvasilache added inline comments.
mlir/lib/Dialect/SCF/SCF.cpp
659–662

Ugh no .. that is wrong, I am stupidly missing replacements .. sorry

rriddle added inline comments.Mar 4 2021, 12:51 PM
mlir/lib/Dialect/SCF/SCF.cpp
581

Where are you checking that it feeds into the tensor_load?

nicolasvasilache marked 3 inline comments as done.

Address review.

mlir/lib/Dialect/SCF/SCF.cpp
659–662

@rriddle ok replaceOpWithIf wins.

It would be great to find a naming scheme that conveys the fact that replaceOp replace unconditionally whereas replaceOpWithIf does not replace ever and it is the user's responsibility to eraseOp if allUsesReplaced is set to true (if they so wish).

I had completely overlooked the fact that not all replaceOpXXX do a blanket eraseOp.

ftynse accepted this revision.Mar 5 2021, 12:14 AM
ftynse added inline comments.
mlir/lib/Dialect/SCF/SCF.cpp
605–607

I've seen numerous asserts (and wrote some) on op state that are enforced by the verifier. I do this as part of "assert liberally" policy in the LLVM style guide, often in situations where I suspect the op may evolve and having an assertion will make it easier to find the places that need changing.

This revision is now accepted and ready to land.Mar 5 2021, 12:14 AM
mlir/lib/Dialect/SCF/SCF.cpp
605–607

I'm def. +1 on defensive asserts.

After considering your comments here I found it quite unlikely that scf::ForOp will evolve to multi-blocks in the future.
Given the current state of the codebase I'd prefer to see a new type of op.

I'd still lean towards defensive style in general, I can revive the assert if there is a stronger feeling, I don't have a strong preference here.

This revision was landed with ongoing or failed builds.Mar 5 2021, 1:53 AM
This revision was automatically updated to reflect the committed changes.
mehdi_amini added inline comments.Mar 7 2021, 10:14 AM
mlir/lib/Dialect/SCF/SCF.cpp
605–607

I'm +1 on assert as well: these are both debugging and documentation help.

It duplicates the verifier sometimes, but that should be OK: if it wasn't in the verifier then it couldn't be an assert in the first place :)