[Mlir-commits] [mlir] [mlir][tblgen] Define enum `FieldParser` specializations out-of-line (PR #233730)

Markus Böck llvmlistbot at llvm.org
Tue Oct 20 01:18:32 PDT 2026


https://github.com/zero9178 updated https://github.com/llvm/llvm-project/pull/233730

>From 13bb6874c45aeab021574a620eb222e55c38e8d4 Mon Sep 17 00:00:00 2001
From: Markus Boeck <mboeck at nvidia.com>
Date: Tue, 20 Oct 2026 07:45:32 +0000
Subject: [PATCH 1/2] [mlir][tblgen] Define enum `FieldParser` specializations
 out-of-line

The `FieldParser` specializations generated for every enum were defined
inline in the enum declarations header. Since their bodies do not depend
on the `ParserT` template parameter, they are type-checked eagerly in
every translation unit including the header, which instantiates
`FailureOr<E>`, `FailureOr<std::optional<E>>` and their `std::optional`
bases and type traits for every enum. Only a small fraction of these
specializations is ever used, and only from the generated attribute and
op parsers of the dialect itself.

This patch changes `parse` to a non-template taking an `AsmParser &`,
declares it in the header and emits its definition into the enum
definitions file instead.

Compiling all of MLIR (clang 18 with 64 cores) takes 4.5% less CPU time, with the median
translation unit compiling 2.8% faster and some up to 15% faster. The
summed peak memory usage of all translation units drops by 4.5% and the
largest peak memory usage of a single translation unit (`mlir/lib/CAPI/Conversion/Passes.cpp`) drops by 13%
(1710 MB -> 1492 MB). A translation unit only including `NVVMDialect.h`
compiles 12% faster with 14% lower peak memory usage.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 mlir/include/mlir/TableGen/EnumInfo.h         |  10 +
 mlir/lib/TableGen/EnumInfo.cpp                |  14 +
 mlir/test/mlir-tblgen/enums-gen.td            |  73 ++--
 .../tools/mlir-tblgen/AttrOrTypeFormatGen.cpp |   2 +-
 mlir/tools/mlir-tblgen/EnumsGen.cpp           | 338 ++++++++++--------
 mlir/tools/mlir-tblgen/OpFormatGen.cpp        |   2 +-
 mlir/unittests/TableGen/EnumsGenTest.cpp      |   1 +
 7 files changed, 257 insertions(+), 183 deletions(-)

diff --git a/mlir/include/mlir/TableGen/EnumInfo.h b/mlir/include/mlir/TableGen/EnumInfo.h
index aea76c01e7e7a..d8dfd3489cf6b 100644
--- a/mlir/include/mlir/TableGen/EnumInfo.h
+++ b/mlir/include/mlir/TableGen/EnumInfo.h
@@ -72,12 +72,22 @@ class EnumInfo {
   // Returns true if this is a bit enum.
   bool isBitEnum() const;
 
+  // Returns true if this is a bit enum whose cases are parsed and printed as
+  // separate, unquoted keywords.
+  bool isNewStyleBitEnum() const;
+
+  // Returns true if a 'FieldParser' and a printer are generated for the enum.
+  bool hasParserPrinter() const;
+
   // Returns the enum class name.
   StringRef getEnumClassName() const;
 
   // Returns the C++ namespaces this enum class should be placed in.
   StringRef getCppNamespace() const;
 
+  // Returns the fully qualified C++ name of the enum class.
+  std::string getQualifiedName() const;
+
   // Returns the summary of the enum.
   StringRef getSummary() const;
 
diff --git a/mlir/lib/TableGen/EnumInfo.cpp b/mlir/lib/TableGen/EnumInfo.cpp
index 6128c53557cc4..99960c8df29d2 100644
--- a/mlir/lib/TableGen/EnumInfo.cpp
+++ b/mlir/lib/TableGen/EnumInfo.cpp
@@ -57,6 +57,16 @@ std::optional<Attribute> EnumInfo::asEnumAttr() const {
 
 bool EnumInfo::isBitEnum() const { return isSubClassOf("BitEnumBase"); }
 
+bool EnumInfo::isNewStyleBitEnum() const {
+  return isBitEnum() && !printBitEnumQuoted();
+}
+
+bool EnumInfo::hasParserPrinter() const {
+  std::optional<Attribute> enumAttrInfo = asEnumAttr();
+  return !getUnderlyingType().empty() &&
+         (!enumAttrInfo || !enumAttrInfo->getConstBuilderTemplate().empty());
+}
+
 StringRef EnumInfo::getEnumClassName() const {
   return def->getValueAsString("className");
 }
@@ -73,6 +83,10 @@ StringRef EnumInfo::getCppNamespace() const {
   return def->getValueAsString("cppNamespace");
 }
 
+std::string EnumInfo::getQualifiedName() const {
+  return (getCppNamespace() + "::" + getEnumClassName()).str();
+}
+
 int64_t EnumInfo::getBitwidth() const { return def->getValueAsInt("bitwidth"); }
 
 StringRef EnumInfo::getUnderlyingType() const {
diff --git a/mlir/test/mlir-tblgen/enums-gen.td b/mlir/test/mlir-tblgen/enums-gen.td
index e64e50b2382cb..dc0caab3652d9 100644
--- a/mlir/test/mlir-tblgen/enums-gen.td
+++ b/mlir/test/mlir-tblgen/enums-gen.td
@@ -32,21 +32,13 @@ def MyBitEnum: I32BitEnumAttr<"MyBitEnum", "An example bit enum",
 // DECL: ::std::optional<MyBitEnum> symbolizeMyBitEnum(::llvm::StringRef);
 
 // DECL: struct FieldParser<::MyBitEnum, ::MyBitEnum> {
-// DECL:   template <typename ParserT>
-// DECL:   static FailureOr<::MyBitEnum> parse(ParserT &parser) {
-// DECL:     // Parse the keyword/string containing the enum.
-// DECL:     std::string enumKeyword;
-// DECL:     auto loc = parser.getCurrentLocation();
-// DECL:     if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))
-// DECL:       return parser.emitError(loc, "expected keyword for An example bit enum");
-// DECL:     // Symbolize the keyword.
-// DECL:     if (::std::optional<::MyBitEnum> attr = ::symbolizeEnum<::MyBitEnum>(enumKeyword))
-// DECL:       return *attr;
-// DECL:     return parser.emitError(loc, "expected one of [none, tagged, Bit1, Bit2, Bit3, BitGroup] for An example bit enum, got: ") << enumKeyword;
-// DECL:   }
+// DECL-NEXT:   static FailureOr<::MyBitEnum> parse(AsmParser &parser);
+// DECL-NEXT: };
 
 // DECL: struct FieldParser<std::optional<::MyBitEnum>, std::optional<::MyBitEnum>> {
 // DECL:   static constexpr bool isKeyValueCompositional = false;
+// DECL:   static FailureOr<std::optional<::MyBitEnum>> parse(AsmParser &parser);
+// DECL-NEXT: };
 
 // DECL: inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, ::MyBitEnum value) {
 // DECL:   auto valueStr = stringifyEnum(value);
@@ -101,6 +93,23 @@ def MyBitEnum: I32BitEnumAttr<"MyBitEnum", "An example bit enum",
 // DEF: .Case("tagged", 1)
 // DEF: .Case("Bit1", 2)
 
+// DEF-LABEL: FailureOr<::MyBitEnum> FieldParser<::MyBitEnum, ::MyBitEnum>::parse(AsmParser &parser) {
+// DEF:   // Parse the keyword/string containing the enum.
+// DEF:   std::string enumKeyword;
+// DEF:   auto loc = parser.getCurrentLocation();
+// DEF:   if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))
+// DEF:     return parser.emitError(loc, "expected keyword for An example bit enum");
+// DEF:   // Symbolize the keyword.
+// DEF:   if (::std::optional<::MyBitEnum> attr = ::symbolizeEnum<::MyBitEnum>(enumKeyword))
+// DEF:     return *attr;
+// DEF:   return parser.emitError(loc, "expected one of [none, tagged, Bit1, Bit2, Bit3, BitGroup] for An example bit enum, got: ") << enumKeyword;
+// DEF: }
+
+// DEF-LABEL: FailureOr<std::optional<::MyBitEnum>>
+// DEF-NEXT: FieldParser<std::optional<::MyBitEnum>, std::optional<::MyBitEnum>>::parse(AsmParser &parser) {
+// DEF:   if (failed(parser.parseOptionalKeywordOrString(&enumKeyword, {"none", "tagged", "Bit1", "Bit2", "Bit3", "BitGroup"})))
+// DEF-NEXT:     return std::optional<::MyBitEnum>{};
+
 // Test enum printer generation for non non-keyword enums.
 
 def NonKeywordBit: I32BitEnumCaseBit<"Bit0", 0, "tag-ged">;
@@ -138,25 +147,27 @@ def MyNonQuotedPrintBitEnum
 
 // DECL: struct FieldParser<::MyNonQuotedPrintBitEnum, ::MyNonQuotedPrintBitEnum> {
 // DECL:   static constexpr bool isKeyValueCompositional = true;
-// DECL:   template <typename ParserT>
-// DECL:   static FailureOr<::MyNonQuotedPrintBitEnum> parse(ParserT &parser) {
-// DECL:     ::MyNonQuotedPrintBitEnum flags = {};
-// DECL:     do {
-  // DECL:     // Parse the keyword containing a part of the enum.
-// DECL:       ::llvm::StringRef enumKeyword;
-// DECL:       auto loc = parser.getCurrentLocation();
-// DECL:       if (failed(parser.parseOptionalKeyword(&enumKeyword))) {
-// DECL:         return parser.emitError(loc, "expected keyword for Example new-style bit enum");
-// DECL:       }
-// DECL:       // Symbolize the keyword.
-// DECL:       if (::std::optional<::MyNonQuotedPrintBitEnum> flag = ::symbolizeEnum<::MyNonQuotedPrintBitEnum>(enumKeyword))
-// DECL:         flags = flags | *flag;
-// DECL:       } else {
-// DECL:         return parser.emitError(loc, "expected one of [none, tagged, Bit1, Bit2, Bit3, BitGroup] for Example new-style bit enum, got: ") << enumKeyword;
-// DECL:       }
-// DECL:     } while (::mlir::succeeded(parser.parseOptionalVerticalBar()));
-// DECL:     return flags;
-// DECL:   }
+// DECL:   static FailureOr<::MyNonQuotedPrintBitEnum> parse(AsmParser &parser);
+// DECL-NEXT: };
+
+// DEF-LABEL: FailureOr<::MyNonQuotedPrintBitEnum> FieldParser<::MyNonQuotedPrintBitEnum, ::MyNonQuotedPrintBitEnum>::parse(AsmParser &parser) {
+// DEF:   ::MyNonQuotedPrintBitEnum flags = {};
+// DEF:   do {
+// DEF:     // Parse the keyword containing a part of the enum.
+// DEF:     ::llvm::StringRef enumKeyword;
+// DEF:     auto loc = parser.getCurrentLocation();
+// DEF:     if (failed(parser.parseOptionalKeyword(&enumKeyword))) {
+// DEF:       return parser.emitError(loc, "expected keyword for Example new-style bit enum");
+// DEF:     }
+// DEF:     // Symbolize the keyword.
+// DEF:     if (::std::optional<::MyNonQuotedPrintBitEnum> flag = ::symbolizeEnum<::MyNonQuotedPrintBitEnum>(enumKeyword)) {
+// DEF:       flags = flags | *flag;
+// DEF:     } else {
+// DEF:       return parser.emitError(loc, "expected one of [none, tagged, Bit1, Bit2, Bit3, BitGroup] for Example new-style bit enum, got: ") << enumKeyword;
+// DEF:     }
+// DEF:   } while (::mlir::succeeded(parser.parseOptionalVerticalBar()));
+// DEF:   return flags;
+// DEF: }
 
 def MyCommaSeparatedBitEnum
   : I32BitEnum<"MyCommaSeparatedBitEnum", "Comma-separated bit enum",
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.cpp
index eed05558447de..853ba5b0d0c71 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.cpp
@@ -125,7 +125,7 @@ static bool isUndelimitedCommaBitEnumParam(const ParameterElement *el) {
   if (!enumAttr)
     return false;
   EnumInfo enumInfo(enumAttr->getValueAsDef("enum"));
-  return enumInfo.isBitEnum() && !enumInfo.printBitEnumQuoted() &&
+  return enumInfo.isNewStyleBitEnum() &&
          enumInfo.getDef().getValueAsString("separator").trim() == ",";
 }
 
diff --git a/mlir/tools/mlir-tblgen/EnumsGen.cpp b/mlir/tools/mlir-tblgen/EnumsGen.cpp
index 845f1ef46cf35..c83ed3cf42fcd 100644
--- a/mlir/tools/mlir-tblgen/EnumsGen.cpp
+++ b/mlir/tools/mlir-tblgen/EnumsGen.cpp
@@ -66,26 +66,71 @@ static void emitEnumClass(const Record &enumDef, StringRef enumName,
   os << "};\n\n";
 }
 
-static void emitParserPrinter(const EnumInfo &enumInfo, StringRef qualName,
-                              StringRef cppNamespace, raw_ostream &os) {
-  std::optional<Attribute> enumAttrInfo = enumInfo.asEnumAttr();
-  if (enumInfo.getUnderlyingType().empty() ||
-      (enumAttrInfo && enumAttrInfo->getConstBuilderTemplate().empty()))
-    return;
+/// Returns a bit vector with a bit set for every case that cannot be printed
+/// as a keyword.
+static BitVector getNonKeywordCases(ArrayRef<EnumCase> cases) {
+  BitVector nonKeywordCases(cases.size());
+  for (auto [index, enumerant] : llvm::enumerate(cases))
+    if (!tblgen::canFormatStringAsKeyword(enumerant.getStr()))
+      nonKeywordCases.set(index);
+  return nonKeywordCases;
+}
+
+/// Emits the declarations of the 'FieldParser' specializations of the enum.
+/// The definitions are emitted by 'emitFieldParserDefs' into the enum
+/// definitions file instead of inline in the header. The bodies would otherwise
+/// be type-checked and require instantiating the 'FailureOr' and
+/// 'std::optional' types in every translation unit including the header,
+/// despite rarely being used. The forward declarations of 'AsmParser' and
+/// 'FieldParser' are emitted once per file by 'emitEnumDecls'.
+static void emitFieldParserDecls(const EnumInfo &enumInfo, StringRef qualName,
+                                 raw_ostream &os) {
+  const char *const fieldParserDecls = R"(
+namespace mlir {
+template<>
+struct FieldParser<{0}, {0}> {{{1}
+  static FailureOr<{0}> parse(AsmParser &parser);
+};
+
+/// Support for std::optional, useful in attribute/type definition where the enum is
+/// used as:
+///
+///    let parameters = (ins OptionalParameter<"std::optional<TheEnumName>">:$value);
+template<>
+struct FieldParser<std::optional<{0}>, std::optional<{0}>> {{
+  static constexpr bool isKeyValueCompositional = false;
+
+  static FailureOr<std::optional<{0}>> parse(AsmParser &parser);
+};
+} // namespace mlir
+)";
+
+  std::string isKeyValueCompositional;
+  if (enumInfo.isNewStyleBitEnum()) {
+    StringRef separator = enumInfo.getDef().getValueAsString("separator");
+    isKeyValueCompositional =
+        formatv("\n  static constexpr bool isKeyValueCompositional = {0};\n",
+                separator.trim() == "," ? "false" : "true");
+  }
+  os << formatv(fieldParserDecls, qualName, isKeyValueCompositional);
+}
+
+/// Emits the definitions of the 'FieldParser' specializations declared by
+/// 'emitFieldParserDecls'.
+static void emitFieldParserDefs(const EnumInfo &enumInfo, raw_ostream &os) {
+  std::string qualName = enumInfo.getQualifiedName();
+  StringRef cppNamespace = enumInfo.getCppNamespace();
   auto cases = enumInfo.getAllCases();
+  BitVector nonKeywordCases = getNonKeywordCases(cases);
 
-  // Check which cases shouldn't be printed using a keyword.
-  llvm::BitVector nonKeywordCases(cases.size());
   std::string casesList;
   llvm::raw_string_ostream caseListOs(casesList);
   caseListOs << "[";
   llvm::interleaveComma(llvm::enumerate(cases), caseListOs,
                         [&](auto enumerant) {
                           StringRef name = enumerant.value().getStr();
-                          if (!mlir::tblgen::canFormatStringAsKeyword(name)) {
-                            nonKeywordCases.set(enumerant.index());
+                          if (nonKeywordCases.test(enumerant.index()))
                             caseListOs << "\\\"" << name << "\\\"";
-                          }
                           caseListOs << name;
                         });
   caseListOs << "]";
@@ -99,151 +144,125 @@ static void emitParserPrinter(const EnumInfo &enumInfo, StringRef qualName,
                         });
   casesInitListOs << "}";
 
-  // Generate the parser and the start of the printer for the enum, excluding
-  // non-quoted bit enums.
-  const char *parsedAndPrinterStart = R"(
+  // Generate the parser for the enum, excluding non-quoted bit enums.
+  const char *const fieldParserDefs = R"(
 namespace mlir {
-template <typename T, typename>
-struct FieldParser;
+FailureOr<{0}> FieldParser<{0}, {0}>::parse(AsmParser &parser) {{
+  // Parse the keyword/string containing the enum.
+  std::string enumKeyword;
+  auto loc = parser.getCurrentLocation();
+  if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))
+    return parser.emitError(loc, "expected keyword for {2}");
+
+  // Symbolize the keyword.
+  if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))
+    return *attr;
+  return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
+}
 
-template<>
-struct FieldParser<{0}, {0}> {{
-  template <typename ParserT>
-  static FailureOr<{0}> parse(ParserT &parser) {{
-    // Parse the keyword/string containing the enum.
-    std::string enumKeyword;
+FailureOr<std::optional<{0}>>
+FieldParser<std::optional<{0}>, std::optional<{0}>>::parse(AsmParser &parser) {{
+  // Parse the keyword/string containing the enum.
+  std::string enumKeyword;
+  auto loc = parser.getCurrentLocation();
+  if (failed(parser.parseOptionalKeywordOrString(&enumKeyword, {4})))
+    return std::optional<{0}>{{};
+
+  // Symbolize the keyword.
+  if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))
+    return attr;
+  return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
+}
+} // namespace mlir
+)";
+
+  const char *const fieldParserDefsUnquotedBitEnum = R"(
+namespace mlir {
+FailureOr<{0}> FieldParser<{0}, {0}>::parse(AsmParser &parser) {{
+  {0} flags = {{};
+  do {{
+    // Parse the keyword containing a part of the enum.
+    ::llvm::StringRef enumKeyword;
     auto loc = parser.getCurrentLocation();
-    if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))
+    if (failed(parser.parseOptionalKeyword(&enumKeyword))) {{
       return parser.emitError(loc, "expected keyword for {2}");
+    }
 
     // Symbolize the keyword.
-    if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))
-      return *attr;
-    return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
-  }
-};
-
-/// Support for std::optional, useful in attribute/type definition where the enum is
-/// used as:
-///
-///    let parameters = (ins OptionalParameter<"std::optional<TheEnumName>">:$value);
-template<>
-struct FieldParser<std::optional<{0}>, std::optional<{0}>> {{
-  static constexpr bool isKeyValueCompositional = false;
+    if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{
+      flags = flags | *flag;
+    } else {{
+      return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
+    }
+  } while (::mlir::succeeded(parser.{5}()));
+  return flags;
+}
 
-  template <typename ParserT>
-  static FailureOr<std::optional<{0}>> parse(ParserT &parser) {{
-    // Parse the keyword/string containing the enum.
-    std::string enumKeyword;
+FailureOr<std::optional<{0}>>
+FieldParser<std::optional<{0}>, std::optional<{0}>>::parse(AsmParser &parser) {{
+  {0} flags = {{};
+  bool firstIter = true;
+  do {{
+    // Parse the keyword containing a part of the enum.
+    ::llvm::StringRef enumKeyword;
     auto loc = parser.getCurrentLocation();
-    if (failed(parser.parseOptionalKeywordOrString(&enumKeyword, {4})))
-      return std::optional<{0}>{{};
+    if (failed(parser.parseOptionalKeyword(&enumKeyword, {6}))) {{
+      if (firstIter)
+        return std::optional<{0}>{{};
+      return parser.emitError(loc, "expected keyword for {2} after '{4}'");
+    }
+    firstIter = false;
 
     // Symbolize the keyword.
-    if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))
-      return attr;
-    return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
-  }
-};
+    if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{
+      flags = flags | *flag;
+    } else {{
+      return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
+    }
+  } while(::mlir::succeeded(parser.{5}()));
+  return std::optional<{0}>{{flags};
+}
 } // namespace mlir
-
-namespace llvm {
-inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{
-  auto valueStr = stringifyEnum(value);
 )";
 
-  const char *parsedAndPrinterStartUnquotedBitEnum = R"(
-  namespace mlir {
-  template <typename T, typename>
-  struct FieldParser;
-
-  template<>
-  struct FieldParser<{0}, {0}> {{
-    static constexpr bool isKeyValueCompositional = {7};
-
-    template <typename ParserT>
-    static FailureOr<{0}> parse(ParserT &parser) {{
-      {0} flags = {{};
-      do {{
-        // Parse the keyword containing a part of the enum.
-        ::llvm::StringRef enumKeyword;
-        auto loc = parser.getCurrentLocation();
-        if (failed(parser.parseOptionalKeyword(&enumKeyword))) {{
-          return parser.emitError(loc, "expected keyword for {2}");
-        }
-
-        // Symbolize the keyword.
-        if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{
-          flags = flags | *flag;
-        } else {{
-          return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
-        }
-      } while (::mlir::succeeded(parser.{5}()));
-      return flags;
-    }
-  };
-
-  /// Support for std::optional, useful in attribute/type definition where the enum is
-  /// used as:
-  ///
-  ///    let parameters = (ins OptionalParameter<"std::optional<TheEnumName>">:$value);
-  template<>
-  struct FieldParser<std::optional<{0}>, std::optional<{0}>> {{
-    static constexpr bool isKeyValueCompositional = false;
-
-    template <typename ParserT>
-    static FailureOr<std::optional<{0}>> parse(ParserT &parser) {{
-      {0} flags = {{};
-      bool firstIter = true;
-      do {{
-        // Parse the keyword containing a part of the enum.
-        ::llvm::StringRef enumKeyword;
-        auto loc = parser.getCurrentLocation();
-        if (failed(parser.parseOptionalKeyword(&enumKeyword, {6}))) {{
-          if (firstIter)
-            return std::optional<{0}>{{};
-          return parser.emitError(loc, "expected keyword for {2} after '{4}'");
-        }
-        firstIter = false;
-
-        // Symbolize the keyword.
-        if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{
-          flags = flags | *flag;
-        } else {{
-          return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;
-        }
-      } while(::mlir::succeeded(parser.{5}()));
-      return std::optional<{0}>{{flags};
-    }
-  };
-  } // namespace mlir
-
-  namespace llvm {
-  inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{
-    auto valueStr = stringifyEnum(value);
-  )";
-
-  bool isNewStyleBitEnum =
-      enumInfo.isBitEnum() && !enumInfo.printBitEnumQuoted();
-
-  if (isNewStyleBitEnum) {
-    if (nonKeywordCases.any())
-      return PrintFatalError(
-          "bit enum " + qualName +
-          " cannot be printed unquoted with cases that cannot be keywords");
+  if (enumInfo.isNewStyleBitEnum()) {
     StringRef separator = enumInfo.getDef().getValueAsString("separator");
     StringRef parseSeparatorFn =
         llvm::StringSwitch<StringRef>(separator.trim())
             .Case("|", "parseOptionalVerticalBar")
             .Case(",", "parseOptionalComma")
             .Default("error, enum separator must be '|' or ','");
-    os << formatv(parsedAndPrinterStartUnquotedBitEnum, qualName, cppNamespace,
+    os << formatv(fieldParserDefsUnquotedBitEnum, qualName, cppNamespace,
                   enumInfo.getSummary(), casesList, separator, parseSeparatorFn,
-                  casesInitList, separator.trim() == "," ? "false" : "true");
+                  casesInitList);
   } else {
-    os << formatv(parsedAndPrinterStart, qualName, cppNamespace,
+    os << formatv(fieldParserDefs, qualName, cppNamespace,
                   enumInfo.getSummary(), casesList, casesInitList);
   }
+}
+
+static void emitParserPrinter(const EnumInfo &enumInfo, StringRef qualName,
+                              raw_ostream &os) {
+  if (!enumInfo.hasParserPrinter())
+    return;
+
+  auto cases = enumInfo.getAllCases();
+  llvm::BitVector nonKeywordCases = getNonKeywordCases(cases);
+
+  if (enumInfo.isNewStyleBitEnum() && nonKeywordCases.any())
+    return PrintFatalError(
+        "bit enum " + qualName +
+        " cannot be printed unquoted with cases that cannot be keywords");
+
+  emitFieldParserDecls(enumInfo, qualName, os);
+
+  // Generate the start of the printer for the enum.
+  const char *const printerStart = R"(
+namespace llvm {
+inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{
+  auto valueStr = stringifyEnum(value);
+)";
+  os << formatv(printerStart, qualName);
 
   // If all cases require a string, always wrap.
   if (nonKeywordCases.all()) {
@@ -274,7 +293,7 @@ inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{
     // Only do this if we're using the old-style parser that parses the enum as
     // one keyword, as opposed to the new form, where we can print the value
     // as-is.
-  } else if (enumInfo.isBitEnum() && !isNewStyleBitEnum) {
+  } else if (enumInfo.isBitEnum() && !enumInfo.isNewStyleBitEnum()) {
     // Process the known multi-bit cases that use valid keywords.
     SmallVector<EnumCase *> validMultiBitCases;
     for (auto [index, caseVal] : llvm::enumerate(cases)) {
@@ -776,9 +795,8 @@ class {1} : public ::mlir::{2} {
   } // close `ns`.
 
   // Generate a generic parser and printer for the enum.
-  std::string qualName =
-      std::string(formatv("{0}::{1}", cppNamespace, enumName));
-  emitParserPrinter(enumInfo, qualName, cppNamespace, os);
+  std::string qualName = enumInfo.getQualifiedName();
+  emitParserPrinter(enumInfo, qualName, os);
 
   // Emit DenseMapInfo for this enum class
   emitDenseMapInfo(qualName, underlyingType, cppNamespace, os);
@@ -787,8 +805,21 @@ class {1} : public ::mlir::{2} {
 static bool emitEnumDecls(const RecordKeeper &records, raw_ostream &os) {
   llvm::emitSourceFileHeader("Enum Utility Declarations", os, records);
 
-  for (const Record *def :
-       records.getAllDerivedDefinitionsIfDefined("EnumInfo"))
+  ArrayRef<const Record *> defs =
+      records.getAllDerivedDefinitionsIfDefined("EnumInfo");
+  if (llvm::any_of(defs, [](const Record *def) {
+        return EnumInfo(def).hasParserPrinter();
+      }))
+    os << R"(
+namespace mlir {
+class AsmParser;
+
+template <typename T, typename>
+struct FieldParser;
+} // namespace mlir
+)";
+
+  for (const Record *def : defs)
     emitEnumDecl(*def, os);
 
   return false;
@@ -796,21 +827,28 @@ static bool emitEnumDecls(const RecordKeeper &records, raw_ostream &os) {
 
 static void emitEnumDef(const Record &enumDef, raw_ostream &os) {
   EnumInfo enumInfo(enumDef);
+  StringRef cppNamespace = enumInfo.getCppNamespace();
 
-  llvm::NamespaceEmitter ns(os, enumInfo.getCppNamespace());
+  {
+    llvm::NamespaceEmitter ns(os, cppNamespace);
 
-  if (enumInfo.isBitEnum()) {
-    emitSymToStrFnForBitEnum(enumDef, os);
-    emitStrToSymFnForBitEnum(enumDef, os);
-    emitUnderlyingToSymFnForBitEnum(enumDef, os);
-  } else {
-    emitSymToStrFnForIntEnum(enumDef, os);
-    emitStrToSymFnForIntEnum(enumDef, os);
-    emitUnderlyingToSymFnForIntEnum(enumDef, os);
+    if (enumInfo.isBitEnum()) {
+      emitSymToStrFnForBitEnum(enumDef, os);
+      emitStrToSymFnForBitEnum(enumDef, os);
+      emitUnderlyingToSymFnForBitEnum(enumDef, os);
+    } else {
+      emitSymToStrFnForIntEnum(enumDef, os);
+      emitStrToSymFnForIntEnum(enumDef, os);
+      emitUnderlyingToSymFnForIntEnum(enumDef, os);
+    }
+
+    if (enumInfo.genSpecializedAttr())
+      emitSpecializedAttrDef(enumDef, os);
   }
 
-  if (enumInfo.genSpecializedAttr())
-    emitSpecializedAttrDef(enumDef, os);
+  // Generate the definitions of the parsers declared in the header.
+  if (enumInfo.hasParserPrinter())
+    emitFieldParserDefs(enumInfo, os);
 }
 
 static bool emitEnumDefs(const RecordKeeper &records, raw_ostream &os) {
diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
index dd635aef1e3a2..1fd993cc197a9 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -3990,7 +3990,7 @@ OpFormatParser::verifyEnumAdjacentLiterals(SMLoc loc,
 
     Attribute baseAttr = attr->getVar()->attr.getBaseAttr();
     EnumInfo enumInfo(getEnumInfoRecord(baseAttr));
-    if (!enumInfo.isBitEnum() || enumInfo.printBitEnumQuoted())
+    if (!enumInfo.isNewStyleBitEnum())
       continue;
 
     auto remaining = elements.drop_front(index + 1);
diff --git a/mlir/unittests/TableGen/EnumsGenTest.cpp b/mlir/unittests/TableGen/EnumsGenTest.cpp
index 3bc02fc2cb3ae..377d41f85d604 100644
--- a/mlir/unittests/TableGen/EnumsGenTest.cpp
+++ b/mlir/unittests/TableGen/EnumsGenTest.cpp
@@ -9,6 +9,7 @@
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/OpImplementation.h"
 #include "mlir/Support/LLVM.h"
 
 #include "llvm/ADT/DenseMap.h"

>From 081ff9b0e14aaf905683032e48b8908588782542 Mon Sep 17 00:00:00 2001
From: Markus Boeck <mboeck at nvidia.com>
Date: Tue, 20 Oct 2026 08:15:21 +0000
Subject: [PATCH 2/2] missing includes

---
 mlir/lib/Dialect/SPIRV/IR/SPIRVEnums.cpp      | 1 +
 mlir/lib/Dialect/Utils/StructuredOpsUtils.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVEnums.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVEnums.cpp
index 3cab289998763..d6831f7f59cb0 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVEnums.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVEnums.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"
+#include "mlir/IR/OpImplementation.h"
 
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringExtras.h"
diff --git a/mlir/lib/Dialect/Utils/StructuredOpsUtils.cpp b/mlir/lib/Dialect/Utils/StructuredOpsUtils.cpp
index 4139b71033780..ab126997c8b86 100644
--- a/mlir/lib/Dialect/Utils/StructuredOpsUtils.cpp
+++ b/mlir/lib/Dialect/Utils/StructuredOpsUtils.cpp
@@ -11,6 +11,7 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/IRMapping.h"
+#include "mlir/IR/OpImplementation.h"
 #include "llvm/ADT/StringSet.h"
 
 #include "mlir/Dialect/Utils/DialectUtilsEnums.cpp.inc"



More information about the Mlir-commits mailing list