This is an archive of the discontinued LLVM Phabricator instance.

[mlir] Add RewriterBase::replaceAllUsesWith for Blocks.
ClosedPublic

Authored by ingomueller-net on Jan 25 2023, 12:56 AM.

Details

Summary

When changing IR in a RewriterPattern, all changes must go through the
rewriter. There are several convenience functions in RewriterBase that
help with high-level modifications, such as replaceAllUsesWith for
Values, but there is currently none to do the same task for Blocks.

Diff Detail

Event Timeline

ingomueller-net requested review of this revision.Jan 25 2023, 12:56 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 25 2023, 12:56 AM

Please review carefully; this is just copy'n'pasting from similar places and I am not sure whether this actually does the notification that is required.

Also, the template may be over-engineering. I have seen IRObjectWithUseList only being inherited from Block and Value, so I might just add this:

cpp
void replaceAllUsesWith(Block *from, Block *to) {
  for (BlockOperand &operand : llvm::make_early_inc_range(from->getUses())) {
    Operation *op = operand.getOwner();
    updateRootInPlace(op, [&]() { operand.set(to); });
  }
}
`

Finally, note that this code has no tests yet. I have not found similar code being tested other than indirectly through more high-level tests and I have not found any place that could use the new function now. Suggestions welcome.

Ping. (Also added more reviewers.)

Can you replace the original Value one with the new method? Missing testing right now.

@rriddle: Thanks a lot for the suggestions!

The new revision implements replaceAllUsesWith(Value, Value) in terms of the new template. I think I can't just replace it with the templated function because Value doesn't itself inherit from IRObjectWithUseList (instead, it contains a ValueImpl object which does), so existing usages of the function would break.

The new revision also implements a test via -test-strict-pattern-driver. (Thanks @springerm for the brainstorming!) I guess this way of testing is somewhat contreived but I did not find examples of similar code being tested. If somebody has a better idea, please let me know.

Rebasing to latest main...

I added my comment about modification on replaceAllUsesWith.

I don't have much experience with replacing block.

mlir/include/mlir/IR/PatternMatch.h
509

Why did we move this function here?

511

The template is not needed unless I am missing something. It will increase compilation time here.
All from are Value anyway and they all implement getUses(), and operand.set takes a Value.

Thanks for the comment @guraypp! I hope my answers inline are helpful?

mlir/include/mlir/IR/PatternMatch.h
509

Both Block and ValueImpl inherit from IRObjectWithUseList, so if we implement the logic for the latter, we don't have to do it again for the two subclasses. For Value, we need an overload that extracts the ValueImpl and calls the function on that.

511

IRObjectWithUseList is also subclassed by Block, and adding that is the whole point of this patch.

operand is a IROperand, which is a template and the parameter to set is of template type IRValueT. IRObjectWithUseList needs to specify the operand type; Block this is BlockOperand, for ValueImpl it is OpOperand, which are two instances of IROperand. The set function thus accepts a Value if it is called for the ValueImpl instance of replaceAllUsesWith and a Block if it is called for the Block instance.

Thanks for clarification. I am not familiar this part of MLIR, approving this change is not my call.

Just out of curiosity - when you replace two Blocks, does it change the tree and dominance information?

mlir/include/mlir/IR/PatternMatch.h
511

Ah I see, you are calling this function in your pass. There is still an extra function generation due to the use of template. I have slight preference for explicit implementation, but it's up to you.

ingomueller-net marked 2 inline comments as done.Feb 13 2023, 7:42 AM

Just out of curiosity - when you replace two Blocks, does it change the tree and dominance information?

I have no idea, TBH! I haven't worked with tree/dominance information yet...

mlir/include/mlir/IR/PatternMatch.h
511

I have thought about that possibility and mentioned it in my initial comment. I don't mind either way but River suggested the template solution (possibly assuming that the Value overload would disappear) so I went with that...

LG, but I'm unsure I totally understand the test setup.

mlir/test/Transforms/test-strict-pattern-driver.mlir
84

Why is this op not on the worklist initially?

LG, but I'm unsure I totally understand the test setup.

Thanks! See my reply inline and let me know if you have suggestions/requests.

mlir/test/Transforms/test-strict-pattern-driver.mlir
84

That's an important point and the fact that it isn't clear indicates that improvements/changes to the test might be required.

That op isn't on the worklist initially, but it gets put into the list when change_block_op modifies its usage of bb1 through replaceAllUsesWith. That call notifies the rewriter about the change of bb1, which then puts all users of it into its worklist. By observing that implicit_change_op is rewritten in the final output, we hence see that replaceAllUsesWith has signaled the updated correctly.

The large comment block below attempts to explain this mechanism. Please let me know if you think it needs clarification.

Also, it is not too late to come up with a better/simpler/clearer way of testing this ;)

mehdi_amini accepted this revision.Feb 14 2023, 9:58 AM
mehdi_amini added inline comments.
mlir/test/Transforms/test-strict-pattern-driver.mlir
84

I understood we wanted to see if it gets back on the worklist, I just didn't look carefully enough at how the initial worklist was built:

getOperation()->walk([&](Operation *op) {
  StringRef opName = op->getName().getStringRef();
  if (opName == "test.insert_same_op" || opName == "test.change_block_op" ||
      opName == "test.replace_with_new_op" || opName == "test.erase_op") {
    ops.push_back(op);
  }
});

It's all fine :)

This revision is now accepted and ready to land.Feb 14 2023, 9:58 AM
ingomueller-net accepted this revision.Feb 14 2023, 11:22 PM
ingomueller-net marked 3 inline comments as done.