brintos

brintos / llvm-project-archived public Read only

0
0
Text · 195.9 KiB · 3b10842 Raw
5032 lines · cpp
1//===- OpDefinitionsGen.cpp - MLIR op definitions 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// OpDefinitionsGen uses the description of operations to generate C++10// definitions for ops.11//12//===----------------------------------------------------------------------===//13 14#include "CppGenUtilities.h"15#include "OpClass.h"16#include "OpFormatGen.h"17#include "OpGenHelpers.h"18#include "mlir/TableGen/Argument.h"19#include "mlir/TableGen/Attribute.h"20#include "mlir/TableGen/Builder.h"21#include "mlir/TableGen/Class.h"22#include "mlir/TableGen/CodeGenHelpers.h"23#include "mlir/TableGen/Format.h"24#include "mlir/TableGen/GenInfo.h"25#include "mlir/TableGen/Interfaces.h"26#include "mlir/TableGen/Operator.h"27#include "mlir/TableGen/Property.h"28#include "mlir/TableGen/Region.h"29#include "mlir/TableGen/SideEffects.h"30#include "mlir/TableGen/Successor.h"31#include "mlir/TableGen/Trait.h"32#include "llvm/ADT/BitVector.h"33#include "llvm/ADT/MapVector.h"34#include "llvm/ADT/PointerUnion.h"35#include "llvm/ADT/STLExtras.h"36#include "llvm/ADT/Sequence.h"37#include "llvm/ADT/SmallVector.h"38#include "llvm/ADT/StringExtras.h"39#include "llvm/ADT/StringMap.h"40#include "llvm/ADT/StringRef.h"41#include "llvm/ADT/StringSet.h"42#include "llvm/Support/Casting.h"43#include "llvm/Support/Debug.h"44#include "llvm/Support/ErrorHandling.h"45#include "llvm/Support/FormatVariadic.h"46#include "llvm/Support/Signals.h"47#include "llvm/Support/raw_ostream.h"48#include "llvm/TableGen/CodeGenHelpers.h"49#include "llvm/TableGen/Error.h"50#include "llvm/TableGen/Record.h"51#include "llvm/TableGen/TableGenBackend.h"52 53#define DEBUG_TYPE "mlir-tblgen-opdefgen"54 55using namespace llvm;56using namespace mlir;57using namespace mlir::tblgen;58 59static const char *const tblgenNamePrefix = "tblgen_";60static const char *const generatedArgName = "odsArg";61static const char *const odsBuilder = "odsBuilder";62static const char *const builderOpState = "odsState";63static const char *const propertyStorage = "propStorage";64static const char *const propertyValue = "propValue";65static const char *const propertyAttr = "propAttr";66static const char *const propertyDiag = "emitError";67 68/// The names of the implicit attributes that contain variadic operand and69/// result segment sizes.70static const char *const operandSegmentAttrName = "operandSegmentSizes";71static const char *const resultSegmentAttrName = "resultSegmentSizes";72 73/// Code for an Op to lookup an attribute. Uses cached identifiers and subrange74/// lookup.75///76/// {0}: Code snippet to get the attribute's name or identifier.77/// {1}: The lower bound on the sorted subrange.78/// {2}: The upper bound on the sorted subrange.79/// {3}: Code snippet to get the array of named attributes.80/// {4}: "Named" to get the named attribute.81static const char *const subrangeGetAttr =82    "::mlir::impl::get{4}AttrFromSortedRange({3}.begin() + {1}, {3}.end() - "83    "{2}, {0})";84 85/// The logic to calculate the actual value range for a declared operand/result86/// of an op with variadic operands/results. Note that this logic is not for87/// general use; it assumes all variadic operands/results must have the same88/// number of values.89///90/// {0}: The list of whether each declared operand/result is variadic.91/// {1}: The total number of non-variadic operands/results.92/// {2}: The total number of variadic operands/results.93/// {3}: The total number of actual values.94/// {4}: "operand" or "result".95static const char *const sameVariadicSizeValueRangeCalcCode = R"(96  bool isVariadic[] = {{{0}};97  int prevVariadicCount = 0;98  for (unsigned i = 0; i < index; ++i)99    if (isVariadic[i]) ++prevVariadicCount;100 101  // Calculate how many dynamic values a static variadic {4} corresponds to.102  // This assumes all static variadic {4}s have the same dynamic value count.103  int variadicSize = ({3} - {1}) / {2};104  // `index` passed in as the parameter is the static index which counts each105  // {4} (variadic or not) as size 1. So here for each previous static variadic106  // {4}, we need to offset by (variadicSize - 1) to get where the dynamic107  // value pack for this static {4} starts.108  int start = index + (variadicSize - 1) * prevVariadicCount;109  int size = isVariadic[index] ? variadicSize : 1;110  return {{start, size};111)";112 113/// The logic to calculate the actual value range for a declared operand/result114/// of an op with variadic operands/results. Note that this logic is assumes115/// the op has an attribute specifying the size of each operand/result segment116/// (variadic or not).117static const char *const attrSizedSegmentValueRangeCalcCode = R"(118  unsigned start = 0;119  for (unsigned i = 0; i < index; ++i)120    start += sizeAttr[i];121  return {start, sizeAttr[index]};122)";123/// The code snippet to initialize the sizes for the value range calculation.124///125/// {0}: The code to get the attribute.126static const char *const adapterSegmentSizeAttrInitCode = R"(127  assert({0} && "missing segment size attribute for op");128  auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});129)";130static const char *const adapterSegmentSizeAttrInitCodeProperties = R"(131  ::llvm::ArrayRef<int32_t> sizeAttr = {0};132)";133 134/// The code snippet to initialize the sizes for the value range calculation.135///136/// {0}: The code to get the attribute.137static const char *const opSegmentSizeAttrInitCode = R"(138  auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});139)";140 141/// The logic to calculate the actual value range for a declared operand142/// of an op with variadic of variadic operands within the OpAdaptor.143///144/// {0}: The name of the segment attribute.145/// {1}: The index of the main operand.146/// {2}: The range type of adaptor.147static const char *const variadicOfVariadicAdaptorCalcCode = R"(148  auto tblgenTmpOperands = getODSOperands({1});149  auto sizes = {0}();150 151  ::llvm::SmallVector<{2}> tblgenTmpOperandGroups;152  for (int i = 0, e = sizes.size(); i < e; ++i) {{153    tblgenTmpOperandGroups.push_back(tblgenTmpOperands.take_front(sizes[i]));154    tblgenTmpOperands = tblgenTmpOperands.drop_front(sizes[i]);155  }156  return tblgenTmpOperandGroups;157)";158 159/// The logic to build a range of either operand or result values.160///161/// {0}: The begin iterator of the actual values.162/// {1}: The call to generate the start and length of the value range.163static const char *const valueRangeReturnCode = R"(164  auto valueRange = {1};165  return {{std::next({0}, valueRange.first),166           std::next({0}, valueRange.first + valueRange.second)};167)";168 169/// Parse operand/result segment_size property.170/// {0}: Number of elements in the segment array171static const char *const parseTextualSegmentSizeFormat = R"(172  size_t i = 0;173  auto parseElem = [&]() -> ::mlir::ParseResult {174    if (i >= {0})175      return $_parser.emitError($_parser.getCurrentLocation(),176        "expected `]` after {0} segment sizes");177    if (failed($_parser.parseInteger($_storage[i])))178      return ::mlir::failure();179    i += 1;180    return ::mlir::success();181  };182  if (failed($_parser.parseCommaSeparatedList(183      ::mlir::AsmParser::Delimeter::Square, parseElem)))184    return failure();185  if (i < {0})186    return $_parser.emitError($_parser.getCurrentLocation(),187      "expected {0} segment sizes, found only ") << i;188  return success();189)";190 191static const char *const printTextualSegmentSize = R"(192  [&]() {193    $_printer << '[';194    ::llvm::interleaveComma($_storage, $_printer);195    $_printer << ']';196  }()197)";198 199/// Read operand/result segment_size from bytecode.200static const char *const readBytecodeSegmentSizeNative = R"(201  if ($_reader.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)202    return $_reader.readSparseArray(::llvm::MutableArrayRef($_storage));203)";204 205static const char *const readBytecodeSegmentSizeLegacy = R"(206  if ($_reader.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {207    auto &$_storage = prop.$_propName;208    ::mlir::DenseI32ArrayAttr attr;209    if (::mlir::failed($_reader.readAttribute(attr))) return ::mlir::failure();210    if (attr.size() > static_cast<int64_t>(sizeof($_storage) / sizeof(int32_t))) {211      $_reader.emitError("size mismatch for operand/result_segment_size");212      return ::mlir::failure();213    }214    ::llvm::copy(::llvm::ArrayRef<int32_t>(attr), $_storage.begin());215  }216)";217 218/// Write operand/result segment_size to bytecode.219static const char *const writeBytecodeSegmentSizeNative = R"(220  if ($_writer.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)221    $_writer.writeSparseArray(::llvm::ArrayRef($_storage));222)";223 224/// Write operand/result segment_size to bytecode.225static const char *const writeBytecodeSegmentSizeLegacy = R"(226if ($_writer.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {227  auto &$_storage = prop.$_propName;228  $_writer.writeAttribute(::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage));229}230)";231 232/// A header for indicating code sections.233///234/// {0}: Some text, or a class name.235/// {1}: Some text.236static const char *const opCommentHeader = R"(237//===----------------------------------------------------------------------===//238// {0} {1}239//===----------------------------------------------------------------------===//240 241)";242 243static const char *const inlineCreateBody = R"(244  ::mlir::OperationState __state__({0}, getOperationName());245  build(builder, __state__{1});246  auto __res__ = ::llvm::dyn_cast<{2}>(builder.create(__state__));247  assert(__res__ && "builder didn't return the right type");248  return __res__;249)";250 251static const char *const inlineCreateBodyImplicitLoc = R"(252  return create(builder, builder.getLoc(){0});253)";254 255//===----------------------------------------------------------------------===//256// Utility structs and functions257//===----------------------------------------------------------------------===//258 259// Replaces all occurrences of `match` in `str` with `substitute`.260static std::string replaceAllSubstrs(std::string str, const std::string &match,261                                     const std::string &substitute) {262  std::string::size_type scanLoc = 0, matchLoc = std::string::npos;263  while ((matchLoc = str.find(match, scanLoc)) != std::string::npos) {264    str = str.replace(matchLoc, match.size(), substitute);265    scanLoc = matchLoc + substitute.size();266  }267  return str;268}269 270// Returns whether the record has a value of the given name that can be returned271// via getValueAsString.272static inline bool hasStringAttribute(const Record &record,273                                      StringRef fieldName) {274  auto *valueInit = record.getValueInit(fieldName);275  return isa<StringInit>(valueInit);276}277 278static std::string getArgumentName(const Operator &op, int index) {279  const auto &operand = op.getOperand(index);280  if (!operand.name.empty())281    return std::string(operand.name);282  return std::string(formatv("{0}_{1}", generatedArgName, index));283}284 285// Returns true if we can use unwrapped value for the given `attr` in builders.286static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {287  return attr.getReturnType() != attr.getStorageType() &&288         // We need to wrap the raw value into an attribute in the builder impl289         // so we need to make sure that the attribute specifies how to do that.290         !attr.getConstBuilderTemplate().empty();291}292 293/// Build an attribute from a parameter value using the constant builder.294static std::string constBuildAttrFromParam(const tblgen::Attribute &attr,295                                           FmtContext &fctx,296                                           StringRef paramName) {297  std::string builderTemplate = attr.getConstBuilderTemplate().str();298 299  // For StringAttr, its constant builder call will wrap the input in300  // quotes, which is correct for normal string literals, but incorrect301  // here given we use function arguments. So we need to strip the302  // wrapping quotes.303  if (StringRef(builderTemplate).contains("\"$0\""))304    builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0");305 306  return tgfmt(builderTemplate, &fctx, paramName).str();307}308 309namespace {310/// Metadata on a registered attribute. Given that attributes are stored in311/// sorted order on operations, we can use information from ODS to deduce the312/// number of required attributes less and and greater than each attribute,313/// allowing us to search only a subrange of the attributes in ODS-generated314/// getters.315struct AttributeMetadata {316  /// The attribute name.317  StringRef attrName;318  /// Whether the attribute is required.319  bool isRequired;320  /// The ODS attribute constraint. Not present for implicit attributes.321  std::optional<Attribute> constraint;322  /// The number of required attributes less than this attribute.323  unsigned lowerBound = 0;324  /// The number of required attributes greater than this attribute.325  unsigned upperBound = 0;326};327 328/// Helper class to select between OpAdaptor and Op code templates.329class OpOrAdaptorHelper {330public:331  OpOrAdaptorHelper(const Operator &op, bool emitForOp)332      : op(op), emitForOp(emitForOp) {333    computeAttrMetadata();334  }335 336  /// Object that wraps a functor in a stream operator for interop with337  /// llvm::formatv.338  class Formatter {339  public:340    template <typename Functor>341    Formatter(Functor &&func) : func(std::forward<Functor>(func)) {}342 343    std::string str() const {344      std::string result;345      llvm::raw_string_ostream os(result);346      os << *this;347      return os.str();348    }349 350  private:351    std::function<raw_ostream &(raw_ostream &)> func;352 353    friend raw_ostream &operator<<(raw_ostream &os, const Formatter &fmt) {354      return fmt.func(os);355    }356  };357 358  // Generate code for getting an attribute.359  Formatter getAttr(StringRef attrName, bool isNamed = false) const {360    assert(attrMetadata.count(attrName) && "expected attribute metadata");361    return [this, attrName, isNamed](raw_ostream &os) -> raw_ostream & {362      const AttributeMetadata &attr = attrMetadata.find(attrName)->second;363      if (hasProperties()) {364        assert(!isNamed);365        return os << "getProperties()." << attrName;366      }367      return os << formatv(subrangeGetAttr, getAttrName(attrName),368                           attr.lowerBound, attr.upperBound, getAttrRange(),369                           isNamed ? "Named" : "");370    };371  }372 373  // Generate code for getting the name of an attribute.374  Formatter getAttrName(StringRef attrName) const {375    return [this, attrName](raw_ostream &os) -> raw_ostream & {376      if (emitForOp)377        return os << op.getGetterName(attrName) << "AttrName()";378      return os << formatv("{0}::{1}AttrName(*odsOpName)", op.getCppClassName(),379                           op.getGetterName(attrName));380    };381  }382 383  // Get the code snippet for getting the named attribute range.384  StringRef getAttrRange() const {385    return emitForOp ? "(*this)->getAttrs()" : "odsAttrs";386  }387 388  // Get the prefix code for emitting an error.389  Formatter emitErrorPrefix() const {390    return [this](raw_ostream &os) -> raw_ostream & {391      if (emitForOp)392        return os << "emitOpError(\"";393      return os << formatv("emitError(loc, \"'{0}' op ", op.getOperationName());394    };395  }396 397  // Get the call to get an operand or segment of operands.398  Formatter getOperand(unsigned index) const {399    return [this, index](raw_ostream &os) -> raw_ostream & {400      return os << formatv(op.getOperand(index).isVariadic()401                               ? "this->getODSOperands({0})"402                               : "(*this->getODSOperands({0}).begin())",403                           index);404    };405  }406 407  // Get the call to get a result of segment of results.408  Formatter getResult(unsigned index) const {409    return [this, index](raw_ostream &os) -> raw_ostream & {410      if (!emitForOp)411        return os << "<no results should be generated>";412      return os << formatv(op.getResult(index).isVariadic()413                               ? "this->getODSResults({0})"414                               : "(*this->getODSResults({0}).begin())",415                           index);416    };417  }418 419  // Return whether an op instance is available.420  bool isEmittingForOp() const { return emitForOp; }421 422  // Return the ODS operation wrapper.423  const Operator &getOp() const { return op; }424 425  // Get the attribute metadata sorted by name.426  const llvm::MapVector<StringRef, AttributeMetadata> &getAttrMetadata() const {427    return attrMetadata;428  }429 430  /// Returns whether to emit a `Properties` struct for this operation or not.431  bool hasProperties() const {432    if (!op.getProperties().empty())433      return true;434    if (!op.getDialect().usePropertiesForAttributes())435      return false;436    return true;437  }438 439  /// Returns whether the operation will have a non-empty `Properties` struct.440  bool hasNonEmptyPropertiesStruct() const {441    if (!op.getProperties().empty())442      return true;443    if (!hasProperties())444      return false;445    if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments") ||446        op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))447      return true;448    return llvm::any_of(getAttrMetadata(),449                        [](const std::pair<StringRef, AttributeMetadata> &it) {450                          return !it.second.constraint ||451                                 !it.second.constraint->isDerivedAttr();452                        });453  }454 455  std::optional<NamedProperty> &getOperandSegmentsSize() {456    return operandSegmentsSize;457  }458 459  std::optional<NamedProperty> &getResultSegmentsSize() {460    return resultSegmentsSize;461  }462 463  uint32_t getOperandSegmentSizesLegacyIndex() {464    return operandSegmentSizesLegacyIndex;465  }466 467  uint32_t getResultSegmentSizesLegacyIndex() {468    return resultSegmentSizesLegacyIndex;469  }470 471private:472  // Compute the attribute metadata.473  void computeAttrMetadata();474 475  // The operation ODS wrapper.476  const Operator &op;477  // True if code is being generate for an op. False for an adaptor.478  const bool emitForOp;479 480  // The attribute metadata, mapped by name.481  llvm::MapVector<StringRef, AttributeMetadata> attrMetadata;482 483  // Property484  std::optional<NamedProperty> operandSegmentsSize;485  std::string operandSegmentsSizeStorage;486  std::string operandSegmentsSizeParser;487  std::optional<NamedProperty> resultSegmentsSize;488  std::string resultSegmentsSizeStorage;489  std::string resultSegmentsSizeParser;490 491  // Indices to store the position in the emission order of the operand/result492  // segment sizes attribute if emitted as part of the properties for legacy493  // bytecode encodings, i.e. versions less than 6.494  uint32_t operandSegmentSizesLegacyIndex = 0;495  uint32_t resultSegmentSizesLegacyIndex = 0;496 497  // The number of required attributes.498  unsigned numRequired;499};500 501} // namespace502 503void OpOrAdaptorHelper::computeAttrMetadata() {504  // Enumerate the attribute names of this op, ensuring the attribute names are505  // unique in case implicit attributes are explicitly registered.506  for (const NamedAttribute &namedAttr : op.getAttributes()) {507    Attribute attr = namedAttr.attr;508    bool isOptional =509        attr.hasDefaultValue() || attr.isOptional() || attr.isDerivedAttr();510    attrMetadata.insert(511        {namedAttr.name, AttributeMetadata{namedAttr.name, !isOptional, attr}});512  }513 514  auto makeProperty = [&](StringRef storageType, StringRef parserCall) {515    return Property(/*maybeDef=*/nullptr,516                    /*summary=*/"",517                    /*description=*/"",518                    /*storageType=*/storageType,519                    /*interfaceType=*/"::llvm::ArrayRef<int32_t>",520                    /*convertFromStorageCall=*/"$_storage",521                    /*assignToStorageCall=*/522                    "::llvm::copy($_value, $_storage.begin())",523                    /*convertToAttributeCall=*/524                    "return ::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage);",525                    /*convertFromAttributeCall=*/526                    "return convertFromAttribute($_storage, $_attr, $_diag);",527                    /*parserCall=*/parserCall,528                    /*optionalParserCall=*/"",529                    /*printerCall=*/printTextualSegmentSize,530                    /*readFromMlirBytecodeCall=*/readBytecodeSegmentSizeNative,531                    /*writeToMlirBytecodeCall=*/writeBytecodeSegmentSizeNative,532                    /*hashPropertyCall=*/533                    "::llvm::hash_combine_range(std::begin($_storage), "534                    "std::end($_storage));",535                    /*StringRef defaultValue=*/"",536                    /*storageTypeValueOverride=*/"");537  };538  // Include key attributes from several traits as implicitly registered.539  if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {540    if (op.getDialect().usePropertiesForAttributes()) {541      operandSegmentsSizeStorage =542          llvm::formatv("std::array<int32_t, {0}>", op.getNumOperands());543      operandSegmentsSizeParser =544          llvm::formatv(parseTextualSegmentSizeFormat, op.getNumOperands());545      operandSegmentsSize = {546          "operandSegmentSizes",547          makeProperty(operandSegmentsSizeStorage, operandSegmentsSizeParser)};548    } else {549      attrMetadata.insert(550          {operandSegmentAttrName, AttributeMetadata{operandSegmentAttrName,551                                                     /*isRequired=*/true,552                                                     /*attr=*/std::nullopt}});553    }554  }555  if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {556    if (op.getDialect().usePropertiesForAttributes()) {557      resultSegmentsSizeStorage =558          llvm::formatv("std::array<int32_t, {0}>", op.getNumResults());559      resultSegmentsSizeParser =560          llvm::formatv(parseTextualSegmentSizeFormat, op.getNumResults());561      resultSegmentsSize = {562          "resultSegmentSizes",563          makeProperty(resultSegmentsSizeStorage, resultSegmentsSizeParser)};564    } else {565      attrMetadata.insert(566          {resultSegmentAttrName,567           AttributeMetadata{resultSegmentAttrName, /*isRequired=*/true,568                             /*attr=*/std::nullopt}});569    }570  }571 572  // Store the metadata in sorted order.573  SmallVector<AttributeMetadata> sortedAttrMetadata =574      llvm::to_vector(llvm::make_second_range(attrMetadata.takeVector()));575  llvm::sort(sortedAttrMetadata,576             [](const AttributeMetadata &lhs, const AttributeMetadata &rhs) {577               return lhs.attrName < rhs.attrName;578             });579 580  // Store the position of the legacy operand_segment_sizes /581  // result_segment_sizes so we can emit a backward compatible property readers582  // and writers.583  StringRef legacyOperandSegmentSizeName =584      StringLiteral("operand_segment_sizes");585  StringRef legacyResultSegmentSizeName = StringLiteral("result_segment_sizes");586  operandSegmentSizesLegacyIndex = 0;587  resultSegmentSizesLegacyIndex = 0;588  for (auto item : sortedAttrMetadata) {589    if (item.attrName < legacyOperandSegmentSizeName)590      ++operandSegmentSizesLegacyIndex;591    if (item.attrName < legacyResultSegmentSizeName)592      ++resultSegmentSizesLegacyIndex;593  }594 595  // Compute the subrange bounds for each attribute.596  numRequired = 0;597  for (AttributeMetadata &attr : sortedAttrMetadata) {598    attr.lowerBound = numRequired;599    numRequired += attr.isRequired;600  };601  for (AttributeMetadata &attr : sortedAttrMetadata)602    attr.upperBound = numRequired - attr.lowerBound - attr.isRequired;603 604  // Store the results back into the map.605  for (const AttributeMetadata &attr : sortedAttrMetadata)606    attrMetadata.insert({attr.attrName, attr});607}608 609//===----------------------------------------------------------------------===//610// Op emitter611//===----------------------------------------------------------------------===//612 613namespace {614// Helper class to emit a record into the given output stream.615class OpEmitter {616  using ConstArgument =617      llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;618 619public:620  static void621  emitDecl(const Operator &op, raw_ostream &os,622           const StaticVerifierFunctionEmitter &staticVerifierEmitter);623  static void624  emitDef(const Operator &op, raw_ostream &os,625          const StaticVerifierFunctionEmitter &staticVerifierEmitter);626 627private:628  OpEmitter(const Operator &op,629            const StaticVerifierFunctionEmitter &staticVerifierEmitter);630 631  void emitDecl(raw_ostream &os);632  void emitDef(raw_ostream &os);633 634  // Generate methods for accessing the attribute names of this operation.635  void genAttrNameGetters();636 637  // Generates the OpAsmOpInterface for this operation if possible.638  void genOpAsmInterface();639 640  // Generates the `getOperationName` method for this op.641  void genOpNameGetter();642 643  // Generates code to manage the properties, if any!644  void genPropertiesSupport();645 646  // Generates code to manage the encoding of properties to bytecode.647  void648  genPropertiesSupportForBytecode(ArrayRef<ConstArgument> attrOrProperties);649 650  // Generates getters for the properties.651  void genPropGetters();652 653  // Generates seters for the properties.654  void genPropSetters();655 656  // Generates getters for the attributes.657  void genAttrGetters();658 659  // Generates setter for the attributes.660  void genAttrSetters();661 662  // Generates removers for optional attributes.663  void genOptionalAttrRemovers();664 665  // Generates getters for named operands.666  void genNamedOperandGetters();667 668  // Generates setters for named operands.669  void genNamedOperandSetters();670 671  // Generates getters for named results.672  void genNamedResultGetters();673 674  // Generates getters for named regions.675  void genNamedRegionGetters();676 677  // Generates getters for named successors.678  void genNamedSuccessorGetters();679 680  // Generates the method to populate default attributes.681  void genPopulateDefaultAttributes();682 683  // Generates builder methods for the operation.684  void genBuilder();685 686  // Generates the build() method that takes each operand/attribute687  // as a stand-alone parameter.688  void genSeparateArgParamBuilder();689  void genInlineCreateBody(const SmallVector<MethodParameter> &paramList);690 691  // Generates the build() method that takes each operand/attribute as a692  // stand-alone parameter. The generated build() method uses first operand's693  // type as all results' types.694  void genUseOperandAsResultTypeSeparateParamBuilder();695 696  // The kind of collective builder to generate697  enum class CollectiveBuilderKind {698    PropStruct, // Inherent attributes/properties are passed by `const699                // Properties&`700    AttrDict,   // Inherent attributes/properties are passed by attribute701                // dictionary702  };703 704  // Generates the build() method that takes all operands/attributes705  // collectively as one parameter. The generated build() method uses first706  // operand's type as all results' types.707  void708  genUseOperandAsResultTypeCollectiveParamBuilder(CollectiveBuilderKind kind);709 710  // Generates the build() method that takes aggregate operands/attributes711  // parameters. This build() method uses inferred types as result types.712  // Requires: The type needs to be inferable via InferTypeOpInterface.713  void genInferredTypeCollectiveParamBuilder(CollectiveBuilderKind kind);714 715  // Generates the build() method that takesaggregate operands/attributes as716  // parameters. The generated build() method uses first attribute's717  // type as all result's types.718  void genUseAttrAsResultTypeCollectiveParamBuilder(CollectiveBuilderKind kind);719 720  // Generates the build() method that takes all result types collectively as721  // one parameter. Similarly for operands and attributes.722  void genCollectiveParamBuilder(CollectiveBuilderKind kind);723 724  // The kind of parameter to generate for result types in builders.725  enum class TypeParamKind {726    None,       // No result type in parameter list.727    Separate,   // A separate parameter for each result type.728    Collective, // An ArrayRef<Type> for all result types.729  };730 731  // The kind of parameter to generate for attributes in builders.732  enum class AttrParamKind {733    WrappedAttr,    // A wrapped MLIR Attribute instance.734    UnwrappedValue, // A raw value without MLIR Attribute wrapper.735  };736 737  // Builds the parameter list for build() method of this op. This method writes738  // to `paramList` the comma-separated parameter list and updates739  // `resultTypeNames` with the names for parameters for specifying result740  // types. `inferredAttributes` is populated with any attributes that are741  // elided from the build list. The given `typeParamKind` and `attrParamKind`742  // controls how result types and attributes are placed in the parameter list.743  void buildParamList(SmallVectorImpl<MethodParameter> &paramList,744                      llvm::StringSet<> &inferredAttributes,745                      SmallVectorImpl<std::string> &resultTypeNames,746                      TypeParamKind typeParamKind,747                      AttrParamKind attrParamKind = AttrParamKind::WrappedAttr);748 749  // Adds op arguments and regions into operation state for build() methods.750  void751  genCodeForAddingArgAndRegionForBuilder(MethodBody &body,752                                         llvm::StringSet<> &inferredAttributes,753                                         bool isRawValueAttr = false);754 755  // Generates canonicalizer declaration for the operation.756  void genCanonicalizerDecls();757 758  // Generates the folder declaration for the operation.759  void genFolderDecls();760 761  // Generates the parser for the operation.762  void genParser();763 764  // Generates the printer for the operation.765  void genPrinter();766 767  // Generates verify method for the operation.768  void genVerifier();769 770  // Generates custom verify methods for the operation.771  void genCustomVerifier();772 773  // Generates verify statements for operands and results in the operation.774  // The generated code will be attached to `body`.775  void genOperandResultVerifier(MethodBody &body,776                                Operator::const_value_range values,777                                StringRef valueKind);778 779  // Generates verify statements for regions in the operation.780  // The generated code will be attached to `body`.781  void genRegionVerifier(MethodBody &body);782 783  // Generates verify statements for successors in the operation.784  // The generated code will be attached to `body`.785  void genSuccessorVerifier(MethodBody &body);786 787  // Generates the traits used by the object.788  void genTraits();789 790  // Generate the OpInterface methods for all interfaces.791  void genOpInterfaceMethods();792 793  // Generate op interface methods for the given interface.794  void genOpInterfaceMethods(const tblgen::InterfaceTrait *trait);795 796  // Generate op interface method for the given interface method. If797  // 'declaration' is true, generates a declaration, else a definition.798  Method *genOpInterfaceMethod(const tblgen::InterfaceMethod &method,799                               bool declaration = true);800 801  // Generate a `using` declaration for the op interface method to include802  // the default implementation from the interface trait.803  // This is needed when the interface defines multiple methods with the same804  // name, but some have a default implementation and some don't.805  UsingDeclaration *806  genOpInterfaceMethodUsingDecl(const tblgen::InterfaceTrait *opTrait,807                                const tblgen::InterfaceMethod &method);808 809  // Generate the side effect interface methods.810  void genSideEffectInterfaceMethods();811 812  // Generate the type inference interface methods.813  void genTypeInterfaceMethods();814 815private:816  // The TableGen record for this op.817  // TODO: OpEmitter should not have a Record directly,818  // it should rather go through the Operator for better abstraction.819  const Record &def;820 821  // The wrapper operator class for querying information from this op.822  const Operator &op;823 824  // The C++ code builder for this op825  OpClass opClass;826 827  // The format context for verification code generation.828  FmtContext verifyCtx;829 830  // The emitter containing all of the locally emitted verification functions.831  const StaticVerifierFunctionEmitter &staticVerifierEmitter;832 833  // Helper for emitting op code.834  OpOrAdaptorHelper emitHelper;835 836  // Keep track of the interface using declarations that have been generated to837  // avoid duplicates.838  llvm::StringSet<> interfaceUsingNames;839};840 841} // namespace842 843// Populate the format context `ctx` with substitutions of attributes, operands844// and results.845static void populateSubstitutions(const OpOrAdaptorHelper &emitHelper,846                                  FmtContext &ctx) {847  // Populate substitutions for attributes.848  auto &op = emitHelper.getOp();849  for (const auto &namedAttr : op.getAttributes())850    ctx.addSubst(namedAttr.name,851                 emitHelper.getOp().getGetterName(namedAttr.name) + "()");852 853  // Populate substitutions for named operands.854  for (int i = 0, e = op.getNumOperands(); i < e; ++i) {855    auto &value = op.getOperand(i);856    if (!value.name.empty())857      ctx.addSubst(value.name, emitHelper.getOperand(i).str());858  }859 860  // Populate substitutions for results.861  for (int i = 0, e = op.getNumResults(); i < e; ++i) {862    auto &value = op.getResult(i);863    if (!value.name.empty())864      ctx.addSubst(value.name, emitHelper.getResult(i).str());865  }866}867 868/// Generate verification on native traits requiring attributes.869static void genNativeTraitAttrVerifier(MethodBody &body,870                                       const OpOrAdaptorHelper &emitHelper) {871  // Check that the variadic segment sizes attribute exists and contains the872  // expected number of elements.873  //874  // {0}: Attribute name.875  // {1}: Expected number of elements.876  // {2}: "operand" or "result".877  // {3}: Emit error prefix.878  const char *const checkAttrSizedValueSegmentsCode = R"(879  {880    auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>(tblgen_{0});881    auto numElements = sizeAttr.asArrayRef().size();882    if (numElements != {1})883      return {3}"'{0}' attribute for specifying {2} segments must have {1} "884                "elements, but got ") << numElements;885  }886  )";887 888  // Verify a few traits first so that we can use getODSOperands() and889  // getODSResults() in the rest of the verifier.890  auto &op = emitHelper.getOp();891  if (!op.getDialect().usePropertiesForAttributes()) {892    if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {893      body << formatv(checkAttrSizedValueSegmentsCode, operandSegmentAttrName,894                      op.getNumOperands(), "operand",895                      emitHelper.emitErrorPrefix());896    }897    if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {898      body << formatv(checkAttrSizedValueSegmentsCode, resultSegmentAttrName,899                      op.getNumResults(), "result",900                      emitHelper.emitErrorPrefix());901    }902  }903}904 905// Return true if a verifier can be emitted for the attribute: it is not a906// derived attribute, it has a predicate, its condition is not empty, and, for907// adaptors, the condition does not reference the op.908static bool canEmitAttrVerifier(Attribute attr, bool isEmittingForOp) {909  if (attr.isDerivedAttr())910    return false;911  Pred pred = attr.getPredicate();912  if (pred.isNull())913    return false;914  std::string condition = pred.getCondition();915  return !condition.empty() &&916         (!StringRef(condition).contains("$_op") || isEmittingForOp);917}918 919// Generate attribute verification. If an op instance is not available, then920// attribute checks that require one will not be emitted.921//922// Attribute verification is performed as follows:923//924// 1. Verify that all required attributes are present in sorted order. This925// ensures that we can use subrange lookup even with potentially missing926// attributes.927// 2. Verify native trait attributes so that other attributes may call methods928// that depend on the validity of these attributes, e.g. segment size attributes929// and operand or result getters.930// 3. Verify the constraints on all present attributes.931static void932genAttributeVerifier(const OpOrAdaptorHelper &emitHelper, FmtContext &ctx,933                     MethodBody &body,934                     const StaticVerifierFunctionEmitter &staticVerifierEmitter,935                     bool useProperties) {936  if (emitHelper.getAttrMetadata().empty())937    return;938 939  // Verify the attribute if it is present. This assumes that default values940  // are valid. This code snippet pastes the condition inline.941  //942  // TODO: verify the default value is valid (perhaps in debug mode only).943  //944  // {0}: Attribute variable name.945  // {1}: Attribute condition code.946  // {2}: Emit error prefix.947  // {3}: Attribute name.948  // {4}: Attribute/constraint description.949  const char *const verifyAttrInline = R"(950  if ({0} && !({1}))951    return {2}attribute '{3}' failed to satisfy constraint: {4}");952)";953  // Verify the attribute using a uniqued constraint. Can only be used within954  // the context of an op.955  //956  // {0}: Unique constraint name.957  // {1}: Attribute variable name.958  // {2}: Attribute name.959  const char *const verifyAttrUnique = R"(960  if (::mlir::failed({0}(*this, {1}, "{2}")))961    return ::mlir::failure();962)";963 964  // Traverse the array until the required attribute is found. Return an error965  // if the traversal reached the end.966  //967  // {0}: Code to get the name of the attribute.968  // {1}: The emit error prefix.969  // {2}: The name of the attribute.970  const char *const findRequiredAttr = R"(971while (true) {{972  if (namedAttrIt == namedAttrRange.end())973    return {1}"requires attribute '{2}'");974  if (namedAttrIt->getName() == {0}) {{975    tblgen_{2} = namedAttrIt->getValue();976    break;977  })";978 979  // Emit a check to see if the iteration has encountered an optional attribute.980  //981  // {0}: Code to get the name of the attribute.982  // {1}: The name of the attribute.983  const char *const checkOptionalAttr = R"(984  else if (namedAttrIt->getName() == {0}) {{985    tblgen_{1} = namedAttrIt->getValue();986  })";987 988  // Emit the start of the loop for checking trailing attributes.989  const char *const checkTrailingAttrs = R"(while (true) {990  if (namedAttrIt == namedAttrRange.end()) {991    break;992  })";993 994  // Emit the verifier for the attribute.995  const auto emitVerifier = [&](Attribute attr, StringRef attrName,996                                StringRef varName) {997    std::string condition = attr.getPredicate().getCondition();998 999    std::optional<StringRef> constraintFn;1000    if (emitHelper.isEmittingForOp() &&1001        (constraintFn = staticVerifierEmitter.getAttrConstraintFn(attr))) {1002      body << formatv(verifyAttrUnique, *constraintFn, varName, attrName);1003    } else {1004      body << formatv(1005          verifyAttrInline, varName, tgfmt(condition, &ctx.withSelf(varName)),1006          emitHelper.emitErrorPrefix(), attrName,1007          buildErrorStreamingString(attr.getSummary(), ctx.withSelf(varName),1008                                    ErrorStreamType::InsideOpError));1009    }1010  };1011 1012  // Prefix variables with `tblgen_` to avoid hiding the attribute accessor.1013  const auto getVarName = [&](StringRef attrName) {1014    return (tblgenNamePrefix + attrName).str();1015  };1016 1017  body.indent();1018  if (useProperties) {1019    for (const std::pair<StringRef, AttributeMetadata> &it :1020         emitHelper.getAttrMetadata()) {1021      const AttributeMetadata &metadata = it.second;1022      if (metadata.constraint && metadata.constraint->isDerivedAttr())1023        continue;1024      body << formatv(1025          "auto tblgen_{0} = getProperties().{0}; (void)tblgen_{0};\n",1026          it.first);1027      if (metadata.isRequired)1028        body << formatv(1029            "if (!tblgen_{0}) return {1}requires attribute '{0}'\");\n",1030            it.first, emitHelper.emitErrorPrefix());1031    }1032  } else {1033    body << formatv("auto namedAttrRange = {0};\n", emitHelper.getAttrRange());1034    body << "auto namedAttrIt = namedAttrRange.begin();\n";1035 1036    // Iterate over the attributes in sorted order. Keep track of the optional1037    // attributes that may be encountered along the way.1038    SmallVector<const AttributeMetadata *> optionalAttrs;1039 1040    for (const std::pair<StringRef, AttributeMetadata> &it :1041         emitHelper.getAttrMetadata()) {1042      const AttributeMetadata &metadata = it.second;1043      if (!metadata.isRequired) {1044        optionalAttrs.push_back(&metadata);1045        continue;1046      }1047 1048      body << formatv("::mlir::Attribute {0};\n", getVarName(it.first));1049      for (const AttributeMetadata *optional : optionalAttrs) {1050        body << formatv("::mlir::Attribute {0};\n",1051                        getVarName(optional->attrName));1052      }1053      body << formatv(findRequiredAttr, emitHelper.getAttrName(it.first),1054                      emitHelper.emitErrorPrefix(), it.first);1055      for (const AttributeMetadata *optional : optionalAttrs) {1056        body << formatv(checkOptionalAttr,1057                        emitHelper.getAttrName(optional->attrName),1058                        optional->attrName);1059      }1060      body << "\n  ++namedAttrIt;\n}\n";1061      optionalAttrs.clear();1062    }1063    // Get trailing optional attributes.1064    if (!optionalAttrs.empty()) {1065      for (const AttributeMetadata *optional : optionalAttrs) {1066        body << formatv("::mlir::Attribute {0};\n",1067                        getVarName(optional->attrName));1068      }1069      body << checkTrailingAttrs;1070      for (const AttributeMetadata *optional : optionalAttrs) {1071        body << formatv(checkOptionalAttr,1072                        emitHelper.getAttrName(optional->attrName),1073                        optional->attrName);1074      }1075      body << "\n  ++namedAttrIt;\n}\n";1076    }1077  }1078  body.unindent();1079 1080  // Emit the checks for segment attributes first so that the other1081  // constraints can call operand and result getters.1082  genNativeTraitAttrVerifier(body, emitHelper);1083 1084  bool isEmittingForOp = emitHelper.isEmittingForOp();1085  for (const auto &namedAttr : emitHelper.getOp().getAttributes())1086    if (canEmitAttrVerifier(namedAttr.attr, isEmittingForOp))1087      emitVerifier(namedAttr.attr, namedAttr.name, getVarName(namedAttr.name));1088}1089 1090static void genPropertyVerifier(1091    const OpOrAdaptorHelper &emitHelper, FmtContext &ctx, MethodBody &body,1092    const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1093 1094  // Code to get a reference to a property into a variable to avoid multiple1095  // evaluations while verifying a property.1096  // {0}: Property variable name.1097  // {1}: Property name, with the first letter capitalized, to find the getter.1098  // {2}: Property interface type.1099  const char *const fetchProperty = R"(1100  [[maybe_unused]] {2} {0} = this->get{1}();1101)";1102 1103  // Code to verify that the predicate of a property holds. Embeds the1104  // condition inline.1105  // {0}: Property condition code, with tgfmt() applied.1106  // {1}: Emit error prefix.1107  // {2}: Property name.1108  // {3}: Property description.1109  const char *const verifyPropertyInline = R"(1110  if (!({0}))1111    return {1}property '{2}' failed to satisfy constraint: {3}");1112)";1113 1114  // Verify the property using a uniqued constraint. Can only be used1115  // within the context of an op.1116  //1117  // {0}: Unique constraint name.1118  // {1}: Property variable name in interface type.1119  // {2}: Property name.1120  const char *const verifyPropertyUniqued = R"(1121    if (::mlir::failed({0}(*this, {1}, "{2}")))1122      return ::mlir::failure();1123)";1124 1125  // Prefix variables with `tblgen_` to avoid hiding the attribute accessor.1126  const auto getVarName = [&](const NamedProperty &prop) {1127    std::string varName =1128        convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/false);1129    return (tblgenNamePrefix + Twine(varName)).str();1130  };1131 1132  for (const NamedProperty &prop : emitHelper.getOp().getProperties()) {1133    Pred predicate = prop.prop.getPredicate();1134    // Null predicate, nothing to verify.1135    if (predicate == Pred())1136      continue;1137 1138    std::string rawCondition = predicate.getCondition();1139    if (rawCondition == "true")1140      continue;1141    bool needsOp = StringRef(rawCondition).contains("$_op");1142    if (needsOp && !emitHelper.isEmittingForOp())1143      continue;1144 1145    auto scope = body.scope("{\n", "}\n", /*indent=*/true);1146    std::string varName = getVarName(prop);1147    std::string getterName =1148        convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/true);1149    body << formatv(fetchProperty, varName, getterName,1150                    prop.prop.getInterfaceType());1151    auto uniquedFn = staticVerifierEmitter.getPropConstraintFn(prop.prop);1152    if (uniquedFn.has_value() && emitHelper.isEmittingForOp())1153      body << formatv(verifyPropertyUniqued, *uniquedFn, varName, prop.name);1154    else1155      body << formatv(verifyPropertyInline,1156                      tgfmt(rawCondition, &ctx.withSelf(varName)),1157                      emitHelper.emitErrorPrefix(), prop.name,1158                      buildErrorStreamingString(1159                          prop.prop.getSummary(), ctx.withSelf(varName),1160                          ErrorStreamType::InsideOpError));1161  }1162}1163 1164/// Include declarations specified on NativeTrait1165static std::string formatExtraDeclarations(const Operator &op) {1166  SmallVector<StringRef> extraDeclarations;1167  // Include extra class declarations from NativeTrait1168  for (const auto &trait : op.getTraits()) {1169    if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {1170      StringRef value = opTrait->getExtraConcreteClassDeclaration();1171      if (value.empty())1172        continue;1173      extraDeclarations.push_back(value);1174    }1175  }1176  extraDeclarations.push_back(op.getExtraClassDeclaration());1177  return llvm::join(extraDeclarations, "\n");1178}1179 1180/// Op extra class definitions have a `$cppClass` substitution that is to be1181/// replaced by the C++ class name.1182/// Include declarations specified on NativeTrait1183static std::string formatExtraDefinitions(const Operator &op) {1184  SmallVector<StringRef> extraDefinitions;1185  // Include extra class definitions from NativeTrait1186  for (const auto &trait : op.getTraits()) {1187    if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {1188      StringRef value = opTrait->getExtraConcreteClassDefinition();1189      if (value.empty())1190        continue;1191      extraDefinitions.push_back(value);1192    }1193  }1194  extraDefinitions.push_back(op.getExtraClassDefinition());1195  FmtContext ctx = FmtContext().addSubst("cppClass", op.getCppClassName());1196  return tgfmt(llvm::join(extraDefinitions, "\n"), &ctx).str();1197}1198 1199OpEmitter::OpEmitter(const Operator &op,1200                     const StaticVerifierFunctionEmitter &staticVerifierEmitter)1201    : def(op.getDef()), op(op),1202      opClass(op.getCppClassName(), formatExtraDeclarations(op),1203              formatExtraDefinitions(op)),1204      staticVerifierEmitter(staticVerifierEmitter),1205      emitHelper(op, /*emitForOp=*/true) {1206  verifyCtx.addSubst("_op", "(*this->getOperation())");1207  verifyCtx.addSubst("_ctxt", "this->getOperation()->getContext()");1208 1209  genTraits();1210 1211  // Generate C++ code for various op methods. The order here determines the1212  // methods in the generated file.1213  genAttrNameGetters();1214  genOpAsmInterface();1215  genOpNameGetter();1216  genNamedOperandGetters();1217  genNamedOperandSetters();1218  genNamedResultGetters();1219  genNamedRegionGetters();1220  genNamedSuccessorGetters();1221  genPropertiesSupport();1222  genPropGetters();1223  genPropSetters();1224  genAttrGetters();1225  genAttrSetters();1226  genOptionalAttrRemovers();1227  genBuilder();1228  genPopulateDefaultAttributes();1229  genParser();1230  genPrinter();1231  genVerifier();1232  genCustomVerifier();1233  genCanonicalizerDecls();1234  genFolderDecls();1235  genTypeInterfaceMethods();1236  genOpInterfaceMethods();1237  generateOpFormat(op, opClass, emitHelper.hasProperties());1238  genSideEffectInterfaceMethods();1239}1240void OpEmitter::emitDecl(1241    const Operator &op, raw_ostream &os,1242    const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1243  OpEmitter(op, staticVerifierEmitter).emitDecl(os);1244}1245 1246void OpEmitter::emitDef(1247    const Operator &op, raw_ostream &os,1248    const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1249  OpEmitter(op, staticVerifierEmitter).emitDef(os);1250}1251 1252void OpEmitter::emitDecl(raw_ostream &os) {1253  opClass.finalize();1254  opClass.writeDeclTo(os);1255}1256 1257void OpEmitter::emitDef(raw_ostream &os) {1258  opClass.finalize();1259  opClass.writeDefTo(os);1260}1261 1262static void errorIfPruned(size_t line, Method *m, const Twine &methodName,1263                          const Operator &op) {1264  if (m)1265    return;1266  PrintFatalError(op.getLoc(), "Unexpected overlap when generating `" +1267                                   methodName + "` for " +1268                                   op.getOperationName() + " (from line " +1269                                   Twine(line) + ")");1270}1271 1272#define ERROR_IF_PRUNED(M, N, O) errorIfPruned(__LINE__, M, N, O)1273 1274void OpEmitter::genAttrNameGetters() {1275  const llvm::MapVector<StringRef, AttributeMetadata> &attributes =1276      emitHelper.getAttrMetadata();1277  bool hasOperandSegmentsSize =1278      op.getDialect().usePropertiesForAttributes() &&1279      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");1280  // Emit the getAttributeNames method.1281  {1282    auto *method = opClass.addStaticInlineMethod(1283        "::llvm::ArrayRef<::llvm::StringRef>", "getAttributeNames");1284    ERROR_IF_PRUNED(method, "getAttributeNames", op);1285    auto &body = method->body();1286    if (!hasOperandSegmentsSize && attributes.empty()) {1287      body << "  return {};";1288      // Nothing else to do if there are no registered attributes. Exit early.1289      return;1290    }1291    body << "  static ::llvm::StringRef attrNames[] = {";1292    llvm::interleaveComma(llvm::make_first_range(attributes), body,1293                          [&](StringRef attrName) {1294                            body << "::llvm::StringRef(\"" << attrName << "\")";1295                          });1296    if (hasOperandSegmentsSize) {1297      if (!attributes.empty())1298        body << ", ";1299      body << "::llvm::StringRef(\"" << operandSegmentAttrName << "\")";1300    }1301    body << "};\n  return ::llvm::ArrayRef(attrNames);";1302  }1303 1304  // Emit the getAttributeNameForIndex methods.1305  {1306    auto *method = opClass.addInlineMethod<Method::Private>(1307        "::mlir::StringAttr", "getAttributeNameForIndex",1308        MethodParameter("unsigned", "index"));1309    ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);1310    method->body()1311        << "  return getAttributeNameForIndex((*this)->getName(), index);";1312  }1313  {1314    auto *method = opClass.addStaticInlineMethod<Method::Private>(1315        "::mlir::StringAttr", "getAttributeNameForIndex",1316        MethodParameter("::mlir::OperationName", "name"),1317        MethodParameter("unsigned", "index"));1318    ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);1319 1320    if (attributes.empty()) {1321      method->body() << "  return {};";1322    } else {1323      const char *const getAttrName = R"(1324  assert(index < {0} && "invalid attribute index");1325  assert(name.getStringRef() == getOperationName() && "invalid operation name");1326  assert(name.isRegistered() && "Operation isn't registered, missing a "1327        "dependent dialect loading?");1328  return name.getAttributeNames()[index];1329)";1330      method->body() << formatv(getAttrName, attributes.size());1331    }1332  }1333 1334  // Generate the <attr>AttrName methods, that expose the attribute names to1335  // users.1336  const char *attrNameMethodBody = "  return getAttributeNameForIndex({0});";1337  for (auto [index, attr] :1338       llvm::enumerate(llvm::make_first_range(attributes))) {1339    std::string name = op.getGetterName(attr);1340    std::string methodName = name + "AttrName";1341 1342    // Generate the non-static variant.1343    {1344      auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);1345      ERROR_IF_PRUNED(method, methodName, op);1346      method->body() << llvm::formatv(attrNameMethodBody, index);1347    }1348 1349    // Generate the static variant.1350    {1351      auto *method = opClass.addStaticInlineMethod(1352          "::mlir::StringAttr", methodName,1353          MethodParameter("::mlir::OperationName", "name"));1354      ERROR_IF_PRUNED(method, methodName, op);1355      method->body() << llvm::formatv(attrNameMethodBody,1356                                      "name, " + Twine(index));1357    }1358  }1359  if (hasOperandSegmentsSize) {1360    std::string name = op.getGetterName(operandSegmentAttrName);1361    std::string methodName = name + "AttrName";1362    // Generate the non-static variant.1363    {1364      auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);1365      ERROR_IF_PRUNED(method, methodName, op);1366      method->body()1367          << " return (*this)->getName().getAttributeNames().back();";1368    }1369 1370    // Generate the static variant.1371    {1372      auto *method = opClass.addStaticInlineMethod(1373          "::mlir::StringAttr", methodName,1374          MethodParameter("::mlir::OperationName", "name"));1375      ERROR_IF_PRUNED(method, methodName, op);1376      method->body() << " return name.getAttributeNames().back();";1377    }1378  }1379}1380 1381// Emit the getter for a named property.1382// It is templated to be shared between the Op and the adaptor class.1383template <typename OpClassOrAdaptor>1384static void emitPropGetter(OpClassOrAdaptor &opClass, const Operator &op,1385                           StringRef name, const Property &prop) {1386  auto *method = opClass.addInlineMethod(prop.getInterfaceType(), name);1387  ERROR_IF_PRUNED(method, name, op);1388  method->body() << formatv("  return getProperties().{0}();", name);1389}1390 1391// Emit the getter for an attribute with the return type specified.1392// It is templated to be shared between the Op and the adaptor class.1393template <typename OpClassOrAdaptor>1394static void emitAttrGetterWithReturnType(FmtContext &fctx,1395                                         OpClassOrAdaptor &opClass,1396                                         const Operator &op, StringRef name,1397                                         Attribute attr) {1398  auto *method = opClass.addMethod(attr.getReturnType(), name);1399  ERROR_IF_PRUNED(method, name, op);1400  auto &body = method->body();1401  body << "  auto attr = " << name << "Attr();\n";1402  if (attr.hasDefaultValue() && attr.isOptional()) {1403    // Returns the default value if not set.1404    // TODO: this is inefficient, we are recreating the attribute for every1405    // call. This should be set instead.1406    if (!attr.isConstBuildable()) {1407      PrintFatalError("DefaultValuedAttr of type " + attr.getAttrDefName() +1408                      " must have a constBuilder");1409    }1410    std::string defaultValue =1411        std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,1412                          tgfmt(attr.getDefaultValue(), &fctx)));1413    body << "    if (!attr)\n      return "1414         << tgfmt(attr.getConvertFromStorageCall(),1415                  &fctx.withSelf(defaultValue))1416         << ";\n";1417  }1418  body << "  return "1419       << tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr"))1420       << ";\n";1421}1422 1423void OpEmitter::genPropertiesSupport() {1424  if (!emitHelper.hasProperties())1425    return;1426 1427  SmallVector<ConstArgument> attrOrProperties;1428  for (const std::pair<StringRef, AttributeMetadata> &it :1429       emitHelper.getAttrMetadata()) {1430    if (!it.second.constraint || !it.second.constraint->isDerivedAttr())1431      attrOrProperties.push_back(&it.second);1432  }1433  for (const NamedProperty &prop : op.getProperties())1434    attrOrProperties.push_back(&prop);1435  if (emitHelper.getOperandSegmentsSize())1436    attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());1437  if (emitHelper.getResultSegmentsSize())1438    attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());1439  auto &setPropMethod =1440      opClass1441          .addStaticMethod(1442              "::llvm::LogicalResult", "setPropertiesFromAttr",1443              MethodParameter("Properties &", "prop"),1444              MethodParameter("::mlir::Attribute", "attr"),1445              MethodParameter(1446                  "::llvm::function_ref<::mlir::InFlightDiagnostic()>",1447                  "emitError"))1448          ->body();1449  auto &getPropMethod =1450      opClass1451          .addStaticMethod("::mlir::Attribute", "getPropertiesAsAttr",1452                           MethodParameter("::mlir::MLIRContext *", "ctx"),1453                           MethodParameter("const Properties &", "prop"))1454          ->body();1455  auto &hashMethod =1456      opClass1457          .addStaticMethod("llvm::hash_code", "computePropertiesHash",1458                           MethodParameter("const Properties &", "prop"))1459          ->body();1460  auto &getInherentAttrMethod =1461      opClass1462          .addStaticMethod("std::optional<mlir::Attribute>", "getInherentAttr",1463                           MethodParameter("::mlir::MLIRContext *", "ctx"),1464                           MethodParameter("const Properties &", "prop"),1465                           MethodParameter("llvm::StringRef", "name"))1466          ->body();1467  auto &setInherentAttrMethod =1468      opClass1469          .addStaticMethod("void", "setInherentAttr",1470                           MethodParameter("Properties &", "prop"),1471                           MethodParameter("llvm::StringRef", "name"),1472                           MethodParameter("mlir::Attribute", "value"))1473          ->body();1474  auto &populateInherentAttrsMethod =1475      opClass1476          .addStaticMethod("void", "populateInherentAttrs",1477                           MethodParameter("::mlir::MLIRContext *", "ctx"),1478                           MethodParameter("const Properties &", "prop"),1479                           MethodParameter("::mlir::NamedAttrList &", "attrs"))1480          ->body();1481  auto &verifyInherentAttrsMethod =1482      opClass1483          .addStaticMethod(1484              "::llvm::LogicalResult", "verifyInherentAttrs",1485              MethodParameter("::mlir::OperationName", "opName"),1486              MethodParameter("::mlir::NamedAttrList &", "attrs"),1487              MethodParameter(1488                  "llvm::function_ref<::mlir::InFlightDiagnostic()>",1489                  "emitError"))1490          ->body();1491 1492  opClass.declare<UsingDeclaration>("Properties", "FoldAdaptor::Properties");1493 1494  // Convert the property to the attribute form.1495 1496  setPropMethod << R"decl(1497  ::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr);1498  if (!dict) {1499    emitError() << "expected DictionaryAttr to set properties";1500    return ::mlir::failure();1501  }1502    )decl";1503  const char *propFromAttrFmt = R"decl(1504      auto setFromAttr = [] (auto &propStorage, ::mlir::Attribute propAttr,1505               ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) -> ::mlir::LogicalResult {{1506        {0}1507      };1508      {1};1509)decl";1510  const char *attrGetNoDefaultFmt = R"decl(;1511      if (attr && ::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))1512        return ::mlir::failure();1513)decl";1514  const char *attrGetDefaultFmt = R"decl(;1515      if (attr) {{1516        if (::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))1517          return ::mlir::failure();1518      } else {{1519        prop.{0} = {1};1520      }1521)decl";1522 1523  for (const auto &attrOrProp : attrOrProperties) {1524    if (const auto *namedProperty =1525            llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1526      StringRef name = namedProperty->name;1527      auto &prop = namedProperty->prop;1528      FmtContext fctx;1529 1530      std::string getAttr;1531      llvm::raw_string_ostream os(getAttr);1532      os << "   auto attr = dict.get(\"" << name << "\");";1533      if (name == operandSegmentAttrName) {1534        // Backward compat for now, TODO: Remove at some point.1535        os << "   if (!attr) attr = dict.get(\"operand_segment_sizes\");";1536      }1537      if (name == resultSegmentAttrName) {1538        // Backward compat for now, TODO: Remove at some point.1539        os << "   if (!attr) attr = dict.get(\"result_segment_sizes\");";1540      }1541 1542      fctx.withBuilder(odsBuilder);1543      setPropMethod << "{\n"1544                    << formatv(propFromAttrFmt,1545                               tgfmt(prop.getConvertFromAttributeCall(),1546                                     &fctx.addSubst("_attr", propertyAttr)1547                                          .addSubst("_storage", propertyStorage)1548                                          .addSubst("_diag", propertyDiag)),1549                               getAttr);1550      if (prop.hasStorageTypeValueOverride()) {1551        setPropMethod << formatv(attrGetDefaultFmt, name,1552                                 prop.getStorageTypeValueOverride());1553      } else if (prop.hasDefaultValue()) {1554        setPropMethod << formatv(attrGetDefaultFmt, name,1555                                 tgfmt(prop.getDefaultValue(), &fctx));1556      } else {1557        setPropMethod << formatv(attrGetNoDefaultFmt, name);1558      }1559      setPropMethod << "  }\n";1560    } else {1561      const auto *namedAttr =1562          llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1563      StringRef name = namedAttr->attrName;1564      std::string getAttr;1565      llvm::raw_string_ostream os(getAttr);1566      os << "   auto attr = dict.get(\"" << name << "\");";1567      if (name == operandSegmentAttrName) {1568        // Backward compat for now1569        os << "   if (!attr) attr = dict.get(\"operand_segment_sizes\");";1570      }1571      if (name == resultSegmentAttrName) {1572        // Backward compat for now1573        os << "   if (!attr) attr = dict.get(\"result_segment_sizes\");";1574      }1575 1576      setPropMethod << formatv(R"decl(1577  {{1578    auto &propStorage = prop.{0};1579    {1}1580    if (attr) {{1581      auto convertedAttr = ::llvm::dyn_cast<std::remove_reference_t<decltype(propStorage)>>(attr);1582      if (convertedAttr) {{1583        propStorage = convertedAttr;1584      } else {{1585        emitError() << "Invalid attribute `{0}` in property conversion: " << attr;1586        return ::mlir::failure();1587      }1588    }1589  }1590)decl",1591                               name, getAttr);1592    }1593  }1594  setPropMethod << "  return ::mlir::success();\n";1595 1596  // Convert the attribute form to the property.1597 1598  getPropMethod << "    ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n"1599                << "    ::mlir::Builder odsBuilder{ctx};\n";1600  const char *propToAttrFmt = R"decl(1601    {1602      const auto &propStorage = prop.{0};1603      auto attr = [&]() -> ::mlir::Attribute {{1604        {1}1605      }();1606      attrs.push_back(odsBuilder.getNamedAttr("{0}", attr));1607    }1608)decl";1609  for (const auto &attrOrProp : attrOrProperties) {1610    if (const auto *namedProperty =1611            llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1612      StringRef name = namedProperty->name;1613      auto &prop = namedProperty->prop;1614      FmtContext fctx;1615      getPropMethod << formatv(1616          propToAttrFmt, name,1617          tgfmt(prop.getConvertToAttributeCall(),1618                &fctx.addSubst("_ctxt", "ctx")1619                     .addSubst("_storage", propertyStorage)));1620      continue;1621    }1622    const auto *namedAttr =1623        llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1624    StringRef name = namedAttr->attrName;1625    getPropMethod << formatv(R"decl(1626    {{1627      const auto &propStorage = prop.{0};1628      if (propStorage)1629        attrs.push_back(odsBuilder.getNamedAttr("{0}",1630                                       propStorage));1631    }1632)decl",1633                             name);1634  }1635  getPropMethod << R"decl(1636  if (!attrs.empty())1637    return odsBuilder.getDictionaryAttr(attrs);1638  return {};1639)decl";1640 1641  // Hashing for the property1642 1643  const char *propHashFmt = R"decl(1644  auto hash_{0}_ = [] (const auto &propStorage) -> llvm::hash_code {1645    using ::llvm::hash_value;1646    return {1};1647  };1648)decl";1649  for (const auto &attrOrProp : attrOrProperties) {1650    if (const auto *namedProperty =1651            llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1652      StringRef name = namedProperty->name;1653      auto &prop = namedProperty->prop;1654      FmtContext fctx;1655      if (!prop.getHashPropertyCall().empty()) {1656        hashMethod << formatv(1657            propHashFmt, name,1658            tgfmt(prop.getHashPropertyCall(),1659                  &fctx.addSubst("_storage", propertyStorage)));1660      }1661    }1662  }1663  hashMethod << "  using llvm::hash_value;\n";1664  hashMethod << "  return llvm::hash_combine(";1665  llvm::interleaveComma(1666      attrOrProperties, hashMethod, [&](const ConstArgument &attrOrProp) {1667        if (const auto *namedProperty =1668                llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1669          if (!namedProperty->prop.getHashPropertyCall().empty()) {1670            hashMethod << "\n    hash_" << namedProperty->name << "_(prop."1671                       << namedProperty->name << ")";1672          } else {1673            hashMethod << "\n    hash_value(prop." << namedProperty->name1674                       << ")";1675          }1676          return;1677        }1678        const auto *namedAttr =1679            llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1680        StringRef name = namedAttr->attrName;1681        hashMethod << "\n    llvm::hash_value(prop." << name1682                   << ".getAsOpaquePointer())";1683      });1684  hashMethod << ");\n";1685 1686  const char *getInherentAttrMethodFmt = R"decl(1687    if (name == "{0}")1688      return prop.{0};1689)decl";1690  const char *setInherentAttrMethodFmt = R"decl(1691    if (name == "{0}") {{1692       prop.{0} = ::llvm::dyn_cast_or_null<std::remove_reference_t<decltype(prop.{0})>>(value);1693       return;1694    }1695)decl";1696  const char *populateInherentAttrsMethodFmt = R"decl(1697    if (prop.{0}) attrs.append("{0}", prop.{0});1698)decl";1699  for (const auto &attrOrProp : attrOrProperties) {1700    if (const auto *namedAttr =1701            llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp)) {1702      StringRef name = namedAttr->attrName;1703      getInherentAttrMethod << formatv(getInherentAttrMethodFmt, name);1704      setInherentAttrMethod << formatv(setInherentAttrMethodFmt, name);1705      populateInherentAttrsMethod1706          << formatv(populateInherentAttrsMethodFmt, name);1707      continue;1708    }1709    // The ODS segment size property is "special": we expose it as an attribute1710    // even though it is a native property.1711    const auto *namedProperty = cast<const NamedProperty *>(attrOrProp);1712    StringRef name = namedProperty->name;1713    if (name != operandSegmentAttrName && name != resultSegmentAttrName)1714      continue;1715    auto &prop = namedProperty->prop;1716    FmtContext fctx;1717    fctx.addSubst("_ctxt", "ctx");1718    fctx.addSubst("_storage", Twine("prop.") + name);1719    if (name == operandSegmentAttrName) {1720      getInherentAttrMethod1721          << formatv("    if (name == \"operand_segment_sizes\" || name == "1722                     "\"{0}\") return ",1723                     operandSegmentAttrName);1724    } else {1725      getInherentAttrMethod1726          << formatv("    if (name == \"result_segment_sizes\" || name == "1727                     "\"{0}\") return ",1728                     resultSegmentAttrName);1729    }1730    getInherentAttrMethod << "[&]() -> ::mlir::Attribute { "1731                          << tgfmt(prop.getConvertToAttributeCall(), &fctx)1732                          << " }();\n";1733 1734    if (name == operandSegmentAttrName) {1735      setInherentAttrMethod1736          << formatv("        if (name == \"operand_segment_sizes\" || name == "1737                     "\"{0}\") {{",1738                     operandSegmentAttrName);1739    } else {1740      setInherentAttrMethod1741          << formatv("        if (name == \"result_segment_sizes\" || name == "1742                     "\"{0}\") {{",1743                     resultSegmentAttrName);1744    }1745    setInherentAttrMethod << formatv(R"decl(1746       auto arrAttr = ::llvm::dyn_cast_or_null<::mlir::DenseI32ArrayAttr>(value);1747       if (!arrAttr) return;1748       if (arrAttr.size() != sizeof(prop.{0}) / sizeof(int32_t))1749         return;1750       llvm::copy(arrAttr.asArrayRef(), prop.{0}.begin());1751       return;1752    }1753)decl",1754                                     name);1755    if (name == operandSegmentAttrName) {1756      populateInherentAttrsMethod << formatv(1757          "  attrs.append(\"{0}\", [&]() -> ::mlir::Attribute { {1} }());\n",1758          operandSegmentAttrName,1759          tgfmt(prop.getConvertToAttributeCall(), &fctx));1760    } else {1761      populateInherentAttrsMethod << formatv(1762          "  attrs.append(\"{0}\", [&]() -> ::mlir::Attribute { {1} }());\n",1763          resultSegmentAttrName,1764          tgfmt(prop.getConvertToAttributeCall(), &fctx));1765    }1766  }1767  getInherentAttrMethod << "  return std::nullopt;\n";1768 1769  // Emit the verifiers method for backward compatibility with the generic1770  // syntax. This method verifies the constraint on the properties attributes1771  // before they are set, since dyn_cast<> will silently omit failures.1772  for (const auto &attrOrProp : attrOrProperties) {1773    const auto *namedAttr =1774        llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1775    if (!namedAttr || !namedAttr->constraint)1776      continue;1777    Attribute attr = *namedAttr->constraint;1778    std::optional<StringRef> constraintFn =1779        staticVerifierEmitter.getAttrConstraintFn(attr);1780    if (!constraintFn)1781      continue;1782    if (canEmitAttrVerifier(attr,1783                            /*isEmittingForOp=*/false)) {1784      std::string name = op.getGetterName(namedAttr->attrName);1785      verifyInherentAttrsMethod1786          << formatv(R"(1787    {{1788      ::mlir::Attribute attr = attrs.get({0}AttrName(opName));1789      if (attr && ::mlir::failed({1}(attr, "{2}", emitError)))1790        return ::mlir::failure();1791    }1792)",1793                     name, constraintFn, namedAttr->attrName);1794    }1795  }1796  verifyInherentAttrsMethod << "    return ::mlir::success();";1797 1798  // Generate methods to interact with bytecode.1799  genPropertiesSupportForBytecode(attrOrProperties);1800}1801 1802void OpEmitter::genPropertiesSupportForBytecode(1803    ArrayRef<ConstArgument> attrOrProperties) {1804  if (attrOrProperties.empty())1805    return;1806 1807  if (op.useCustomPropertiesEncoding()) {1808    opClass.declareStaticMethod(1809        "::llvm::LogicalResult", "readProperties",1810        MethodParameter("::mlir::DialectBytecodeReader &", "reader"),1811        MethodParameter("::mlir::OperationState &", "state"));1812    opClass.declareMethod(1813        "void", "writeProperties",1814        MethodParameter("::mlir::DialectBytecodeWriter &", "writer"));1815    return;1816  }1817 1818  auto &readPropertiesMethod =1819      opClass1820          .addStaticMethod(1821              "::llvm::LogicalResult", "readProperties",1822              MethodParameter("::mlir::DialectBytecodeReader &", "reader"),1823              MethodParameter("::mlir::OperationState &", "state"))1824          ->body();1825 1826  auto &writePropertiesMethod =1827      opClass1828          .addMethod(1829              "void", "writeProperties",1830              MethodParameter("::mlir::DialectBytecodeWriter &", "writer"))1831          ->body();1832 1833  // Populate bytecode serialization logic.1834  readPropertiesMethod1835      << "  auto &prop = state.getOrAddProperties<Properties>(); (void)prop;";1836  writePropertiesMethod << "  auto &prop = getProperties(); (void)prop;\n";1837  for (const auto &item : llvm::enumerate(attrOrProperties)) {1838    auto &attrOrProp = item.value();1839    FmtContext fctx;1840    fctx.addSubst("_reader", "reader")1841        .addSubst("_writer", "writer")1842        .addSubst("_storage", propertyStorage)1843        .addSubst("_ctxt", "this->getContext()");1844    // If the op emits operand/result segment sizes as a property, emit the1845    // legacy reader/writer in the appropriate order to allow backward1846    // compatibility and back deployment.1847    if (emitHelper.getOperandSegmentsSize().has_value() &&1848        item.index() == emitHelper.getOperandSegmentSizesLegacyIndex()) {1849      FmtContext fmtCtxt(fctx);1850      fmtCtxt.addSubst("_propName", operandSegmentAttrName);1851      readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);1852      writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);1853    }1854    if (emitHelper.getResultSegmentsSize().has_value() &&1855        item.index() == emitHelper.getResultSegmentSizesLegacyIndex()) {1856      FmtContext fmtCtxt(fctx);1857      fmtCtxt.addSubst("_propName", resultSegmentAttrName);1858      readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);1859      writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);1860    }1861    if (const auto *namedProperty =1862            dyn_cast<const NamedProperty *>(attrOrProp)) {1863      StringRef name = namedProperty->name;1864      readPropertiesMethod << formatv(1865          R"(1866  {{1867    auto &propStorage = prop.{0};1868    auto readProp = [&]() {1869      {1};1870      return ::mlir::success();1871    };1872    if (::mlir::failed(readProp()))1873      return ::mlir::failure();1874  }1875)",1876          name,1877          tgfmt(namedProperty->prop.getReadFromMlirBytecodeCall(), &fctx));1878      writePropertiesMethod << formatv(1879          R"(1880  {{1881    auto &propStorage = prop.{0};1882    {1};1883  }1884)",1885          name, tgfmt(namedProperty->prop.getWriteToMlirBytecodeCall(), &fctx));1886      continue;1887    }1888    const auto *namedAttr = dyn_cast<const AttributeMetadata *>(attrOrProp);1889    StringRef name = namedAttr->attrName;1890    if (namedAttr->isRequired) {1891      readPropertiesMethod << formatv(R"(1892  if (::mlir::failed(reader.readAttribute(prop.{0})))1893    return ::mlir::failure();1894)",1895                                      name);1896      writePropertiesMethod1897          << formatv("  writer.writeAttribute(prop.{0});\n", name);1898    } else {1899      readPropertiesMethod << formatv(R"(1900  if (::mlir::failed(reader.readOptionalAttribute(prop.{0})))1901    return ::mlir::failure();1902)",1903                                      name);1904      writePropertiesMethod << formatv(R"(1905  writer.writeOptionalAttribute(prop.{0});1906)",1907                                       name);1908    }1909  }1910  readPropertiesMethod << "  return ::mlir::success();";1911}1912 1913void OpEmitter::genPropGetters() {1914  for (const NamedProperty &prop : op.getProperties()) {1915    std::string name = op.getGetterName(prop.name);1916    emitPropGetter(opClass, op, name, prop.prop);1917  }1918}1919 1920void OpEmitter::genPropSetters() {1921  for (const NamedProperty &prop : op.getProperties()) {1922    std::string name = op.getSetterName(prop.name);1923    std::string argName = "new" + convertToCamelFromSnakeCase(1924                                      prop.name, /*capitalizeFirst=*/true);1925    auto *method = opClass.addInlineMethod(1926        "void", name, MethodParameter(prop.prop.getInterfaceType(), argName));1927    if (!method)1928      return;1929    method->body() << formatv("  getProperties().{0}({1});", name, argName);1930  }1931}1932 1933void OpEmitter::genAttrGetters() {1934  FmtContext fctx;1935  fctx.withBuilder("::mlir::Builder((*this)->getContext())");1936 1937  // Emit the derived attribute body.1938  auto emitDerivedAttr = [&](StringRef name, Attribute attr) {1939    if (auto *method = opClass.addMethod(attr.getReturnType(), name))1940      method->body() << "  " << attr.getDerivedCodeBody() << "\n";1941  };1942 1943  // Generate named accessor with Attribute return type. This is a wrapper1944  // class that allows referring to the attributes via accessors instead of1945  // having to use the string interface for better compile time verification.1946  auto emitAttrWithStorageType = [&](StringRef name, StringRef attrName,1947                                     Attribute attr) {1948    // The method body for this getter is trivial. Emit it inline.1949    auto *method =1950        opClass.addInlineMethod(attr.getStorageType(), name + "Attr");1951    if (!method)1952      return;1953    method->body() << formatv(1954        "  return ::llvm::{1}<{2}>({0});", emitHelper.getAttr(attrName),1955        attr.isOptional() || attr.hasDefaultValue() ? "dyn_cast_or_null"1956                                                    : "cast",1957        attr.getStorageType());1958  };1959 1960  for (const NamedAttribute &namedAttr : op.getAttributes()) {1961    std::string name = op.getGetterName(namedAttr.name);1962    if (namedAttr.attr.isDerivedAttr()) {1963      emitDerivedAttr(name, namedAttr.attr);1964    } else {1965      emitAttrWithStorageType(name, namedAttr.name, namedAttr.attr);1966      emitAttrGetterWithReturnType(fctx, opClass, op, name, namedAttr.attr);1967    }1968  }1969 1970  auto derivedAttrs = make_filter_range(op.getAttributes(),1971                                        [](const NamedAttribute &namedAttr) {1972                                          return namedAttr.attr.isDerivedAttr();1973                                        });1974  if (derivedAttrs.empty())1975    return;1976 1977  opClass.addTrait("::mlir::DerivedAttributeOpInterface::Trait");1978  // Generate helper method to query whether a named attribute is a derived1979  // attribute. This enables, for example, avoiding adding an attribute that1980  // overlaps with a derived attribute.1981  {1982    auto *method =1983        opClass.addStaticMethod("bool", "isDerivedAttribute",1984                                MethodParameter("::llvm::StringRef", "name"));1985    ERROR_IF_PRUNED(method, "isDerivedAttribute", op);1986    auto &body = method->body();1987    for (auto namedAttr : derivedAttrs)1988      body << "  if (name == \"" << namedAttr.name << "\") return true;\n";1989    body << " return false;";1990  }1991  // Generate method to materialize derived attributes as a DictionaryAttr.1992  {1993    auto *method = opClass.addMethod("::mlir::DictionaryAttr",1994                                     "materializeDerivedAttributes");1995    ERROR_IF_PRUNED(method, "materializeDerivedAttributes", op);1996    auto &body = method->body();1997 1998    auto nonMaterializable =1999        make_filter_range(derivedAttrs, [](const NamedAttribute &namedAttr) {2000          return namedAttr.attr.getConvertFromStorageCall().empty();2001        });2002    if (!nonMaterializable.empty()) {2003      std::string attrs;2004      llvm::raw_string_ostream os(attrs);2005      interleaveComma(nonMaterializable, os, [&](const NamedAttribute &attr) {2006        os << op.getGetterName(attr.name);2007      });2008      PrintWarning(2009          op.getLoc(),2010          formatv(2011              "op has non-materializable derived attributes '{0}', skipping",2012              os.str()));2013      body << formatv("  emitOpError(\"op has non-materializable derived "2014                      "attributes '{0}'\");\n",2015                      attrs);2016      body << "  return nullptr;";2017      return;2018    }2019 2020    body << "  ::mlir::MLIRContext* ctx = getContext();\n";2021    body << "  ::mlir::Builder odsBuilder(ctx); (void)odsBuilder;\n";2022    body << "  return ::mlir::DictionaryAttr::get(";2023    body << "  ctx, {\n";2024    interleave(2025        derivedAttrs, body,2026        [&](const NamedAttribute &namedAttr) {2027          auto tmpl = namedAttr.attr.getConvertFromStorageCall();2028          std::string name = op.getGetterName(namedAttr.name);2029          body << "    {" << name << "AttrName(),\n"2030               << tgfmt(tmpl, &fctx.withSelf(name + "()")2031                                   .withBuilder("odsBuilder")2032                                   .addSubst("_ctxt", "ctx")2033                                   .addSubst("_storage", "ctx"))2034               << "}";2035        },2036        ",\n");2037    body << "});";2038  }2039}2040 2041void OpEmitter::genAttrSetters() {2042  bool useProperties = op.getDialect().usePropertiesForAttributes();2043 2044  // Generate the code to set an attribute.2045  auto emitSetAttr = [&](Method *method, StringRef getterName,2046                         StringRef attrName, StringRef attrVar) {2047    if (useProperties) {2048      method->body() << formatv("  getProperties().{0} = {1};", attrName,2049                                attrVar);2050    } else {2051      method->body() << formatv("  (*this)->setAttr({0}AttrName(), {1});",2052                                getterName, attrVar);2053    }2054  };2055 2056  // Generate raw named setter type. This is a wrapper class that allows setting2057  // to the attributes via setters instead of having to use the string interface2058  // for better compile time verification.2059  auto emitAttrWithStorageType = [&](StringRef setterName, StringRef getterName,2060                                     StringRef attrName, Attribute attr) {2061    // This method body is trivial, so emit it inline.2062    auto *method =2063        opClass.addInlineMethod("void", setterName + "Attr",2064                                MethodParameter(attr.getStorageType(), "attr"));2065    if (method)2066      emitSetAttr(method, getterName, attrName, "attr");2067  };2068 2069  // Generate a setter that accepts the underlying C++ type as opposed to the2070  // attribute type.2071  auto emitAttrWithReturnType = [&](StringRef setterName, StringRef getterName,2072                                    StringRef attrName, Attribute attr) {2073    Attribute baseAttr = attr.getBaseAttr();2074    if (!canUseUnwrappedRawValue(baseAttr))2075      return;2076    FmtContext fctx;2077    fctx.withBuilder("::mlir::Builder((*this)->getContext())");2078    bool isUnitAttr = attr.getAttrDefName() == "UnitAttr";2079    bool isOptional = attr.isOptional();2080 2081    auto createMethod = [&](const Twine &paramType) {2082      return opClass.addMethod("void", setterName,2083                               MethodParameter(paramType.str(), "attrValue"));2084    };2085 2086    // Build the method using the correct parameter type depending on2087    // optionality.2088    Method *method = nullptr;2089    if (isUnitAttr)2090      method = createMethod("bool");2091    else if (isOptional)2092      method =2093          createMethod("::std::optional<" + baseAttr.getReturnType() + ">");2094    else2095      method = createMethod(attr.getReturnType());2096    if (!method)2097      return;2098 2099    // If the value isn't optional, just set it directly.2100    if (!isOptional) {2101      emitSetAttr(method, getterName, attrName,2102                  constBuildAttrFromParam(attr, fctx, "attrValue"));2103      return;2104    }2105 2106    // Otherwise, we only set if the provided value is valid. If it isn't, we2107    // remove the attribute.2108 2109    // TODO: Handle unit attr parameters specially, given that it is treated as2110    // optional but not in the same way as the others (i.e. it uses bool over2111    // std::optional<>).2112    StringRef paramStr = isUnitAttr ? "attrValue" : "*attrValue";2113    if (!useProperties) {2114      const char *optionalCodeBody = R"(2115    if (attrValue)2116      return (*this)->setAttr({0}AttrName(), {1});2117    (*this)->removeAttr({0}AttrName());)";2118      method->body() << formatv(2119          optionalCodeBody, getterName,2120          constBuildAttrFromParam(baseAttr, fctx, paramStr));2121    } else {2122      const char *optionalCodeBody = R"(2123    auto &odsProp = getProperties().{0};2124    if (attrValue)2125      odsProp = {1};2126    else2127      odsProp = nullptr;)";2128      method->body() << formatv(2129          optionalCodeBody, attrName,2130          constBuildAttrFromParam(baseAttr, fctx, paramStr));2131    }2132  };2133 2134  for (const NamedAttribute &namedAttr : op.getAttributes()) {2135    if (namedAttr.attr.isDerivedAttr())2136      continue;2137    std::string setterName = op.getSetterName(namedAttr.name);2138    std::string getterName = op.getGetterName(namedAttr.name);2139    emitAttrWithStorageType(setterName, getterName, namedAttr.name,2140                            namedAttr.attr);2141    emitAttrWithReturnType(setterName, getterName, namedAttr.name,2142                           namedAttr.attr);2143  }2144}2145 2146void OpEmitter::genOptionalAttrRemovers() {2147  // Generate methods for removing optional attributes, instead of having to2148  // use the string interface. Enables better compile time verification.2149  auto emitRemoveAttr = [&](StringRef name, bool useProperties) {2150    auto *method = opClass.addInlineMethod("::mlir::Attribute",2151                                           op.getRemoverName(name) + "Attr");2152    if (!method)2153      return;2154    if (useProperties) {2155      method->body() << formatv(R"(2156    auto attr = getProperties().{0};2157    getProperties().{0} = {{};2158    return attr;2159)",2160                                name);2161      return;2162    }2163    method->body() << formatv("return (*this)->removeAttr({0}AttrName());",2164                              op.getGetterName(name));2165  };2166 2167  for (const NamedAttribute &namedAttr : op.getAttributes())2168    if (namedAttr.attr.isOptional())2169      emitRemoveAttr(namedAttr.name,2170                     op.getDialect().usePropertiesForAttributes());2171}2172 2173// Generates the code to compute the start and end index of an operand or result2174// range.2175template <typename RangeT>2176static void generateValueRangeStartAndEnd(2177    Class &opClass, bool isGenericAdaptorBase, StringRef methodName,2178    int numVariadic, int numNonVariadic, StringRef rangeSizeCall,2179    bool hasAttrSegmentSize, StringRef sizeAttrInit, RangeT &&odsValues) {2180 2181  SmallVector<MethodParameter> parameters{MethodParameter("unsigned", "index")};2182  if (isGenericAdaptorBase) {2183    parameters.emplace_back("unsigned", "odsOperandsSize");2184    // The range size is passed per parameter for generic adaptor bases as2185    // using the rangeSizeCall would require the operands, which are not2186    // accessible in the base class.2187    rangeSizeCall = "odsOperandsSize";2188  }2189 2190  // The method is trivial if the operation does not have any variadic operands.2191  // In that case, make sure to generate it in-line.2192  auto *method = opClass.addMethod("std::pair<unsigned, unsigned>", methodName,2193                                   numVariadic == 0 ? Method::Properties::Inline2194                                                    : Method::Properties::None,2195                                   parameters);2196  if (!method)2197    return;2198  auto &body = method->body();2199  if (numVariadic == 0) {2200    body << "  return {index, 1};\n";2201  } else if (hasAttrSegmentSize) {2202    body << sizeAttrInit << attrSizedSegmentValueRangeCalcCode;2203  } else {2204    // Because the op can have arbitrarily interleaved variadic and non-variadic2205    // operands, we need to embed a list in the "sink" getter method for2206    // calculation at run-time.2207    SmallVector<StringRef, 4> isVariadic;2208    isVariadic.reserve(llvm::size(odsValues));2209    for (auto &it : odsValues)2210      isVariadic.push_back(it.isVariableLength() ? "true" : "false");2211    std::string isVariadicList = llvm::join(isVariadic, ", ");2212    body << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList,2213                    numNonVariadic, numVariadic, rangeSizeCall, "operand");2214  }2215}2216 2217static std::string generateTypeForGetter(const NamedTypeConstraint &value) {2218  return llvm::formatv("::mlir::TypedValue<{0}>", value.constraint.getCppType())2219      .str();2220}2221 2222// Generates the named operand getter methods for the given Operator `op` and2223// puts them in `opClass`.  Uses `rangeType` as the return type of getters that2224// return a range of operands (individual operands are `Value ` and each2225// element in the range must also be `Value `); use `rangeBeginCall` to get2226// an iterator to the beginning of the operand range; use `rangeSizeCall` to2227// obtain the number of operands. `getOperandCallPattern` contains the code2228// necessary to obtain a single operand whose position will be substituted2229// instead of2230// "{0}" marker in the pattern.  Note that the pattern should work for any kind2231// of ops, in particular for one-operand ops that may not have the2232// `getOperand(unsigned)` method.2233static void2234generateNamedOperandGetters(const Operator &op, Class &opClass,2235                            Class *genericAdaptorBase, StringRef sizeAttrInit,2236                            StringRef rangeType, StringRef rangeElementType,2237                            StringRef rangeBeginCall, StringRef rangeSizeCall,2238                            StringRef getOperandCallPattern) {2239  const int numOperands = op.getNumOperands();2240  const int numVariadicOperands = op.getNumVariableLengthOperands();2241  const int numNormalOperands = numOperands - numVariadicOperands;2242 2243  const auto *sameVariadicSize =2244      op.getTrait("::mlir::OpTrait::SameVariadicOperandSize");2245  const auto *attrSizedOperands =2246      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");2247 2248  if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) {2249    PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "2250                                 "specification over their sizes");2251  }2252 2253  if (numVariadicOperands < 2 && attrSizedOperands) {2254    PrintFatalError(op.getLoc(), "op must have at least two variadic operands "2255                                 "to use 'AttrSizedOperandSegments' trait");2256  }2257 2258  if (attrSizedOperands && sameVariadicSize) {2259    PrintFatalError(op.getLoc(),2260                    "op cannot have both 'AttrSizedOperandSegments' and "2261                    "'SameVariadicOperandSize' traits");2262  }2263 2264  // Print the ods names so they don't need to be hardcoded in the source.2265  for (int i = 0; i != numOperands; ++i) {2266    const auto &operand = op.getOperand(i);2267    if (operand.name.empty())2268      continue;2269 2270    opClass.declare<Field>("static constexpr int", Twine("odsIndex_") +2271                                                       operand.name + " = " +2272                                                       Twine(i));2273  }2274 2275  // First emit a few "sink" getter methods upon which we layer all nicer named2276  // getter methods.2277  // If generating for an adaptor, the method is put into the non-templated2278  // generic base class, to not require being defined in the header.2279  // Since the operand size can't be determined from the base class however,2280  // it has to be passed as an additional argument. The trampoline below2281  // generates the function with the same signature as the Op in the generic2282  // adaptor.2283  bool isGenericAdaptorBase = genericAdaptorBase != nullptr;2284  generateValueRangeStartAndEnd(2285      /*opClass=*/isGenericAdaptorBase ? *genericAdaptorBase : opClass,2286      isGenericAdaptorBase,2287      /*methodName=*/"getODSOperandIndexAndLength", numVariadicOperands,2288      numNormalOperands, rangeSizeCall, attrSizedOperands, sizeAttrInit,2289      const_cast<Operator &>(op).getOperands());2290  if (isGenericAdaptorBase) {2291    // Generate trampoline for calling 'getODSOperandIndexAndLength' with just2292    // the index. This just calls the implementation in the base class but2293    // passes the operand size as parameter.2294    Method *method = opClass.addInlineMethod(2295        "std::pair<unsigned, unsigned>", "getODSOperandIndexAndLength",2296        MethodParameter("unsigned", "index"));2297    ERROR_IF_PRUNED(method, "getODSOperandIndexAndLength", op);2298    MethodBody &body = method->body();2299    body.indent() << formatv(2300        "return Base::getODSOperandIndexAndLength(index, {0});", rangeSizeCall);2301  }2302 2303  // The implementation of this method is trivial and it is very load-bearing.2304  // Generate it inline.2305  auto *m = opClass.addInlineMethod(rangeType, "getODSOperands",2306                                    MethodParameter("unsigned", "index"));2307  ERROR_IF_PRUNED(m, "getODSOperands", op);2308  auto &body = m->body();2309  body << formatv(valueRangeReturnCode, rangeBeginCall,2310                  "getODSOperandIndexAndLength(index)");2311 2312  // Then we emit nicer named getter methods by redirecting to the "sink" getter2313  // method.2314  for (int i = 0; i != numOperands; ++i) {2315    const auto &operand = op.getOperand(i);2316    if (operand.name.empty())2317      continue;2318    std::string name = op.getGetterName(operand.name);2319    if (operand.isOptional()) {2320      m = opClass.addInlineMethod(isGenericAdaptorBase2321                                      ? rangeElementType2322                                      : generateTypeForGetter(operand),2323                                  name);2324      ERROR_IF_PRUNED(m, name, op);2325      m->body().indent() << formatv("auto operands = getODSOperands({0});\n"2326                                    "return operands.empty() ? {1}{{} : ",2327                                    i, m->getReturnType());2328      if (!isGenericAdaptorBase)2329        m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());2330      m->body() << "(*operands.begin());";2331    } else if (operand.isVariadicOfVariadic()) {2332      std::string segmentAttr = op.getGetterName(2333          operand.constraint.getVariadicOfVariadicSegmentSizeAttr());2334      if (genericAdaptorBase) {2335        m = opClass.addMethod("::llvm::SmallVector<" + rangeType + ">", name);2336        ERROR_IF_PRUNED(m, name, op);2337        m->body() << llvm::formatv(variadicOfVariadicAdaptorCalcCode,2338                                   segmentAttr, i, rangeType);2339        continue;2340      }2341 2342      m = opClass.addInlineMethod("::mlir::OperandRangeRange", name);2343      ERROR_IF_PRUNED(m, name, op);2344      m->body() << "  return getODSOperands(" << i << ").split(" << segmentAttr2345                << "Attr());";2346    } else if (operand.isVariadic()) {2347      m = opClass.addInlineMethod(rangeType, name);2348      ERROR_IF_PRUNED(m, name, op);2349      m->body() << "  return getODSOperands(" << i << ");";2350    } else {2351      m = opClass.addInlineMethod(isGenericAdaptorBase2352                                      ? rangeElementType2353                                      : generateTypeForGetter(operand),2354                                  name);2355      ERROR_IF_PRUNED(m, name, op);2356      m->body().indent() << "return ";2357      if (!isGenericAdaptorBase)2358        m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());2359      m->body() << llvm::formatv("(*getODSOperands({0}).begin());", i);2360    }2361  }2362}2363 2364void OpEmitter::genNamedOperandGetters() {2365  // Build the code snippet used for initializing the operand_segment_size)s2366  // array.2367  std::string attrSizeInitCode;2368  if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {2369    if (op.getDialect().usePropertiesForAttributes())2370      attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,2371                                 "getProperties().operandSegmentSizes");2372 2373    else2374      attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,2375                                 emitHelper.getAttr(operandSegmentAttrName));2376  }2377 2378  generateNamedOperandGetters(2379      op, opClass,2380      /*genericAdaptorBase=*/nullptr,2381      /*sizeAttrInit=*/attrSizeInitCode,2382      /*rangeType=*/"::mlir::Operation::operand_range",2383      /*rangeElementType=*/"::mlir::Value",2384      /*rangeBeginCall=*/"getOperation()->operand_begin()",2385      /*rangeSizeCall=*/"getOperation()->getNumOperands()",2386      /*getOperandCallPattern=*/"getOperation()->getOperand({0})");2387}2388 2389void OpEmitter::genNamedOperandSetters() {2390  auto *attrSizedOperands =2391      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");2392  for (int i = 0, e = op.getNumOperands(); i != e; ++i) {2393    const auto &operand = op.getOperand(i);2394    if (operand.name.empty())2395      continue;2396    std::string name = op.getGetterName(operand.name);2397 2398    StringRef returnType;2399    if (operand.isVariadicOfVariadic()) {2400      returnType = "::mlir::MutableOperandRangeRange";2401    } else if (operand.isVariableLength()) {2402      returnType = "::mlir::MutableOperandRange";2403    } else {2404      returnType = "::mlir::OpOperand &";2405    }2406    bool isVariadicOperand =2407        operand.isVariadicOfVariadic() || operand.isVariableLength();2408    auto *m = opClass.addMethod(returnType, name + "Mutable",2409                                isVariadicOperand ? Method::Properties::None2410                                                  : Method::Properties::Inline);2411    ERROR_IF_PRUNED(m, name, op);2412    auto &body = m->body();2413    body << "  auto range = getODSOperandIndexAndLength(" << i << ");\n";2414 2415    if (!isVariadicOperand) {2416      // In case of a single operand, return a single OpOperand.2417      body << "  return getOperation()->getOpOperand(range.first);\n";2418      continue;2419    }2420 2421    body << "  auto mutableRange = "2422            "::mlir::MutableOperandRange(getOperation(), "2423            "range.first, range.second";2424    if (attrSizedOperands) {2425      if (emitHelper.hasProperties())2426        body << formatv(", ::mlir::MutableOperandRange::OperandSegment({0}u, "2427                        "{{getOperandSegmentSizesAttrName(), "2428                        "::mlir::DenseI32ArrayAttr::get(getContext(), "2429                        "getProperties().operandSegmentSizes)})",2430                        i);2431      else2432        body << formatv(2433            ", ::mlir::MutableOperandRange::OperandSegment({0}u, *{1})", i,2434            emitHelper.getAttr(operandSegmentAttrName, /*isNamed=*/true));2435    }2436    body << ");\n";2437 2438    // If this operand is a nested variadic, we split the range into a2439    // MutableOperandRangeRange that provides a range over all of the2440    // sub-ranges.2441    if (operand.isVariadicOfVariadic()) {2442      body << "  return "2443              "mutableRange.split(*(*this)->getAttrDictionary().getNamed("2444           << op.getGetterName(2445                  operand.constraint.getVariadicOfVariadicSegmentSizeAttr())2446           << "AttrName()));\n";2447    } else {2448      // Otherwise, we use the full range directly.2449      body << "  return mutableRange;\n";2450    }2451  }2452}2453 2454void OpEmitter::genNamedResultGetters() {2455  const int numResults = op.getNumResults();2456  const int numVariadicResults = op.getNumVariableLengthResults();2457  const int numNormalResults = numResults - numVariadicResults;2458 2459  // If we have more than one variadic results, we need more complicated logic2460  // to calculate the value range for each result.2461 2462  const auto *sameVariadicSize =2463      op.getTrait("::mlir::OpTrait::SameVariadicResultSize");2464  const auto *attrSizedResults =2465      op.getTrait("::mlir::OpTrait::AttrSizedResultSegments");2466 2467  if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) {2468    PrintFatalError(op.getLoc(), "op has multiple variadic results but no "2469                                 "specification over their sizes");2470  }2471 2472  if (numVariadicResults < 2 && attrSizedResults) {2473    PrintFatalError(op.getLoc(), "op must have at least two variadic results "2474                                 "to use 'AttrSizedResultSegments' trait");2475  }2476 2477  if (attrSizedResults && sameVariadicSize) {2478    PrintFatalError(op.getLoc(),2479                    "op cannot have both 'AttrSizedResultSegments' and "2480                    "'SameVariadicResultSize' traits");2481  }2482 2483  // Build the initializer string for the result segment size attribute.2484  std::string attrSizeInitCode;2485  if (attrSizedResults) {2486    if (op.getDialect().usePropertiesForAttributes())2487      attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,2488                                 "getProperties().resultSegmentSizes");2489 2490    else2491      attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,2492                                 emitHelper.getAttr(resultSegmentAttrName));2493  }2494 2495  generateValueRangeStartAndEnd(2496      opClass, /*isGenericAdaptorBase=*/false, "getODSResultIndexAndLength",2497      numVariadicResults, numNormalResults, "getOperation()->getNumResults()",2498      attrSizedResults, attrSizeInitCode, op.getResults());2499 2500  // The implementation of this method is trivial and it is very load-bearing.2501  // Generate it inline.2502  auto *m = opClass.addInlineMethod("::mlir::Operation::result_range",2503                                    "getODSResults",2504                                    MethodParameter("unsigned", "index"));2505  ERROR_IF_PRUNED(m, "getODSResults", op);2506  m->body() << formatv(valueRangeReturnCode, "getOperation()->result_begin()",2507                       "getODSResultIndexAndLength(index)");2508 2509  for (int i = 0; i != numResults; ++i) {2510    const auto &result = op.getResult(i);2511    if (result.name.empty())2512      continue;2513    std::string name = op.getGetterName(result.name);2514    if (result.isOptional()) {2515      m = opClass.addInlineMethod(generateTypeForGetter(result), name);2516      ERROR_IF_PRUNED(m, name, op);2517      m->body() << "  auto results = getODSResults(" << i << ");\n"2518                << llvm::formatv("  return results.empty()"2519                                 " ? {0}()"2520                                 " : ::llvm::cast<{0}>(*results.begin());",2521                                 m->getReturnType());2522    } else if (result.isVariadic()) {2523      m = opClass.addInlineMethod("::mlir::Operation::result_range", name);2524      ERROR_IF_PRUNED(m, name, op);2525      m->body() << "  return getODSResults(" << i << ");";2526    } else {2527      m = opClass.addInlineMethod(generateTypeForGetter(result), name);2528      ERROR_IF_PRUNED(m, name, op);2529      m->body() << llvm::formatv(2530          "  return ::llvm::cast<{0}>(*getODSResults({1}).begin());",2531          m->getReturnType(), i);2532    }2533  }2534}2535 2536void OpEmitter::genNamedRegionGetters() {2537  unsigned numRegions = op.getNumRegions();2538  for (unsigned i = 0; i < numRegions; ++i) {2539    const auto &region = op.getRegion(i);2540    if (region.name.empty())2541      continue;2542    std::string name = op.getGetterName(region.name);2543 2544    // Generate the accessors for a variadic region.2545    if (region.isVariadic()) {2546      auto *m = opClass.addInlineMethod(2547          "::mlir::MutableArrayRef<::mlir::Region>", name);2548      ERROR_IF_PRUNED(m, name, op);2549      m->body() << formatv("  return (*this)->getRegions().drop_front({0});",2550                           i);2551      continue;2552    }2553 2554    auto *m = opClass.addInlineMethod("::mlir::Region &", name);2555    ERROR_IF_PRUNED(m, name, op);2556    m->body() << formatv("  return (*this)->getRegion({0});", i);2557  }2558}2559 2560void OpEmitter::genNamedSuccessorGetters() {2561  unsigned numSuccessors = op.getNumSuccessors();2562  for (unsigned i = 0; i < numSuccessors; ++i) {2563    const NamedSuccessor &successor = op.getSuccessor(i);2564    if (successor.name.empty())2565      continue;2566    std::string name = op.getGetterName(successor.name);2567    // Generate the accessors for a variadic successor list.2568    if (successor.isVariadic()) {2569      auto *m = opClass.addInlineMethod("::mlir::SuccessorRange", name);2570      ERROR_IF_PRUNED(m, name, op);2571      m->body() << formatv(2572          "  return {std::next((*this)->successor_begin(), {0}), "2573          "(*this)->successor_end()};",2574          i);2575      continue;2576    }2577 2578    auto *m = opClass.addInlineMethod("::mlir::Block *", name);2579    ERROR_IF_PRUNED(m, name, op);2580    m->body() << formatv("  return (*this)->getSuccessor({0});", i);2581  }2582}2583 2584static bool canGenerateUnwrappedBuilder(const Operator &op) {2585  // If this op does not have native attributes at all, return directly to avoid2586  // redefining builders.2587  if (op.getNumNativeAttributes() == 0)2588    return false;2589 2590  bool canGenerate = false;2591  // We are generating builders that take raw values for attributes. We need to2592  // make sure the native attributes have a meaningful "unwrapped" value type2593  // different from the wrapped mlir::Attribute type to avoid redefining2594  // builders. This checks for the op has at least one such native attribute.2595  for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) {2596    const NamedAttribute &namedAttr = op.getAttribute(i);2597    if (canUseUnwrappedRawValue(namedAttr.attr)) {2598      canGenerate = true;2599      break;2600    }2601  }2602  return canGenerate;2603}2604 2605static bool canInferType(const Operator &op) {2606  return op.getTrait("::mlir::InferTypeOpInterface::Trait");2607}2608 2609void OpEmitter::genInlineCreateBody(2610    const SmallVector<MethodParameter> &paramList) {2611  SmallVector<MethodParameter> createParamListOpBuilder;2612  SmallVector<MethodParameter> createParamListImplicitLocOpBuilder;2613  SmallVector<llvm::StringRef, 4> nonBuilderStateArgsList;2614  createParamListOpBuilder.emplace_back("::mlir::OpBuilder &", "builder");2615  createParamListImplicitLocOpBuilder.emplace_back(2616      "::mlir::ImplicitLocOpBuilder &", "builder");2617  std::string locParamName = "location";2618  while (llvm::find_if(paramList, [&locParamName](const MethodParameter &p) {2619           return p.getName() == locParamName;2620         }) != paramList.end()) {2621    locParamName += "_";2622  }2623  createParamListOpBuilder.emplace_back("::mlir::Location", locParamName);2624 2625  for (auto &param : paramList) {2626    if (param.getType() == "::mlir::OpBuilder &" ||2627        param.getType() == "::mlir::OperationState &")2628      continue;2629    createParamListOpBuilder.emplace_back(param.getType(), param.getName(),2630                                          param.getDefaultValue(),2631                                          param.isOptional());2632    createParamListImplicitLocOpBuilder.emplace_back(2633        param.getType(), param.getName(), param.getDefaultValue(),2634        param.isOptional());2635    nonBuilderStateArgsList.push_back(param.getName());2636  }2637  auto *cWithLoc = opClass.addStaticMethod(opClass.getClassName(), "create",2638                                           createParamListOpBuilder);2639  auto *cImplicitLoc = opClass.addStaticMethod(2640      opClass.getClassName(), "create", createParamListImplicitLocOpBuilder);2641  std::string nonBuilderStateArgs = "";2642  if (!nonBuilderStateArgsList.empty()) {2643    llvm::raw_string_ostream nonBuilderStateArgsOS(nonBuilderStateArgs);2644    interleaveComma(nonBuilderStateArgsList, nonBuilderStateArgsOS);2645    nonBuilderStateArgs = ", " + nonBuilderStateArgs;2646  }2647  if (cWithLoc)2648    cWithLoc->body() << llvm::formatv(inlineCreateBody, locParamName,2649                                      nonBuilderStateArgs,2650                                      opClass.getClassName());2651  if (cImplicitLoc)2652    cImplicitLoc->body() << llvm::formatv(inlineCreateBodyImplicitLoc,2653                                          nonBuilderStateArgs);2654}2655 2656void OpEmitter::genSeparateArgParamBuilder() {2657  SmallVector<AttrParamKind, 2> attrBuilderType;2658  attrBuilderType.push_back(AttrParamKind::WrappedAttr);2659  if (canGenerateUnwrappedBuilder(op))2660    attrBuilderType.push_back(AttrParamKind::UnwrappedValue);2661 2662  // Emit with separate builders with or without unwrapped attributes and/or2663  // inferring result type.2664  auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind,2665                  bool inferType) {2666    SmallVector<MethodParameter> paramList;2667    SmallVector<std::string, 4> resultNames;2668    llvm::StringSet<> inferredAttributes;2669    buildParamList(paramList, inferredAttributes, resultNames, paramKind,2670                   attrType);2671 2672    auto *m = opClass.addStaticMethod("void", "build", paramList);2673    // If the builder is redundant, skip generating the method.2674    if (!m)2675      return;2676    genInlineCreateBody(paramList);2677 2678    auto &body = m->body();2679    genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,2680                                           /*isRawValueAttr=*/attrType ==2681                                               AttrParamKind::UnwrappedValue);2682 2683    // Push all result types to the operation state2684 2685    if (inferType) {2686      // Generate builder that infers type too.2687      // TODO: Subsume this with general checking if type can be2688      // inferred automatically.2689      body << formatv(R"(2690        ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;2691        if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),2692                      {1}.location, {1}.operands,2693                      {1}.attributes.getDictionary({1}.getContext()),2694                      {1}.getRawProperties(),2695                      {1}.regions, inferredReturnTypes)))2696          {1}.addTypes(inferredReturnTypes);2697        else2698          ::mlir::detail::reportFatalInferReturnTypesError({1});2699        )",2700                      opClass.getClassName(), builderOpState);2701      return;2702    }2703 2704    switch (paramKind) {2705    case TypeParamKind::None:2706      return;2707    case TypeParamKind::Separate:2708      for (int i = 0, e = op.getNumResults(); i < e; ++i) {2709        if (op.getResult(i).isOptional())2710          body << "  if (" << resultNames[i] << ")\n  ";2711        body << "  " << builderOpState << ".addTypes(" << resultNames[i]2712             << ");\n";2713      }2714 2715      // Automatically create the 'resultSegmentSizes' attribute using2716      // the length of the type ranges.2717      if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {2718        if (op.getDialect().usePropertiesForAttributes()) {2719          body << "  ::llvm::copy(::llvm::ArrayRef<int32_t>({";2720        } else {2721          std::string getterName = op.getGetterName(resultSegmentAttrName);2722          body << " " << builderOpState << ".addAttribute(" << getterName2723               << "AttrName(" << builderOpState << ".name), "2724               << "odsBuilder.getDenseI32ArrayAttr({";2725        }2726        interleaveComma(2727            llvm::seq<int>(0, op.getNumResults()), body, [&](int i) {2728              const NamedTypeConstraint &result = op.getResult(i);2729              if (!result.isVariableLength()) {2730                body << "1";2731              } else if (result.isOptional()) {2732                body << "(" << resultNames[i] << " ? 1 : 0)";2733              } else {2734                // VariadicOfVariadic of results are currently unsupported in2735                // MLIR, hence it can only be a simple variadic.2736                // TODO: Add implementation for VariadicOfVariadic results here2737                //       once supported.2738                assert(result.isVariadic());2739                body << "static_cast<int32_t>(" << resultNames[i] << ".size())";2740              }2741            });2742        if (op.getDialect().usePropertiesForAttributes()) {2743          body << "}), " << builderOpState2744               << ".getOrAddProperties<Properties>()."2745                  "resultSegmentSizes.begin());\n";2746        } else {2747          body << "}));\n";2748        }2749      }2750 2751      return;2752    case TypeParamKind::Collective: {2753      int numResults = op.getNumResults();2754      int numVariadicResults = op.getNumVariableLengthResults();2755      int numNonVariadicResults = numResults - numVariadicResults;2756      bool hasVariadicResult = numVariadicResults != 0;2757 2758      // Avoid emitting "resultTypes.size() >= 0u" which is always true.2759      if (!hasVariadicResult || numNonVariadicResults != 0)2760        body << "  " << "assert(resultTypes.size() "2761             << (hasVariadicResult ? ">=" : "==") << " "2762             << numNonVariadicResults2763             << "u && \"mismatched number of results\");\n";2764      body << "  " << builderOpState << ".addTypes(resultTypes);\n";2765    }2766      return;2767    }2768    llvm_unreachable("unhandled TypeParamKind");2769  };2770 2771  // Some of the build methods generated here may be ambiguous, but TableGen's2772  // ambiguous function detection will elide those ones.2773  for (auto attrType : attrBuilderType) {2774    emit(attrType, TypeParamKind::Separate, /*inferType=*/false);2775    if (canInferType(op))2776      emit(attrType, TypeParamKind::None, /*inferType=*/true);2777    emit(attrType, TypeParamKind::Collective, /*inferType=*/false);2778  }2779}2780 2781void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder(2782    CollectiveBuilderKind kind) {2783  int numResults = op.getNumResults();2784 2785  // Signature2786  SmallVector<MethodParameter> paramList;2787  paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");2788  paramList.emplace_back("::mlir::OperationState &", builderOpState);2789  paramList.emplace_back("::mlir::ValueRange", "operands");2790  if (kind == CollectiveBuilderKind::PropStruct)2791    paramList.emplace_back("const Properties &", "properties");2792  // Provide default value for `attributes` when its the last parameter2793  StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";2794  StringRef attributesName = kind == CollectiveBuilderKind::PropStruct2795                                 ? "discardableAttributes"2796                                 : "attributes";2797  paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",2798                         attributesName, attributesDefaultValue);2799  if (op.getNumVariadicRegions())2800    paramList.emplace_back("unsigned", "numRegions");2801 2802  auto *m = opClass.addStaticMethod("void", "build", paramList);2803  // If the builder is redundant, skip generating the method2804  if (!m)2805    return;2806  genInlineCreateBody(paramList);2807  auto &body = m->body();2808 2809  // Operands2810  body << "  " << builderOpState << ".addOperands(operands);\n";2811 2812  if (kind == CollectiveBuilderKind::PropStruct)2813    body << "  " << builderOpState2814         << ".useProperties(const_cast<Properties&>(properties));\n";2815  // Attributes2816  body << "  " << builderOpState << ".addAttributes(" << attributesName2817       << ");\n";2818 2819  // Create the correct number of regions2820  if (int numRegions = op.getNumRegions()) {2821    body << llvm::formatv(2822        "  for (unsigned i = 0; i != {0}; ++i)\n",2823        (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));2824    body << "    (void)" << builderOpState << ".addRegion();\n";2825  }2826 2827  // Result types2828  SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()");2829  body << "  " << builderOpState << ".addTypes({"2830       << llvm::join(resultTypes, ", ") << "});\n\n";2831}2832 2833void OpEmitter::genPopulateDefaultAttributes() {2834  // All done if no attributes, except optional ones, have default values.2835  if (llvm::all_of(op.getAttributes(), [](const NamedAttribute &named) {2836        return !named.attr.hasDefaultValue() || named.attr.isOptional();2837      }))2838    return;2839 2840  if (emitHelper.hasProperties()) {2841    SmallVector<MethodParameter> paramList;2842    paramList.emplace_back("::mlir::OperationName", "opName");2843    paramList.emplace_back("Properties &", "properties");2844    auto *m =2845        opClass.addStaticMethod("void", "populateDefaultProperties", paramList);2846    ERROR_IF_PRUNED(m, "populateDefaultProperties", op);2847    auto &body = m->body();2848    body.indent();2849    body << "::mlir::Builder " << odsBuilder << "(opName.getContext());\n";2850    for (const NamedAttribute &namedAttr : op.getAttributes()) {2851      auto &attr = namedAttr.attr;2852      if (!attr.hasDefaultValue() || attr.isOptional())2853        continue;2854      StringRef name = namedAttr.name;2855      FmtContext fctx;2856      fctx.withBuilder(odsBuilder);2857      body << "if (!properties." << name << ")\n"2858           << "  properties." << name << " = "2859           << std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2860                                tgfmt(attr.getDefaultValue(), &fctx)))2861           << ";\n";2862    }2863    return;2864  }2865 2866  SmallVector<MethodParameter> paramList;2867  paramList.emplace_back("const ::mlir::OperationName &", "opName");2868  paramList.emplace_back("::mlir::NamedAttrList &", "attributes");2869  auto *m = opClass.addStaticMethod("void", "populateDefaultAttrs", paramList);2870  ERROR_IF_PRUNED(m, "populateDefaultAttrs", op);2871  auto &body = m->body();2872  body.indent();2873 2874  // Set default attributes that are unset.2875  body << "auto attrNames = opName.getAttributeNames();\n";2876  body << "::mlir::Builder " << odsBuilder2877       << "(attrNames.front().getContext());\n";2878  StringMap<int> attrIndex;2879  for (const auto &it : llvm::enumerate(emitHelper.getAttrMetadata())) {2880    attrIndex[it.value().first] = it.index();2881  }2882  for (const NamedAttribute &namedAttr : op.getAttributes()) {2883    auto &attr = namedAttr.attr;2884    if (!attr.hasDefaultValue() || attr.isOptional())2885      continue;2886    auto index = attrIndex[namedAttr.name];2887    body << "if (!attributes.get(attrNames[" << index << "])) {\n";2888    FmtContext fctx;2889    fctx.withBuilder(odsBuilder);2890 2891    std::string defaultValue =2892        std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2893                          tgfmt(attr.getDefaultValue(), &fctx)));2894    body.indent() << formatv("attributes.append(attrNames[{0}], {1});\n", index,2895                             defaultValue);2896    body.unindent() << "}\n";2897  }2898}2899 2900void OpEmitter::genInferredTypeCollectiveParamBuilder(2901    CollectiveBuilderKind kind) {2902  SmallVector<MethodParameter> paramList;2903  paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");2904  paramList.emplace_back("::mlir::OperationState &", builderOpState);2905  paramList.emplace_back("::mlir::ValueRange", "operands");2906  if (kind == CollectiveBuilderKind::PropStruct)2907    paramList.emplace_back("const Properties &", "properties");2908  StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";2909  StringRef attributesName = kind == CollectiveBuilderKind::PropStruct2910                                 ? "discardableAttributes"2911                                 : "attributes";2912  paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",2913                         attributesName, attributesDefaultValue);2914  if (op.getNumVariadicRegions())2915    paramList.emplace_back("unsigned", "numRegions");2916 2917  auto *m = opClass.addStaticMethod("void", "build", paramList);2918  // If the builder is redundant, skip generating the method2919  if (!m)2920    return;2921  genInlineCreateBody(paramList);2922  auto &body = m->body();2923 2924  int numResults = op.getNumResults();2925  int numVariadicResults = op.getNumVariableLengthResults();2926  int numNonVariadicResults = numResults - numVariadicResults;2927 2928  int numOperands = op.getNumOperands();2929  int numVariadicOperands = op.getNumVariableLengthOperands();2930  int numNonVariadicOperands = numOperands - numVariadicOperands;2931 2932  // Operands2933  if (numVariadicOperands == 0 || numNonVariadicOperands != 0)2934    body << "  assert(operands.size()"2935         << (numVariadicOperands != 0 ? " >= " : " == ")2936         << numNonVariadicOperands2937         << "u && \"mismatched number of parameters\");\n";2938  body << "  " << builderOpState << ".addOperands(operands);\n";2939  if (kind == CollectiveBuilderKind::PropStruct)2940    body << "  " << builderOpState2941         << ".useProperties(const_cast<Properties &>(properties));\n";2942  body << "  " << builderOpState << ".addAttributes(" << attributesName2943       << ");\n";2944 2945  // Create the correct number of regions2946  if (int numRegions = op.getNumRegions()) {2947    body << llvm::formatv(2948        "  for (unsigned i = 0; i != {0}; ++i)\n",2949        (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));2950    body << "    (void)" << builderOpState << ".addRegion();\n";2951  }2952 2953  // Result types2954  if (emitHelper.hasNonEmptyPropertiesStruct() &&2955      kind == CollectiveBuilderKind::AttrDict) {2956    // Initialize the properties from Attributes before invoking the infer2957    // function.2958    body << formatv(R"(2959  if (!attributes.empty()) {2960    ::mlir::OpaqueProperties properties =2961      &{1}.getOrAddProperties<{0}::Properties>();2962    std::optional<::mlir::RegisteredOperationName> info =2963      {1}.name.getRegisteredInfo();2964    if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,2965        {1}.attributes.getDictionary({1}.getContext()), nullptr)))2966      ::llvm::report_fatal_error("Property conversion failed.");2967  })",2968                    opClass.getClassName(), builderOpState);2969  }2970  body << formatv(R"(2971  ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;2972  if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),2973          {1}.location, operands,2974          {1}.attributes.getDictionary({1}.getContext()),2975          {1}.getRawProperties(),2976          {1}.regions, inferredReturnTypes))) {{)",2977                  opClass.getClassName(), builderOpState);2978  if (numVariadicResults == 0 || numNonVariadicResults != 0)2979    body << "\n    assert(inferredReturnTypes.size()"2980         << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults2981         << "u && \"mismatched number of return types\");";2982  body << "\n    " << builderOpState << ".addTypes(inferredReturnTypes);";2983 2984  body << R"(2985  } else {2986    ::llvm::report_fatal_error("Failed to infer result type(s).");2987  })";2988}2989 2990void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() {2991  auto emit = [&](AttrParamKind attrType) {2992    SmallVector<MethodParameter> paramList;2993    SmallVector<std::string, 4> resultNames;2994    llvm::StringSet<> inferredAttributes;2995    buildParamList(paramList, inferredAttributes, resultNames,2996                   TypeParamKind::None, attrType);2997 2998    auto *m = opClass.addStaticMethod("void", "build", paramList);2999    // If the builder is redundant, skip generating the method3000    if (!m)3001      return;3002    genInlineCreateBody(paramList);3003    auto &body = m->body();3004    genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,3005                                           /*isRawValueAttr=*/attrType ==3006                                               AttrParamKind::UnwrappedValue);3007 3008    auto numResults = op.getNumResults();3009    if (numResults == 0)3010      return;3011 3012    // Push all result types to the operation state3013    const char *index = op.getOperand(0).isVariadic() ? ".front()" : "";3014    std::string resultType =3015        formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str();3016    body << "  " << builderOpState << ".addTypes({" << resultType;3017    for (int i = 1; i != numResults; ++i)3018      body << ", " << resultType;3019    body << "});\n\n";3020  };3021 3022  emit(AttrParamKind::WrappedAttr);3023  // Generate additional builder(s) if any attributes can be "unwrapped"3024  if (canGenerateUnwrappedBuilder(op))3025    emit(AttrParamKind::UnwrappedValue);3026}3027 3028void OpEmitter::genUseAttrAsResultTypeCollectiveParamBuilder(3029    CollectiveBuilderKind kind) {3030  SmallVector<MethodParameter> paramList;3031  paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");3032  paramList.emplace_back("::mlir::OperationState &", builderOpState);3033  paramList.emplace_back("::mlir::ValueRange", "operands");3034  if (kind == CollectiveBuilderKind::PropStruct)3035    paramList.emplace_back("const Properties &", "properties");3036  StringRef attributesName = kind == CollectiveBuilderKind::PropStruct3037                                 ? "discardableAttributes"3038                                 : "attributes";3039  paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",3040                         attributesName, "{}");3041  auto *m = opClass.addStaticMethod("void", "build", paramList);3042  // If the builder is redundant, skip generating the method3043  if (!m)3044    return;3045  genInlineCreateBody(paramList);3046 3047  auto &body = m->body();3048 3049  // Push all result types to the operation state3050  std::string resultType;3051  const auto &namedAttr = op.getAttribute(0);3052 3053  if (namedAttr.attr.isTypeAttr()) {3054    resultType = "::llvm::cast<::mlir::TypeAttr>(typeAttr).getValue()";3055  } else {3056    resultType = "::llvm::cast<::mlir::TypedAttr>(typeAttr).getType()";3057  }3058 3059  if (kind == CollectiveBuilderKind::PropStruct) {3060    body << "  ::mlir::Attribute typeAttr = properties."3061         << op.getGetterName(namedAttr.name) << "();\n";3062  } else {3063    body << "  ::mlir::Attribute typeAttr;\n"3064         << "  auto attrName = " << op.getGetterName(namedAttr.name)3065         << "AttrName(" << builderOpState3066         << ".name);\n"3067            "  for (auto attr : attributes) {\n"3068            "    if (attr.getName() == attrName) {\n"3069            "      typeAttr = attr.getValue();\n"3070            "      break;\n"3071            "    }\n"3072            "  }\n";3073  }3074 3075  // Operands3076  body << "  " << builderOpState << ".addOperands(operands);\n";3077 3078  // Properties3079  if (kind == CollectiveBuilderKind::PropStruct)3080    body << "  " << builderOpState3081         << ".useProperties(const_cast<Properties&>(properties));\n";3082 3083  // Attributes3084  body << "  " << builderOpState << ".addAttributes(" << attributesName3085       << ");\n";3086 3087  // Result types3088  SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType);3089  body << "    " << builderOpState << ".addTypes({"3090       << llvm::join(resultTypes, ", ") << "});\n";3091}3092 3093/// Returns a signature of the builder. Updates the context `fctx` to enable3094/// replacement of $_builder and $_state in the body.3095static SmallVector<MethodParameter>3096getBuilderSignature(const Builder &builder) {3097  ArrayRef<Builder::Parameter> params(builder.getParameters());3098 3099  // Inject builder and state arguments.3100  SmallVector<MethodParameter> arguments;3101  arguments.reserve(params.size() + 2);3102  arguments.emplace_back("::mlir::OpBuilder &", odsBuilder);3103  arguments.emplace_back("::mlir::OperationState &", builderOpState);3104 3105  FmtContext fctx;3106  fctx.withBuilder(odsBuilder);3107 3108  for (unsigned i = 0, e = params.size(); i < e; ++i) {3109    // If no name is provided, generate one.3110    std::optional<StringRef> paramName = params[i].getName();3111    std::string name =3112        paramName ? paramName->str() : "odsArg" + std::to_string(i);3113 3114    StringRef defaultValue;3115    if (std::optional<StringRef> defaultParamValue =3116            params[i].getDefaultValue())3117      defaultValue = *defaultParamValue;3118 3119    arguments.emplace_back(params[i].getCppType(), std::move(name),3120                           tgfmt(defaultValue, &fctx));3121  }3122 3123  return arguments;3124}3125 3126void OpEmitter::genBuilder() {3127  // Handle custom builders if provided.3128  for (const Builder &builder : op.getBuilders()) {3129    SmallVector<MethodParameter> arguments = getBuilderSignature(builder);3130 3131    std::optional<StringRef> body = builder.getBody();3132    auto properties = body ? Method::Static : Method::StaticDeclaration;3133    auto *method = opClass.addMethod("void", "build", properties, arguments);3134 3135    ERROR_IF_PRUNED(method, "build", op);3136 3137    if (method)3138      method->setDeprecated(builder.getDeprecatedMessage());3139 3140    FmtContext fctx;3141    fctx.withBuilder(odsBuilder);3142    fctx.addSubst("_state", builderOpState);3143    if (body)3144      method->body() << tgfmt(*body, &fctx);3145    genInlineCreateBody(arguments);3146  }3147 3148  // Generate default builders that requires all result type, operands, and3149  // attributes as parameters.3150  if (op.skipDefaultBuilders())3151    return;3152 3153  // We generate three classes of builders here:3154  // 1. one having a stand-alone parameter for each operand / attribute, and3155  genSeparateArgParamBuilder();3156  // 2. one having an aggregated parameter for all result types / operands /3157  //    [properties / discardable] attributes, and3158  genCollectiveParamBuilder(CollectiveBuilderKind::AttrDict);3159  if (emitHelper.hasProperties())3160    genCollectiveParamBuilder(CollectiveBuilderKind::PropStruct);3161  // 3. one having a stand-alone parameter for each operand and attribute,3162  //    use the first operand or attribute's type as all result types3163  //    to facilitate different call patterns.3164  if (op.getNumVariableLengthResults() == 0) {3165    if (op.getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {3166      genUseOperandAsResultTypeSeparateParamBuilder();3167      genUseOperandAsResultTypeCollectiveParamBuilder(3168          CollectiveBuilderKind::AttrDict);3169      if (emitHelper.hasProperties())3170        genUseOperandAsResultTypeCollectiveParamBuilder(3171            CollectiveBuilderKind::PropStruct);3172    }3173    if (op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType")) {3174      genUseAttrAsResultTypeCollectiveParamBuilder(3175          CollectiveBuilderKind::AttrDict);3176      genUseAttrAsResultTypeCollectiveParamBuilder(3177          CollectiveBuilderKind::PropStruct);3178    }3179  }3180}3181 3182void OpEmitter::genCollectiveParamBuilder(CollectiveBuilderKind kind) {3183  int numResults = op.getNumResults();3184  int numVariadicResults = op.getNumVariableLengthResults();3185  int numNonVariadicResults = numResults - numVariadicResults;3186 3187  int numOperands = op.getNumOperands();3188  int numVariadicOperands = op.getNumVariableLengthOperands();3189  int numNonVariadicOperands = numOperands - numVariadicOperands;3190 3191  SmallVector<MethodParameter> paramList;3192  paramList.emplace_back("::mlir::OpBuilder &", "");3193  paramList.emplace_back("::mlir::OperationState &", builderOpState);3194  paramList.emplace_back("::mlir::TypeRange", "resultTypes");3195  paramList.emplace_back("::mlir::ValueRange", "operands");3196  if (kind == CollectiveBuilderKind::PropStruct)3197    paramList.emplace_back("const Properties &", "properties");3198  // Provide default value for `attributes` when its the last parameter3199  StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";3200  StringRef attributesName = kind == CollectiveBuilderKind::PropStruct3201                                 ? "discardableAttributes"3202                                 : "attributes";3203  paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",3204                         attributesName, attributesDefaultValue);3205  if (op.getNumVariadicRegions())3206    paramList.emplace_back("unsigned", "numRegions");3207 3208  auto *m = opClass.addStaticMethod("void", "build", paramList);3209  // If the builder is redundant, skip generating the method3210  if (!m)3211    return;3212  genInlineCreateBody(paramList);3213  auto &body = m->body();3214 3215  // Operands3216  if (numVariadicOperands == 0 || numNonVariadicOperands != 0)3217    body << "  assert(operands.size()"3218         << (numVariadicOperands != 0 ? " >= " : " == ")3219         << numNonVariadicOperands3220         << "u && \"mismatched number of parameters\");\n";3221  body << "  " << builderOpState << ".addOperands(operands);\n";3222 3223  // Properties3224  if (kind == CollectiveBuilderKind::PropStruct)3225    body << "  " << builderOpState3226         << ".useProperties(const_cast<Properties&>(properties));\n";3227 3228  // Attributes3229  body << "  " << builderOpState << ".addAttributes(" << attributesName3230       << ");\n";3231 3232  // Create the correct number of regions3233  if (int numRegions = op.getNumRegions()) {3234    body << llvm::formatv(3235        "  for (unsigned i = 0; i != {0}; ++i)\n",3236        (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));3237    body << "    (void)" << builderOpState << ".addRegion();\n";3238  }3239 3240  // Result types3241  if (numVariadicResults == 0 || numNonVariadicResults != 0)3242    body << "  assert(resultTypes.size()"3243         << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults3244         << "u && \"mismatched number of return types\");\n";3245  body << "  " << builderOpState << ".addTypes(resultTypes);\n";3246 3247  if (emitHelper.hasNonEmptyPropertiesStruct() &&3248      kind == CollectiveBuilderKind::AttrDict) {3249    // Initialize the properties from Attributes before invoking the infer3250    // function.3251    body << formatv(R"(3252  if (!attributes.empty()) {3253    ::mlir::OpaqueProperties properties =3254      &{1}.getOrAddProperties<{0}::Properties>();3255    std::optional<::mlir::RegisteredOperationName> info =3256      {1}.name.getRegisteredInfo();3257    if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,3258        {1}.attributes.getDictionary({1}.getContext()), nullptr)))3259      ::llvm::report_fatal_error("Property conversion failed.");3260  })",3261                    opClass.getClassName(), builderOpState);3262  }3263 3264  // Generate builder that infers type too.3265  // TODO: Expand to handle successors.3266  if (canInferType(op) && op.getNumSuccessors() == 0)3267    genInferredTypeCollectiveParamBuilder(kind);3268}3269 3270void OpEmitter::buildParamList(SmallVectorImpl<MethodParameter> &paramList,3271                               llvm::StringSet<> &inferredAttributes,3272                               SmallVectorImpl<std::string> &resultTypeNames,3273                               TypeParamKind typeParamKind,3274                               AttrParamKind attrParamKind) {3275  resultTypeNames.clear();3276  auto numResults = op.getNumResults();3277  resultTypeNames.reserve(numResults);3278 3279  paramList.emplace_back("::mlir::OpBuilder &", odsBuilder);3280  paramList.emplace_back("::mlir::OperationState &", builderOpState);3281 3282  switch (typeParamKind) {3283  case TypeParamKind::None:3284    break;3285  case TypeParamKind::Separate: {3286    // Add parameters for all return types3287    for (int i = 0; i < numResults; ++i) {3288      const auto &result = op.getResult(i);3289      std::string resultName = std::string(result.name);3290      if (resultName.empty())3291        resultName = std::string(formatv("resultType{0}", i));3292 3293      StringRef type =3294          result.isVariadic() ? "::mlir::TypeRange" : "::mlir::Type";3295 3296      paramList.emplace_back(type, resultName, result.isOptional());3297      resultTypeNames.emplace_back(std::move(resultName));3298    }3299  } break;3300  case TypeParamKind::Collective: {3301    paramList.emplace_back("::mlir::TypeRange", "resultTypes");3302    resultTypeNames.push_back("resultTypes");3303  } break;3304  }3305 3306  // Add parameters for all arguments (operands and attributes).3307  // Track "attr-like" (property and attribute) optional values separate from3308  // attributes themselves so that the disambiguation code can look at the first3309  // attribute specifically when determining where to trim the optional-value3310  // list to avoid ambiguity while preserving the ability of all-property ops to3311  // use default parameters.3312  int defaultValuedAttrLikeStartIndex = op.getNumArgs();3313  int defaultValuedAttrStartIndex = op.getNumArgs();3314  // Successors and variadic regions go at the end of the parameter list, so no3315  // default arguments are possible.3316  bool hasTrailingParams = op.getNumSuccessors() || op.getNumVariadicRegions();3317  if (!hasTrailingParams) {3318    // Calculate the start index from which we can attach default values in the3319    // builder declaration.3320    for (int i = op.getNumArgs() - 1; i >= 0; --i) {3321      auto *namedAttr =3322          llvm::dyn_cast_if_present<tblgen::NamedAttribute *>(op.getArg(i));3323      auto *namedProperty =3324          llvm::dyn_cast_if_present<tblgen::NamedProperty *>(op.getArg(i));3325      if (namedProperty) {3326        Property prop = namedProperty->prop;3327        if (!prop.hasDefaultValue())3328          break;3329        defaultValuedAttrLikeStartIndex = i;3330        continue;3331      }3332      if (!namedAttr)3333        break;3334 3335      Attribute attr = namedAttr->attr;3336      // TODO: Currently we can't differentiate between optional meaning do not3337      // verify/not always error if missing or optional meaning need not be3338      // specified in builder. Expand isOptional once we can differentiate.3339      if (!attr.hasDefaultValue() && !attr.isDerivedAttr())3340        break;3341 3342      // Creating an APInt requires us to provide bitwidth, value, and3343      // signedness, which is complicated compared to others. Similarly3344      // for APFloat.3345      // TODO: Adjust the 'returnType' field of such attributes3346      // to support them.3347      StringRef retType = namedAttr->attr.getReturnType();3348      if (retType == "::llvm::APInt" || retType == "::llvm::APFloat")3349        break;3350 3351      defaultValuedAttrLikeStartIndex = i;3352      defaultValuedAttrStartIndex = i;3353    }3354  }3355 3356  // Check if parameters besides default valued one are enough to distinguish3357  // between builders with wrapped and unwrapped arguments.3358  bool hasBuilderAmbiguity = true;3359  for (const auto &arg : op.getArgs()) {3360    auto *namedAttr = dyn_cast<NamedAttribute *>(arg);3361    if (!namedAttr)3362      continue;3363    Attribute attr = namedAttr->attr;3364    if (attr.hasDefaultValue() || attr.isDerivedAttr())3365      continue;3366 3367    if (attrParamKind != AttrParamKind::WrappedAttr ||3368        !canUseUnwrappedRawValue(attr))3369      continue;3370 3371    hasBuilderAmbiguity = false;3372    break;3373  }3374 3375  // Avoid generating build methods that are ambiguous due to default values by3376  // requiring at least one attribute.3377  if (defaultValuedAttrStartIndex < op.getNumArgs()) {3378    // TODO: This should have been possible as a cast<NamedAttribute> but3379    // required template instantiations is not yet defined for the tblgen helper3380    // classes.3381    auto *namedAttr =3382        cast<NamedAttribute *>(op.getArg(defaultValuedAttrStartIndex));3383    Attribute attr = namedAttr->attr;3384    if ((attrParamKind == AttrParamKind::WrappedAttr &&3385         canUseUnwrappedRawValue(attr) && hasBuilderAmbiguity) ||3386        (attrParamKind == AttrParamKind::UnwrappedValue &&3387         !canUseUnwrappedRawValue(attr) && hasBuilderAmbiguity)) {3388      ++defaultValuedAttrStartIndex;3389      defaultValuedAttrLikeStartIndex = defaultValuedAttrStartIndex;3390    }3391  }3392 3393  /// Collect any inferred attributes.3394  for (const NamedTypeConstraint &operand : op.getOperands()) {3395    if (operand.isVariadicOfVariadic()) {3396      inferredAttributes.insert(3397          operand.constraint.getVariadicOfVariadicSegmentSizeAttr());3398    }3399  }3400 3401  FmtContext fctx;3402  fctx.withBuilder(odsBuilder);3403 3404  for (int i = 0, e = op.getNumArgs(), numOperands = 0; i < e; ++i) {3405    Argument arg = op.getArg(i);3406    if (const auto *operand =3407            llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg)) {3408      StringRef type;3409      if (operand->isVariadicOfVariadic())3410        type = "::llvm::ArrayRef<::mlir::ValueRange>";3411      else if (operand->isVariadic())3412        type = "::mlir::ValueRange";3413      else3414        type = "::mlir::Value";3415 3416      paramList.emplace_back(type, getArgumentName(op, numOperands++),3417                             operand->isOptional());3418      continue;3419    }3420    if (auto *propArg = llvm::dyn_cast_if_present<NamedProperty *>(arg)) {3421      const Property &prop = propArg->prop;3422      StringRef type = prop.getInterfaceType();3423      std::string defaultValue;3424      if (prop.hasDefaultValue() && i >= defaultValuedAttrLikeStartIndex) {3425        defaultValue = tgfmt(prop.getDefaultValue(), &fctx);3426      }3427      bool isOptional = prop.hasDefaultValue();3428      paramList.emplace_back(type, propArg->name, StringRef(defaultValue),3429                             isOptional);3430      continue;3431    }3432    const NamedAttribute &namedAttr = *cast<NamedAttribute *>(arg);3433    const Attribute &attr = namedAttr.attr;3434 3435    // Inferred attributes don't need to be added to the param list.3436    if (inferredAttributes.contains(namedAttr.name))3437      continue;3438 3439    StringRef type;3440    switch (attrParamKind) {3441    case AttrParamKind::WrappedAttr:3442      type = attr.getStorageType();3443      break;3444    case AttrParamKind::UnwrappedValue:3445      if (canUseUnwrappedRawValue(attr))3446        type = attr.getReturnType();3447      else3448        type = attr.getStorageType();3449      break;3450    }3451 3452    // Attach default value if requested and possible.3453    std::string defaultValue;3454    if (i >= defaultValuedAttrStartIndex) {3455      if (attrParamKind == AttrParamKind::UnwrappedValue &&3456          canUseUnwrappedRawValue(attr))3457        defaultValue += tgfmt(attr.getDefaultValue(), &fctx);3458      else3459        defaultValue += "nullptr";3460    }3461    paramList.emplace_back(type, namedAttr.name, StringRef(defaultValue),3462                           attr.isOptional());3463  }3464 3465  /// Insert parameters for each successor.3466  for (const NamedSuccessor &succ : op.getSuccessors()) {3467    StringRef type =3468        succ.isVariadic() ? "::mlir::BlockRange" : "::mlir::Block *";3469    paramList.emplace_back(type, succ.name);3470  }3471 3472  /// Insert parameters for variadic regions.3473  for (const NamedRegion &region : op.getRegions())3474    if (region.isVariadic())3475      paramList.emplace_back("unsigned",3476                             llvm::formatv("{0}Count", region.name).str());3477}3478 3479void OpEmitter::genCodeForAddingArgAndRegionForBuilder(3480    MethodBody &body, llvm::StringSet<> &inferredAttributes,3481    bool isRawValueAttr) {3482  // Push all operands to the result.3483  for (int i = 0, e = op.getNumOperands(); i < e; ++i) {3484    std::string argName = getArgumentName(op, i);3485    const NamedTypeConstraint &operand = op.getOperand(i);3486    if (operand.constraint.isVariadicOfVariadic()) {3487      body << "  for (::mlir::ValueRange range : " << argName << ")\n   "3488           << builderOpState << ".addOperands(range);\n";3489 3490      // Add the segment attribute.3491      body << "  {\n"3492           << "    ::llvm::SmallVector<int32_t> rangeSegments;\n"3493           << "    for (::mlir::ValueRange range : " << argName << ")\n"3494           << "      rangeSegments.push_back(range.size());\n"3495           << "    auto rangeAttr = " << odsBuilder3496           << ".getDenseI32ArrayAttr(rangeSegments);\n";3497      if (op.getDialect().usePropertiesForAttributes()) {3498        body << "    " << builderOpState << ".getOrAddProperties<Properties>()."3499             << operand.constraint.getVariadicOfVariadicSegmentSizeAttr()3500             << " = rangeAttr;";3501      } else {3502        body << "    " << builderOpState << ".addAttribute("3503             << op.getGetterName(3504                    operand.constraint.getVariadicOfVariadicSegmentSizeAttr())3505             << "AttrName(" << builderOpState << ".name), rangeAttr);";3506      }3507      body << "  }\n";3508      continue;3509    }3510 3511    if (operand.isOptional())3512      body << "  if (" << argName << ")\n  ";3513    body << "  " << builderOpState << ".addOperands(" << argName << ");\n";3514  }3515 3516  // If the operation has the operand segment size attribute, add it here.3517  auto emitSegment = [&]() {3518    interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {3519      const NamedTypeConstraint &operand = op.getOperand(i);3520      if (!operand.isVariableLength()) {3521        body << "1";3522        return;3523      }3524 3525      std::string operandName = getArgumentName(op, i);3526      if (operand.isOptional()) {3527        body << "(" << operandName << " ? 1 : 0)";3528      } else if (operand.isVariadicOfVariadic()) {3529        body << llvm::formatv(3530            "llvm::accumulate({0}, int32_t(0), "3531            "[](int32_t curSum, ::mlir::ValueRange range) {{ return curSum + "3532            "static_cast<int32_t>(range.size()); })",3533            operandName);3534      } else {3535        body << "static_cast<int32_t>(" << getArgumentName(op, i) << ".size())";3536      }3537    });3538  };3539  if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {3540    std::string sizes = op.getGetterName(operandSegmentAttrName);3541    if (op.getDialect().usePropertiesForAttributes()) {3542      body << "  ::llvm::copy(::llvm::ArrayRef<int32_t>({";3543      emitSegment();3544      body << "}), " << builderOpState3545           << ".getOrAddProperties<Properties>()."3546              "operandSegmentSizes.begin());\n";3547    } else {3548      body << "  " << builderOpState << ".addAttribute(" << sizes << "AttrName("3549           << builderOpState << ".name), "3550           << "odsBuilder.getDenseI32ArrayAttr({";3551      emitSegment();3552      body << "}));\n";3553    }3554  }3555 3556  // Push all properties to the result.3557  for (const auto &namedProp : op.getProperties()) {3558    // Use the setter from the Properties struct since the conversion from the3559    // interface type (used in the builder argument) to the storage type (used3560    // in the state) is not necessarily trivial.3561    std::string setterName = op.getSetterName(namedProp.name);3562    body << formatv("  {0}.getOrAddProperties<Properties>().{1}({2});\n",3563                    builderOpState, setterName, namedProp.name);3564  }3565  // Push all attributes to the result.3566  for (const auto &namedAttr : op.getAttributes()) {3567    auto &attr = namedAttr.attr;3568    if (attr.isDerivedAttr() || inferredAttributes.contains(namedAttr.name))3569      continue;3570 3571    // TODO: The wrapping of optional is different for default or not, so don't3572    // unwrap for default ones that would fail below.3573    bool emitNotNullCheck =3574        (attr.isOptional() && !attr.hasDefaultValue()) ||3575        (attr.hasDefaultValue() && !isRawValueAttr) ||3576        // TODO: UnitAttr is optional, not wrapped, but needs to be guarded as3577        // the constant materialization is only for true case.3578        (isRawValueAttr && attr.getAttrDefName() == "UnitAttr");3579    if (emitNotNullCheck)3580      body.indent() << formatv("if ({0}) ", namedAttr.name) << "{\n";3581 3582    if (isRawValueAttr && canUseUnwrappedRawValue(attr)) {3583      // If this is a raw value, then we need to wrap it in an Attribute3584      // instance.3585      FmtContext fctx;3586      fctx.withBuilder("odsBuilder");3587      if (op.getDialect().usePropertiesForAttributes()) {3588        body << formatv("  {0}.getOrAddProperties<Properties>().{1} = {2};\n",3589                        builderOpState, namedAttr.name,3590                        constBuildAttrFromParam(attr, fctx, namedAttr.name));3591      } else {3592        body << formatv("  {0}.addAttribute({1}AttrName({0}.name), {2});\n",3593                        builderOpState, op.getGetterName(namedAttr.name),3594                        constBuildAttrFromParam(attr, fctx, namedAttr.name));3595      }3596    } else {3597      if (op.getDialect().usePropertiesForAttributes()) {3598        body << formatv("  {0}.getOrAddProperties<Properties>().{1} = {1};\n",3599                        builderOpState, namedAttr.name);3600      } else {3601        body << formatv("  {0}.addAttribute({1}AttrName({0}.name), {2});\n",3602                        builderOpState, op.getGetterName(namedAttr.name),3603                        namedAttr.name);3604      }3605    }3606    if (emitNotNullCheck)3607      body.unindent() << "  }\n";3608  }3609 3610  // Create the correct number of regions.3611  for (const NamedRegion &region : op.getRegions()) {3612    if (region.isVariadic())3613      body << formatv("  for (unsigned i = 0; i < {0}Count; ++i)\n  ",3614                      region.name);3615 3616    body << "  (void)" << builderOpState << ".addRegion();\n";3617  }3618 3619  // Push all successors to the result.3620  for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) {3621    body << formatv("  {0}.addSuccessors({1});\n", builderOpState,3622                    namedSuccessor.name);3623  }3624}3625 3626void OpEmitter::genCanonicalizerDecls() {3627  bool hasCanonicalizeMethod = def.getValueAsBit("hasCanonicalizeMethod");3628  if (hasCanonicalizeMethod) {3629    // static LogicResult FooOp::3630    // canonicalize(FooOp op, PatternRewriter &rewriter);3631    SmallVector<MethodParameter> paramList;3632    paramList.emplace_back(op.getCppClassName(), "op");3633    paramList.emplace_back("::mlir::PatternRewriter &", "rewriter");3634    auto *m = opClass.declareStaticMethod("::llvm::LogicalResult",3635                                          "canonicalize", std::move(paramList));3636    ERROR_IF_PRUNED(m, "canonicalize", op);3637  }3638 3639  // We get a prototype for 'getCanonicalizationPatterns' if requested directly3640  // or if using a 'canonicalize' method.3641  bool hasCanonicalizer = def.getValueAsBit("hasCanonicalizer");3642  if (!hasCanonicalizeMethod && !hasCanonicalizer)3643    return;3644 3645  // We get a body for 'getCanonicalizationPatterns' when using a 'canonicalize'3646  // method, but not implementing 'getCanonicalizationPatterns' manually.3647  bool hasBody = hasCanonicalizeMethod && !hasCanonicalizer;3648 3649  // Add a signature for getCanonicalizationPatterns if implemented by the3650  // dialect or if synthesized to call 'canonicalize'.3651  SmallVector<MethodParameter> paramList;3652  paramList.emplace_back("::mlir::RewritePatternSet &", "results");3653  paramList.emplace_back("::mlir::MLIRContext *", "context");3654  auto kind = hasBody ? Method::Static : Method::StaticDeclaration;3655  auto *method = opClass.addMethod("void", "getCanonicalizationPatterns", kind,3656                                   std::move(paramList));3657 3658  // If synthesizing the method, fill it.3659  if (hasBody) {3660    ERROR_IF_PRUNED(method, "getCanonicalizationPatterns", op);3661    method->body() << "  results.add(canonicalize);\n";3662  }3663}3664 3665void OpEmitter::genFolderDecls() {3666  if (!op.hasFolder())3667    return;3668 3669  SmallVector<MethodParameter> paramList;3670  paramList.emplace_back("FoldAdaptor", "adaptor");3671 3672  StringRef retType;3673  bool hasSingleResult =3674      op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0;3675  if (hasSingleResult) {3676    retType = "::mlir::OpFoldResult";3677  } else {3678    paramList.emplace_back("::llvm::SmallVectorImpl<::mlir::OpFoldResult> &",3679                           "results");3680    retType = "::llvm::LogicalResult";3681  }3682 3683  auto *m = opClass.declareMethod(retType, "fold", std::move(paramList));3684  ERROR_IF_PRUNED(m, "fold", op);3685}3686 3687void OpEmitter::genOpInterfaceMethods(const tblgen::InterfaceTrait *opTrait) {3688  Interface interface = opTrait->getInterface();3689 3690  // Get the set of methods that should always be declared.3691  auto alwaysDeclaredMethodsVec = opTrait->getAlwaysDeclaredMethods();3692  llvm::StringSet<> alwaysDeclaredMethods;3693  alwaysDeclaredMethods.insert_range(alwaysDeclaredMethodsVec);3694 3695  for (const InterfaceMethod &method : interface.getMethods()) {3696    // Don't declare if the method has a body.3697    if (method.getBody())3698      continue;3699    // Don't declare if the method has a default implementation and the op3700    // didn't request that it always be declared.3701    if (method.getDefaultImplementation() &&3702        !alwaysDeclaredMethods.count(method.getName())) {3703      genOpInterfaceMethodUsingDecl(opTrait, method);3704      continue;3705    }3706    // Interface methods are allowed to overlap with existing methods, so don't3707    // check if pruned.3708    (void)genOpInterfaceMethod(method);3709  }3710}3711 3712Method *OpEmitter::genOpInterfaceMethod(const InterfaceMethod &method,3713                                        bool declaration) {3714  SmallVector<MethodParameter> paramList;3715  for (const InterfaceMethod::Argument &arg : method.getArguments())3716    paramList.emplace_back(arg.type, arg.name);3717 3718  auto props = (method.isStatic() ? Method::Static : Method::None) |3719               (declaration ? Method::Declaration : Method::None);3720  return opClass.addMethod(method.getReturnType(), method.getName(), props,3721                           std::move(paramList));3722}3723 3724UsingDeclaration *3725OpEmitter::genOpInterfaceMethodUsingDecl(const tblgen::InterfaceTrait *opTrait,3726                                         const InterfaceMethod &method) {3727  std::string name = (llvm::Twine(opTrait->getFullyQualifiedTraitName()) + "<" +3728                      op.getCppClassName() + ">::" + method.getName())3729                         .str();3730  if (interfaceUsingNames.insert(name).second)3731    return opClass.declare<UsingDeclaration>(std::move(name));3732  return nullptr;3733}3734 3735void OpEmitter::genOpInterfaceMethods() {3736  for (const auto &trait : op.getTraits()) {3737    if (const auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait))3738      if (opTrait->shouldDeclareMethods())3739        genOpInterfaceMethods(opTrait);3740  }3741}3742 3743void OpEmitter::genSideEffectInterfaceMethods() {3744  enum EffectKind { Operand, Result, Symbol, Static };3745  struct EffectLocation {3746    /// The effect applied.3747    SideEffect effect;3748 3749    /// The index if the kind is not static.3750    unsigned index;3751 3752    /// The kind of the location.3753    unsigned kind;3754  };3755 3756  StringMap<SmallVector<EffectLocation, 1>> interfaceEffects;3757  auto resolveDecorators = [&](Operator::var_decorator_range decorators,3758                               unsigned index, unsigned kind) {3759    for (auto decorator : decorators)3760      if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) {3761        opClass.addTrait(effect->getInterfaceTrait());3762        interfaceEffects[effect->getBaseEffectName()].push_back(3763            EffectLocation{*effect, index, kind});3764      }3765  };3766 3767  // Collect effects that were specified via:3768  /// Traits.3769  for (const auto &trait : op.getTraits()) {3770    const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait);3771    if (!opTrait)3772      continue;3773    auto &effects = interfaceEffects[opTrait->getBaseEffectName()];3774    for (auto decorator : opTrait->getEffects())3775      effects.push_back(EffectLocation{cast<SideEffect>(decorator),3776                                       /*index=*/0, EffectKind::Static});3777  }3778  /// Attributes and Operands.3779  for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) {3780    Argument arg = op.getArg(i);3781    if (isa<NamedTypeConstraint *>(arg)) {3782      resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand);3783      ++operandIt;3784      continue;3785    }3786    if (isa<NamedProperty *>(arg))3787      continue;3788    const NamedAttribute *attr = cast<NamedAttribute *>(arg);3789    if (attr->attr.getBaseAttr().isSymbolRefAttr())3790      resolveDecorators(op.getArgDecorators(i), i, EffectKind::Symbol);3791  }3792  /// Results.3793  for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)3794    resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result);3795 3796  // The code used to add an effect instance.3797  // {0}: The effect class.3798  // {1}: Optional value or symbol reference.3799  // {2}: The side effect stage.3800  // {3}: Does this side effect act on every single value of resource.3801  // {4}: The resource class.3802  const char *addEffectCode =3803      "  effects.emplace_back({0}::get(), {1}{2}, {3}, {4}::get());\n";3804 3805  for (auto &it : interfaceEffects) {3806    // Generate the 'getEffects' method.3807    std::string type = llvm::formatv("::llvm::SmallVectorImpl<::mlir::"3808                                     "SideEffects::EffectInstance<{0}>> &",3809                                     it.first())3810                           .str();3811    auto *getEffects = opClass.addMethod("void", "getEffects",3812                                         MethodParameter(type, "effects"));3813    ERROR_IF_PRUNED(getEffects, "getEffects", op);3814    auto &body = getEffects->body();3815 3816    // Add effect instances for each of the locations marked on the operation.3817    for (auto &location : it.second) {3818      StringRef effect = location.effect.getName();3819      StringRef resource = location.effect.getResource();3820      int stage = (int)location.effect.getStage();3821      bool effectOnFullRegion = (int)location.effect.getEffectOnfullRegion();3822      if (location.kind == EffectKind::Static) {3823        // A static instance has no attached value.3824        body << llvm::formatv(addEffectCode, effect, "", stage,3825                              effectOnFullRegion, resource)3826                    .str();3827      } else if (location.kind == EffectKind::Symbol) {3828        // A symbol reference requires adding the proper attribute.3829        const auto *attr = cast<NamedAttribute *>(op.getArg(location.index));3830        std::string argName = op.getGetterName(attr->name);3831        if (attr->attr.isOptional()) {3832          body << "  if (auto symbolRef = " << argName << "Attr())\n  "3833               << llvm::formatv(addEffectCode, effect, "symbolRef, ", stage,3834                                effectOnFullRegion, resource)3835                      .str();3836        } else {3837          body << llvm::formatv(addEffectCode, effect, argName + "Attr(), ",3838                                stage, effectOnFullRegion, resource)3839                      .str();3840        }3841      } else {3842        // Otherwise this is an operand/result, so we need to attach the Value.3843        body << "  {\n    auto valueRange = getODS"3844             << (location.kind == EffectKind::Operand ? "Operand" : "Result")3845             << "IndexAndLength(" << location.index << ");\n"3846             << "    for (unsigned idx = valueRange.first; idx < "3847                "valueRange.first"3848             << " + valueRange.second; idx++) {\n    "3849             << llvm::formatv(addEffectCode, effect,3850                              (location.kind == EffectKind::Operand3851                                   ? "&getOperation()->getOpOperand(idx), "3852                                   : "getOperation()->getOpResult(idx), "),3853                              stage, effectOnFullRegion, resource)3854             << "    }\n  }\n";3855      }3856    }3857  }3858}3859 3860void OpEmitter::genTypeInterfaceMethods() {3861  if (!op.allResultTypesKnown())3862    return;3863  // Generate 'inferReturnTypes' method declaration using the interface method3864  // declared in 'InferTypeOpInterface' op interface.3865  const auto *trait =3866      cast<InterfaceTrait>(op.getTrait("::mlir::InferTypeOpInterface::Trait"));3867  Interface interface = trait->getInterface();3868  Method *method = [&]() -> Method * {3869    for (const InterfaceMethod &interfaceMethod : interface.getMethods()) {3870      if (interfaceMethod.getName() == "inferReturnTypes") {3871        return genOpInterfaceMethod(interfaceMethod, /*declaration=*/false);3872      }3873    }3874    assert(0 && "unable to find inferReturnTypes interface method");3875    return nullptr;3876  }();3877  ERROR_IF_PRUNED(method, "inferReturnTypes", op);3878  auto &body = method->body();3879  body << "  inferredReturnTypes.resize(" << op.getNumResults() << ");\n";3880 3881  FmtContext fctx;3882  fctx.withBuilder("odsBuilder");3883  fctx.addSubst("_ctxt", "context");3884  body << "  ::mlir::Builder odsBuilder(context);\n";3885 3886  // Preprocessing stage to verify all accesses to operands are valid.3887  int maxAccessedIndex = -1;3888  for (int i = 0, e = op.getNumResults(); i != e; ++i) {3889    const InferredResultType &infer = op.getInferredResultType(i);3890    if (!infer.isArg())3891      continue;3892    Operator::OperandAttrOrProp arg =3893        op.getArgToOperandAttrOrProp(infer.getIndex());3894    if (arg.kind() == Operator::OperandAttrOrProp::Kind::Operand) {3895      maxAccessedIndex =3896          std::max(maxAccessedIndex, arg.operandOrAttributeIndex());3897    }3898  }3899  if (maxAccessedIndex != -1) {3900    body << "  if (operands.size() <= " << Twine(maxAccessedIndex) << ")\n";3901    body << "    return ::mlir::failure();\n";3902  }3903 3904  // Process the type inference graph in topological order, starting from types3905  // that are always fully-inferred: operands and results with constructible3906  // types. The type inference graph here will always be a DAG, so this gives3907  // us the correct order for generating the types. -1 is a placeholder to3908  // indicate the type for a result has not been generated.3909  SmallVector<int> constructedIndices(op.getNumResults(), -1);3910  int inferredTypeIdx = 0;3911  for (int numResults = op.getNumResults(); inferredTypeIdx != numResults;) {3912    for (int i = 0, e = op.getNumResults(); i != e; ++i) {3913      if (constructedIndices[i] >= 0)3914        continue;3915      const InferredResultType &infer = op.getInferredResultType(i);3916      std::string typeStr;3917      if (infer.isArg()) {3918        // If this is an operand, just index into operand list to access the3919        // type.3920        Operator::OperandAttrOrProp arg =3921            op.getArgToOperandAttrOrProp(infer.getIndex());3922        if (arg.kind() == Operator::OperandAttrOrProp::Kind::Operand) {3923          typeStr = ("operands[" + Twine(arg.operandOrAttributeIndex()) +3924                     "].getType()")3925                        .str();3926 3927          // If this is an attribute, index into the attribute dictionary.3928        } else if (auto *attr = dyn_cast<NamedAttribute *>(3929                       op.getArg(arg.operandOrAttributeIndex()))) {3930          body << "  ::mlir::TypedAttr odsInferredTypeAttr" << inferredTypeIdx3931               << " = ";3932          if (op.getDialect().usePropertiesForAttributes()) {3933            body << "(properties ? properties.as<Properties *>()->"3934                 << attr->name3935                 << " : "3936                    "::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."3937                    "get(\"" +3938                        attr->name + "\")));\n";3939          } else {3940            body << "::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."3941                    "get(\"" +3942                        attr->name + "\"));\n";3943          }3944          body << "  if (!odsInferredTypeAttr" << inferredTypeIdx3945               << ") return ::mlir::failure();\n";3946          typeStr =3947              ("odsInferredTypeAttr" + Twine(inferredTypeIdx) + ".getType()")3948                  .str();3949        } else {3950          llvm::PrintFatalError(&op.getDef(),3951                                "Properties cannot be used for type inference");3952        }3953      } else if (std::optional<StringRef> builder =3954                     op.getResult(infer.getResultIndex())3955                         .constraint.getBuilderCall()) {3956        typeStr = tgfmt(*builder, &fctx).str();3957      } else if (int index = constructedIndices[infer.getResultIndex()];3958                 index >= 0) {3959        typeStr = ("odsInferredType" + Twine(index)).str();3960      } else {3961        continue;3962      }3963      body << "  ::mlir::Type odsInferredType" << inferredTypeIdx++ << " = "3964           << tgfmt(infer.getTransformer(), &fctx.withSelf(typeStr)) << ";\n";3965      constructedIndices[i] = inferredTypeIdx - 1;3966    }3967  }3968  for (auto [i, index] : llvm::enumerate(constructedIndices))3969    body << "  inferredReturnTypes[" << i << "] = odsInferredType" << index3970         << ";\n";3971  body << "  return ::mlir::success();";3972}3973 3974void OpEmitter::genParser() {3975  if (hasStringAttribute(def, "assemblyFormat"))3976    return;3977 3978  if (!def.getValueAsBit("hasCustomAssemblyFormat"))3979    return;3980 3981  SmallVector<MethodParameter> paramList;3982  paramList.emplace_back("::mlir::OpAsmParser &", "parser");3983  paramList.emplace_back("::mlir::OperationState &", "result");3984 3985  auto *method = opClass.declareStaticMethod("::mlir::ParseResult", "parse",3986                                             std::move(paramList));3987  ERROR_IF_PRUNED(method, "parse", op);3988}3989 3990void OpEmitter::genPrinter() {3991  if (hasStringAttribute(def, "assemblyFormat"))3992    return;3993 3994  // Check to see if this op uses a c++ format.3995  if (!def.getValueAsBit("hasCustomAssemblyFormat"))3996    return;3997  auto *method = opClass.declareMethod(3998      "void", "print", MethodParameter("::mlir::OpAsmPrinter &", "p"));3999  ERROR_IF_PRUNED(method, "print", op);4000}4001 4002void OpEmitter::genVerifier() {4003  auto *implMethod =4004      opClass.addMethod("::llvm::LogicalResult", "verifyInvariantsImpl");4005  ERROR_IF_PRUNED(implMethod, "verifyInvariantsImpl", op);4006  auto &implBody = implMethod->body();4007  bool useProperties = emitHelper.hasProperties();4008 4009  populateSubstitutions(emitHelper, verifyCtx);4010  genPropertyVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter);4011  genAttributeVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter,4012                       useProperties);4013  genOperandResultVerifier(implBody, op.getOperands(), "operand");4014  genOperandResultVerifier(implBody, op.getResults(), "result");4015 4016  for (auto &trait : op.getTraits()) {4017    if (auto *t = dyn_cast<tblgen::PredTrait>(&trait)) {4018      implBody << tgfmt("  if (!($0))\n    "4019                        "return emitOpError(\"failed to verify that $1\");\n",4020                        &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx),4021                        t->getSummary());4022    }4023  }4024 4025  genRegionVerifier(implBody);4026  genSuccessorVerifier(implBody);4027 4028  implBody << "  return ::mlir::success();\n";4029 4030  // TODO: Some places use the `verifyInvariants` to do operation verification.4031  // This may not act as their expectation because this doesn't call any4032  // verifiers of native/interface traits. Needs to review those use cases and4033  // see if we should use the mlir::verify() instead.4034  auto *method = opClass.addMethod("::llvm::LogicalResult", "verifyInvariants");4035  ERROR_IF_PRUNED(method, "verifyInvariants", op);4036  auto &body = method->body();4037  if (def.getValueAsBit("hasVerifier")) {4038    body << "  if(::mlir::succeeded(verifyInvariantsImpl()) && "4039            "::mlir::succeeded(verify()))\n";4040    body << "    return ::mlir::success();\n";4041    body << "  return ::mlir::failure();";4042  } else {4043    body << "  return verifyInvariantsImpl();";4044  }4045}4046 4047void OpEmitter::genCustomVerifier() {4048  if (def.getValueAsBit("hasVerifier")) {4049    auto *method = opClass.declareMethod("::llvm::LogicalResult", "verify");4050    ERROR_IF_PRUNED(method, "verify", op);4051  }4052 4053  if (def.getValueAsBit("hasRegionVerifier")) {4054    auto *method =4055        opClass.declareMethod("::llvm::LogicalResult", "verifyRegions");4056    ERROR_IF_PRUNED(method, "verifyRegions", op);4057  }4058}4059 4060void OpEmitter::genOperandResultVerifier(MethodBody &body,4061                                         Operator::const_value_range values,4062                                         StringRef valueKind) {4063  // Check that an optional value is at most 1 element.4064  //4065  // {0}: Value index.4066  // {1}: "operand" or "result"4067  const char *const verifyOptional = R"(4068    if (valueGroup{0}.size() > 1) {4069      return emitOpError("{1} group starting at #") << index4070          << " requires 0 or 1 element, but found " << valueGroup{0}.size();4071    }4072)";4073  // Check the types of a range of values.4074  //4075  // {0}: Value index.4076  // {1}: Type constraint function.4077  // {2}: "operand" or "result"4078  const char *const verifyValues = R"(4079    for (auto v : valueGroup{0}) {4080      if (::mlir::failed({1}(*this, v.getType(), "{2}", index++)))4081        return ::mlir::failure();4082    }4083)";4084 4085  const auto canSkip = [](const NamedTypeConstraint &value) {4086    return !value.hasPredicate() && !value.isOptional() &&4087           !value.isVariadicOfVariadic();4088  };4089  if (values.empty() || llvm::all_of(values, canSkip))4090    return;4091 4092  FmtContext fctx;4093 4094  body << "  {\n    unsigned index = 0; (void)index;\n";4095 4096  for (const auto &staticValue : llvm::enumerate(values)) {4097    const NamedTypeConstraint &value = staticValue.value();4098 4099    bool hasPredicate = value.hasPredicate();4100    bool isOptional = value.isOptional();4101    bool isVariadicOfVariadic = value.isVariadicOfVariadic();4102    if (!hasPredicate && !isOptional && !isVariadicOfVariadic)4103      continue;4104    body << formatv("    auto valueGroup{2} = getODS{0}{1}s({2});\n",4105                    // Capitalize the first letter to match the function name4106                    valueKind.substr(0, 1).upper(), valueKind.substr(1),4107                    staticValue.index());4108 4109    // If the constraint is optional check that the value group has at most 14110    // value.4111    if (isOptional) {4112      body << formatv(verifyOptional, staticValue.index(), valueKind);4113    } else if (isVariadicOfVariadic) {4114      body << formatv(4115          "    if (::mlir::failed(::mlir::OpTrait::impl::verifyValueSizeAttr("4116          "*this, \"{0}\", \"{1}\", valueGroup{2}.size())))\n"4117          "      return ::mlir::failure();\n",4118          value.constraint.getVariadicOfVariadicSegmentSizeAttr(), value.name,4119          staticValue.index());4120    }4121 4122    // Otherwise, if there is no predicate there is nothing left to do.4123    if (!hasPredicate)4124      continue;4125    // Emit a loop to check all the dynamic values in the pack.4126    StringRef constraintFn =4127        staticVerifierEmitter.getTypeConstraintFn(value.constraint);4128    body << formatv(verifyValues, staticValue.index(), constraintFn, valueKind);4129  }4130 4131  body << "  }\n";4132}4133 4134void OpEmitter::genRegionVerifier(MethodBody &body) {4135  /// Code to verify a region.4136  ///4137  /// {0}: Getter for the regions.4138  /// {1}: The region constraint.4139  /// {2}: The region's name.4140  /// {3}: The region description.4141  const char *const verifyRegion = R"(4142    for (auto &region : {0})4143      if (::mlir::failed({1}(*this, region, "{2}", index++)))4144        return ::mlir::failure();4145)";4146  /// Get a single region.4147  ///4148  /// {0}: The region's index.4149  const char *const getSingleRegion =4150      "::llvm::MutableArrayRef((*this)->getRegion({0}))";4151 4152  // If we have no regions, there is nothing more to do.4153  const auto canSkip = [](const NamedRegion &region) {4154    return region.constraint.getPredicate().isNull();4155  };4156  auto regions = op.getRegions();4157  if (regions.empty() && llvm::all_of(regions, canSkip))4158    return;4159 4160  body << "  {\n    unsigned index = 0; (void)index;\n";4161  for (const auto &it : llvm::enumerate(regions)) {4162    const auto &region = it.value();4163    if (canSkip(region))4164      continue;4165 4166    auto getRegion = region.isVariadic()4167                         ? formatv("{0}()", op.getGetterName(region.name)).str()4168                         : formatv(getSingleRegion, it.index()).str();4169    auto constraintFn =4170        staticVerifierEmitter.getRegionConstraintFn(region.constraint);4171    body << formatv(verifyRegion, getRegion, constraintFn, region.name);4172  }4173  body << "  }\n";4174}4175 4176void OpEmitter::genSuccessorVerifier(MethodBody &body) {4177  const char *const verifySuccessor = R"(4178    for (auto *successor : {0})4179      if (::mlir::failed({1}(*this, successor, "{2}", index++)))4180        return ::mlir::failure();4181)";4182  /// Get a single successor.4183  ///4184  /// {0}: The successor's name.4185  const char *const getSingleSuccessor = "::llvm::MutableArrayRef({0}())";4186 4187  // If we have no successors, there is nothing more to do.4188  const auto canSkip = [](const NamedSuccessor &successor) {4189    return successor.constraint.getPredicate().isNull();4190  };4191  auto successors = op.getSuccessors();4192  if (successors.empty() && llvm::all_of(successors, canSkip))4193    return;4194 4195  body << "  {\n    unsigned index = 0; (void)index;\n";4196 4197  for (auto it : llvm::enumerate(successors)) {4198    const auto &successor = it.value();4199    if (canSkip(successor))4200      continue;4201 4202    auto getSuccessor =4203        formatv(successor.isVariadic() ? "{0}()" : getSingleSuccessor,4204                successor.name)4205            .str();4206    auto constraintFn =4207        staticVerifierEmitter.getSuccessorConstraintFn(successor.constraint);4208    body << formatv(verifySuccessor, getSuccessor, constraintFn,4209                    successor.name);4210  }4211  body << "  }\n";4212}4213 4214/// Add a size count trait to the given operation class.4215static void addSizeCountTrait(OpClass &opClass, StringRef traitKind,4216                              int numTotal, int numVariadic) {4217  if (numVariadic != 0) {4218    if (numTotal == numVariadic)4219      opClass.addTrait("::mlir::OpTrait::Variadic" + traitKind + "s");4220    else4221      opClass.addTrait("::mlir::OpTrait::AtLeastN" + traitKind + "s<" +4222                       Twine(numTotal - numVariadic) + ">::Impl");4223    return;4224  }4225  switch (numTotal) {4226  case 0:4227    opClass.addTrait("::mlir::OpTrait::Zero" + traitKind + "s");4228    break;4229  case 1:4230    opClass.addTrait("::mlir::OpTrait::One" + traitKind);4231    break;4232  default:4233    opClass.addTrait("::mlir::OpTrait::N" + traitKind + "s<" + Twine(numTotal) +4234                     ">::Impl");4235    break;4236  }4237}4238 4239void OpEmitter::genTraits() {4240  // Add region size trait.4241  unsigned numRegions = op.getNumRegions();4242  unsigned numVariadicRegions = op.getNumVariadicRegions();4243  addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions);4244 4245  // Add result size traits.4246  int numResults = op.getNumResults();4247  int numVariadicResults = op.getNumVariableLengthResults();4248  addSizeCountTrait(opClass, "Result", numResults, numVariadicResults);4249 4250  // For single result ops with a known specific type, generate a OneTypedResult4251  // trait.4252  if (numResults == 1 && numVariadicResults == 0) {4253    auto cppName = op.getResults().begin()->constraint.getCppType();4254    opClass.addTrait("::mlir::OpTrait::OneTypedResult<" + cppName + ">::Impl");4255  }4256 4257  // Add successor size trait.4258  unsigned numSuccessors = op.getNumSuccessors();4259  unsigned numVariadicSuccessors = op.getNumVariadicSuccessors();4260  addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors);4261 4262  // Add variadic size trait and normal op traits.4263  int numOperands = op.getNumOperands();4264  int numVariadicOperands = op.getNumVariableLengthOperands();4265 4266  // Add operand size trait.4267  addSizeCountTrait(opClass, "Operand", numOperands, numVariadicOperands);4268 4269  // The op traits defined internal are ensured that they can be verified4270  // earlier.4271  for (const auto &trait : op.getTraits()) {4272    if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {4273      if (opTrait->isStructuralOpTrait())4274        opClass.addTrait(opTrait->getFullyQualifiedTraitName());4275    }4276  }4277 4278  // OpInvariants wrapps the verifyInvariants which needs to be run before4279  // native/interface traits and after all the traits with `StructuralOpTrait`.4280  opClass.addTrait("::mlir::OpTrait::OpInvariants");4281 4282  if (emitHelper.hasNonEmptyPropertiesStruct())4283    opClass.addTrait("::mlir::BytecodeOpInterface::Trait");4284 4285  // Add the native and interface traits.4286  for (const auto &trait : op.getTraits()) {4287    if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {4288      if (!opTrait->isStructuralOpTrait())4289        opClass.addTrait(opTrait->getFullyQualifiedTraitName());4290    } else if (auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) {4291      opClass.addTrait(opTrait->getFullyQualifiedTraitName());4292    }4293  }4294}4295 4296void OpEmitter::genOpNameGetter() {4297  auto *method = opClass.addStaticMethod<Method::Constexpr>(4298      "::llvm::StringLiteral", "getOperationName");4299  ERROR_IF_PRUNED(method, "getOperationName", op);4300  method->body() << "  return ::llvm::StringLiteral(\"" << op.getOperationName()4301                 << "\");";4302}4303 4304void OpEmitter::genOpAsmInterface() {4305  // If the user only has one results or specifically added the Asm trait,4306  // then don't generate it for them. We specifically only handle multi result4307  // operations, because the name of a single result in the common case is not4308  // interesting(generally 'result'/'output'/etc.).4309  // TODO: We could also add a flag to allow operations to opt in to this4310  // generation, even if they only have a single operation.4311  int numResults = op.getNumResults();4312  if (numResults <= 1 || op.getTrait("::mlir::OpAsmOpInterface::Trait"))4313    return;4314 4315  SmallVector<StringRef, 4> resultNames(numResults);4316  for (int i = 0; i != numResults; ++i)4317    resultNames[i] = op.getResultName(i);4318 4319  // Don't add the trait if none of the results have a valid name.4320  if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); }))4321    return;4322  opClass.addTrait("::mlir::OpAsmOpInterface::Trait");4323 4324  // Generate the right accessor for the number of results.4325  auto *method = opClass.addMethod(4326      "void", "getAsmResultNames",4327      MethodParameter("::mlir::OpAsmSetValueNameFn", "setNameFn"));4328  ERROR_IF_PRUNED(method, "getAsmResultNames", op);4329  auto &body = method->body();4330  for (int i = 0; i != numResults; ++i) {4331    body << "  auto resultGroup" << i << " = getODSResults(" << i << ");\n"4332         << "  if (!resultGroup" << i << ".empty())\n"4333         << "    setNameFn(*resultGroup" << i << ".begin(), \""4334         << resultNames[i] << "\");\n";4335  }4336}4337 4338//===----------------------------------------------------------------------===//4339// OpOperandAdaptor emitter4340//===----------------------------------------------------------------------===//4341 4342namespace {4343// Helper class to emit Op operand adaptors to an output stream.  Operand4344// adaptors are wrappers around random access ranges that provide named operand4345// getters identical to those defined in the Op.4346// This currently generates 3 classes per Op:4347// * A Base class within the 'detail' namespace, which contains all logic and4348//   members independent of the random access range that is indexed into.4349//   In other words, it contains all the attribute and region getters.4350// * A templated class named '{OpName}GenericAdaptor' with a template parameter4351//   'RangeT' that is indexed into by the getters to access the operands.4352//   It contains all getters to access operands and inherits from the previous4353//   class.4354// * A class named '{OpName}Adaptor', which inherits from the 'GenericAdaptor'4355//   with 'mlir::ValueRange' as template parameter. It adds a constructor from4356//   an instance of the op type and a verify function.4357class OpOperandAdaptorEmitter {4358public:4359  static void4360  emitDecl(const Operator &op,4361           const StaticVerifierFunctionEmitter &staticVerifierEmitter,4362           raw_ostream &os);4363  static void4364  emitDef(const Operator &op,4365          const StaticVerifierFunctionEmitter &staticVerifierEmitter,4366          raw_ostream &os);4367 4368private:4369  explicit OpOperandAdaptorEmitter(4370      const Operator &op,4371      const StaticVerifierFunctionEmitter &staticVerifierEmitter);4372 4373  // Add verification function. This generates a verify method for the adaptor4374  // which verifies all the op-independent attribute constraints.4375  void addVerification();4376 4377  // The operation for which to emit an adaptor.4378  const Operator &op;4379 4380  // The generated adaptor classes.4381  Class genericAdaptorBase;4382  Class genericAdaptor;4383  Class adaptor;4384 4385  // The emitter containing all of the locally emitted verification functions.4386  const StaticVerifierFunctionEmitter &staticVerifierEmitter;4387 4388  // Helper for emitting adaptor code.4389  OpOrAdaptorHelper emitHelper;4390};4391} // namespace4392 4393OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(4394    const Operator &op,4395    const StaticVerifierFunctionEmitter &staticVerifierEmitter)4396    : op(op), genericAdaptorBase(op.getGenericAdaptorName() + "Base"),4397      genericAdaptor(op.getGenericAdaptorName()), adaptor(op.getAdaptorName()),4398      staticVerifierEmitter(staticVerifierEmitter),4399      emitHelper(op, /*emitForOp=*/false) {4400 4401  FmtContext fctx;4402  fctx.withBuilder(odsBuilder);4403 4404  genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Public);4405  bool useProperties = emitHelper.hasProperties();4406  if (useProperties) {4407    // Define the properties struct with multiple members.4408    using ConstArgument =4409        llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;4410    SmallVector<ConstArgument> attrOrProperties;4411    for (const std::pair<StringRef, AttributeMetadata> &it :4412         emitHelper.getAttrMetadata()) {4413      if (!it.second.constraint || !it.second.constraint->isDerivedAttr())4414        attrOrProperties.push_back(&it.second);4415    }4416    for (const NamedProperty &prop : op.getProperties())4417      attrOrProperties.push_back(&prop);4418    if (emitHelper.getOperandSegmentsSize())4419      attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());4420    if (emitHelper.getResultSegmentsSize())4421      attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());4422    std::string declarations = "  struct Properties {\n";4423    llvm::raw_string_ostream os(declarations);4424    std::string comparator =4425        "    bool operator==(const Properties &rhs) const {\n"4426        "      return \n";4427    llvm::raw_string_ostream comparatorOs(comparator);4428    for (const auto &attrOrProp : attrOrProperties) {4429      if (const auto *namedProperty =4430              llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {4431        StringRef name = namedProperty->name;4432        if (name.empty())4433          report_fatal_error("missing name for property");4434        std::string camelName =4435            convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);4436        auto &prop = namedProperty->prop;4437        // Generate the data member using the storage type.4438        os << "    using " << name << "Ty = " << prop.getStorageType() << ";\n"4439           << "    " << name << "Ty " << name;4440        if (prop.hasStorageTypeValueOverride())4441          os << " = " << prop.getStorageTypeValueOverride();4442        else if (prop.hasDefaultValue())4443          os << " = " << tgfmt(prop.getDefaultValue(), &fctx);4444        comparatorOs << "        rhs." << name << " == this->" << name4445                     << " &&\n";4446        // Emit accessors using the interface type.4447        const char *accessorFmt = R"decl(;4448    {0} get{1}() const {4449      auto &propStorage = this->{2};4450      return {3};4451    }4452    void set{1}({0} propValue) {4453      auto &propStorage = this->{2};4454      {4};4455    }4456)decl";4457        FmtContext fctx;4458        os << formatv(accessorFmt, prop.getInterfaceType(), camelName, name,4459                      tgfmt(prop.getConvertFromStorageCall(),4460                            &fctx.addSubst("_storage", propertyStorage)),4461                      tgfmt(prop.getAssignToStorageCall(),4462                            &fctx.addSubst("_value", propertyValue)4463                                 .addSubst("_storage", propertyStorage)));4464        continue;4465      }4466      const auto *namedAttr =4467          llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);4468      const Attribute *attr = nullptr;4469      if (namedAttr->constraint)4470        attr = &*namedAttr->constraint;4471      StringRef name = namedAttr->attrName;4472      if (name.empty())4473        report_fatal_error("missing name for property attr");4474      std::string camelName =4475          convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);4476      // Generate the data member using the storage type.4477      StringRef storageType;4478      if (attr) {4479        storageType = attr->getStorageType();4480      } else {4481        if (name != operandSegmentAttrName && name != resultSegmentAttrName) {4482          report_fatal_error("unexpected AttributeMetadata");4483        }4484        // TODO: update to use native integers.4485        storageType = "::mlir::DenseI32ArrayAttr";4486      }4487      os << "    using " << name << "Ty = " << storageType << ";\n"4488         << "    " << name << "Ty " << name << ";\n";4489      comparatorOs << "        rhs." << name << " == this->" << name << " &&\n";4490 4491      // Emit accessors using the interface type.4492      if (attr) {4493        const char *accessorFmt = R"decl(4494    auto get{0}() const {4495      auto &propStorage = this->{1};4496      return ::llvm::{2}<{3}>(propStorage);4497    }4498    void set{0}(const {3} &propValue) {4499      this->{1} = propValue;4500    }4501)decl";4502        os << formatv(accessorFmt, camelName, name,4503                      attr->isOptional() || attr->hasDefaultValue()4504                          ? "dyn_cast_or_null"4505                          : "cast",4506                      storageType);4507      }4508    }4509    comparatorOs << "        true;\n    }\n"4510                    "    bool operator!=(const Properties &rhs) const {\n"4511                    "      return !(*this == rhs);\n"4512                    "    }\n";4513    os << comparator;4514    os << "  };\n";4515 4516    if (attrOrProperties.empty())4517      genericAdaptorBase.declare<UsingDeclaration>("Properties",4518                                                   "::mlir::EmptyProperties");4519    else4520      genericAdaptorBase.declare<ExtraClassDeclaration>(4521          std::move(declarations));4522  }4523  genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Protected);4524  genericAdaptorBase.declare<Field>("::mlir::DictionaryAttr", "odsAttrs");4525  genericAdaptorBase.declare<Field>("::std::optional<::mlir::OperationName>",4526                                    "odsOpName");4527  if (useProperties)4528    genericAdaptorBase.declare<Field>("Properties", "properties");4529  genericAdaptorBase.declare<Field>("::mlir::RegionRange", "odsRegions");4530 4531  genericAdaptor.addTemplateParam("RangeT");4532  genericAdaptor.addField("RangeT", "odsOperands");4533  genericAdaptor.addParent(4534      ParentClass("detail::" + genericAdaptorBase.getClassName()));4535  genericAdaptor.declare<UsingDeclaration>(4536      "ValueT", "::llvm::detail::ValueOfRange<RangeT>");4537  genericAdaptor.declare<UsingDeclaration>(4538      "Base", "detail::" + genericAdaptorBase.getClassName());4539 4540  const auto *attrSizedOperands =4541      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");4542  {4543    SmallVector<MethodParameter> paramList;4544    if (useProperties) {4545      // Properties can't be given a default constructor here due to Properties4546      // struct being defined in the enclosing class which isn't complete by4547      // here.4548      paramList.emplace_back("::mlir::DictionaryAttr", "attrs");4549      paramList.emplace_back("const Properties &", "properties");4550    } else {4551      paramList.emplace_back("::mlir::DictionaryAttr", "attrs", "{}");4552      paramList.emplace_back("const ::mlir::EmptyProperties &", "properties",4553                             "{}");4554    }4555    paramList.emplace_back("::mlir::RegionRange", "regions", "{}");4556    auto *baseConstructor =4557        genericAdaptorBase.addConstructor<Method::Inline>(paramList);4558    baseConstructor->addMemberInitializer("odsAttrs", "attrs");4559    if (useProperties)4560      baseConstructor->addMemberInitializer("properties", "properties");4561    baseConstructor->addMemberInitializer("odsRegions", "regions");4562 4563    MethodBody &body = baseConstructor->body();4564    body.indent() << "if (odsAttrs)\n";4565    body.indent() << formatv(4566        "odsOpName.emplace(\"{0}\", odsAttrs.getContext());\n",4567        op.getOperationName());4568 4569    paramList.insert(paramList.begin(), MethodParameter("RangeT", "values"));4570    auto *constructor = genericAdaptor.addConstructor(paramList);4571    constructor->addMemberInitializer("Base", "attrs, properties, regions");4572    constructor->addMemberInitializer("odsOperands", "values");4573 4574    // Add a forwarding constructor to the previous one that accepts4575    // OpaqueProperties instead and check for null and perform the cast to the4576    // actual properties type.4577    paramList[1] = MethodParameter("::mlir::DictionaryAttr", "attrs");4578    paramList[2] = MethodParameter("::mlir::OpaqueProperties", "properties");4579    auto *opaquePropertiesConstructor =4580        genericAdaptor.addConstructor(std::move(paramList));4581    if (useProperties) {4582      opaquePropertiesConstructor->addMemberInitializer(4583          genericAdaptor.getClassName(),4584          "values, "4585          "attrs, "4586          "(properties ? *properties.as<Properties *>() : Properties{}), "4587          "regions");4588    } else {4589      opaquePropertiesConstructor->addMemberInitializer(4590          genericAdaptor.getClassName(),4591          "values, "4592          "attrs, "4593          "(properties ? *properties.as<::mlir::EmptyProperties *>() : "4594          "::mlir::EmptyProperties{}), "4595          "regions");4596    }4597 4598    // Add forwarding constructor that constructs Properties.4599    if (useProperties) {4600      SmallVector<MethodParameter> paramList;4601      paramList.emplace_back("RangeT", "values");4602      paramList.emplace_back("::mlir::DictionaryAttr", "attrs",4603                             attrSizedOperands ? "" : "nullptr");4604      auto *noPropertiesConstructor =4605          genericAdaptor.addConstructor(std::move(paramList));4606      noPropertiesConstructor->addMemberInitializer(4607          genericAdaptor.getClassName(), "values, "4608                                         "attrs, "4609                                         "Properties{}, "4610                                         "{}");4611    }4612  }4613 4614  // Create a constructor that creates a new generic adaptor by copying4615  // everything from another adaptor, except for the values.4616  {4617    SmallVector<MethodParameter> paramList;4618    paramList.emplace_back("RangeT", "values");4619    paramList.emplace_back("const " + op.getGenericAdaptorName() + "Base &",4620                           "base");4621    auto *constructor =4622        genericAdaptor.addConstructor<Method::Inline>(paramList);4623    constructor->addMemberInitializer("Base", "base");4624    constructor->addMemberInitializer("odsOperands", "values");4625  }4626 4627  // Create constructors constructing the adaptor from an instance of the op.4628  // This takes the attributes, properties and regions from the op instance4629  // and the value range from the parameter.4630  {4631    // Base class is in the cpp file and can simply access the members of the op4632    // class to initialize the template independent fields. If the op doesn't4633    // have properties, we can emit a generic constructor inline. Otherwise,4634    // emit it out-of-line because we need the op to be defined.4635    Constructor *constructor;4636    if (useProperties) {4637      constructor = genericAdaptorBase.addConstructor(4638          MethodParameter(op.getCppClassName(), "op"));4639    } else {4640      constructor = genericAdaptorBase.addConstructor<Method::Inline>(4641          MethodParameter("::mlir::Operation *", "op"));4642    }4643    constructor->addMemberInitializer("odsAttrs",4644                                      "op->getRawDictionaryAttrs()");4645    // Retrieve the operation name from the op directly.4646    constructor->addMemberInitializer("odsOpName", "op->getName()");4647    if (useProperties)4648      constructor->addMemberInitializer("properties", "op.getProperties()");4649    constructor->addMemberInitializer("odsRegions", "op->getRegions()");4650 4651    // Generic adaptor is templated and therefore defined inline in the header.4652    // We cannot use the Op class here as it is an incomplete type (we have a4653    // circular reference between the two).4654    // Use a template trick to make the constructor be instantiated at call site4655    // when the op class is complete.4656    constructor = genericAdaptor.addConstructor(4657        MethodParameter("RangeT", "values"), MethodParameter("LateInst", "op"));4658    constructor->addTemplateParam("LateInst = " + op.getCppClassName());4659    constructor->addTemplateParam(4660        "= std::enable_if_t<std::is_same_v<LateInst, " + op.getCppClassName() +4661        ">>");4662    constructor->addMemberInitializer("Base", "op");4663    constructor->addMemberInitializer("odsOperands", "values");4664  }4665 4666  std::string sizeAttrInit;4667  if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {4668    if (op.getDialect().usePropertiesForAttributes())4669      sizeAttrInit =4670          formatv(adapterSegmentSizeAttrInitCodeProperties,4671                  llvm::formatv("getProperties().operandSegmentSizes"));4672    else4673      sizeAttrInit = formatv(adapterSegmentSizeAttrInitCode,4674                             emitHelper.getAttr(operandSegmentAttrName));4675  }4676  generateNamedOperandGetters(op, genericAdaptor,4677                              /*genericAdaptorBase=*/&genericAdaptorBase,4678                              /*sizeAttrInit=*/sizeAttrInit,4679                              /*rangeType=*/"RangeT",4680                              /*rangeElementType=*/"ValueT",4681                              /*rangeBeginCall=*/"odsOperands.begin()",4682                              /*rangeSizeCall=*/"odsOperands.size()",4683                              /*getOperandCallPattern=*/"odsOperands[{0}]");4684 4685  // Any invalid overlap for `getOperands` will have been diagnosed before4686  // here already.4687  if (auto *m = genericAdaptor.addMethod("RangeT", "getOperands"))4688    m->body() << "  return odsOperands;";4689 4690  fctx.withBuilder("::mlir::Builder(odsAttrs.getContext())");4691 4692  // Generate named accessor with Attribute return type.4693  auto emitAttrWithStorageType = [&](StringRef name, StringRef emitName,4694                                     Attribute attr) {4695    // The method body is trivial if the attribute does not have a default4696    // value, in which case the default value may be arbitrary code.4697    auto *method = genericAdaptorBase.addMethod(4698        attr.getStorageType(), emitName + "Attr",4699        attr.hasDefaultValue() || !useProperties ? Method::Properties::None4700                                                 : Method::Properties::Inline);4701    ERROR_IF_PRUNED(method, "Adaptor::" + emitName + "Attr", op);4702    auto &body = method->body().indent();4703    if (!useProperties)4704      body << "assert(odsAttrs && \"no attributes when constructing "4705              "adapter\");\n";4706    body << formatv(4707        "auto attr = ::llvm::{1}<{2}>({0});\n", emitHelper.getAttr(name),4708        attr.hasDefaultValue() || attr.isOptional() ? "dyn_cast_or_null"4709                                                    : "cast",4710        attr.getStorageType());4711 4712    if (attr.hasDefaultValue() && attr.isOptional()) {4713      // Use the default value if attribute is not set.4714      // TODO: this is inefficient, we are recreating the attribute for every4715      // call. This should be set instead.4716      std::string defaultValue =4717          std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,4718                            tgfmt(attr.getDefaultValue(), &fctx)));4719      body << "if (!attr)\n  attr = " << defaultValue << ";\n";4720    }4721    body << "return attr;\n";4722  };4723 4724  if (useProperties) {4725    auto *m = genericAdaptorBase.addInlineMethod("const Properties &",4726                                                 "getProperties");4727    ERROR_IF_PRUNED(m, "Adaptor::getProperties", op);4728    m->body() << "  return properties;";4729  }4730  {4731    auto *m = genericAdaptorBase.addInlineMethod("::mlir::DictionaryAttr",4732                                                 "getAttributes");4733    ERROR_IF_PRUNED(m, "Adaptor::getAttributes", op);4734    m->body() << "  return odsAttrs;";4735  }4736  for (auto &namedProp : op.getProperties()) {4737    std::string name = op.getGetterName(namedProp.name);4738    emitPropGetter(genericAdaptorBase, op, name, namedProp.prop);4739  }4740 4741  for (auto &namedAttr : op.getAttributes()) {4742    const auto &name = namedAttr.name;4743    const auto &attr = namedAttr.attr;4744    if (attr.isDerivedAttr())4745      continue;4746    std::string emitName = op.getGetterName(name);4747    emitAttrWithStorageType(name, emitName, attr);4748    emitAttrGetterWithReturnType(fctx, genericAdaptorBase, op, emitName, attr);4749  }4750 4751  unsigned numRegions = op.getNumRegions();4752  for (unsigned i = 0; i < numRegions; ++i) {4753    const auto &region = op.getRegion(i);4754    if (region.name.empty())4755      continue;4756 4757    // Generate the accessors for a variadic region.4758    std::string name = op.getGetterName(region.name);4759    if (region.isVariadic()) {4760      auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange", name);4761      ERROR_IF_PRUNED(m, "Adaptor::" + name, op);4762      m->body() << formatv("  return odsRegions.drop_front({0});", i);4763      continue;4764    }4765 4766    auto *m = genericAdaptorBase.addInlineMethod("::mlir::Region &", name);4767    ERROR_IF_PRUNED(m, "Adaptor::" + name, op);4768    m->body() << formatv("  return *odsRegions[{0}];", i);4769  }4770  if (numRegions > 0) {4771    // Any invalid overlap for `getRegions` will have been diagnosed before4772    // here already.4773    if (auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange",4774                                                     "getRegions"))4775      m->body() << "  return odsRegions;";4776  }4777 4778  StringRef genericAdaptorClassName = genericAdaptor.getClassName();4779  adaptor.addParent(ParentClass(genericAdaptorClassName))4780      .addTemplateParam("::mlir::ValueRange");4781  adaptor.declare<VisibilityDeclaration>(Visibility::Public);4782  adaptor.declare<UsingDeclaration>(genericAdaptorClassName +4783                                    "::" + genericAdaptorClassName);4784  {4785    // Constructor taking the Op as single parameter.4786    auto *constructor =4787        adaptor.addConstructor(MethodParameter(op.getCppClassName(), "op"));4788    constructor->addMemberInitializer(genericAdaptorClassName,4789                                      "op->getOperands(), op");4790  }4791 4792  // Add verification function.4793  addVerification();4794 4795  genericAdaptorBase.finalize();4796  genericAdaptor.finalize();4797  adaptor.finalize();4798}4799 4800void OpOperandAdaptorEmitter::addVerification() {4801  auto *method = adaptor.addMethod("::llvm::LogicalResult", "verify",4802                                   MethodParameter("::mlir::Location", "loc"));4803  ERROR_IF_PRUNED(method, "verify", op);4804  auto &body = method->body();4805  bool useProperties = emitHelper.hasProperties();4806 4807  FmtContext verifyCtx;4808  populateSubstitutions(emitHelper, verifyCtx);4809  genPropertyVerifier(emitHelper, verifyCtx, body, staticVerifierEmitter);4810  genAttributeVerifier(emitHelper, verifyCtx, body, staticVerifierEmitter,4811                       useProperties);4812 4813  body << "  return ::mlir::success();";4814}4815 4816void OpOperandAdaptorEmitter::emitDecl(4817    const Operator &op,4818    const StaticVerifierFunctionEmitter &staticVerifierEmitter,4819    raw_ostream &os) {4820  OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);4821  {4822    NamespaceEmitter ns(os, "detail");4823    emitter.genericAdaptorBase.writeDeclTo(os);4824  }4825  emitter.genericAdaptor.writeDeclTo(os);4826  emitter.adaptor.writeDeclTo(os);4827}4828 4829void OpOperandAdaptorEmitter::emitDef(4830    const Operator &op,4831    const StaticVerifierFunctionEmitter &staticVerifierEmitter,4832    raw_ostream &os) {4833  OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);4834  {4835    NamespaceEmitter ns(os, "detail");4836    emitter.genericAdaptorBase.writeDefTo(os);4837  }4838  emitter.genericAdaptor.writeDefTo(os);4839  emitter.adaptor.writeDefTo(os);4840}4841 4842/// Emit the class declarations or definitions for the given op defs.4843static void emitOpClasses(4844    const RecordKeeper &records, ArrayRef<const Record *> defs, raw_ostream &os,4845    const StaticVerifierFunctionEmitter &staticVerifierEmitter, bool emitDecl) {4846  if (defs.empty())4847    return;4848 4849  for (auto *def : defs) {4850    Operator op(*def);4851    if (emitDecl) {4852      {4853        NamespaceEmitter emitter(os, op.getCppNamespace());4854        os << formatv(opCommentHeader, op.getQualCppClassName(),4855                      "declarations");4856        OpOperandAdaptorEmitter::emitDecl(op, staticVerifierEmitter, os);4857        OpEmitter::emitDecl(op, os, staticVerifierEmitter);4858      }4859      // Emit the TypeID explicit specialization to have a single definition.4860      if (!op.getCppNamespace().empty())4861        os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()4862           << "::" << op.getCppClassName() << ")\n\n";4863    } else {4864      {4865        NamespaceEmitter emitter(os, op.getCppNamespace());4866        os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions");4867        OpOperandAdaptorEmitter::emitDef(op, staticVerifierEmitter, os);4868        OpEmitter::emitDef(op, os, staticVerifierEmitter);4869      }4870      // Emit the TypeID explicit specialization to have a single definition.4871      if (!op.getCppNamespace().empty())4872        os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()4873           << "::" << op.getCppClassName() << ")\n\n";4874    }4875  }4876}4877 4878/// Emit the declarations for the provided op classes.4879static void emitOpClassDecls(const RecordKeeper &records,4880                             ArrayRef<const Record *> defs, raw_ostream &os) {4881  // First emit forward declaration for each class, this allows them to refer4882  // to each others in traits for example.4883  for (const Record *def : defs) {4884    Operator op(*def);4885    NamespaceEmitter emitter(os, op.getCppNamespace());4886    tblgen::emitSummaryAndDescComments(os, op.getSummary(),4887                                       op.getDescription());4888    os << "class " << op.getCppClassName() << ";\n";4889  }4890 4891  // Emit the op class declarations.4892  IfDefEmitter scope(os, "GET_OP_CLASSES");4893  if (defs.empty())4894    return;4895  StaticVerifierFunctionEmitter staticVerifierEmitter(os, records);4896  staticVerifierEmitter.collectOpConstraints(defs);4897  emitOpClasses(records, defs, os, staticVerifierEmitter,4898                /*emitDecl=*/true);4899}4900 4901/// Emit the definitions for the provided op classes.4902static void emitOpClassDefs(const RecordKeeper &records,4903                            ArrayRef<const Record *> defs, raw_ostream &os,4904                            StringRef constraintPrefix = "") {4905  if (defs.empty())4906    return;4907 4908  // Generate all of the locally instantiated methods first.4909  StaticVerifierFunctionEmitter staticVerifierEmitter(os, records,4910                                                      constraintPrefix);4911  os << formatv(opCommentHeader, "Local Utility Method", "Definitions");4912  staticVerifierEmitter.collectOpConstraints(defs);4913  staticVerifierEmitter.emitOpConstraints();4914 4915  // Emit the classes.4916  emitOpClasses(records, defs, os, staticVerifierEmitter,4917                /*emitDecl=*/false);4918}4919 4920/// Emit op declarations for all op records.4921static bool emitOpDecls(const RecordKeeper &records, raw_ostream &os) {4922  emitSourceFileHeader("Op Declarations", os, records);4923 4924  std::vector<const Record *> defs = getRequestedOpDefinitions(records);4925  emitOpClassDecls(records, defs, os);4926 4927  // If we are generating sharded op definitions, emit the sharded op4928  // registration hooks.4929  SmallVector<ArrayRef<const Record *>, 4> shardedDefs;4930  shardOpDefinitions(defs, shardedDefs);4931  if (defs.empty() || shardedDefs.size() <= 1)4932    return false;4933 4934  Dialect dialect = Operator(defs.front()).getDialect();4935  DialectNamespaceEmitter ns(os, dialect);4936 4937  const char *const opRegistrationHook =4938      "void register{0}Operations{1}({2}::{0} *dialect);\n";4939  os << formatv(opRegistrationHook, dialect.getCppClassName(), "",4940                dialect.getCppNamespace());4941  for (unsigned i = 0; i < shardedDefs.size(); ++i) {4942    os << formatv(opRegistrationHook, dialect.getCppClassName(), i,4943                  dialect.getCppNamespace());4944  }4945 4946  return false;4947}4948 4949/// Generate the dialect op registration hook and the op class definitions for a4950/// shard of ops.4951static void emitOpDefShard(const RecordKeeper &records,4952                           ArrayRef<const Record *> defs,4953                           const Dialect &dialect, unsigned shardIndex,4954                           unsigned shardCount, raw_ostream &os) {4955  std::string shardGuard = "GET_OP_DEFS_";4956  std::string indexStr = std::to_string(shardIndex);4957  shardGuard += indexStr;4958  IfDefEmitter scope(os, shardGuard);4959 4960  // Emit the op registration hook in the first shard.4961  const char *const opRegistrationHook =4962      "void {0}::register{1}Operations{2}({0}::{1} *dialect) {{\n";4963  if (shardIndex == 0) {4964    os << formatv(opRegistrationHook, dialect.getCppNamespace(),4965                  dialect.getCppClassName(), "");4966    for (unsigned i = 0; i < shardCount; ++i) {4967      os << formatv("  {0}::register{1}Operations{2}(dialect);\n",4968                    dialect.getCppNamespace(), dialect.getCppClassName(), i);4969    }4970    os << "}\n";4971  }4972 4973  // Generate the per-shard op registration hook.4974  os << formatv(opCommentHeader, dialect.getCppClassName(),4975                "Op Registration Hook")4976     << formatv(opRegistrationHook, dialect.getCppNamespace(),4977                dialect.getCppClassName(), shardIndex);4978  for (const Record *def : defs) {4979    os << formatv("  ::mlir::RegisteredOperationName::insert<{0}>(*dialect);\n",4980                  Operator(def).getQualCppClassName());4981  }4982  os << "}\n";4983 4984  // Generate the per-shard op definitions.4985  emitOpClassDefs(records, defs, os, indexStr);4986}4987 4988/// Emit op definitions for all op records.4989static bool emitOpDefs(const RecordKeeper &records, raw_ostream &os) {4990  emitSourceFileHeader("Op Definitions", os, records);4991 4992  std::vector<const Record *> defs = getRequestedOpDefinitions(records);4993  SmallVector<ArrayRef<const Record *>, 4> shardedDefs;4994  shardOpDefinitions(defs, shardedDefs);4995 4996  // If no shard was requested, emit the regular op list and class definitions.4997  if (shardedDefs.size() == 1) {4998    {4999      IfDefEmitter scope(os, "GET_OP_LIST");5000      interleave(5001          defs, os,5002          [&](const Record *def) { os << Operator(def).getQualCppClassName(); },5003          ",\n");5004    }5005    {5006      IfDefEmitter scope(os, "GET_OP_CLASSES");5007      emitOpClassDefs(records, defs, os);5008    }5009    return false;5010  }5011 5012  if (defs.empty())5013    return false;5014  Dialect dialect = Operator(defs.front()).getDialect();5015  for (auto [idx, value] : llvm::enumerate(shardedDefs)) {5016    emitOpDefShard(records, value, dialect, idx, shardedDefs.size(), os);5017  }5018  return false;5019}5020 5021static mlir::GenRegistration5022    genOpDecls("gen-op-decls", "Generate op declarations",5023               [](const RecordKeeper &records, raw_ostream &os) {5024                 return emitOpDecls(records, os);5025               });5026 5027static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions",5028                                       [](const RecordKeeper &records,5029                                          raw_ostream &os) {5030                                         return emitOpDefs(records, os);5031                                       });5032