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

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Sep 24 15:05:00 PDT 2026


Author: Mehdi Amini
Date: 2026-09-24T22:04:54Z
New Revision: 4010c1ac00e25464ecc7e20950cba84edd0e3880

URL: https://github.com/llvm/llvm-project/commit/4010c1ac00e25464ecc7e20950cba84edd0e3880
DIFF: https://github.com/llvm/llvm-project/commit/4010c1ac00e25464ecc7e20950cba84edd0e3880.diff

LOG: [MLIR] Reduce generated assembly printer compile time (NFC) (#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

Added: 
    

Modified: 
    mlir/include/mlir/IR/OpImplementation.h
    mlir/lib/IR/AsmPrinter.cpp
    mlir/test/mlir-tblgen/op-decl-and-defs.td
    mlir/test/mlir-tblgen/op-format.td
    mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
    mlir/tools/mlir-tblgen/OpFormatGen.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index e43cb98b89253..ad5e43868e86c 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -477,6 +477,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) {
@@ -564,6 +571,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 f7e269867c9be..f78d33338904b 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 bcc81cd74d976..602ef834bdcf9 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 fe37237dab27b..54add551c1f18 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,32 @@ def AttrDictDefaultInherentAttr : TestFormat_Op<[{
   $attr attr-dict
 }]>, Arguments<(ins I64Attr:$attr)>;
 
+// CHECK-LABEL: AttrDictElideDefaultAndFixedAttr::print
+// CHECK: ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs = {"used"};
+// CHECK: elidedAttrs.push_back("attr");
+def AttrDictElideDefaultAndFixedAttr : TestFormat_Op<"$used attr-dict">,
+    Arguments<(ins I64Attr:$used,
+                   DefaultValuedStrAttr<StrAttr, "default">:$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: AttrDictStrictDefaultAttr::print
+// CHECK-NOT: elidedAttrs
+// CHECK: _odsPrinter.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
+def AttrDictStrictDefaultAttr : TestStrictPropertiesFormat_Op<
+    "prop-dict 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-NOT: elidedAttrs
+// CHECK: _odsPrinter.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
 def AttrDictStrictInferredSegmentAttr : TestStrictPropertiesFormat_Op<[{
   custom<Foo>($inputs, type($inputs)) attr-dict
 }]>, Arguments<(ins VariadicOfVariadic<I64, "segment_sizes">:$inputs,
@@ -85,6 +108,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
 }]>;
@@ -221,7 +245,7 @@ def DefaultEnumAttrPropDictOp : TestFormat_Op<"prop-dict attr-dict">,
 
 // Default-valued optional attributes have a non-optional getter.
 
-// CHECK-LABEL: DefaultOptionalEnumAttrOp::print
+// CHECK-LABEL: LLVM_ATTRIBUTE_MINSIZE void DefaultOptionalEnumAttrOp::print
 // CHECK: auto caseValue = getAttr();
 def DefaultOptionalEnumAttrOp : TestFormat_Op<"(enum($attr)^)? attr-dict">,
     Arguments<(ins DefaultValuedOptionalAttr<
@@ -329,7 +353,7 @@ def LegacyUnquotedBitEnumOp : TestFormat_Op<"$attr attr-dict">,
 // CHECK: ::mlir::detail::parseOptionalOperandInto(parser, aOperands)
 // 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 +367,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 +381,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 +391,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 f963d70630136..a6f8040cd9fb2 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -2096,8 +2096,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 81269c90aadb2..33a7ac7c42244 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -2729,14 +2729,49 @@ 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");
+  // TODO: Remove the non-strict path when strict properties in assembly
+  // formats are always enabled.
+  if (fmt.useStrictPropertiesInAssemblyFormat) {
+    // Inherent attributes are printed by their bindings or prop-dict.
+    body << "  _odsPrinter.printOptionalAttrDict"
+         << (withKeyword ? "WithKeyword" : "")
+         << "((*this)->getDiscardableAttrDictionary().getValue());\n";
+    return;
+  }
 
+  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 = {";
+    llvm::interleaveComma(fixedElidedAttrs, body, [&](StringRef name) {
+      body << "\"" << name << "\"";
+    });
+    body << "};\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
@@ -2785,12 +2820,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 =
@@ -3349,8 +3384,10 @@ void OperationFormat::genElementPrinter(FormatElement *element,
 }
 
 void OperationFormat::genPrinter(Operator &op, OpClass &opClass) {
+  // Printing is not performance sensitive. LLVM_ATTRIBUTE_MINSIZE reduces
+  // binary size and compiler work for printers generated for every operation.
   auto *method = opClass.addMethod(
-      "void", "print",
+      "LLVM_ATTRIBUTE_MINSIZE void", "print",
       MethodParameter("::mlir::OpAsmPrinter &", "_odsPrinter"));
   auto &body = method->body();
 


        


More information about the Mlir-commits mailing list