[Mlir-commits] [mlir] [MLIR] Reduce generated assembly printer compile time (NFC) (PR #225740)

Mehdi Amini llvmlistbot at llvm.org
Wed Sep 23 05:00:26 PDT 2026


https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/225740

Use fixed arrays for statically elided attributes and combine leading spaces with literals. Move common operand printing out of generated call sites and emit sliced operand getters to reduce optimizer work in large printers.

For omp.target, optimized print IR drops from 3,893 to 2,667 instructions. 

Assisted-by: Codex

>From dd45ed8a619beb9b8f330fb9b64f879dad61b693 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Wed, 23 Sep 2026 04:53:47 -0700
Subject: [PATCH] [mlir] Reduce generated assembly printer compile time

Use fixed arrays for statically elided attributes and combine leading spaces
with literals. Move common operand printing out of generated call sites and
emit sliced operand getters to reduce optimizer work in large printers.

For omp.target, optimized print IR drops from 3,893 to 2,667 instructions.
The printing benchmark is 2.1% slower; check-mlir passes.

Assisted-by: Codex
---
 mlir/include/mlir/IR/OpImplementation.h     | 18 +++++++++
 mlir/lib/IR/AsmPrinter.cpp                  |  9 +++++
 mlir/test/mlir-tblgen/op-decl-and-defs.td   |  2 +
 mlir/test/mlir-tblgen/op-format.td          | 23 ++++++++----
 mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp |  9 ++++-
 mlir/tools/mlir-tblgen/OpFormatGen.cpp      | 41 ++++++++++++++++-----
 6 files changed, 83 insertions(+), 19 deletions(-)

diff --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index 63dde74c33d519..45385e06c3c27b 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -475,6 +475,13 @@ class OpAsmPrinter : public AsmPrinter {
   virtual void printOperand(Value value) = 0;
   virtual void printOperand(Value value, raw_ostream &os) = 0;
 
+  /// Print a comma separated range of operation operands out of line to avoid
+  /// instantiating the range iteration in every generated operation printer.
+  void printOperands(OperandRange operands);
+
+  /// Print the types of a comma separated range of operation operands.
+  void printOperandTypes(ValueTypeRange<OperandRange> types);
+
   /// Print a comma separated list of operands.
   template <typename ContainerType>
   void printOperands(const ContainerType &container) {
@@ -562,6 +569,17 @@ inline OpAsmPrinter &operator<<(OpAsmPrinter &p, Value value) {
   return p;
 }
 
+inline OpAsmPrinter &operator<<(OpAsmPrinter &p, OperandRange values) {
+  p.printOperands(values);
+  return p;
+}
+
+inline OpAsmPrinter &operator<<(OpAsmPrinter &p,
+                                ValueTypeRange<OperandRange> types) {
+  p.printOperandTypes(types);
+  return p;
+}
+
 template <typename T,
           std::enable_if_t<std::is_convertible<T &, ValueRange>::value &&
                                !std::is_convertible<T &, Value &>::value,
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index f7e269867c9be2..f78d33338904b0 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -91,6 +91,15 @@ DialectAsmPrinter::~DialectAsmPrinter() = default;
 
 OpAsmPrinter::~OpAsmPrinter() = default;
 
+void OpAsmPrinter::printOperands(OperandRange operands) {
+  llvm::interleaveComma(operands, getStream(),
+                        [this](Value value) { printOperand(value); });
+}
+
+void OpAsmPrinter::printOperandTypes(ValueTypeRange<OperandRange> types) {
+  llvm::interleaveComma(types, *this);
+}
+
 void OpAsmPrinter::printFunctionalType(Operation *op) {
   auto &os = getStream();
   os << '(';
diff --git a/mlir/test/mlir-tblgen/op-decl-and-defs.td b/mlir/test/mlir-tblgen/op-decl-and-defs.td
index 32c4bb88d188be..5b4b0b4bc0a494 100644
--- a/mlir/test/mlir-tblgen/op-decl-and-defs.td
+++ b/mlir/test/mlir-tblgen/op-decl-and-defs.td
@@ -100,6 +100,8 @@ def NS_AOp : NS_Op<"a_op", [IsolatedFromAbove, IsolatedFromAbove]> {
 // CHECK:     return ::llvm::StringLiteral("test.a_op");
 // CHECK:   }
 // CHECK:   ::mlir::Operation::operand_range getODSOperands(unsigned index) {
+// CHECK-NEXT:     auto [start, length] = getODSOperandIndexAndLength(index);
+// CHECK-NEXT:     return getOperation()->getOperands().slice(start, length);
 // CHECK:   ::mlir::TypedValue<::mlir::IntegerType> getA() {
 // CHECK:   ::mlir::Operation::operand_range getB() {
 // CHECK:   ::mlir::OpOperand &getAMutable() {
diff --git a/mlir/test/mlir-tblgen/op-format.td b/mlir/test/mlir-tblgen/op-format.td
index f7f0c1b212e044..af35dc4f62e6f2 100644
--- a/mlir/test/mlir-tblgen/op-format.td
+++ b/mlir/test/mlir-tblgen/op-format.td
@@ -32,6 +32,7 @@ class TestStrictPropertiesFormat_Op<string fmt, list<Trait> traits = []>
 // CHECK: // coverity[ARRAY_VS_SINGLETON]
 // CHECK-NEXT: if (parser.resolveOperands(
 // CHECK-LABEL: AttrDictContextOperand::print
+// CHECK: ::llvm::ArrayRef<::llvm::StringRef> elidedAttrs;
 // CHECK: _odsPrinter.printOptionalAttrDict(_odsAttrs.getDictionary((*this)->getContext()).getValue(), elidedAttrs);
 def AttrDictContextOperand : TestFormat_Op<[{
   $context `:` type($context) attr-dict
@@ -43,10 +44,17 @@ def AttrDictDefaultInherentAttr : TestFormat_Op<[{
   $attr attr-dict
 }]>, Arguments<(ins I64Attr:$attr)>;
 
+// CHECK-LABEL: AttrDictElideDefaultAttr::print
+// CHECK: ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
+// CHECK: if(attr && (attr ==
+// CHECK: elidedAttrs.push_back("attr");
+def AttrDictElideDefaultAttr : TestFormat_Op<"attr-dict">,
+    Arguments<(ins DefaultValuedStrAttr<StrAttr, "default">:$attr)>;
+
 // CHECK-LABEL: AttrDictStrictInferredSegmentAttr::parse
 // CHECK: result.getOrAddProperties<AttrDictStrictInferredSegmentAttr::Properties>().segment_sizes = parser.getBuilder().getDenseI32ArrayAttr(inputsOperandGroupSizes);
 // CHECK-LABEL: AttrDictStrictInferredSegmentAttr::print
-// CHECK: elidedAttrs.push_back("segment_sizes");
+// CHECK: static constexpr ::llvm::StringRef elidedAttrs[] = {"segment_sizes"};
 def AttrDictStrictInferredSegmentAttr : TestStrictPropertiesFormat_Op<[{
   custom<Foo>($inputs, type($inputs)) attr-dict
 }]>, Arguments<(ins VariadicOfVariadic<I64, "segment_sizes">:$inputs,
@@ -85,6 +93,7 @@ def AttrDictStrictPropDictInherentAttr : TestStrictPropertiesFormat_Op<[{
 // CHECK: parseFoo({{.*}}, parser.getBuilder().getI1Type())
 // CHECK-LABEL: CustomStringLiteralA::print
 // CHECK: printFoo({{.*}}, ::mlir::Builder(getContext()).getI1Type())
+// CHECK: ::llvm::ArrayRef<::llvm::StringRef> elidedAttrs;
 def CustomStringLiteralA : TestFormat_Op<[{
   custom<Foo>("$_builder.getI1Type()") attr-dict
 }]>;
@@ -329,7 +338,7 @@ def LegacyUnquotedBitEnumOp : TestFormat_Op<"$attr attr-dict">,
 // CHECK: parser.parseOptionalOperand
 // CHECK-LABEL: OptionalGroupA::print
 // CHECK: if (!getA())
-// CHECK-NEXT: odsPrinter << ' ' << "?";
+// CHECK-NEXT: odsPrinter << " ?";
 // CHECK-NEXT: else
 // CHECK: odsPrinter << value;
 def OptionalGroupA : TestFormat_Op<[{
@@ -343,9 +352,9 @@ def OptionalGroupA : TestFormat_Op<[{
 // CHECK: parser.parseKeyword("bar")
 // CHECK-LABEL: OptionalGroupB::print
 // CHECK: if (!(getAAttr() && getAAttr() != ((false) ? ::mlir::OpBuilder((*this)->getContext()).getUnitAttr() : nullptr)))
-// CHECK-NEXT: odsPrinter << ' ' << "foo"
+// CHECK-NEXT: odsPrinter << " foo"
 // CHECK-NEXT: else
-// CHECK-NEXT: odsPrinter << ' ' << "bar"
+// CHECK-NEXT: odsPrinter << " bar"
 def OptionalGroupB : TestFormat_Op<[{
   (`foo`) : (`bar` $a^)? attr-dict
 }]>, Arguments<(ins UnitAttr:$a)>;
@@ -357,8 +366,7 @@ def OptionalGroupB : TestFormat_Op<[{
 //  CHECK-NEXT:   odsPrinter << ' ';
 //  CHECK-NEXT:   odsPrinter.printAttributeWithoutType(getAAttr());
 //  CHECK-NEXT: }
-//       CHECK: elidedAttrs.push_back("a");
-//   CHECK-NOT: elidedAttrs.push_back("a");
+//       CHECK: static constexpr ::llvm::StringRef elidedAttrs[] = {"a"};
 //       CHECK: _odsPrinter.printOptionalAttrDict
 def OptionalGroupC : TestFormat_Op<[{
   ($a^)? attr-dict
@@ -368,8 +376,7 @@ def OptionalGroupC : TestFormat_Op<[{
 //       CHECK: elidedProps.push_back("a");
 //   CHECK-NOT: elidedProps.push_back("a");
 //       CHECK: printProperties
-//       CHECK: elidedAttrs.push_back("a");
-//   CHECK-NOT: elidedAttrs.push_back("a");
+//       CHECK: static constexpr ::llvm::StringRef elidedAttrs[] = {"a"};
 //       CHECK: _odsPrinter.printOptionalAttrDict
 def OptionalGroupCPropDict : TestFormat_Op<[{
   ($a^)? prop-dict attr-dict
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index aa9ff159903bc6..95cb292bbe7fc6 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -2108,8 +2108,13 @@ generateNamedOperandGetters(const Operator &op, Class &opClass,
                                     MethodParameter("unsigned", "index"));
   ERROR_IF_PRUNED(m, "getODSOperands", op);
   auto &body = m->body();
-  body << formatv(valueRangeReturnCode, rangeBeginCall,
-                  "getODSOperandIndexAndLength(index)");
+  if (isGenericAdaptorBase) {
+    body << formatv(valueRangeReturnCode, rangeBeginCall,
+                    "getODSOperandIndexAndLength(index)");
+  } else {
+    body << "  auto [start, length] = getODSOperandIndexAndLength(index);\n"
+            "  return getOperation()->getOperands().slice(start, length);\n";
+  }
 
   // Then we emit nicer named getter methods by redirecting to the "sink" getter
   // method.
diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
index becdcc825d5470..9a9c0a67ec71ab 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -2723,14 +2723,37 @@ static void genPropDictPrinter(OperationFormat &fmt, Operator &op,
 /// Generate the printer for the 'attr-dict' directive.
 static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
                                MethodBody &body, bool withKeyword) {
-  body << "  ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;\n";
-
-  genVariadicSegmentElision(fmt, op, body, "elidedAttrs");
-
+  SmallVector<StringRef> fixedElidedAttrs;
+  if (!fmt.allOperands &&
+      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"))
+    fixedElidedAttrs.push_back("operandSegmentSizes");
+  if (!fmt.allResultTypes &&
+      op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
+    fixedElidedAttrs.push_back("resultSegmentSizes");
   for (StringRef key : fmt.inferredAttributes.keys())
-    body << "  elidedAttrs.push_back(\"" << key << "\");\n";
+    fixedElidedAttrs.push_back(key);
   for (const NamedAttribute *attr : fmt.usedAttributes)
-    body << "  elidedAttrs.push_back(\"" << attr->name << "\");\n";
+    fixedElidedAttrs.push_back(attr->name);
+
+  bool hasConditionalElision =
+      llvm::any_of(op.getAttributes(), [&](const NamedAttribute &namedAttr) {
+        return !fmt.usedAttributes.contains(&namedAttr) &&
+               !namedAttr.attr.isDerivedAttr() &&
+               namedAttr.attr.hasDefaultValue();
+      });
+  if (hasConditionalElision) {
+    body << "  ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;\n";
+    for (StringRef name : fixedElidedAttrs)
+      body << "  elidedAttrs.push_back(\"" << name << "\");\n";
+  } else if (fixedElidedAttrs.empty()) {
+    body << "  ::llvm::ArrayRef<::llvm::StringRef> elidedAttrs;\n";
+  } else {
+    body << "  static constexpr ::llvm::StringRef elidedAttrs[] = {";
+    llvm::interleaveComma(fixedElidedAttrs, body, [&](StringRef name) {
+      body << "\"" << name << "\"";
+    });
+    body << "};\n";
+  }
 
   // Add code to check attributes for equality with their default values.
   // Default-valued attributes will not be printed when their value matches the
@@ -2779,12 +2802,12 @@ static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
 /// the previous element was a punctuation literal.
 static void genLiteralPrinter(StringRef value, MethodBody &body,
                               bool &shouldEmitSpace, bool &lastWasPunctuation) {
-  body << "  _odsPrinter";
+  body << "  _odsPrinter << \"";
 
   // Don't insert a space for certain punctuation.
   if (shouldEmitSpace && shouldEmitSpaceBefore(value, lastWasPunctuation))
-    body << " << ' '";
-  body << " << \"" << value << "\";\n";
+    body << ' ';
+  body << value << "\";\n";
 
   // Insert a space after certain literals.
   shouldEmitSpace =



More information about the Mlir-commits mailing list