832 lines · cpp
1//===- EnumsGen.cpp - MLIR enum utility generator -------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// EnumsGen generates common utility functions for enums.10//11//===----------------------------------------------------------------------===//12 13#include "FormatGen.h"14#include "mlir/TableGen/Attribute.h"15#include "mlir/TableGen/EnumInfo.h"16#include "mlir/TableGen/Format.h"17#include "mlir/TableGen/GenInfo.h"18#include "llvm/ADT/BitVector.h"19#include "llvm/ADT/SmallVector.h"20#include "llvm/ADT/StringExtras.h"21#include "llvm/Support/FormatVariadic.h"22#include "llvm/Support/raw_ostream.h"23#include "llvm/TableGen/CodeGenHelpers.h"24#include "llvm/TableGen/Error.h"25#include "llvm/TableGen/Record.h"26#include "llvm/TableGen/TableGenBackend.h"27 28using llvm::formatv;29using llvm::isDigit;30using llvm::PrintFatalError;31using llvm::Record;32using llvm::RecordKeeper;33using namespace mlir;34using mlir::tblgen::Attribute;35using mlir::tblgen::EnumCase;36using mlir::tblgen::EnumInfo;37using mlir::tblgen::FmtContext;38using mlir::tblgen::tgfmt;39 40static std::string makeIdentifier(StringRef str) {41 if (!str.empty() && isDigit(static_cast<unsigned char>(str.front()))) {42 std::string newStr = std::string("_") + str.str();43 return newStr;44 }45 return str.str();46}47 48static void emitEnumClass(const Record &enumDef, StringRef enumName,49 StringRef underlyingType, StringRef description,50 ArrayRef<EnumCase> enumerants, raw_ostream &os) {51 os << "// " << description << "\n";52 os << "enum class " << enumName;53 54 if (!underlyingType.empty())55 os << " : " << underlyingType;56 os << " {\n";57 58 for (const EnumCase &enumerant : enumerants) {59 auto symbol = makeIdentifier(enumerant.getSymbol());60 auto value = enumerant.getValue();61 if (value >= 0)62 os << formatv(" {0} = {1},\n", symbol, value);63 else64 os << formatv(" {0},\n", symbol);65 }66 os << "};\n\n";67}68 69static void emitParserPrinter(const EnumInfo &enumInfo, StringRef qualName,70 StringRef cppNamespace, raw_ostream &os) {71 std::optional<Attribute> enumAttrInfo = enumInfo.asEnumAttr();72 if (enumInfo.getUnderlyingType().empty() ||73 (enumAttrInfo && enumAttrInfo->getConstBuilderTemplate().empty()))74 return;75 auto cases = enumInfo.getAllCases();76 77 // Check which cases shouldn't be printed using a keyword.78 llvm::BitVector nonKeywordCases(cases.size());79 std::string casesList;80 llvm::raw_string_ostream caseListOs(casesList);81 caseListOs << "[";82 llvm::interleaveComma(llvm::enumerate(cases), caseListOs,83 [&](auto enumerant) {84 StringRef name = enumerant.value().getStr();85 if (!mlir::tblgen::canFormatStringAsKeyword(name)) {86 nonKeywordCases.set(enumerant.index());87 caseListOs << "\\\"" << name << "\\\"";88 }89 caseListOs << name;90 });91 caseListOs << "]";92 93 // Generate the parser and the start of the printer for the enum, excluding94 // non-quoted bit enums.95 const char *parsedAndPrinterStart = R"(96namespace mlir {97template <typename T, typename>98struct FieldParser;99 100template<>101struct FieldParser<{0}, {0}> {{102 template <typename ParserT>103 static FailureOr<{0}> parse(ParserT &parser) {{104 // Parse the keyword/string containing the enum.105 std::string enumKeyword;106 auto loc = parser.getCurrentLocation();107 if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))108 return parser.emitError(loc, "expected keyword for {2}");109 110 // Symbolize the keyword.111 if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))112 return *attr;113 return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;114 }115};116 117/// Support for std::optional, useful in attribute/type definition where the enum is118/// used as:119///120/// let parameters = (ins OptionalParameter<"std::optional<TheEnumName>">:$value);121template<>122struct FieldParser<std::optional<{0}>, std::optional<{0}>> {{123 template <typename ParserT>124 static FailureOr<std::optional<{0}>> parse(ParserT &parser) {{125 // Parse the keyword/string containing the enum.126 std::string enumKeyword;127 auto loc = parser.getCurrentLocation();128 if (failed(parser.parseOptionalKeywordOrString(&enumKeyword)))129 return std::optional<{0}>{{};130 131 // Symbolize the keyword.132 if (::std::optional<{0}> attr = {1}::symbolizeEnum<{0}>(enumKeyword))133 return attr;134 return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;135 }136};137} // namespace mlir138 139namespace llvm {140inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{141 auto valueStr = stringifyEnum(value);142)";143 144 const char *parsedAndPrinterStartUnquotedBitEnum = R"(145 namespace mlir {146 template <typename T, typename>147 struct FieldParser;148 149 template<>150 struct FieldParser<{0}, {0}> {{151 template <typename ParserT>152 static FailureOr<{0}> parse(ParserT &parser) {{153 {0} flags = {{};154 do {{155 // Parse the keyword containing a part of the enum.156 ::llvm::StringRef enumKeyword;157 auto loc = parser.getCurrentLocation();158 if (failed(parser.parseOptionalKeyword(&enumKeyword))) {{159 return parser.emitError(loc, "expected keyword for {2}");160 }161 162 // Symbolize the keyword.163 if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{164 flags = flags | *flag;165 } else {{166 return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;167 }168 } while (::mlir::succeeded(parser.{5}()));169 return flags;170 }171 };172 173 /// Support for std::optional, useful in attribute/type definition where the enum is174 /// used as:175 ///176 /// let parameters = (ins OptionalParameter<"std::optional<TheEnumName>">:$value);177 template<>178 struct FieldParser<std::optional<{0}>, std::optional<{0}>> {{179 template <typename ParserT>180 static FailureOr<std::optional<{0}>> parse(ParserT &parser) {{181 {0} flags = {{};182 bool firstIter = true;183 do {{184 // Parse the keyword containing a part of the enum.185 ::llvm::StringRef enumKeyword;186 auto loc = parser.getCurrentLocation();187 if (failed(parser.parseOptionalKeyword(&enumKeyword))) {{188 if (firstIter)189 return std::optional<{0}>{{};190 return parser.emitError(loc, "expected keyword for {2} after '{4}'");191 }192 firstIter = false;193 194 // Symbolize the keyword.195 if (::std::optional<{0}> flag = {1}::symbolizeEnum<{0}>(enumKeyword)) {{196 flags = flags | *flag;197 } else {{198 return parser.emitError(loc, "expected one of {3} for {2}, got: ") << enumKeyword;199 }200 } while(::mlir::succeeded(parser.{5}()));201 return std::optional<{0}>{{flags};202 }203 };204 } // namespace mlir205 206 namespace llvm {207 inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, {0} value) {{208 auto valueStr = stringifyEnum(value);209 )";210 211 bool isNewStyleBitEnum =212 enumInfo.isBitEnum() && !enumInfo.printBitEnumQuoted();213 214 if (isNewStyleBitEnum) {215 if (nonKeywordCases.any())216 return PrintFatalError(217 "bit enum " + qualName +218 " cannot be printed unquoted with cases that cannot be keywords");219 StringRef separator = enumInfo.getDef().getValueAsString("separator");220 StringRef parseSeparatorFn =221 llvm::StringSwitch<StringRef>(separator.trim())222 .Case("|", "parseOptionalVerticalBar")223 .Case(",", "parseOptionalComma")224 .Default("error, enum separator must be '|' or ','");225 os << formatv(parsedAndPrinterStartUnquotedBitEnum, qualName, cppNamespace,226 enumInfo.getSummary(), casesList, separator,227 parseSeparatorFn);228 } else {229 os << formatv(parsedAndPrinterStart, qualName, cppNamespace,230 enumInfo.getSummary(), casesList);231 }232 233 // If all cases require a string, always wrap.234 if (nonKeywordCases.all()) {235 os << " return p << '\"' << valueStr << '\"';\n"236 "}\n"237 "} // namespace llvm\n";238 return;239 }240 241 // If there are any cases that can't be used with a keyword, switch on the242 // case value to determine when to print in the string form.243 if (nonKeywordCases.any()) {244 os << " switch (value) {\n";245 for (auto it : llvm::enumerate(cases)) {246 if (nonKeywordCases.test(it.index()))247 continue;248 StringRef symbol = it.value().getSymbol();249 os << llvm::formatv(" case {0}::{1}:\n", qualName,250 makeIdentifier(symbol));251 }252 os << " break;\n"253 " default:\n"254 " return p << '\"' << valueStr << '\"';\n"255 " }\n";256 257 // If this is a bit enum, conservatively print the string form if the value258 // is not a power of two (i.e. not a single bit case) and not a known case.259 // Only do this if we're using the old-style parser that parses the enum as260 // one keyword, as opposed to the new form, where we can print the value261 // as-is.262 } else if (enumInfo.isBitEnum() && !isNewStyleBitEnum) {263 // Process the known multi-bit cases that use valid keywords.264 SmallVector<EnumCase *> validMultiBitCases;265 for (auto [index, caseVal] : llvm::enumerate(cases)) {266 uint64_t value = caseVal.getValue();267 if (value && !llvm::has_single_bit(value) && !nonKeywordCases.test(index))268 validMultiBitCases.push_back(&caseVal);269 }270 if (!validMultiBitCases.empty()) {271 os << " switch (value) {\n";272 for (EnumCase *caseVal : validMultiBitCases) {273 StringRef symbol = caseVal->getSymbol();274 os << llvm::formatv(" case {0}::{1}:\n", qualName,275 llvm::isDigit(symbol.front()) ? ("_" + symbol)276 : symbol);277 }278 os << " return p << valueStr;\n"279 " default:\n"280 " break;\n"281 " }\n";282 }283 284 // All other multi-bit cases should be printed as strings.285 os << formatv(" auto underlyingValue = "286 "static_cast<std::make_unsigned_t<{0}>>(value);\n",287 qualName);288 os << " if (underlyingValue && !llvm::has_single_bit(underlyingValue))\n"289 " return p << '\"' << valueStr << '\"';\n";290 }291 os << " return p << valueStr;\n"292 "}\n"293 "} // namespace llvm\n";294}295 296static void emitDenseMapInfo(StringRef qualName, std::string underlyingType,297 StringRef cppNamespace, raw_ostream &os) {298 if (underlyingType.empty())299 underlyingType =300 std::string(formatv("std::underlying_type_t<{0}>", qualName));301 302 const char *const mapInfo = R"(303namespace llvm {304template<> struct DenseMapInfo<{0}> {{305 using StorageInfo = ::llvm::DenseMapInfo<{1}>;306 307 static inline {0} getEmptyKey() {{308 return static_cast<{0}>(StorageInfo::getEmptyKey());309 }310 311 static inline {0} getTombstoneKey() {{312 return static_cast<{0}>(StorageInfo::getTombstoneKey());313 }314 315 static unsigned getHashValue(const {0} &val) {{316 return StorageInfo::getHashValue(static_cast<{1}>(val));317 }318 319 static bool isEqual(const {0} &lhs, const {0} &rhs) {{320 return lhs == rhs;321 }322};323})";324 os << formatv(mapInfo, qualName, underlyingType);325 os << "\n\n";326}327 328static void emitMaxValueFn(const Record &enumDef, raw_ostream &os) {329 EnumInfo enumInfo(enumDef);330 StringRef maxEnumValFnName = enumInfo.getMaxEnumValFnName();331 auto enumerants = enumInfo.getAllCases();332 333 unsigned maxEnumVal = 0;334 for (const auto &enumerant : enumerants) {335 int64_t value = enumerant.getValue();336 // Avoid generating the max value function if there is an enumerant without337 // explicit value.338 if (value < 0)339 return;340 341 maxEnumVal = std::max(maxEnumVal, static_cast<unsigned>(value));342 }343 344 // Emit the function to return the max enum value345 os << formatv("inline constexpr unsigned {0}() {{\n", maxEnumValFnName);346 os << formatv(" return {0};\n", maxEnumVal);347 os << "}\n\n";348}349 350// Returns the EnumCase whose value is zero if exists; returns std::nullopt351// otherwise.352static std::optional<EnumCase>353getAllBitsUnsetCase(llvm::ArrayRef<EnumCase> cases) {354 for (auto attrCase : cases) {355 if (attrCase.getValue() == 0)356 return attrCase;357 }358 return std::nullopt;359}360 361// Emits the following inline function for bit enums:362//363// inline constexpr <enum-type> operator|(<enum-type> a, <enum-type> b);364// inline constexpr <enum-type> operator&(<enum-type> a, <enum-type> b);365// inline constexpr <enum-type> operator^(<enum-type> a, <enum-type> b);366// inline constexpr <enum-type> &operator|=(<enum-type> &a, <enum-type> b);367// inline constexpr <enum-type> &operator&=(<enum-type> &a, <enum-type> b);368// inline constexpr <enum-type> &operator^=(<enum-type> &a, <enum-type> b);369// inline constexpr <enum-type> operator~(<enum-type> bits);370// inline constexpr bool bitEnumContainsAll(<enum-type> bits, <enum-type> bit);371// inline constexpr bool bitEnumContainsAny(<enum-type> bits, <enum-type> bit);372// inline constexpr <enum-type> bitEnumClear(<enum-type> bits, <enum-type> bit);373// inline constexpr <enum-type> bitEnumSet(<enum-type> bits, <enum-type> bit,374// bool value=true);375static void emitOperators(const Record &enumDef, raw_ostream &os) {376 EnumInfo enumInfo(enumDef);377 StringRef enumName = enumInfo.getEnumClassName();378 std::string underlyingType = std::string(enumInfo.getUnderlyingType());379 int64_t validBits = enumDef.getValueAsInt("validBits");380 const char *const operators = R"(381inline constexpr {0} operator|({0} a, {0} b) {{382 return static_cast<{0}>(static_cast<{1}>(a) | static_cast<{1}>(b));383}384inline constexpr {0} operator&({0} a, {0} b) {{385 return static_cast<{0}>(static_cast<{1}>(a) & static_cast<{1}>(b));386}387inline constexpr {0} operator^({0} a, {0} b) {{388 return static_cast<{0}>(static_cast<{1}>(a) ^ static_cast<{1}>(b));389}390inline constexpr {0} &operator|=({0} &a, {0} b) {{391 return a = a | b;392}393inline constexpr {0} &operator&=({0} &a, {0} b) {{394 return a = a & b;395}396inline constexpr {0} &operator^=({0} &a, {0} b) {{397 return a = a ^ b;398}399inline constexpr {0} operator~({0} bits) {{400 // Ensure only bits that can be present in the enum are set401 return static_cast<{0}>(~static_cast<{1}>(bits) & static_cast<{1}>({2}u));402}403inline constexpr bool bitEnumContainsAll({0} bits, {0} bit) {{404 return (bits & bit) == bit;405}406inline constexpr bool bitEnumContainsAny({0} bits, {0} bit) {{407 return (static_cast<{1}>(bits) & static_cast<{1}>(bit)) != 0;408}409inline constexpr {0} bitEnumClear({0} bits, {0} bit) {{410 return bits & ~bit;411}412inline constexpr {0} bitEnumSet({0} bits, {0} bit, /*optional*/bool value=true) {{413 return value ? (bits | bit) : bitEnumClear(bits, bit);414}415 )";416 os << formatv(operators, enumName, underlyingType, validBits);417}418 419static void emitSymToStrFnForIntEnum(const Record &enumDef, raw_ostream &os) {420 EnumInfo enumInfo(enumDef);421 StringRef enumName = enumInfo.getEnumClassName();422 StringRef symToStrFnName = enumInfo.getSymbolToStringFnName();423 StringRef symToStrFnRetType = enumInfo.getSymbolToStringFnRetType();424 auto enumerants = enumInfo.getAllCases();425 426 os << formatv("{2} {1}({0} val) {{\n", enumName, symToStrFnName,427 symToStrFnRetType);428 os << " switch (val) {\n";429 for (const auto &enumerant : enumerants) {430 auto symbol = enumerant.getSymbol();431 auto str = enumerant.getStr();432 os << formatv(" case {0}::{1}: return \"{2}\";\n", enumName,433 makeIdentifier(symbol), str);434 }435 os << " }\n";436 os << " return \"\";\n";437 os << "}\n\n";438}439 440static void emitSymToStrFnForBitEnum(const Record &enumDef, raw_ostream &os) {441 EnumInfo enumInfo(enumDef);442 StringRef enumName = enumInfo.getEnumClassName();443 StringRef symToStrFnName = enumInfo.getSymbolToStringFnName();444 StringRef symToStrFnRetType = enumInfo.getSymbolToStringFnRetType();445 StringRef separator = enumDef.getValueAsString("separator");446 auto enumerants = enumInfo.getAllCases();447 auto allBitsUnsetCase = getAllBitsUnsetCase(enumerants);448 449 os << formatv("{2} {1}({0} symbol) {{\n", enumName, symToStrFnName,450 symToStrFnRetType);451 452 os << formatv(" auto val = static_cast<{0}>(symbol);\n",453 enumInfo.getUnderlyingType());454 // If we have unknown bit set, return an empty string to signal errors.455 int64_t validBits = enumDef.getValueAsInt("validBits");456 os << formatv(" assert({0}u == ({0}u | val) && \"invalid bits set in bit "457 "enum\");\n",458 validBits);459 if (allBitsUnsetCase) {460 os << " // Special case for all bits unset.\n";461 os << formatv(" if (val == 0) return \"{0}\";\n\n",462 allBitsUnsetCase->getStr());463 }464 os << " ::llvm::SmallVector<::llvm::StringRef, 2> strs;\n";465 466 // Add case string if the value has all case bits, and remove them to avoid467 // printing again. Used only for groups, when printBitEnumPrimaryGroups is 1.468 const char *const formatCompareRemove = R"(469 if ({0}u == ({0}u & val)) {{470 strs.push_back("{1}");471 val &= ~static_cast<{2}>({0});472 }473)";474 // Add case string if the value has all case bits. Used for individual bit475 // cases, and for groups when printBitEnumPrimaryGroups is 0.476 const char *const formatCompare = R"(477 if ({0}u == ({0}u & val))478 strs.push_back("{1}");479)";480 // Optionally elide bits that are members of groups that will also be printed481 // for more concise output.482 if (enumInfo.printBitEnumPrimaryGroups()) {483 os << " // Print bit enum groups before individual bits\n";484 // Emit comparisons for group bit cases in reverse tablegen declaration485 // order, removing bits for groups with all bits present.486 for (const auto &enumerant : llvm::reverse(enumerants)) {487 if ((enumerant.getValue() != 0) &&488 (enumerant.getDef().isSubClassOf("BitEnumCaseGroup") ||489 enumerant.getDef().isSubClassOf("BitEnumAttrCaseGroup"))) {490 os << formatv(formatCompareRemove, enumerant.getValue(),491 enumerant.getStr(), enumInfo.getUnderlyingType());492 }493 }494 // Emit comparisons for individual bit cases in tablegen declaration order.495 for (const auto &enumerant : enumerants) {496 if ((enumerant.getValue() != 0) &&497 (enumerant.getDef().isSubClassOf("BitEnumCaseBit") ||498 enumerant.getDef().isSubClassOf("BitEnumAttrCaseBit")))499 os << formatv(formatCompare, enumerant.getValue(), enumerant.getStr());500 }501 } else {502 // Emit comparisons for ALL nonzero cases (individual bits and groups) in503 // tablegen declaration order.504 for (const auto &enumerant : enumerants) {505 if (enumerant.getValue() != 0)506 os << formatv(formatCompare, enumerant.getValue(), enumerant.getStr());507 }508 }509 os << formatv(" return ::llvm::join(strs, \"{0}\");\n", separator);510 511 os << "}\n\n";512}513 514static void emitStrToSymFnForIntEnum(const Record &enumDef, raw_ostream &os) {515 EnumInfo enumInfo(enumDef);516 StringRef enumName = enumInfo.getEnumClassName();517 StringRef strToSymFnName = enumInfo.getStringToSymbolFnName();518 auto enumerants = enumInfo.getAllCases();519 520 os << formatv("::std::optional<{0}> {1}(::llvm::StringRef str) {{\n",521 enumName, strToSymFnName);522 os << formatv(" return ::llvm::StringSwitch<::std::optional<{0}>>(str)\n",523 enumName);524 for (const auto &enumerant : enumerants) {525 auto symbol = enumerant.getSymbol();526 auto str = enumerant.getStr();527 os << formatv(" .Case(\"{1}\", {0}::{2})\n", enumName, str,528 makeIdentifier(symbol));529 }530 os << " .Default(::std::nullopt);\n";531 os << "}\n";532}533 534static void emitStrToSymFnForBitEnum(const Record &enumDef, raw_ostream &os) {535 EnumInfo enumInfo(enumDef);536 StringRef enumName = enumInfo.getEnumClassName();537 std::string underlyingType = std::string(enumInfo.getUnderlyingType());538 StringRef strToSymFnName = enumInfo.getStringToSymbolFnName();539 StringRef separator = enumDef.getValueAsString("separator");540 StringRef separatorTrimmed = separator.trim();541 auto enumerants = enumInfo.getAllCases();542 auto allBitsUnsetCase = getAllBitsUnsetCase(enumerants);543 544 os << formatv("::std::optional<{0}> {1}(::llvm::StringRef str) {{\n",545 enumName, strToSymFnName);546 547 if (allBitsUnsetCase) {548 os << " // Special case for all bits unset.\n";549 StringRef caseSymbol = allBitsUnsetCase->getSymbol();550 os << formatv(" if (str == \"{1}\") return {0}::{2};\n\n", enumName,551 allBitsUnsetCase->getStr(), makeIdentifier(caseSymbol));552 }553 554 // Split the string to get symbols for all the bits.555 os << " ::llvm::SmallVector<::llvm::StringRef, 2> symbols;\n";556 // Remove whitespace from the separator string when parsing.557 os << formatv(" str.split(symbols, \"{0}\");\n\n", separatorTrimmed);558 559 os << formatv(" {0} val = 0;\n", underlyingType);560 os << " for (auto symbol : symbols) {\n";561 562 // Convert each symbol to the bit ordinal and set the corresponding bit.563 os << formatv(" auto bit = "564 "llvm::StringSwitch<::std::optional<{0}>>(symbol.trim())\n",565 underlyingType);566 for (const auto &enumerant : enumerants) {567 // Skip the special enumerant for None.568 if (auto val = enumerant.getValue())569 os.indent(6) << formatv(".Case(\"{0}\", {1})\n", enumerant.getStr(), val);570 }571 os.indent(6) << ".Default(::std::nullopt);\n";572 573 os << " if (bit) { val |= *bit; } else { return ::std::nullopt; }\n";574 os << " }\n";575 576 os << formatv(" return static_cast<{0}>(val);\n", enumName);577 os << "}\n\n";578}579 580static void emitUnderlyingToSymFnForIntEnum(const Record &enumDef,581 raw_ostream &os) {582 EnumInfo enumInfo(enumDef);583 StringRef enumName = enumInfo.getEnumClassName();584 std::string underlyingType = std::string(enumInfo.getUnderlyingType());585 StringRef underlyingToSymFnName = enumInfo.getUnderlyingToSymbolFnName();586 auto enumerants = enumInfo.getAllCases();587 588 // Avoid generating the underlying value to symbol conversion function if589 // there is an enumerant without explicit value.590 if (llvm::any_of(enumerants,591 [](EnumCase enumerant) { return enumerant.getValue() < 0; }))592 return;593 594 os << formatv("::std::optional<{0}> {1}({2} value) {{\n", enumName,595 underlyingToSymFnName,596 underlyingType.empty() ? std::string("unsigned")597 : underlyingType)598 << " switch (value) {\n";599 for (const auto &enumerant : enumerants) {600 auto symbol = enumerant.getSymbol();601 auto value = enumerant.getValue();602 os << formatv(" case {0}: return {1}::{2};\n", value, enumName,603 makeIdentifier(symbol));604 }605 os << " default: return ::std::nullopt;\n"606 << " }\n"607 << "}\n\n";608}609 610static void emitSpecializedAttrDef(const Record &enumDef, raw_ostream &os) {611 EnumInfo enumInfo(enumDef);612 StringRef enumName = enumInfo.getEnumClassName();613 StringRef attrClassName = enumInfo.getSpecializedAttrClassName();614 const Record *baseAttrDef = enumInfo.getBaseAttrClass();615 Attribute baseAttr(baseAttrDef);616 617 // Emit classof method618 619 os << formatv("bool {0}::classof(::mlir::Attribute attr) {{\n",620 attrClassName);621 622 mlir::tblgen::Pred baseAttrPred = baseAttr.getPredicate();623 if (baseAttrPred.isNull())624 PrintFatalError("ERROR: baseAttrClass for EnumAttr has no Predicate\n");625 626 std::string condition = baseAttrPred.getCondition();627 FmtContext verifyCtx;628 verifyCtx.withSelf("attr");629 os << tgfmt(" return $0;\n", /*ctx=*/nullptr, tgfmt(condition, &verifyCtx));630 631 os << "}\n";632 633 // Emit get method634 635 os << formatv("{0} {0}::get(::mlir::MLIRContext *context, {1} val) {{\n",636 attrClassName, enumName);637 638 StringRef underlyingType = enumInfo.getUnderlyingType();639 640 // Assuming that it is IntegerAttr constraint641 int64_t bitwidth = 64;642 if (baseAttrDef->getValue("valueType")) {643 auto *valueTypeDef = baseAttrDef->getValueAsDef("valueType");644 if (valueTypeDef->getValue("bitwidth"))645 bitwidth = valueTypeDef->getValueAsInt("bitwidth");646 }647 648 os << formatv(" ::mlir::IntegerType intType = "649 "::mlir::IntegerType::get(context, {0});\n",650 bitwidth);651 os << formatv(" ::mlir::IntegerAttr baseAttr = "652 "::mlir::IntegerAttr::get(intType, static_cast<{0}>(val));\n",653 underlyingType);654 os << formatv(" return ::llvm::cast<{0}>(baseAttr);\n", attrClassName);655 656 os << "}\n";657 658 // Emit getValue method659 660 os << formatv("{0} {1}::getValue() const {{\n", enumName, attrClassName);661 662 os << formatv(663 " return "664 "static_cast<{0}>(::mlir::IntegerAttr::getValue().getZExtValue());\n",665 enumName);666 667 os << "}\n";668}669 670static void emitUnderlyingToSymFnForBitEnum(const Record &enumDef,671 raw_ostream &os) {672 EnumInfo enumInfo(enumDef);673 StringRef enumName = enumInfo.getEnumClassName();674 std::string underlyingType = std::string(enumInfo.getUnderlyingType());675 StringRef underlyingToSymFnName = enumInfo.getUnderlyingToSymbolFnName();676 auto enumerants = enumInfo.getAllCases();677 auto allBitsUnsetCase = getAllBitsUnsetCase(enumerants);678 679 os << formatv("::std::optional<{0}> {1}({2} value) {{\n", enumName,680 underlyingToSymFnName, underlyingType);681 if (allBitsUnsetCase) {682 os << " // Special case for all bits unset.\n";683 os << formatv(" if (value == 0) return {0}::{1};\n\n", enumName,684 makeIdentifier(allBitsUnsetCase->getSymbol()));685 }686 int64_t validBits = enumDef.getValueAsInt("validBits");687 os << formatv(" if (value & ~static_cast<{0}>({1}u)) return std::nullopt;\n",688 underlyingType, validBits);689 os << formatv(" return static_cast<{0}>(value);\n", enumName);690 os << "}\n";691}692 693static void emitEnumDecl(const Record &enumDef, raw_ostream &os) {694 EnumInfo enumInfo(enumDef);695 StringRef enumName = enumInfo.getEnumClassName();696 StringRef cppNamespace = enumInfo.getCppNamespace();697 std::string underlyingType = std::string(enumInfo.getUnderlyingType());698 StringRef description = enumInfo.getSummary();699 StringRef strToSymFnName = enumInfo.getStringToSymbolFnName();700 StringRef symToStrFnName = enumInfo.getSymbolToStringFnName();701 StringRef symToStrFnRetType = enumInfo.getSymbolToStringFnRetType();702 StringRef underlyingToSymFnName = enumInfo.getUnderlyingToSymbolFnName();703 auto enumerants = enumInfo.getAllCases();704 705 {706 llvm::NamespaceEmitter ns(os, cppNamespace);707 708 // Emit the enum class definition709 emitEnumClass(enumDef, enumName, underlyingType, description, enumerants,710 os);711 712 // Emit conversion function declarations713 if (llvm::all_of(enumerants, [](EnumCase enumerant) {714 return enumerant.getValue() >= 0;715 })) {716 os << formatv(717 "::std::optional<{0}> {1}({2});\n", enumName, underlyingToSymFnName,718 underlyingType.empty() ? std::string("unsigned") : underlyingType);719 }720 os << formatv("{2} {1}({0});\n", enumName, symToStrFnName,721 symToStrFnRetType);722 os << formatv("::std::optional<{0}> {1}(::llvm::StringRef);\n", enumName,723 strToSymFnName);724 725 if (enumInfo.isBitEnum()) {726 emitOperators(enumDef, os);727 } else {728 emitMaxValueFn(enumDef, os);729 }730 731 // Generate a generic `stringifyEnum` function that forwards to the method732 // specified by the user.733 const char *const stringifyEnumStr = R"(734inline {0} stringifyEnum({1} enumValue) {{735 return {2}(enumValue);736}737)";738 os << formatv(stringifyEnumStr, symToStrFnRetType, enumName,739 symToStrFnName);740 741 // Generate a generic `symbolizeEnum` function that forwards to the method742 // specified by the user.743 const char *const symbolizeEnumStr = R"(744template <typename EnumType>745::std::optional<EnumType> symbolizeEnum(::llvm::StringRef);746 747template <>748inline ::std::optional<{0}> symbolizeEnum<{0}>(::llvm::StringRef str) {749 return {1}(str);750}751)";752 os << formatv(symbolizeEnumStr, enumName, strToSymFnName);753 754 const char *const attrClassDecl = R"(755class {1} : public ::mlir::{2} {756public:757 using ValueType = {0};758 using ::mlir::{2}::{2};759 static bool classof(::mlir::Attribute attr);760 static {1} get(::mlir::MLIRContext *context, {0} val);761 {0} getValue() const;762};763)";764 if (enumInfo.genSpecializedAttr()) {765 StringRef attrClassName = enumInfo.getSpecializedAttrClassName();766 StringRef baseAttrClassName = "IntegerAttr";767 os << formatv(attrClassDecl, enumName, attrClassName, baseAttrClassName);768 }769 } // close `ns`.770 771 // Generate a generic parser and printer for the enum.772 std::string qualName =773 std::string(formatv("{0}::{1}", cppNamespace, enumName));774 emitParserPrinter(enumInfo, qualName, cppNamespace, os);775 776 // Emit DenseMapInfo for this enum class777 emitDenseMapInfo(qualName, underlyingType, cppNamespace, os);778}779 780static bool emitEnumDecls(const RecordKeeper &records, raw_ostream &os) {781 llvm::emitSourceFileHeader("Enum Utility Declarations", os, records);782 783 for (const Record *def :784 records.getAllDerivedDefinitionsIfDefined("EnumInfo"))785 emitEnumDecl(*def, os);786 787 return false;788}789 790static void emitEnumDef(const Record &enumDef, raw_ostream &os) {791 EnumInfo enumInfo(enumDef);792 793 llvm::NamespaceEmitter ns(os, enumInfo.getCppNamespace());794 795 if (enumInfo.isBitEnum()) {796 emitSymToStrFnForBitEnum(enumDef, os);797 emitStrToSymFnForBitEnum(enumDef, os);798 emitUnderlyingToSymFnForBitEnum(enumDef, os);799 } else {800 emitSymToStrFnForIntEnum(enumDef, os);801 emitStrToSymFnForIntEnum(enumDef, os);802 emitUnderlyingToSymFnForIntEnum(enumDef, os);803 }804 805 if (enumInfo.genSpecializedAttr())806 emitSpecializedAttrDef(enumDef, os);807}808 809static bool emitEnumDefs(const RecordKeeper &records, raw_ostream &os) {810 llvm::emitSourceFileHeader("Enum Utility Definitions", os, records);811 812 for (const Record *def :813 records.getAllDerivedDefinitionsIfDefined("EnumInfo"))814 emitEnumDef(*def, os);815 816 return false;817}818 819// Registers the enum utility generator to mlir-tblgen.820static mlir::GenRegistration821 genEnumDecls("gen-enum-decls", "Generate enum utility declarations",822 [](const RecordKeeper &records, raw_ostream &os) {823 return emitEnumDecls(records, os);824 });825 826// Registers the enum utility generator to mlir-tblgen.827static mlir::GenRegistration828 genEnumDefs("gen-enum-defs", "Generate enum utility definitions",829 [](const RecordKeeper &records, raw_ostream &os) {830 return emitEnumDefs(records, os);831 });832