This is an archive of the discontinued LLVM Phabricator instance.

[mlir][linalg] Remove unused payload related OutOpOperand
ClosedPublic

Authored by raikonenfnu on Oct 6 2022, 12:45 PM.

Details

Summary

Some higher level operations such as torch.max generates linalg generic
that returns both the index and the value of the max operation. However
sometimes not all information is being used. This however blocks
vectorization for certain cases which causes performance degradation.
This patch aims to fix this issue.

Diff Detail

Event Timeline

raikonenfnu created this revision.Oct 6 2022, 12:45 PM
raikonenfnu requested review of this revision.Oct 6 2022, 12:45 PM

Added missing check

mravishankar requested changes to this revision.Oct 6 2022, 1:22 PM

Thanks Stanley! Have a few refactoring requests to make the code more understandable.

mravishankar added inline comments.Oct 6 2022, 1:22 PM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1035

No related to this patch, but just to make it more readable since the method is getting too big now. Can you change this to

if (!generic.hasTensorSemantics()) {
  ....
  return dedupedOutputs;
}
/// body of the else goes here....
1061–1063

To make the logic easier to follow do you mind moving lines 1006 - 1017 and 1030 - 1074 into a helper function that just uses the outputOpOperand.

So something like

bool isOutputOpOperandDead(linalg::GenericOp genericOp, OpOperand &outOperand, OpResult result) {
  if (!result.use_empty())
    return false;
  if(!genericOp.payloadUsesValueFromOperand(outputOpOperand))
    return true;
  // Logic for cycle added in this change.
}

... deduplicateOutputOperands ... {
 ...
 for (const auto &outputOperand : ...) {
   ....
   if (isOutputOpOperandDead(...)) {
     droppedOpOperands.push_back(...)
     if (genericOp.canOpOperandsBeDropped(droppedOpOperands))
        continue;
     droppedOpOperands.pop_back();
   }

   // Lines 1024 - 1029....
}

If this wasnt easy to follow, feel free to punt to me.

This revision now requires changes to proceed.Oct 6 2022, 1:22 PM

Addressed refactoring comments.

Test to be added

Thanks Stanley! Have a few refactoring requests to make the code more understandable.

Thanks for the refactoring comments and suggestions Mahesh! :)

mravishankar requested changes to this revision.Oct 6 2022, 3:54 PM

THanks for refactoring. More comments. Marking as requesting change till tests are added.

mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
876

Nit: I think you can use genericOp.getBody().

878

This can be an assert I think

882

Please use getRegionOutputArgs (https://github.com/llvm/llvm-project/blob/3b20765cf725e57d0f99c3d292fe4891b0ee801d/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.td#L364) and get outArg from there.

Even better, just take the BlockArgument as the argument instead of OpIndex.

This revision now requires changes to proceed.Oct 6 2022, 3:54 PM

Rebased on later llvm

Addressed comments/cleaned up with block args

Exclude dynamic case which was unsupported to begin with and is unlikely to be outs anyways.

mravishankar requested changes to this revision.Oct 7 2022, 9:01 AM
mravishankar added inline comments.
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1109

I don't think we need to check this. The dynamic case is not unsupported. It is expected to be supported. Could you provide more context here?

This revision now requires changes to proceed.Oct 7 2022, 9:01 AM

@mravishankar It was breaking one of IREE's e2e test https://github.com/iree-org/iree/blob/main/tests/e2e/regression/dynamic_torch_index_select_negative.mlir. It tried creating a placeholder/dummy tensor for a dynamic tensor and it broke.

ThomasRaoux added inline comments.
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
885–895

the operand index of the yield has to match block argument index otherwise it is wrong.

it could be something like:
^bb(%arg0, %arg1)

yield %arg1, %arg0

raikonenfnu added inline comments.Oct 7 2022, 10:33 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
885–895

shouldn't this not matter for this part of the code since, this part is only checking for uses?

ThomasRaoux added inline comments.Oct 7 2022, 11:42 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
885–895

it's mean to check if the cycle is dead right? Doesn't this affect it?

Addressed indexing mismatch issue and separated unused cycle as it's own pattern.

Minor comment change.

Added some test.

raikonenfnu marked an inline comment as not done.Oct 7 2022, 8:26 PM
raikonenfnu added inline comments.
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
885–895

Understood, Mahesh also helped clarify this, thanks for this suggestion :)

Updated to handle genericOp with in place updates.
Look at tile-parallel-reduce.mlir

minor comment change

Add more indexing check

Added test where some cycles/bout are used by others outArgs.

minor nit on test comments.

nit clean up

Mostly looks good. Have a few nits. Should be ready to land after that!

mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
861

You can drop BlockArgument & for BlockArgument .

Could we make the ABI a little simpler (we want to avoid the implicit assumption that outputs come after inputs in linalg ops). So make this

bool isResultValueDead(linalg::GenericOp genericOp, OpResult result).

Then

outputOpOperand = genericOp.getOutputOperands(result.getResultNumber());
outputArg = genericOp.getRegionOutputArgs(result.getResultNumber());

Also below to check the usage in the yield you can just do

if (yieldOp.getOperand(result.getResultNumber()) != outputArg) return false;
880

You can drop this check. You are checking below argUserOp is a linalg::YieldOp. That has no uses by construction.

885

Replace these lines with

auto yieldOp = dyn_cast<linalg::YieldOp>(argUserOp);
if (!yieldOp) return false;

Then see above for changing the condition check.

1111

Do we still need to do this?

1232

I think you can replace this with

if (!genericOp.hasTensorSemantics())
  return failure()

Also note, LLVM style is to not have braces around bodies that are a single statement (unlike IREE for example)

ThomasRaoux added inline comments.Oct 10 2022, 9:52 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
860

this should be a static function

1269

What is it meant to do? I don't think it does anything?

Clean up and addressed comments.

raikonenfnu added inline comments.Oct 10 2022, 10:35 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1111

we still need it since before dropping the yield it does rewriter.mergeBlocks which will would cause segfault otherwise

1269

I think it's to in

1269

Don't we need the return success to indicate matchAndRewrite is successful?

ThomasRaoux added inline comments.Oct 10 2022, 10:37 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1269

looks like my comment shifted line after you latest update. My comment was about this line:
rewriter.updateRootInPlace(genericOp, [] {});

Addressed Thomas comments and suggetions

mravishankar added inline comments.Oct 10 2022, 10:59 AM
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1268

Instead of returning from within the loop, can you just check if a modification was made or not in the loop and return success or failure after the loop

Moved return out of the loop, brought back updateRootInPlace, and moved yieldOp to before mergeblocks s.t we don't need to replace blockarguments

minor style change

mravishankar accepted this revision.Oct 10 2022, 11:19 AM

Thanks!

mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
1273

Nit: return success(hasRemovedCycles);

This revision is now accepted and ready to land.Oct 10 2022, 11:19 AM

Looks like this broke the windows mlir buildbot: https://lab.llvm.org/buildbot/#/builders/13/builds/26882

Hey @stella.stamenova, thanks for the notifications, I am pushing up a fix here https://reviews.llvm.org/D135612 :)

mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
860

done, thanks!

861

done!