[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