Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[mlir][tblgen] Add custom parsing and printing within struct #133939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2025

Conversation

jtuyls
Copy link
Contributor

@jtuyls jtuyls commented Apr 1, 2025

This PR extends the struct directive in tablegen to support nested custom directives. Note that this assumes/verifies that that custom directive has a single parameter.

This enables defining custom field parsing and printing functions if the struct directive doesn't suffice. There is some existing potential downstream usage for it: https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102

@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 3, 2025

Pinging a couple of people that have reviewed recent PRs to these files: @River707 @joker-eph, could you help review this PR?

@jtuyls jtuyls force-pushed the parse-print-struct branch from 3d95ba3 to 8e20e50 Compare April 3, 2025 16:00
@llvmbot llvmbot added mlir:core MLIR Core Infrastructure mlir labels Apr 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 3, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-core

Author: Jorn Tuyls (jtuyls)

Changes

This PR implements utilities to parse and print a comma-separated list of key-value pairs, similar to the struct directive in tablegen.

From the docs:
> struct Directive
The struct directive accepts a list of variables to capture and will generate a parser and printer for a comma-separated list of key-value pairs. If an optional parameter is included in the struct, it can be elided. The variables are printed in the order they are specified in the argument list but can be parsed in any order.

This enables defining custom struct parsing and printing functions if the struct directive doesn't suffice. There is some existing potential downstream usage for it: https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102


Full diff: https://github.com/llvm/llvm-project/pull/133939.diff

6 Files Affected:

  • (modified) mlir/include/mlir/IR/OpImplementation.h (+43)
  • (modified) mlir/lib/AsmParser/AsmParserImpl.h (+45)
  • (modified) mlir/lib/IR/AsmPrinter.cpp (+22)
  • (added) mlir/test/IR/custom-struct-attr-roundtrip.mlir (+62)
  • (modified) mlir/test/lib/Dialect/Test/TestAttrDefs.td (+9)
  • (modified) mlir/test/lib/Dialect/Test/TestAttributes.cpp (+37)
diff --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index 25c7d15eb8ed5..d77f4147744e1 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -238,6 +238,29 @@ class AsmPrinter {
 
   void printDimensionList(ArrayRef<int64_t> shape);
 
+  //===----------------------------------------------------------------------===//
+  // Struct Printing
+  //===----------------------------------------------------------------------===//
+
+  /// Print a comma-separated list of key-value pairs using the provided
+  /// `keywords` and corresponding printing functions. This performs similar
+  /// printing as the the assembly format's `struct` directive printer, but
+  /// allows bringing in custom printers for fields.
+  ///
+  /// Example:
+  /// <
+  ///   foo = foo_value,
+  ///   bar = bar_value,
+  ///   ...
+  /// >
+  virtual void
+  printStruct(ArrayRef<StringRef> keywords,
+              ArrayRef<llvm::function_ref<void(AsmPrinter &p)>> printFuncs);
+
+  //===----------------------------------------------------------------------===//
+  // Cyclic Printing
+  //===----------------------------------------------------------------------===//
+
   /// Class used to automatically end a cyclic region on destruction.
   class CyclicPrintReset {
   public:
@@ -1409,6 +1432,26 @@ class AsmParser {
     return CyclicParseReset(this);
   }
 
+  //===----------------------------------------------------------------------===//
+  // Struct Parsing
+  //===----------------------------------------------------------------------===//
+
+  /// Parse a comma-separated list of key-value pairs with a specified
+  /// delimiter. This performs similar parsing as the the assembly format
+  /// `struct` directive parser with custom delimiter and/or field parsing. The
+  /// variables are printed in the order they are specified in the argument list
+  /// but can be parsed in any order.
+  ///
+  /// Example:
+  /// <
+  ///   foo = something_parsed_by_a_custom_parser,
+  ///   bar = something_parsed_by_a_different_custom_parser,
+  ///   ...
+  /// >
+  virtual ParseResult
+  parseStruct(Delimiter delimiter, ArrayRef<StringRef> keywords,
+              ArrayRef<llvm::function_ref<ParseResult()>> parseFuncs) = 0;
+
 protected:
   /// Parse a handle to a resource within the assembly format for the given
   /// dialect.
diff --git a/mlir/lib/AsmParser/AsmParserImpl.h b/mlir/lib/AsmParser/AsmParserImpl.h
index 1f8fbfdd93568..cff3f5402dd79 100644
--- a/mlir/lib/AsmParser/AsmParserImpl.h
+++ b/mlir/lib/AsmParser/AsmParserImpl.h
@@ -570,6 +570,51 @@ class AsmParserImpl : public BaseT {
     parser.getState().cyclicParsingStack.pop_back();
   }
 
+  //===----------------------------------------------------------------------===//
+  // Struct Parsing
+  //===----------------------------------------------------------------------===//
+
+  /// Parse a comma-separated list of key-value pairs with a specified
+  /// delimiter.
+  ParseResult
+  parseStruct(Delimiter delimiter, ArrayRef<StringRef> keywords,
+              ArrayRef<llvm::function_ref<ParseResult()>> parseFuncs) override {
+    assert(keywords.size() == parseFuncs.size());
+    auto keyError = [&]() -> ParseResult {
+      InFlightDiagnostic parseError =
+          emitError(getCurrentLocation(), "expected one of: ");
+      llvm::interleaveComma(keywords, parseError, [&](StringRef kw) {
+        parseError << '`' << kw << '`';
+      });
+      return parseError;
+    };
+    SmallVector<bool> seen(keywords.size(), false);
+    DenseMap<StringRef, size_t> keywordToIndex;
+    for (auto &&[idx, keyword] : llvm::enumerate(keywords))
+      keywordToIndex[keyword] = idx;
+    return parseCommaSeparatedList(
+        delimiter,
+        [&]() -> ParseResult {
+          StringRef keyword;
+          if (failed(parseOptionalKeyword(&keyword)))
+            return keyError();
+          if (!keywordToIndex.contains(keyword))
+            return keyError();
+          size_t idx = keywordToIndex[keyword];
+          if (seen[idx]) {
+            return emitError(getCurrentLocation(), "duplicated `")
+                   << keyword << "` entry";
+          }
+          if (failed(parseEqual()))
+            return failure();
+          if (failed(parseFuncs[idx]()))
+            return failure();
+          seen[idx] = true;
+          return success();
+        },
+        "parse struct");
+  }
+
   //===--------------------------------------------------------------------===//
   // Code Completion
   //===--------------------------------------------------------------------===//
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index 5b5ec841917e7..7814d8f2cab18 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -3002,6 +3002,28 @@ void AsmPrinter::printDimensionList(ArrayRef<int64_t> shape) {
   detail::printDimensionList(getStream(), shape);
 }
 
+//===----------------------------------------------------------------------===//
+// Struct Printing
+//===----------------------------------------------------------------------===//
+
+/// Print a comma-separated list of key-value pairs.
+void AsmPrinter::printStruct(
+    ArrayRef<StringRef> keywords,
+    ArrayRef<llvm::function_ref<void(AsmPrinter &p)>> printFuncs) {
+  DenseMap<StringRef, llvm::function_ref<void(AsmPrinter & p)>> keywordToFunc;
+  for (auto &&[kw, printFunc] : llvm::zip(keywords, printFuncs))
+    keywordToFunc[kw] = printFunc;
+  auto &os = getStream();
+  llvm::interleaveComma(keywords, os, [&](StringRef kw) {
+    os << kw << " = ";
+    keywordToFunc[kw](*this);
+  });
+}
+
+//===----------------------------------------------------------------------===//
+// Cyclic Printing
+//===----------------------------------------------------------------------===//
+
 LogicalResult AsmPrinter::pushCyclicPrinting(const void *opaquePointer) {
   return impl->pushCyclicPrinting(opaquePointer);
 }
diff --git a/mlir/test/IR/custom-struct-attr-roundtrip.mlir b/mlir/test/IR/custom-struct-attr-roundtrip.mlir
new file mode 100644
index 0000000000000..68f69f99b86a3
--- /dev/null
+++ b/mlir/test/IR/custom-struct-attr-roundtrip.mlir
@@ -0,0 +1,62 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics| FileCheck %s
+
+// CHECK-LABEL: @test_struct_attr_roundtrip
+func.func @test_struct_attr_roundtrip() -> () {
+  // CHECK: attr = #test.custom_struct<type_str = "struct", value = 2, opt_value = [3, 3]>
+  "test.op"() {attr = #test.custom_struct<type_str = "struct", value = 2, opt_value = [3, 3]>} : () -> ()
+  // CHECK: attr = #test.custom_struct<type_str = "struct", value = 2, opt_value = [3, 3]>
+  "test.op"() {attr = #test.custom_struct<value = 2, type_str = "struct", opt_value = [3, 3]>} : () -> ()
+  // CHECK: attr = #test.custom_struct<type_str = "struct", value = 2>
+  "test.op"() {attr = #test.custom_struct<type_str = "struct", value = 2>} : () -> ()
+  // CHECK: attr = #test.custom_struct<type_str = "struct", value = 2>
+  "test.op"() {attr = #test.custom_struct<value = 2, type_str = "struct">} : () -> ()
+  return
+}
+
+// -----
+
+// Verify all keywords must be provided. All missing.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{expected one of: `type_str`, `value`, `opt_value`}}
+"test.op"() {attr = #test.custom_struct<"struct", 2>} : () -> ()
+
+// -----
+
+// Verify all keywords must be provided. `type_str` missing.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{expected one of: `type_str`, `value`, `opt_value`}}
+"test.op"() {attr = #test.custom_struct<"struct", value = 2, opt_value = [3, 3]>} : () -> ()
+
+// -----
+
+// Verify all keywords must be provided. `value` missing.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{expected one of: `type_str`, `value`, `opt_value`}}
+"test.op"() {attr = #test.custom_struct<type_str = "struct", 2>} : () -> ()
+
+// -----
+
+// Verify invalid keyword provided.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{expected one of: `type_str`, `value`, `opt_value`}}
+"test.op"() {attr = #test.custom_struct<type_str2 = "struct", value = 2>} : () -> ()
+
+// -----
+
+// Verify duplicated keyword provided.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{duplicated `type_str` entry}}
+"test.op"() {attr = #test.custom_struct<type_str = "struct", type_str = "struct2", value = 2>} : () -> ()
+
+// -----
+
+// Verify equals missing.
+
+// expected-error @below {{failed parsing `TestCustomStructAttr`}}
+// expected-error @below {{expected '='}}
+"test.op"() {attr = #test.custom_struct<type_str "struct", value = 2>} : () -> ()
diff --git a/mlir/test/lib/Dialect/Test/TestAttrDefs.td b/mlir/test/lib/Dialect/Test/TestAttrDefs.td
index fc2d77af29f12..2dae52ab7449c 100644
--- a/mlir/test/lib/Dialect/Test/TestAttrDefs.td
+++ b/mlir/test/lib/Dialect/Test/TestAttrDefs.td
@@ -369,6 +369,15 @@ def TestCustomFloatAttr : Test_Attr<"TestCustomFloat"> {
   }];
 }
 
+// Test AsmParser::parseStruct and AsmPrinter::printStruct APIs through the custom
+// parser and printer.
+def TestCustomStructAttr : Test_Attr<"TestCustomStruct"> {
+  let mnemonic = "custom_struct";
+  let parameters = (ins "mlir::StringAttr":$type_str, "int64_t":$value,
+                        OptionalParameter<"mlir::ArrayAttr">:$opt_value);
+  let hasCustomAssemblyFormat = 1;
+}
+
 def NestedPolynomialAttr : Test_Attr<"NestedPolynomialAttr"> {
   let mnemonic = "nested_polynomial";
   let parameters = (ins Polynomial_IntPolynomialAttr:$poly);
diff --git a/mlir/test/lib/Dialect/Test/TestAttributes.cpp b/mlir/test/lib/Dialect/Test/TestAttributes.cpp
index 057d9fb4a215f..89c7c527a2247 100644
--- a/mlir/test/lib/Dialect/Test/TestAttributes.cpp
+++ b/mlir/test/lib/Dialect/Test/TestAttributes.cpp
@@ -316,6 +316,43 @@ static ParseResult parseCustomFloatAttr(AsmParser &p, StringAttr &typeStrAttr,
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// TestCustomStructAttr
+//===----------------------------------------------------------------------===//
+
+Attribute TestCustomStructAttr::parse(AsmParser &p, Type type) {
+  std::string typeStr;
+  int64_t value;
+  FailureOr<ArrayAttr> optValue;
+  if (failed(p.parseStruct(AsmParser::Delimiter::LessGreater,
+                           {"type_str", "value", "opt_value"},
+                           {[&]() { return p.parseString(&typeStr); },
+                            [&]() { return p.parseInteger(value); },
+                            [&]() {
+                              optValue = mlir::FieldParser<ArrayAttr>::parse(p);
+                              return success(succeeded(optValue));
+                            }}))) {
+    p.emitError(p.getCurrentLocation())
+        << "failed parsing `TestCustomStructAttr`";
+    return {};
+  }
+  return get(p.getContext(), StringAttr::get(p.getContext(), typeStr), value,
+             optValue.value_or(ArrayAttr()));
+}
+
+void TestCustomStructAttr::print(AsmPrinter &p) const {
+  p << "<";
+  p.printStruct(
+      {"type_str", "value"},
+      {[&](AsmPrinter &p) { p.printStrippedAttrOrType(getTypeStr()); },
+       [&](AsmPrinter &p) { p.printStrippedAttrOrType(getValue()); }});
+  if (getOptValue() != ArrayAttr()) {
+    p << ", opt_value = ";
+    p.printStrippedAttrOrType(getOptValue());
+  }
+  p << ">";
+}
+
 //===----------------------------------------------------------------------===//
 // TestOpAsmAttrInterfaceAttr
 //===----------------------------------------------------------------------===//

@River707
Copy link
Contributor

River707 commented Apr 3, 2025

Instead of adding more c++ code, did you consider just expanding what the struct directive in tablegen can support? Why not extend that to support using a custom directive for the individual fields? This is generally what we have done when the declarative form is lacking something, try and extend that first.

@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 3, 2025

Instead of adding more c++ code, did you consider just expanding what the struct directive in tablegen can support? Why not extend that to support using a custom directive for the individual fields? This is generally what we have done when the declarative form is lacking something, try and extend that first.

Yes, I briefly thought about that and tried something like struct(field1, custom<Field2>(field2)), but I ran into the current assumption that these directives have top-level context and it felt like a larger change to reconsider that as this potentially opens up a bunch of nested variations of directives that need to be supported or properly excluded:

custom<Field1>(field1, custom<Field2>(field2))
struct(field1, struct(field2, field3), field4) -> probably doesn't make sense, or maybe it does as a nested dictionary or so?
custom<Field1>(field1, struct(field2, field3)) -> probably doesn't make sense?
...

The assumption that this is a larger change might be misguided though as I am not too familiar with tablegen (yet).

Additionally, I didn't see these two approaches as mutually exclusive as some fully custom printer/parser that can't be represented with the above directives might want to reuse these utilities as well for a part of it.

Anyway, if the declarative form is the way to go or if you want both implementations, I am happy to implement the nested directives as well.

@River707
Copy link
Contributor

River707 commented Apr 3, 2025

Instead of adding more c++ code, did you consider just expanding what the struct directive in tablegen can support? Why not extend that to support using a custom directive for the individual fields? This is generally what we have done when the declarative form is lacking something, try and extend that first.

Yes, I briefly thought about that and tried something like struct(field1, custom<Field2>(field2)), but I ran into the current assumption that these directives have top-level context and it felt like a larger change to reconsider that as this potentially opens up a bunch of nested variations of directives that need to be supported or properly excluded:

custom<Field1>(field1, custom<Field2>(field2))
struct(field1, struct(field2, field3), field4) -> probably doesn't make sense, or maybe it does as a nested dictionary or so?
custom<Field1>(field1, struct(field2, field3)) -> probably doesn't make sense?
...

The assumption that this is a larger change might be misguided though as I am not too familiar with tablegen (yet).

Additionally, I didn't see these two approaches as mutually exclusive as some fully custom printer/parser that can't be represented with the above directives might want to reuse these utilities as well for a part of it.

Anyway, if the declarative form is the way to go or if you want both implementations, I am happy to implement the nested directives as well.

The general thing for the assembly formats is that we should always to try to push for and evolve the tablegen side first, you should only default back to C++ when you really have to. Based on my observation of this MR, I would have expected we could just extend struct directive to support the very limited additional case of: a custom directive with a single parameter (aside from ref, though ref is not strictly necessary to support). You can then infer the name from the single parameter, and allow for custom parsing of that parameter. Directives can apply constraints on the form of arguments they support, so this should be quite easy to add. For example, this would correspond to something like:

struct($field1, custom<Field2>($field2))

For one of the cases in the StableHlo link above (though you have quite a few similar ops), you could do something like:

struct(
    custom<Dims>($updateWindowDims),
    custom<Dims>($insertedWindowDims),
    ...,
    $indexVectorDim
)

Then in your code, you only have one custom directive (for dim parsing/printing), and your done (your attr parser/printer are now declarative). That feels much better, and for your case would remove a lot of the C++ (in your case, there are a few ops which would be simplified just from that).

@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 3, 2025

Then in your code, you only have one custom directive (for dim parsing/printing), and your done (your attr parser/printer are now declarative). That feels much better, and for your case would remove a lot of the C++ (in your case, there are a few ops which would be simplified just from that).

Yes, this would remove a lot of code in most cases. Let me give this a shot.

@jtuyls jtuyls force-pushed the parse-print-struct branch from 8e20e50 to faa7628 Compare April 4, 2025 15:33
@jtuyls jtuyls changed the title [mlir] Add struct parsing and printing utilities [mlir][tblgen] Add custom parsing and printing within struct Apr 4, 2025
@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 4, 2025

@River707 I implemented the declarative form. Could you have a look?

Copy link
Contributor

@River707 River707 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on the tablegen side! Added some initial comments

@jtuyls jtuyls force-pushed the parse-print-struct branch from faa7628 to 6d8414d Compare April 7, 2025 21:08
@jtuyls jtuyls force-pushed the parse-print-struct branch 3 times, most recently from 57da6a9 to ee144db Compare April 9, 2025 13:51
@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 17, 2025

@River707 Could you have another look?

@River707
Copy link
Contributor

Thanks for the ping! LGTM. One last comment: Can you update the documentation for struct directive in docs/DefiningDialects/AttributesAndTypes.md

@jtuyls jtuyls force-pushed the parse-print-struct branch 2 times, most recently from 534ff00 to c464edb Compare April 18, 2025 08:31
@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 18, 2025

Thanks for the ping! LGTM. One last comment: Can you update the documentation for struct directive in docs/DefiningDialects/AttributesAndTypes.md

Yes, good point, I updated the documentation now.

@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 18, 2025

@River707 Could you help merge this?

@jtuyls jtuyls force-pushed the parse-print-struct branch from c464edb to 074b02b Compare April 23, 2025 13:32
@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 25, 2025

@joker-eph Could you have another look and help merge if this looks good?

@jtuyls
Copy link
Contributor Author

jtuyls commented Apr 29, 2025

@River707 @joker-eph Could someone please help merge this if it looks good?

@joker-eph joker-eph merged commit de6d010 into llvm:main Apr 30, 2025
11 checks passed
@joker-eph
Copy link
Collaborator

Sorry, I had missed your update last week!

@jtuyls jtuyls deleted the parse-print-struct branch April 30, 2025 13:13
@kazutakahirata
Copy link
Contributor

@jtuyls @joker-eph I've landed 441b683 to fix a warning from this PR. Would you mind checking to see if you actually don't intend to use realParam, the result of dyn_cast, inside the if statement? Thanks!

    if (isa<ParameterElement>(arg))
      genVariableParser(param, ctx, os.indent());

IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…3939)

This PR extends the `struct` directive in tablegen to support nested
`custom` directives. Note that this assumes/verifies that that `custom`
directive has a single parameter.

This enables defining custom field parsing and printing functions if the
`struct` directive doesn't suffice. There is some existing potential
downstream usage for it:
https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…3939)

This PR extends the `struct` directive in tablegen to support nested
`custom` directives. Note that this assumes/verifies that that `custom`
directive has a single parameter.

This enables defining custom field parsing and printing functions if the
`struct` directive doesn't suffice. There is some existing potential
downstream usage for it:
https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…3939)

This PR extends the `struct` directive in tablegen to support nested
`custom` directives. Note that this assumes/verifies that that `custom`
directive has a single parameter.

This enables defining custom field parsing and printing functions if the
`struct` directive doesn't suffice. There is some existing potential
downstream usage for it:
https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102
@jtuyls
Copy link
Contributor Author

jtuyls commented May 7, 2025

@jtuyls @joker-eph I've landed 441b683 to fix a warning from this PR. Would you mind checking to see if you actually don't intend to use realParam, the result of dyn_cast, inside the if statement? Thanks!

    if (isa<ParameterElement>(arg))
      genVariableParser(param, ctx, os.indent());

Thanks for the fix and heads up on the change! I think it's fine as realParam would actually be the same as param, so your change would be functionally equivalent to:

if (auto realParam = dyn_cast<ParameterElement>(arg))
      genVariableParser(realParam, ctx, os.indent());

GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
…3939)

This PR extends the `struct` directive in tablegen to support nested
`custom` directives. Note that this assumes/verifies that that `custom`
directive has a single parameter.

This enables defining custom field parsing and printing functions if the
`struct` directive doesn't suffice. There is some existing potential
downstream usage for it:
https://github.com/openxla/stablehlo/blob/a3c7de92425e8035437dae67ab2318a82eca79a1/stablehlo/dialect/StablehloOps.cpp#L3102
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mlir:core MLIR Core Infrastructure mlir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants