This is an archive of the discontinued LLVM Phabricator instance.

[MLIR] Add option to print users of an operation as comment in the printer
ClosedPublic

Authored by cpillmayer on Apr 19 2022, 3:28 PM.

Details

Summary

This allows printing the users of an operation as proposed in the git issue #53286.
To be able to refer to operations with no result, these operations are assigned an
ID in SSANameState.

Diff Detail

Event Timeline

cpillmayer created this revision.Apr 19 2022, 3:28 PM
Herald added a project: Restricted Project. · View Herald Transcript
cpillmayer requested review of this revision.Apr 19 2022, 3:28 PM

Can you add tests to cover all this? I'd to see how it looks in practice!

Also please include a test with a user operation that customize its result names? An op like %q, %q_1, %q_2, %r = test.string_attr_pretty_name attributes {names = ["q", "q", "q", "r"]} (but that also accepts operands, you need to create one or modify this one to add an optional argument).

mlir/lib/IR/AsmPrinter.cpp
2720

Why wouldn't you print for an operation with operands and regions?

2725

Do you think explicit is better?

2728

What about grouping per results? If an op is like %a, %b = my.op, I could see useful to track it this way: %a, %b = my.op // users: (%42, %56), (%73, %45)

2729

Nit: Operation *
(we reserve auto to case when the type is obvious of the context, or it is more readable than spelling the type)

2732

You can check the return of the insert and save a lookup instead of calling contains separately

2746

(same as above)

This comment was removed by mehdi_amini.

Actually I patched your file to try it:

module {
  func @pretty_names(%arg0: i64) {
    // %arg0 // user: %q
    %c0_i64 = arith.constant 0 : i64 // user: %q
    %q, %q_0, %q_1, %r = "test.custom_result_name"(%c0_i64, %arg0) {names = ["q", "q", "q", "r"]} : (i64, i64) -> (i32, i32, i32, i32)
    return
  }
}

I think the block argument handling isn't ideal right now?

mehdi_amini added a comment.EditedApr 19 2022, 5:57 PM

Slightly more elaborated:

$ ./bin/mlir-opt /tmp/test.mlir -mlir-print-operation-users
module {
  func @pretty_names(%arg0: i32, %arg1: i32) {
    // %arg0 // users: %q_2, %q
    // %arg1 // users: %q_6, %q_2
    %c0_i32 = arith.constant 0 : i32 // users: %q
    %q, %q_0, %q_1, %r = "test.custom_result_name"(%c0_i32, %c0_i32, %c0_i32, %arg0) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32) // users: %q_6, %q_2
    %q_2, %q_3, %q_4, %r_5 = "test.custom_result_name"(%q_0, %q, %arg1, %arg0) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32) // users: %q_6
    %q_6, %q_7, %q_8, %r_9 = "test.custom_result_name"(%q, %r_5, %q_2, %arg1) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32)
    return
  }
}
cpillmayer marked 4 inline comments as done.

Address part of comments

Print id for operation with regions and indicate operation with unused results.
Add suggested code improvements.

cpillmayer planned changes to this revision.Apr 20 2022, 11:32 AM
cpillmayer marked an inline comment as done.Apr 20 2022, 11:41 AM
cpillmayer added inline comments.
mlir/lib/IR/AsmPrinter.cpp
2720

I thought of the operation too much in terms of a function. So I wanted to not print the id for a it as it would already have a name and the extra id comment would be redundant. But that doesn't make sense.

2725

Definitely makes sense whenever there is a result but no user. I added that.

2728

In this example it looks good. From my understanding the printer would always join the named values together and then we could get a result like this

%a:2 = my.op // users: (%1), (%1)
%1 = my.other(%a#0, %a#1) ...

I thought it is not great to have %1 in there twice.
How would you handle this?

mehdi_amini added inline comments.Apr 20 2022, 12:04 PM
mlir/lib/IR/AsmPrinter.cpp
2728

From my understanding the printer would always join the named values together

It does not always do it the op can control that.
Did you see the example I posted? It has this for example:

%q, %q_0, %q_1, %r = "test.custom_result_name"(%c0_i32, %c0_i32, %c0_i32, %arg0) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32) // users: %q_6, %q_2

But even if it collapsed, that is just cosmetic. I would instead wonder what is the best information to provide to the user when there are multiple results? That is: when there are multiple results would the important information be about the user of the operation or the users of an indvidual result? Expanding your example it could look like:

%a:2 = my.op // users: (%1 %2), (%1, %3)
%1 = my.other(%a#0, %a#1) ...
%2 = my.other(%a#0) ...
%3 = my.other(%a#1) ...

There aren't many ops with multiple results, scf.if may be one:

%if#2 = scf.if %cond -> (index, index) { // Users: (%2, %3, %4, %5) or // Users: (%2, %4), (%3, %4, %5)
  ...
  scf.yield %a, %b : index, index
} else {
  ...
  scf.yield %c, %d : index, index
}
%2 = foo(%if#0)
%3 = bar(%if#1)
%4 = foo(%if#0, %if#1)
%5 = bar(%if#1)
cpillmayer requested review of this revision.Apr 20 2022, 12:08 PM

Slightly more elaborated:

$ ./bin/mlir-opt /tmp/test.mlir -mlir-print-operation-users
module {
  func @pretty_names(%arg0: i32, %arg1: i32) {
    // %arg0 // users: %q_2, %q
    // %arg1 // users: %q_6, %q_2
    %c0_i32 = arith.constant 0 : i32 // users: %q
    %q, %q_0, %q_1, %r = "test.custom_result_name"(%c0_i32, %c0_i32, %c0_i32, %arg0) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32) // users: %q_6, %q_2
    %q_2, %q_3, %q_4, %r_5 = "test.custom_result_name"(%q_0, %q, %arg1, %arg0) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32) // users: %q_6
    %q_6, %q_7, %q_8, %r_9 = "test.custom_result_name"(%q, %r_5, %q_2, %arg1) {names = ["q", "q", "q", "r"]} : (i32, i32, i32, i32) -> (i32, i32, i32, i32)
    return
  }
}

Thanks for the comments @mehdi_amini. How would you test the behaviour with the custom result names? For me, mlir-opt does not behave like you show in your comment and always gives anonymous names to the results.

I patched MLIR for this:

diff --git a/mlir/test/lib/Dialect/Test/TestDialect.cpp b/mlir/test/lib/Dialect/Test/TestDialect.cpp
index 5cfefbb55971..699a365a1e2f 100644
--- a/mlir/test/lib/Dialect/Test/TestDialect.cpp
+++ b/mlir/test/lib/Dialect/Test/TestDialect.cpp
@@ -1168,6 +1168,15 @@ void StringAttrPrettyNameOp::getAsmResultNames(
         setNameFn(getResult(i), str.getValue());
 }
 
+void CustomResultsNameOp::getAsmResultNames(
+    function_ref<void(Value, StringRef)> setNameFn) {
+  auto value = getNames();
+  for (size_t i = 0, e = value.size(); i != e; ++i)
+    if (auto str = value[i].dyn_cast<StringAttr>())
+      if (!str.getValue().empty())
+        setNameFn(getResult(i), str.getValue());
+}
+
 //===----------------------------------------------------------------------===//
 // ResultTypeWithTraitOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index f42fb8edcc18..c2aa2cd49fa0 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -727,11 +727,27 @@ def OIListAllowedLiteral : TEST_Op<"oilist_allowed_literal"> {
 def StringAttrPrettyNameOp
  : TEST_Op<"string_attr_pretty_name",
            [DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>]> {
-  let arguments = (ins StrArrayAttr:$names);
+  let arguments = (ins 
+    Optional<I64>:$optional,
+    StrArrayAttr:$names
+  );
   let results = (outs Variadic<I32>:$r);
   let hasCustomAssemblyFormat = 1;
 }
 
+
+// This is used to test encoding of a string attribute into an SSA name of a
+// pretty printed value name.
+def CustomResultsNameOp
+ : TEST_Op<"custom_result_name",
+           [DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>]> {
+  let arguments = (ins 
+    Variadic<AnyInteger>:$optional,
+    StrArrayAttr:$names
+  );
+  let results = (outs Variadic<AnyInteger>:$r);
+}
+
 // This is used to test the OpAsmOpInterface::getDefaultDialect() feature:
 // operations nested in a region under this op will drop the "test." dialect
 // prefix.
cpillmayer added inline comments.Apr 20 2022, 1:10 PM
mlir/lib/IR/AsmPrinter.cpp
2728

You are right, thanks for the example. I think the user would prefer to check where a result is used instead of where any result from an operation is used.

Address comments

  • Add tests
  • Add grouping of users based on results
  • Improve printing of BlockArgument users

I patched MLIR for this:
...

Thanks a lot for your help. This made it much clearer for me. I added your operation to create the tests.

Mogball added inline comments.Apr 21 2022, 4:58 PM
mlir/include/mlir/IR/OperationSupport.h
780

Add a function so that the flag can be set programmatically?

801

It's not just the users of operations but block arguments too. The comment and the names of everything should reflect that. printValueUsers?

mlir/lib/IR/AsmPrinter.cpp
1020

In what scenarios would this case be triggered?

2599
2602
2727

Fit this comment to 80 characters long lines.

2732

Drop the explicit static size specifier unless you have a good reason to have it

2733

spell out the auto

2775
2776
2792
2882

spell out the auto

mlir/test/IR/print-op-users.mlir
62 ↗(On Diff #424252)

nit

mlir/test/lib/Dialect/Test/TestDialect.cpp
1173

spell out the auto

1174

prefer unsigned

LGTM overall!

mlir/include/mlir/IR/OperationSupport.h
780

+1: looks at the setters style line 736-753

mlir/lib/IR/AsmPrinter.cpp
1020

Invalid IR?

2732

That work only with SmallVector AFAIK

mlir/test/lib/Dialect/Test/TestDialect.cpp
1174

prefer int! :)

for (int i : llvm::seq<int>(0, value.size())

Or:

for (auto nameAndResult : llvm::zip(getNames(), getResults()))
  if (auto str = std::get<0>(nameAndResult).dyn_cast<StringAttr>())
      if (!str.getValue().empty())
        setNameFn(std::get<1>(nameAndResult), str.getValue());
rriddle added inline comments.Apr 21 2022, 6:03 PM
mlir/lib/IR/AsmPrinter.cpp
1158

Can you reflow this comment?

2718

Please drop comments from function definitions, prefer having them only on the declaration.

2720

Please spell out auto here.

2724

Prefer op->use_empty() over op->getUsers().empty()

2727

Can you please fix the flow of the comments? clang-format can auto format the comments so that they wrap properly.

2727

Should print feels a bit weird to read, can you tweak this comment?

2729

Please use punctuation for documentation.

2733

Please wrap the for in braces as well.

2739

Please add punctuation to the end of comments.

mlir/test/lib/Dialect/Test/TestDialect.cpp
1174

(I would say size_t is fine and consistent with the codebase)

cpillmayer marked 27 inline comments as done.Apr 22 2022, 8:51 AM
cpillmayer added inline comments.
mlir/lib/IR/AsmPrinter.cpp
1020

This case should never be reached right now imo. An operation id is only printed if the operation has no result, but in that case an id should have been assigned. I added the output in this case to be safe and so that this method always has reasonable output.

mlir/test/lib/Dialect/Test/TestDialect.cpp
1174

This method is the same as for StringAttrPrettyNameOp::getAsmResultNames. Maybe it makes sense to change both at once in a follow up?

Address comments

mehdi_amini accepted this revision.Apr 22 2022, 10:11 AM

LGTM!
Do you have commit access?

This revision is now accepted and ready to land.Apr 22 2022, 10:11 AM
cpillmayer added a comment.EditedApr 22 2022, 11:10 AM

LGTM!
Do you have commit access?

Great, thanks again for the help and the feedback to all of you!
I don't have commit access, so it would be great if you could land the patch for me. Please use "Christoph Pillmayer cpillmayer@gmail.com" for the commit.
And also credit yourself for the new test op you provided me with, if that's possible?

I don't have commit access, so it would be great if you could land the patch for me. Please use "Christoph Pillmayer cpillmayer@gmail.com" for the commit.

Sorry, I applied the patch with arc and forgot to check that the name was right, it appears as Author: cpillmayer <cpillmayer@gmail.com>, hopefully this is good enough?

I don't have commit access, so it would be great if you could land the patch for me. Please use "Christoph Pillmayer cpillmayer@gmail.com" for the commit.

Sorry, I applied the patch with arc and forgot to check that the name was right, it appears as Author: cpillmayer <cpillmayer@gmail.com>, hopefully this is good enough?

Yes, that's good as well. No worries, Thanks.