brintos

brintos / llvm-project-archived public Read only

0
0
Text · 152.6 KiB · ccf21d1 Raw
3888 lines · cpp
1//===- OpFormatGen.cpp - MLIR operation asm format 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#include "OpFormatGen.h"10#include "FormatGen.h"11#include "OpClass.h"12#include "mlir/Support/LLVM.h"13#include "mlir/TableGen/Class.h"14#include "mlir/TableGen/EnumInfo.h"15#include "mlir/TableGen/Format.h"16#include "mlir/TableGen/Operator.h"17#include "mlir/TableGen/Trait.h"18#include "llvm/ADT/MapVector.h"19#include "llvm/ADT/Sequence.h"20#include "llvm/ADT/SetVector.h"21#include "llvm/ADT/SmallBitVector.h"22#include "llvm/ADT/StringExtras.h"23#include "llvm/ADT/TypeSwitch.h"24#include "llvm/Support/Signals.h"25#include "llvm/Support/SourceMgr.h"26#include "llvm/TableGen/Record.h"27 28#define DEBUG_TYPE "mlir-tblgen-opformatgen"29 30using namespace mlir;31using namespace mlir::tblgen;32using llvm::formatv;33using llvm::Record;34using llvm::StringMap;35 36//===----------------------------------------------------------------------===//37// VariableElement38//===----------------------------------------------------------------------===//39 40namespace {41/// This class represents an instance of an op variable element. A variable42/// refers to something registered on the operation itself, e.g. an operand,43/// result, attribute, region, or successor.44template <typename VarT, VariableElement::Kind VariableKind>45class OpVariableElement : public VariableElementBase<VariableKind> {46public:47  using Base = OpVariableElement<VarT, VariableKind>;48 49  /// Create an op variable element with the variable value.50  OpVariableElement(const VarT *var) : var(var) {}51 52  /// Get the variable.53  const VarT *getVar() const { return var; }54 55protected:56  /// The op variable, e.g. a type or attribute constraint.57  const VarT *var;58};59 60/// This class represents a variable that refers to an attribute argument.61struct AttributeVariable62    : public OpVariableElement<NamedAttribute, VariableElement::Attribute> {63  using Base::Base;64 65  /// Return the constant builder call for the type of this attribute, or66  /// std::nullopt if it doesn't have one.67  std::optional<StringRef> getTypeBuilder() const {68    std::optional<Type> attrType = var->attr.getValueType();69    return attrType ? attrType->getBuilderCall() : std::nullopt;70  }71 72  /// Indicate if this attribute is printed "qualified" (that is it is73  /// prefixed with the `#dialect.mnemonic`).74  bool shouldBeQualified() { return shouldBeQualifiedFlag; }75  void setShouldBeQualified(bool qualified = true) {76    shouldBeQualifiedFlag = qualified;77  }78 79private:80  bool shouldBeQualifiedFlag = false;81};82 83/// This class represents a variable that refers to an operand argument.84using OperandVariable =85    OpVariableElement<NamedTypeConstraint, VariableElement::Operand>;86 87/// This class represents a variable that refers to a result.88using ResultVariable =89    OpVariableElement<NamedTypeConstraint, VariableElement::Result>;90 91/// This class represents a variable that refers to a region.92using RegionVariable = OpVariableElement<NamedRegion, VariableElement::Region>;93 94/// This class represents a variable that refers to a successor.95using SuccessorVariable =96    OpVariableElement<NamedSuccessor, VariableElement::Successor>;97 98/// This class represents a variable that refers to a property argument.99using PropertyVariable =100    OpVariableElement<NamedProperty, VariableElement::Property>;101 102/// LLVM RTTI helper for attribute-like variables, that is, attributes or103/// properties. This allows for common handling of attributes and properties in104/// parts of the code that are oblivious to whether something is stored as an105/// attribute or a property.106struct AttributeLikeVariable : public VariableElement {107  enum { AttributeLike = 1 << 0 };108 109  static bool classof(const VariableElement *ve) {110    return ve->getKind() == VariableElement::Attribute ||111           ve->getKind() == VariableElement::Property;112  }113 114  static bool classof(const FormatElement *fe) {115    return isa<VariableElement>(fe) && classof(cast<VariableElement>(fe));116  }117 118  /// Returns true if the variable is a UnitAttr or a UnitProp.119  bool isUnit() const {120    if (const auto *attr = dyn_cast<AttributeVariable>(this))121      return attr->getVar()->attr.getBaseAttr().getAttrDefName() == "UnitAttr";122    if (const auto *prop = dyn_cast<PropertyVariable>(this)) {123      StringRef baseDefName =124          prop->getVar()->prop.getBaseProperty().getPropertyDefName();125      // Note: remove the `UnitProperty` case once the deprecation period is126      // over.127      return baseDefName == "UnitProp" || baseDefName == "UnitProperty";128    }129    llvm_unreachable("Type that wasn't listed in classof()");130  }131 132  StringRef getName() const {133    if (const auto *attr = dyn_cast<AttributeVariable>(this))134      return attr->getVar()->name;135    if (const auto *prop = dyn_cast<PropertyVariable>(this))136      return prop->getVar()->name;137    llvm_unreachable("Type that wasn't listed in classof()");138  }139};140} // namespace141 142//===----------------------------------------------------------------------===//143// DirectiveElement144//===----------------------------------------------------------------------===//145 146namespace {147/// This class represents the `operands` directive. This directive represents148/// all of the operands of an operation.149using OperandsDirective = DirectiveElementBase<DirectiveElement::Operands>;150 151/// This class represents the `results` directive. This directive represents152/// all of the results of an operation.153using ResultsDirective = DirectiveElementBase<DirectiveElement::Results>;154 155/// This class represents the `regions` directive. This directive represents156/// all of the regions of an operation.157using RegionsDirective = DirectiveElementBase<DirectiveElement::Regions>;158 159/// This class represents the `successors` directive. This directive represents160/// all of the successors of an operation.161using SuccessorsDirective = DirectiveElementBase<DirectiveElement::Successors>;162 163/// This class represents the `attr-dict` directive. This directive represents164/// the attribute dictionary of the operation.165class AttrDictDirective166    : public DirectiveElementBase<DirectiveElement::AttrDict> {167public:168  explicit AttrDictDirective(bool withKeyword) : withKeyword(withKeyword) {}169 170  /// Return whether the dictionary should be printed with the 'attributes'171  /// keyword.172  bool isWithKeyword() const { return withKeyword; }173 174private:175  /// If the dictionary should be printed with the 'attributes' keyword.176  bool withKeyword;177};178 179/// This class represents the `prop-dict` directive. This directive represents180/// the properties of the operation, expressed as a directionary.181class PropDictDirective182    : public DirectiveElementBase<DirectiveElement::PropDict> {183public:184  explicit PropDictDirective() = default;185};186 187/// This class represents the `functional-type` directive. This directive takes188/// two arguments and formats them, respectively, as the inputs and results of a189/// FunctionType.190class FunctionalTypeDirective191    : public DirectiveElementBase<DirectiveElement::FunctionalType> {192public:193  FunctionalTypeDirective(FormatElement *inputs, FormatElement *results)194      : inputs(inputs), results(results) {}195 196  FormatElement *getInputs() const { return inputs; }197  FormatElement *getResults() const { return results; }198 199private:200  /// The input and result arguments.201  FormatElement *inputs, *results;202};203 204/// This class represents the `type` directive.205class TypeDirective : public DirectiveElementBase<DirectiveElement::Type> {206public:207  TypeDirective(FormatElement *arg) : arg(arg) {}208 209  FormatElement *getArg() const { return arg; }210 211  /// Indicate if this type is printed "qualified" (that is it is212  /// prefixed with the `!dialect.mnemonic`).213  bool shouldBeQualified() { return shouldBeQualifiedFlag; }214  void setShouldBeQualified(bool qualified = true) {215    shouldBeQualifiedFlag = qualified;216  }217 218private:219  /// The argument that is used to format the directive.220  FormatElement *arg;221 222  bool shouldBeQualifiedFlag = false;223};224 225/// This class represents a group of order-independent optional clauses. Each226/// clause starts with a literal element and has a coressponding parsing227/// element. A parsing element is a continous sequence of format elements.228/// Each clause can appear 0 or 1 time.229class OIListElement : public DirectiveElementBase<DirectiveElement::OIList> {230public:231  OIListElement(std::vector<FormatElement *> &&literalElements,232                std::vector<std::vector<FormatElement *>> &&parsingElements)233      : literalElements(std::move(literalElements)),234        parsingElements(std::move(parsingElements)) {}235 236  /// Returns a range to iterate over the LiteralElements.237  auto getLiteralElements() const {238    return llvm::map_range(literalElements, [](FormatElement *el) {239      return cast<LiteralElement>(el);240    });241  }242 243  /// Returns a range to iterate over the parsing elements corresponding to the244  /// clauses.245  ArrayRef<std::vector<FormatElement *>> getParsingElements() const {246    return parsingElements;247  }248 249  /// Returns a range to iterate over tuples of parsing and literal elements.250  auto getClauses() const {251    return llvm::zip(getLiteralElements(), getParsingElements());252  }253 254  /// If the parsing element is a single UnitAttr element, then it returns the255  /// attribute variable. Otherwise, returns nullptr.256  AttributeLikeVariable *257  getUnitVariableParsingElement(ArrayRef<FormatElement *> pelement) {258    if (pelement.size() == 1) {259      auto *attrElem = dyn_cast<AttributeLikeVariable>(pelement[0]);260      if (attrElem && attrElem->isUnit())261        return attrElem;262    }263    return nullptr;264  }265 266private:267  /// A vector of `LiteralElement` objects. Each element stores the keyword268  /// for one case of oilist element. For example, an oilist element along with269  /// the `literalElements` vector:270  /// ```271  ///  oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]272  ///  literalElements = { `keyword`, `otherKeyword` }273  /// ```274  std::vector<FormatElement *> literalElements;275 276  /// A vector of valid declarative assembly format vectors. Each object in277  /// parsing elements is a vector of elements in assembly format syntax.278  /// For example, an oilist element along with the parsingElements vector:279  /// ```280  ///  oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]281  ///  parsingElements = {282  ///    { `=`, `(`, $arg0, `)` },283  ///    { `<`, $arg1, `>` }284  ///  }285  /// ```286  std::vector<std::vector<FormatElement *>> parsingElements;287};288} // namespace289 290//===----------------------------------------------------------------------===//291// OperationFormat292//===----------------------------------------------------------------------===//293 294namespace {295 296using ConstArgument =297    llvm::PointerUnion<const NamedAttribute *, const NamedTypeConstraint *>;298 299struct OperationFormat {300  /// This class represents a specific resolver for an operand or result type.301  class TypeResolution {302  public:303    TypeResolution() = default;304 305    /// Get the index into the buildable types for this type, or std::nullopt.306    std::optional<int> getBuilderIdx() const { return builderIdx; }307    void setBuilderIdx(int idx) { builderIdx = idx; }308 309    /// Get the variable this type is resolved to, or nullptr.310    const NamedTypeConstraint *getVariable() const {311      return llvm::dyn_cast_if_present<const NamedTypeConstraint *>(resolver);312    }313    /// Get the attribute this type is resolved to, or nullptr.314    const NamedAttribute *getAttribute() const {315      return llvm::dyn_cast_if_present<const NamedAttribute *>(resolver);316    }317    /// Get the transformer for the type of the variable, or std::nullopt.318    std::optional<StringRef> getVarTransformer() const {319      return variableTransformer;320    }321    void setResolver(ConstArgument arg, std::optional<StringRef> transformer) {322      resolver = arg;323      variableTransformer = transformer;324      assert(getVariable() || getAttribute());325    }326 327  private:328    /// If the type is resolved with a buildable type, this is the index into329    /// 'buildableTypes' in the parent format.330    std::optional<int> builderIdx;331    /// If the type is resolved based upon another operand or result, this is332    /// the variable or the attribute that this type is resolved to.333    ConstArgument resolver;334    /// If the type is resolved based upon another operand or result, this is335    /// a transformer to apply to the variable when resolving.336    std::optional<StringRef> variableTransformer;337  };338 339  /// The context in which an element is generated.340  enum class GenContext {341    /// The element is generated at the top-level or with the same behaviour.342    Normal,343    /// The element is generated inside an optional group.344    Optional345  };346 347  OperationFormat(const Operator &op, bool hasProperties)348      : useProperties(hasProperties), opCppClassName(op.getCppClassName()) {349    operandTypes.resize(op.getNumOperands(), TypeResolution());350    resultTypes.resize(op.getNumResults(), TypeResolution());351 352    hasImplicitTermTrait = llvm::any_of(op.getTraits(), [](const Trait &trait) {353      return trait.getDef().isSubClassOf("SingleBlockImplicitTerminatorImpl");354    });355 356    hasSingleBlockTrait = op.getTrait("::mlir::OpTrait::SingleBlock");357  }358 359  /// Generate the operation parser from this format.360  void genParser(Operator &op, OpClass &opClass);361  /// Generate the parser code for a specific format element.362  void genElementParser(FormatElement *element, MethodBody &body,363                        FmtContext &attrTypeCtx,364                        GenContext genCtx = GenContext::Normal);365  /// Generate the C++ to resolve the types of operands and results during366  /// parsing.367  void genParserTypeResolution(Operator &op, MethodBody &body);368  /// Generate the C++ to resolve the types of the operands during parsing.369  void genParserOperandTypeResolution(370      Operator &op, MethodBody &body,371      function_ref<void(TypeResolution &, StringRef)> emitTypeResolver);372  /// Generate the C++ to resolve regions during parsing.373  void genParserRegionResolution(Operator &op, MethodBody &body);374  /// Generate the C++ to resolve successors during parsing.375  void genParserSuccessorResolution(Operator &op, MethodBody &body);376  /// Generate the C++ to handling variadic segment size traits.377  void genParserVariadicSegmentResolution(Operator &op, MethodBody &body);378 379  /// Generate the operation printer from this format.380  void genPrinter(Operator &op, OpClass &opClass);381 382  /// Generate the printer code for a specific format element.383  void genElementPrinter(FormatElement *element, MethodBody &body, Operator &op,384                         bool &shouldEmitSpace, bool &lastWasPunctuation);385 386  /// The various elements in this format.387  std::vector<FormatElement *> elements;388 389  /// A flag indicating if all operand/result types were seen. If the format390  /// contains these, it can not contain individual type resolvers.391  bool allOperands = false, allOperandTypes = false, allResultTypes = false;392 393  /// A flag indicating if this operation infers its result types394  bool infersResultTypes = false;395 396  /// A flag indicating if this operation has the SingleBlockImplicitTerminator397  /// trait.398  bool hasImplicitTermTrait;399 400  /// A flag indicating if this operation has the SingleBlock trait.401  bool hasSingleBlockTrait;402 403  /// Indicate whether we need to use properties for the current operator.404  bool useProperties;405 406  /// Indicate whether prop-dict is used in the format407  bool hasPropDict;408 409  /// The Operation class name410  StringRef opCppClassName;411 412  /// A map of buildable types to indices.413  llvm::MapVector<StringRef, int, StringMap<int>> buildableTypes;414 415  /// The index of the buildable type, if valid, for every operand and result.416  std::vector<TypeResolution> operandTypes, resultTypes;417 418  /// The set of attributes explicitly used within the format.419  llvm::SmallSetVector<const NamedAttribute *, 8> usedAttributes;420  llvm::StringSet<> inferredAttributes;421 422  /// The set of properties explicitly used within the format.423  llvm::SmallSetVector<const NamedProperty *, 8> usedProperties;424};425} // namespace426 427//===----------------------------------------------------------------------===//428// Parser Gen429//===----------------------------------------------------------------------===//430 431/// Returns true if we can format the given attribute as an enum in the432/// parser format.433static bool canFormatEnumAttr(const NamedAttribute *attr) {434  Attribute baseAttr = attr->attr.getBaseAttr();435  if (!baseAttr.isEnumAttr())436    return false;437  EnumInfo enumInfo(&baseAttr.getDef());438 439  // The attribute must have a valid underlying type and a constant builder.440  return !enumInfo.getUnderlyingType().empty() &&441         !baseAttr.getConstBuilderTemplate().empty();442}443 444/// Returns if we should format the given attribute as an SymbolNameAttr.445static bool shouldFormatSymbolNameAttr(const NamedAttribute *attr) {446  return attr->attr.getBaseAttr().getAttrDefName() == "SymbolNameAttr";447}448 449/// The code snippet used to generate a parser call for an attribute.450///451/// {0}: The name of the attribute.452/// {1}: The type for the attribute.453const char *const attrParserCode = R"(454  if (parser.parseCustomAttributeWithFallback({0}Attr, {1})) {{455    return ::mlir::failure();456  }457)";458 459/// The code snippet used to generate a parser call for an attribute.460///461/// {0}: The name of the attribute.462/// {1}: The type for the attribute.463const char *const genericAttrParserCode = R"(464  if (parser.parseAttribute({0}Attr, {1}))465    return ::mlir::failure();466)";467 468const char *const optionalAttrParserCode = R"(469  ::mlir::OptionalParseResult parseResult{0}Attr =470    parser.parseOptionalAttribute({0}Attr, {1});471  if (parseResult{0}Attr.has_value() && failed(*parseResult{0}Attr))472    return ::mlir::failure();473  if (parseResult{0}Attr.has_value() && succeeded(*parseResult{0}Attr))474)";475 476/// The code snippet used to generate a parser call for a symbol name attribute.477///478/// {0}: The name of the attribute.479const char *const symbolNameAttrParserCode = R"(480  if (parser.parseSymbolName({0}Attr))481    return ::mlir::failure();482)";483const char *const optionalSymbolNameAttrParserCode = R"(484  // Parsing an optional symbol name doesn't fail, so no need to check the485  // result.486  (void)parser.parseOptionalSymbolName({0}Attr);487)";488 489/// The code snippet used to generate a parser call for an enum attribute.490///491/// {0}: The name of the attribute.492/// {1}: The c++ namespace for the enum symbolize functions.493/// {2}: The function to symbolize a string of the enum.494/// {3}: The constant builder call to create an attribute of the enum type.495/// {4}: The set of allowed enum keywords.496/// {5}: The error message on failure when the enum isn't present.497/// {6}: The attribute assignment expression498const char *const enumAttrParserCode = R"(499  {500    ::llvm::StringRef attrStr;501    ::mlir::NamedAttrList attrStorage;502    auto loc = parser.getCurrentLocation();503    if (parser.parseOptionalKeyword(&attrStr, {4})) {504      ::mlir::StringAttr attrVal;505      ::mlir::OptionalParseResult parseResult =506        parser.parseOptionalAttribute(attrVal,507                                      parser.getBuilder().getNoneType(),508                                      "{0}", attrStorage);509      if (parseResult.has_value()) {{510        if (failed(*parseResult))511          return ::mlir::failure();512        attrStr = attrVal.getValue();513      } else {514        {5}515      }516    }517    if (!attrStr.empty()) {518      auto attrOptional = {1}::{2}(attrStr);519      if (!attrOptional)520        return parser.emitError(loc, "invalid ")521               << "{0} attribute specification: \"" << attrStr << '"';;522 523      {0}Attr = {3};524      {6}525    }526  }527)";528 529/// The code snippet used to generate a parser call for a property.530/// {0}: The name of the property531/// {1}: The C++ class name of the operation532/// {2}: The property's parser code with appropriate substitutions performed533/// {3}: The description of the expected property for the error message.534const char *const propertyParserCode = R"(535  auto {0}PropLoc = parser.getCurrentLocation();536  auto {0}PropParseResult = [&](auto& propStorage) -> ::mlir::ParseResult {{537    {2}538    return ::mlir::success();539  }(result.getOrAddProperties<{1}::Properties>().{0});540  if (failed({0}PropParseResult)) {{541    return parser.emitError({0}PropLoc, "invalid value for property {0}, expected {3}");542  }543)";544 545/// The code snippet used to generate a parser call for a property.546/// {0}: The name of the property547/// {1}: The C++ class name of the operation548/// {2}: The property's parser code with appropriate substitutions performed549const char *const optionalPropertyParserCode = R"(550  auto {0}PropParseResult = [&](auto& propStorage) -> ::mlir::OptionalParseResult {{551    {2}552    return ::mlir::success();553  }(result.getOrAddProperties<{1}::Properties>().{0});554  if ({0}PropParseResult.has_value() && failed(*{0}PropParseResult)) {{555    return ::mlir::failure();556  }557)";558 559/// The code snippet used to generate a parser call for an operand.560///561/// {0}: The name of the operand.562const char *const variadicOperandParserCode = R"(563  {0}OperandsLoc = parser.getCurrentLocation();564  if (parser.parseOperandList({0}Operands))565    return ::mlir::failure();566)";567const char *const optionalOperandParserCode = R"(568  {569    {0}OperandsLoc = parser.getCurrentLocation();570    ::mlir::OpAsmParser::UnresolvedOperand operand;571    ::mlir::OptionalParseResult parseResult =572                                    parser.parseOptionalOperand(operand);573    if (parseResult.has_value()) {574      if (failed(*parseResult))575        return ::mlir::failure();576      {0}Operands.push_back(operand);577    }578  }579)";580const char *const operandParserCode = R"(581  {0}OperandsLoc = parser.getCurrentLocation();582  if (parser.parseOperand({0}RawOperand))583    return ::mlir::failure();584)";585/// The code snippet used to generate a parser call for a VariadicOfVariadic586/// operand.587///588/// {0}: The name of the operand.589/// {1}: The name of segment size attribute.590const char *const variadicOfVariadicOperandParserCode = R"(591  {592    {0}OperandsLoc = parser.getCurrentLocation();593    int32_t curSize = 0;594    do {595      if (parser.parseOptionalLParen())596        break;597      if (parser.parseOperandList({0}Operands) || parser.parseRParen())598        return ::mlir::failure();599      {0}OperandGroupSizes.push_back({0}Operands.size() - curSize);600      curSize = {0}Operands.size();601    } while (succeeded(parser.parseOptionalComma()));602  }603)";604 605/// The code snippet used to generate a parser call for a type list.606///607/// {0}: The name for the type list.608const char *const variadicOfVariadicTypeParserCode = R"(609  do {610    if (parser.parseOptionalLParen())611      break;612    if (parser.parseOptionalRParen() &&613        (parser.parseTypeList({0}Types) || parser.parseRParen()))614      return ::mlir::failure();615  } while (succeeded(parser.parseOptionalComma()));616)";617const char *const variadicTypeParserCode = R"(618  if (parser.parseTypeList({0}Types))619    return ::mlir::failure();620)";621const char *const optionalTypeParserCode = R"(622  {623    ::mlir::Type optionalType;624    ::mlir::OptionalParseResult parseResult =625                                    parser.parseOptionalType(optionalType);626    if (parseResult.has_value()) {627      if (failed(*parseResult))628        return ::mlir::failure();629      {0}Types.push_back(optionalType);630    }631  }632)";633const char *const typeParserCode = R"(634  {635    {0} type;636    if (parser.parseCustomTypeWithFallback(type))637      return ::mlir::failure();638    {1}RawType = type;639  }640)";641const char *const qualifiedTypeParserCode = R"(642  if (parser.parseType({1}RawType))643    return ::mlir::failure();644)";645 646/// The code snippet used to generate a parser call for a functional type.647///648/// {0}: The name for the input type list.649/// {1}: The name for the result type list.650const char *const functionalTypeParserCode = R"(651  ::mlir::FunctionType {0}__{1}_functionType;652  if (parser.parseType({0}__{1}_functionType))653    return ::mlir::failure();654  {0}Types = {0}__{1}_functionType.getInputs();655  {1}Types = {0}__{1}_functionType.getResults();656)";657 658/// The code snippet used to generate a parser call to infer return types.659///660/// {0}: The operation class name661const char *const inferReturnTypesParserCode = R"(662  ::llvm::SmallVector<::mlir::Type> inferredReturnTypes;663  if (::mlir::failed({0}::inferReturnTypes(parser.getContext(),664      result.location, result.operands,665      result.attributes.getDictionary(parser.getContext()),666      result.getRawProperties(),667      result.regions, inferredReturnTypes)))668    return ::mlir::failure();669  result.addTypes(inferredReturnTypes);670)";671 672/// The code snippet used to generate a parser call for a region list.673///674/// {0}: The name for the region list.675static const char *regionListParserCode = R"(676  {677    std::unique_ptr<::mlir::Region> region;678    auto firstRegionResult = parser.parseOptionalRegion(region);679    if (firstRegionResult.has_value()) {680      if (failed(*firstRegionResult))681        return ::mlir::failure();682      {0}Regions.emplace_back(std::move(region));683 684      // Parse any trailing regions.685      while (succeeded(parser.parseOptionalComma())) {686        region = std::make_unique<::mlir::Region>();687        if (parser.parseRegion(*region))688          return ::mlir::failure();689        {0}Regions.emplace_back(std::move(region));690      }691    }692  }693)";694 695/// The code snippet used to ensure a list of regions have terminators.696///697/// {0}: The name of the region list.698static const char *regionListEnsureTerminatorParserCode = R"(699  for (auto &region : {0}Regions)700    ensureTerminator(*region, parser.getBuilder(), result.location);701)";702 703/// The code snippet used to ensure a list of regions have a block.704///705/// {0}: The name of the region list.706static const char *regionListEnsureSingleBlockParserCode = R"(707  for (auto &region : {0}Regions)708    if (region->empty()) region->emplaceBlock();709)";710 711/// The code snippet used to generate a parser call for an optional region.712///713/// {0}: The name of the region.714static const char *optionalRegionParserCode = R"(715  {716     auto parseResult = parser.parseOptionalRegion(*{0}Region);717     if (parseResult.has_value() && failed(*parseResult))718       return ::mlir::failure();719  }720)";721 722/// The code snippet used to generate a parser call for a region.723///724/// {0}: The name of the region.725static const char *regionParserCode = R"(726  if (parser.parseRegion(*{0}Region))727    return ::mlir::failure();728)";729 730/// The code snippet used to ensure a region has a terminator.731///732/// {0}: The name of the region.733static const char *regionEnsureTerminatorParserCode = R"(734  ensureTerminator(*{0}Region, parser.getBuilder(), result.location);735)";736 737/// The code snippet used to ensure a region has a block.738///739/// {0}: The name of the region.740static const char *regionEnsureSingleBlockParserCode = R"(741  if ({0}Region->empty()) {0}Region->emplaceBlock();742)";743 744/// The code snippet used to generate a parser call for a successor list.745///746/// {0}: The name for the successor list.747static const char *successorListParserCode = R"(748  {749    ::mlir::Block *succ;750    auto firstSucc = parser.parseOptionalSuccessor(succ);751    if (firstSucc.has_value()) {752      if (failed(*firstSucc))753        return ::mlir::failure();754      {0}Successors.emplace_back(succ);755 756      // Parse any trailing successors.757      while (succeeded(parser.parseOptionalComma())) {758        if (parser.parseSuccessor(succ))759          return ::mlir::failure();760        {0}Successors.emplace_back(succ);761      }762    }763  }764)";765 766/// The code snippet used to generate a parser call for a successor.767///768/// {0}: The name of the successor.769static const char *successorParserCode = R"(770  if (parser.parseSuccessor({0}Successor))771    return ::mlir::failure();772)";773 774/// The code snippet used to generate a parser for OIList775///776/// {0}: literal keyword corresponding to a case for oilist777static const char *oilistParserCode = R"(778  if ({0}Clause) {779    return parser.emitError(parser.getNameLoc())780          << "`{0}` clause can appear at most once in the expansion of the "781             "oilist directive";782  }783  {0}Clause = true;784)";785 786namespace {787/// The type of length for a given parse argument.788enum class ArgumentLengthKind {789  /// The argument is a variadic of a variadic, and may contain 0->N range790  /// elements.791  VariadicOfVariadic,792  /// The argument is variadic, and may contain 0->N elements.793  Variadic,794  /// The argument is optional, and may contain 0 or 1 elements.795  Optional,796  /// The argument is a single element, i.e. always represents 1 element.797  Single798};799} // namespace800 801/// Get the length kind for the given constraint.802static ArgumentLengthKind803getArgumentLengthKind(const NamedTypeConstraint *var) {804  if (var->isOptional())805    return ArgumentLengthKind::Optional;806  if (var->isVariadicOfVariadic())807    return ArgumentLengthKind::VariadicOfVariadic;808  if (var->isVariadic())809    return ArgumentLengthKind::Variadic;810  return ArgumentLengthKind::Single;811}812 813/// Get the name used for the type list for the given type directive operand.814/// 'lengthKind' to the corresponding kind for the given argument.815static StringRef getTypeListName(FormatElement *arg,816                                 ArgumentLengthKind &lengthKind) {817  if (auto *operand = dyn_cast<OperandVariable>(arg)) {818    lengthKind = getArgumentLengthKind(operand->getVar());819    return operand->getVar()->name;820  }821  if (auto *result = dyn_cast<ResultVariable>(arg)) {822    lengthKind = getArgumentLengthKind(result->getVar());823    return result->getVar()->name;824  }825  lengthKind = ArgumentLengthKind::Variadic;826  if (isa<OperandsDirective>(arg))827    return "allOperand";828  if (isa<ResultsDirective>(arg))829    return "allResult";830  llvm_unreachable("unknown 'type' directive argument");831}832 833/// Generate the parser for a literal value.834static void genLiteralParser(StringRef value, MethodBody &body) {835  // Handle the case of a keyword/identifier.836  if (value.front() == '_' || isalpha(value.front())) {837    body << "Keyword(\"" << value << "\")";838    return;839  }840  body << (StringRef)StringSwitch<StringRef>(value)841              .Case("->", "Arrow()")842              .Case(":", "Colon()")843              .Case(",", "Comma()")844              .Case("=", "Equal()")845              .Case("<", "Less()")846              .Case(">", "Greater()")847              .Case("{", "LBrace()")848              .Case("}", "RBrace()")849              .Case("(", "LParen()")850              .Case(")", "RParen()")851              .Case("[", "LSquare()")852              .Case("]", "RSquare()")853              .Case("?", "Question()")854              .Case("+", "Plus()")855              .Case("-", "Minus()")856              .Case("*", "Star()")857              .Case("...", "Ellipsis()");858}859 860/// Generate the storage code required for parsing the given element.861static void genElementParserStorage(FormatElement *element, const Operator &op,862                                    MethodBody &body) {863  if (auto *optional = dyn_cast<OptionalElement>(element)) {864    ArrayRef<FormatElement *> elements = optional->getThenElements();865 866    // If the anchor is a unit attribute, it won't be parsed directly so elide867    // it.868    auto *anchor = dyn_cast<AttributeLikeVariable>(optional->getAnchor());869    FormatElement *elidedAnchorElement = nullptr;870    if (anchor && anchor != elements.front() && anchor->isUnit())871      elidedAnchorElement = anchor;872    for (FormatElement *childElement : elements)873      if (childElement != elidedAnchorElement)874        genElementParserStorage(childElement, op, body);875    for (FormatElement *childElement : optional->getElseElements())876      genElementParserStorage(childElement, op, body);877 878  } else if (auto *oilist = dyn_cast<OIListElement>(element)) {879    for (ArrayRef<FormatElement *> pelement : oilist->getParsingElements()) {880      if (!oilist->getUnitVariableParsingElement(pelement))881        for (FormatElement *element : pelement)882          genElementParserStorage(element, op, body);883    }884 885  } else if (auto *custom = dyn_cast<CustomDirective>(element)) {886    for (FormatElement *paramElement : custom->getElements())887      genElementParserStorage(paramElement, op, body);888 889  } else if (isa<OperandsDirective>(element)) {890    body << "  ::llvm::SmallVector<::mlir::OpAsmParser::UnresolvedOperand, 4> "891            "allOperands;\n";892 893  } else if (isa<RegionsDirective>(element)) {894    body << "  ::llvm::SmallVector<std::unique_ptr<::mlir::Region>, 2> "895            "fullRegions;\n";896 897  } else if (isa<SuccessorsDirective>(element)) {898    body << "  ::llvm::SmallVector<::mlir::Block *, 2> fullSuccessors;\n";899 900  } else if (auto *attr = dyn_cast<AttributeVariable>(element)) {901    const NamedAttribute *var = attr->getVar();902    body << formatv("  {0} {1}Attr;\n", var->attr.getStorageType(), var->name);903 904  } else if (auto *operand = dyn_cast<OperandVariable>(element)) {905    StringRef name = operand->getVar()->name;906    if (operand->getVar()->isVariableLength()) {907      body908          << "  ::llvm::SmallVector<::mlir::OpAsmParser::UnresolvedOperand, 4> "909          << name << "Operands;\n";910      if (operand->getVar()->isVariadicOfVariadic()) {911        body << "    llvm::SmallVector<int32_t> " << name912             << "OperandGroupSizes;\n";913      }914    } else {915      body << "  ::mlir::OpAsmParser::UnresolvedOperand " << name916           << "RawOperand{};\n"917           << "  ::llvm::ArrayRef<::mlir::OpAsmParser::UnresolvedOperand> "918           << name << "Operands(&" << name << "RawOperand, 1);";919    }920    body << formatv("  ::llvm::SMLoc {0}OperandsLoc;\n"921                    "  (void){0}OperandsLoc;\n",922                    name);923 924  } else if (auto *region = dyn_cast<RegionVariable>(element)) {925    StringRef name = region->getVar()->name;926    if (region->getVar()->isVariadic()) {927      body << formatv(928          "  ::llvm::SmallVector<std::unique_ptr<::mlir::Region>, 2> "929          "{0}Regions;\n",930          name);931    } else {932      body << formatv("  std::unique_ptr<::mlir::Region> {0}Region = "933                      "std::make_unique<::mlir::Region>();\n",934                      name);935    }936 937  } else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {938    StringRef name = successor->getVar()->name;939    if (successor->getVar()->isVariadic()) {940      body << formatv("  ::llvm::SmallVector<::mlir::Block *, 2> "941                      "{0}Successors;\n",942                      name);943    } else {944      body << formatv("  ::mlir::Block *{0}Successor = nullptr;\n", name);945    }946 947  } else if (auto *dir = dyn_cast<TypeDirective>(element)) {948    ArgumentLengthKind lengthKind;949    StringRef name = getTypeListName(dir->getArg(), lengthKind);950    if (lengthKind != ArgumentLengthKind::Single)951      body << "  ::llvm::SmallVector<::mlir::Type, 1> " << name << "Types;\n";952    else953      body954          << formatv("  ::mlir::Type {0}RawType{{};\n", name)955          << formatv(956                 "  ::llvm::ArrayRef<::mlir::Type> {0}Types(&{0}RawType, 1);\n",957                 name);958  } else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {959    ArgumentLengthKind ignored;960    body << "  ::llvm::ArrayRef<::mlir::Type> "961         << getTypeListName(dir->getInputs(), ignored) << "Types;\n";962    body << "  ::llvm::ArrayRef<::mlir::Type> "963         << getTypeListName(dir->getResults(), ignored) << "Types;\n";964  }965}966 967/// Generate the parser for a parameter to a custom directive.968static void genCustomParameterParser(FormatElement *param, MethodBody &body) {969  if (auto *attr = dyn_cast<AttributeVariable>(param)) {970    body << attr->getVar()->name << "Attr";971  } else if (isa<AttrDictDirective>(param)) {972    body << "result.attributes";973  } else if (isa<PropDictDirective>(param)) {974    body << "result";975  } else if (auto *operand = dyn_cast<OperandVariable>(param)) {976    StringRef name = operand->getVar()->name;977    ArgumentLengthKind lengthKind = getArgumentLengthKind(operand->getVar());978    if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)979      body << formatv("{0}OperandGroups", name);980    else if (lengthKind == ArgumentLengthKind::Variadic)981      body << formatv("{0}Operands", name);982    else if (lengthKind == ArgumentLengthKind::Optional)983      body << formatv("{0}Operand", name);984    else985      body << formatv("{0}RawOperand", name);986 987  } else if (auto *region = dyn_cast<RegionVariable>(param)) {988    StringRef name = region->getVar()->name;989    if (region->getVar()->isVariadic())990      body << formatv("{0}Regions", name);991    else992      body << formatv("*{0}Region", name);993 994  } else if (auto *successor = dyn_cast<SuccessorVariable>(param)) {995    StringRef name = successor->getVar()->name;996    if (successor->getVar()->isVariadic())997      body << formatv("{0}Successors", name);998    else999      body << formatv("{0}Successor", name);1000 1001  } else if (auto *dir = dyn_cast<RefDirective>(param)) {1002    genCustomParameterParser(dir->getArg(), body);1003 1004  } else if (auto *dir = dyn_cast<TypeDirective>(param)) {1005    ArgumentLengthKind lengthKind;1006    StringRef listName = getTypeListName(dir->getArg(), lengthKind);1007    if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)1008      body << formatv("{0}TypeGroups", listName);1009    else if (lengthKind == ArgumentLengthKind::Variadic)1010      body << formatv("{0}Types", listName);1011    else if (lengthKind == ArgumentLengthKind::Optional)1012      body << formatv("{0}Type", listName);1013    else1014      body << formatv("{0}RawType", listName);1015 1016  } else if (auto *string = dyn_cast<StringElement>(param)) {1017    FmtContext ctx;1018    ctx.withBuilder("parser.getBuilder()");1019    ctx.addSubst("_ctxt", "parser.getContext()");1020    body << tgfmt(string->getValue(), &ctx);1021 1022  } else if (auto *property = dyn_cast<PropertyVariable>(param)) {1023    body << formatv("result.getOrAddProperties<Properties>().{0}",1024                    property->getVar()->name);1025  } else {1026    llvm_unreachable("unknown custom directive parameter");1027  }1028}1029 1030/// Generate the parser for a custom directive.1031static void genCustomDirectiveParser(CustomDirective *dir, MethodBody &body,1032                                     bool useProperties,1033                                     StringRef opCppClassName,1034                                     bool isOptional = false) {1035  body << "  {\n";1036 1037  // Preprocess the directive variables.1038  // * Add a local variable for optional operands and types. This provides a1039  //   better API to the user defined parser methods.1040  // * Set the location of operand variables.1041  for (FormatElement *param : dir->getElements()) {1042    if (auto *operand = dyn_cast<OperandVariable>(param)) {1043      auto *var = operand->getVar();1044      body << "    " << var->name1045           << "OperandsLoc = parser.getCurrentLocation();\n";1046      if (var->isOptional()) {1047        body << formatv(1048            "    ::std::optional<::mlir::OpAsmParser::UnresolvedOperand> "1049            "{0}Operand;\n",1050            var->name);1051      } else if (var->isVariadicOfVariadic()) {1052        body << formatv("    "1053                        "::llvm::SmallVector<::llvm::SmallVector<::mlir::"1054                        "OpAsmParser::UnresolvedOperand>> "1055                        "{0}OperandGroups;\n",1056                        var->name);1057      }1058    } else if (auto *dir = dyn_cast<TypeDirective>(param)) {1059      ArgumentLengthKind lengthKind;1060      StringRef listName = getTypeListName(dir->getArg(), lengthKind);1061      if (lengthKind == ArgumentLengthKind::Optional) {1062        body << formatv("    ::mlir::Type {0}Type;\n", listName);1063      } else if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {1064        body << formatv(1065            "    ::llvm::SmallVector<llvm::SmallVector<::mlir::Type>> "1066            "{0}TypeGroups;\n",1067            listName);1068      }1069    } else if (auto *dir = dyn_cast<RefDirective>(param)) {1070      FormatElement *input = dir->getArg();1071      if (auto *operand = dyn_cast<OperandVariable>(input)) {1072        if (!operand->getVar()->isOptional())1073          continue;1074        body << formatv(1075            "    {0} {1}Operand = {1}Operands.empty() ? {0}() : "1076            "{1}Operands[0];\n",1077            "::std::optional<::mlir::OpAsmParser::UnresolvedOperand>",1078            operand->getVar()->name);1079 1080      } else if (auto *type = dyn_cast<TypeDirective>(input)) {1081        ArgumentLengthKind lengthKind;1082        StringRef listName = getTypeListName(type->getArg(), lengthKind);1083        if (lengthKind == ArgumentLengthKind::Optional) {1084          body << formatv("    ::mlir::Type {0}Type = {0}Types.empty() ? "1085                          "::mlir::Type() : {0}Types[0];\n",1086                          listName);1087        }1088      }1089    }1090  }1091 1092  body << "    auto odsResult = parse" << dir->getName() << "(parser";1093  for (FormatElement *param : dir->getElements()) {1094    body << ", ";1095    genCustomParameterParser(param, body);1096  }1097  body << ");\n";1098 1099  if (isOptional) {1100    body << "    if (!odsResult.has_value()) return {};\n"1101         << "    if (::mlir::failed(*odsResult)) return ::mlir::failure();\n";1102  } else {1103    body << "    if (odsResult) return ::mlir::failure();\n";1104  }1105 1106  // After parsing, add handling for any of the optional constructs.1107  for (FormatElement *param : dir->getElements()) {1108    if (auto *attr = dyn_cast<AttributeVariable>(param)) {1109      const NamedAttribute *var = attr->getVar();1110      if (var->attr.isOptional() || var->attr.hasDefaultValue())1111        body << formatv("    if ({0}Attr)\n  ", var->name);1112      if (useProperties) {1113        body << formatv(1114            "    result.getOrAddProperties<{1}::Properties>().{0} = {0}Attr;\n",1115            var->name, opCppClassName);1116      } else {1117        body << formatv("    result.addAttribute(\"{0}\", {0}Attr);\n",1118                        var->name);1119      }1120    } else if (auto *operand = dyn_cast<OperandVariable>(param)) {1121      const NamedTypeConstraint *var = operand->getVar();1122      if (var->isOptional()) {1123        body << formatv("    if ({0}Operand.has_value())\n"1124                        "      {0}Operands.push_back(*{0}Operand);\n",1125                        var->name);1126      } else if (var->isVariadicOfVariadic()) {1127        body << formatv(1128            "    for (const auto &subRange : {0}OperandGroups) {{\n"1129            "      {0}Operands.append(subRange.begin(), subRange.end());\n"1130            "      {0}OperandGroupSizes.push_back(subRange.size());\n"1131            "    }\n",1132            var->name);1133      }1134    } else if (auto *dir = dyn_cast<TypeDirective>(param)) {1135      ArgumentLengthKind lengthKind;1136      StringRef listName = getTypeListName(dir->getArg(), lengthKind);1137      if (lengthKind == ArgumentLengthKind::Optional) {1138        body << formatv("    if ({0}Type)\n"1139                        "      {0}Types.push_back({0}Type);\n",1140                        listName);1141      } else if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {1142        body << formatv(1143            "    for (const auto &subRange : {0}TypeGroups)\n"1144            "      {0}Types.append(subRange.begin(), subRange.end());\n",1145            listName);1146      }1147    }1148  }1149 1150  body << "  }\n";1151}1152 1153/// Generate the parser for a enum attribute.1154static void genEnumAttrParser(const NamedAttribute *var, MethodBody &body,1155                              FmtContext &attrTypeCtx, bool parseAsOptional,1156                              bool useProperties, StringRef opCppClassName) {1157  Attribute baseAttr = var->attr.getBaseAttr();1158  EnumInfo enumInfo(&baseAttr.getDef());1159  std::vector<EnumCase> cases = enumInfo.getAllCases();1160 1161  // Generate the code for building an attribute for this enum.1162  std::string attrBuilderStr;1163  {1164    llvm::raw_string_ostream os(attrBuilderStr);1165    os << tgfmt(baseAttr.getConstBuilderTemplate(), &attrTypeCtx,1166                "*attrOptional");1167  }1168 1169  // Build a string containing the cases that can be formatted as a keyword.1170  std::string validCaseKeywordsStr = "{";1171  llvm::raw_string_ostream validCaseKeywordsOS(validCaseKeywordsStr);1172  for (const EnumCase &attrCase : cases)1173    if (canFormatStringAsKeyword(attrCase.getStr()))1174      validCaseKeywordsOS << '"' << attrCase.getStr() << "\",";1175  validCaseKeywordsOS.str().back() = '}';1176 1177  // If the attribute is not optional, build an error message for the missing1178  // attribute.1179  std::string errorMessage;1180  if (!parseAsOptional) {1181    llvm::raw_string_ostream errorMessageOS(errorMessage);1182    errorMessageOS1183        << "return parser.emitError(loc, \"expected string or "1184           "keyword containing one of the following enum values for attribute '"1185        << var->name << "' [";1186    llvm::interleaveComma(cases, errorMessageOS, [&](const auto &attrCase) {1187      errorMessageOS << attrCase.getStr();1188    });1189    errorMessageOS << "]\");";1190  }1191  std::string attrAssignment;1192  if (useProperties) {1193    attrAssignment =1194        formatv("  "1195                "result.getOrAddProperties<{1}::Properties>().{0} = {0}Attr;",1196                var->name, opCppClassName);1197  } else {1198    attrAssignment =1199        formatv("result.addAttribute(\"{0}\", {0}Attr);", var->name);1200  }1201 1202  body << formatv(enumAttrParserCode, var->name, enumInfo.getCppNamespace(),1203                  enumInfo.getStringToSymbolFnName(), attrBuilderStr,1204                  validCaseKeywordsStr, errorMessage, attrAssignment);1205}1206 1207// Generate the parser for a property.1208static void genPropertyParser(PropertyVariable *propVar, MethodBody &body,1209                              StringRef opCppClassName,1210                              bool requireParse = true) {1211  StringRef name = propVar->getVar()->name;1212  const Property &prop = propVar->getVar()->prop;1213  bool parseOptionally =1214      prop.hasDefaultValue() && !requireParse && prop.hasOptionalParser();1215  FmtContext fmtContext;1216  fmtContext.addSubst("_parser", "parser");1217  fmtContext.addSubst("_ctxt", "parser.getContext()");1218  fmtContext.addSubst("_storage", "propStorage");1219 1220  if (parseOptionally) {1221    body << formatv(optionalPropertyParserCode, name, opCppClassName,1222                    tgfmt(prop.getOptionalParserCall(), &fmtContext));1223  } else {1224    body << formatv(propertyParserCode, name, opCppClassName,1225                    tgfmt(prop.getParserCall(), &fmtContext),1226                    prop.getSummary());1227  }1228}1229 1230// Generate the parser for an attribute.1231static void genAttrParser(AttributeVariable *attr, MethodBody &body,1232                          FmtContext &attrTypeCtx, bool parseAsOptional,1233                          bool useProperties, StringRef opCppClassName) {1234  const NamedAttribute *var = attr->getVar();1235 1236  // Check to see if we can parse this as an enum attribute.1237  if (canFormatEnumAttr(var))1238    return genEnumAttrParser(var, body, attrTypeCtx, parseAsOptional,1239                             useProperties, opCppClassName);1240 1241  // Check to see if we should parse this as a symbol name attribute.1242  if (shouldFormatSymbolNameAttr(var)) {1243    body << formatv(parseAsOptional ? optionalSymbolNameAttrParserCode1244                                    : symbolNameAttrParserCode,1245                    var->name);1246  } else {1247 1248    // If this attribute has a buildable type, use that when parsing the1249    // attribute.1250    std::string attrTypeStr;1251    if (std::optional<StringRef> typeBuilder = attr->getTypeBuilder()) {1252      llvm::raw_string_ostream os(attrTypeStr);1253      os << tgfmt(*typeBuilder, &attrTypeCtx);1254    } else {1255      attrTypeStr = "::mlir::Type{}";1256    }1257    if (parseAsOptional) {1258      body << formatv(optionalAttrParserCode, var->name, attrTypeStr);1259    } else {1260      if (attr->shouldBeQualified() ||1261          var->attr.getStorageType() == "::mlir::Attribute")1262        body << formatv(genericAttrParserCode, var->name, attrTypeStr);1263      else1264        body << formatv(attrParserCode, var->name, attrTypeStr);1265    }1266  }1267  if (useProperties) {1268    body << formatv(1269        "  if ({0}Attr) result.getOrAddProperties<{1}::Properties>().{0} = "1270        "{0}Attr;\n",1271        var->name, opCppClassName);1272  } else {1273    body << formatv(1274        "  if ({0}Attr) result.attributes.append(\"{0}\", {0}Attr);\n",1275        var->name);1276  }1277}1278 1279// Generates the 'setPropertiesFromParsedAttr' used to set properties from a1280// 'prop-dict' dictionary attr.1281static void genParsedAttrPropertiesSetter(OperationFormat &fmt, Operator &op,1282                                          OpClass &opClass) {1283  // Not required unless 'prop-dict' is present or we are not using properties.1284  if (!fmt.hasPropDict || !fmt.useProperties)1285    return;1286 1287  SmallVector<MethodParameter> paramList;1288  paramList.emplace_back("Properties &", "prop");1289  paramList.emplace_back("::mlir::Attribute", "attr");1290  paramList.emplace_back("::llvm::function_ref<::mlir::InFlightDiagnostic()>",1291                         "emitError");1292 1293  Method *method = opClass.addStaticMethod("::llvm::LogicalResult",1294                                           "setPropertiesFromParsedAttr",1295                                           std::move(paramList));1296  MethodBody &body = method->body().indent();1297 1298  body << R"decl(1299::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr);1300if (!dict) {1301  emitError() << "expected DictionaryAttr to set properties";1302  return ::mlir::failure();1303}1304// keep track of used keys in the input dictionary to be able to error out1305// if there are some unknown ones.1306::mlir::DenseSet<::mlir::StringAttr> usedKeys;1307::mlir::MLIRContext *ctx = dict.getContext();1308(void)ctx;1309)decl";1310 1311  // {0}: fromAttribute call1312  // {1}: property name1313  // {2}: isRequired1314  const char *propFromAttrFmt = R"decl(1315auto setFromAttr = [] (auto &propStorage, ::mlir::Attribute propAttr,1316         ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) -> ::mlir::LogicalResult {{1317  {0};1318};1319auto {1}AttrName = ::mlir::StringAttr::get(ctx, "{1}");1320usedKeys.insert({1}AttrName);1321auto attr = dict.get({1}AttrName);1322if (!attr && {2}) {{1323  emitError() << "expected key entry for {1} in DictionaryAttr to set "1324             "Properties.";1325  return ::mlir::failure();1326}1327if (attr && ::mlir::failed(setFromAttr(prop.{1}, attr, emitError)))1328  return ::mlir::failure();1329)decl";1330 1331  // Generate the setter for any property not parsed elsewhere.1332  for (const NamedProperty &namedProperty : op.getProperties()) {1333    if (fmt.usedProperties.contains(&namedProperty))1334      continue;1335 1336    auto scope = body.scope("{\n", "}\n", /*indent=*/true);1337 1338    StringRef name = namedProperty.name;1339    const Property &prop = namedProperty.prop;1340    bool isRequired = !prop.hasDefaultValue();1341    FmtContext fctx;1342    body << formatv(propFromAttrFmt,1343                    tgfmt(prop.getConvertFromAttributeCall(),1344                          &fctx.addSubst("_attr", "propAttr")1345                               .addSubst("_storage", "propStorage")1346                               .addSubst("_diag", "emitError")),1347                    name, isRequired);1348  }1349 1350  // Generate the setter for any attribute not parsed elsewhere.1351  for (const NamedAttribute &namedAttr : op.getAttributes()) {1352    if (fmt.usedAttributes.contains(&namedAttr))1353      continue;1354 1355    const Attribute &attr = namedAttr.attr;1356    // Derived attributes do not need to be parsed.1357    if (attr.isDerivedAttr())1358      continue;1359 1360    auto scope = body.scope("{\n", "}\n", /*indent=*/true);1361 1362    // If the attribute has a default value or is optional, it does not need to1363    // be present in the parsed dictionary attribute.1364    bool isRequired = !attr.isOptional() && !attr.hasDefaultValue();1365    body << formatv(R"decl(1366auto &propStorage = prop.{0};1367auto {0}AttrName = ::mlir::StringAttr::get(ctx, "{0}");1368auto attr = dict.get({0}AttrName);1369usedKeys.insert({0}AttrName);1370if (attr || /*isRequired=*/{1}) {{1371  if (!attr) {{1372    emitError() << "expected key entry for {0} in DictionaryAttr to set "1373               "Properties.";1374    return ::mlir::failure();1375  }1376  auto convertedAttr = ::llvm::dyn_cast<std::remove_reference_t<decltype(propStorage)>>(attr);1377  if (convertedAttr) {{1378    propStorage = convertedAttr;1379  } else {{1380    emitError() << "Invalid attribute `{0}` in property conversion: " << attr;1381    return ::mlir::failure();1382  }1383}1384)decl",1385                    namedAttr.name, isRequired);1386  }1387  body << R"decl(1388for (::mlir::NamedAttribute attr : dict) {1389  if (!usedKeys.contains(attr.getName()))1390    return emitError() << "unknown key '" << attr.getName() <<1391        "' when parsing properties dictionary";1392}1393return ::mlir::success();1394)decl";1395}1396 1397void OperationFormat::genParser(Operator &op, OpClass &opClass) {1398  SmallVector<MethodParameter> paramList;1399  paramList.emplace_back("::mlir::OpAsmParser &", "parser");1400  paramList.emplace_back("::mlir::OperationState &", "result");1401 1402  auto *method = opClass.addStaticMethod("::mlir::ParseResult", "parse",1403                                         std::move(paramList));1404  auto &body = method->body();1405 1406  // Generate variables to store the operands and type within the format. This1407  // allows for referencing these variables in the presence of optional1408  // groupings.1409  for (FormatElement *element : elements)1410    genElementParserStorage(element, op, body);1411 1412  // A format context used when parsing attributes with buildable types.1413  FmtContext attrTypeCtx;1414  attrTypeCtx.withBuilder("parser.getBuilder()");1415 1416  // Generate parsers for each of the elements.1417  for (FormatElement *element : elements)1418    genElementParser(element, body, attrTypeCtx);1419 1420  // Generate the code to resolve the operand/result types and successors now1421  // that they have been parsed.1422  genParserRegionResolution(op, body);1423  genParserSuccessorResolution(op, body);1424  genParserVariadicSegmentResolution(op, body);1425  genParserTypeResolution(op, body);1426 1427  body << "  return ::mlir::success();\n";1428 1429  genParsedAttrPropertiesSetter(*this, op, opClass);1430}1431 1432void OperationFormat::genElementParser(FormatElement *element, MethodBody &body,1433                                       FmtContext &attrTypeCtx,1434                                       GenContext genCtx) {1435  /// Optional Group.1436  if (auto *optional = dyn_cast<OptionalElement>(element)) {1437    auto genElementParsers = [&](FormatElement *firstElement,1438                                 ArrayRef<FormatElement *> elements,1439                                 bool thenGroup) {1440      // If the anchor is a unit attribute, we don't need to print it. When1441      // parsing, we will add this attribute if this group is present.1442      FormatElement *elidedAnchorElement = nullptr;1443      auto *anchorVar = dyn_cast<AttributeLikeVariable>(optional->getAnchor());1444      if (anchorVar && anchorVar != firstElement && anchorVar->isUnit()) {1445        elidedAnchorElement = anchorVar;1446 1447        if (!thenGroup == optional->isInverted()) {1448          // Add the anchor unit attribute or property to the operation state1449          // or set the property to true.1450          if (isa<PropertyVariable>(anchorVar)) {1451            body << formatv(1452                "    result.getOrAddProperties<{1}::Properties>().{0} = true;",1453                anchorVar->getName(), opCppClassName);1454          } else if (useProperties) {1455            body << formatv(1456                "    result.getOrAddProperties<{1}::Properties>().{0} = "1457                "parser.getBuilder().getUnitAttr();",1458                anchorVar->getName(), opCppClassName);1459          } else {1460            body << "    result.addAttribute(\"" << anchorVar->getName()1461                 << "\", parser.getBuilder().getUnitAttr());\n";1462          }1463        }1464      }1465 1466      // Generate the rest of the elements inside an optional group. Elements in1467      // an optional group after the guard are parsed as required.1468      for (FormatElement *childElement : elements)1469        if (childElement != elidedAnchorElement)1470          genElementParser(childElement, body, attrTypeCtx,1471                           GenContext::Optional);1472    };1473 1474    ArrayRef<FormatElement *> thenElements =1475        optional->getThenElements(/*parseable=*/true);1476 1477    // Generate a special optional parser for the first element to gate the1478    // parsing of the rest of the elements.1479    FormatElement *firstElement = thenElements.front();1480    if (auto *attrVar = dyn_cast<AttributeVariable>(firstElement)) {1481      genAttrParser(attrVar, body, attrTypeCtx, /*parseAsOptional=*/true,1482                    useProperties, opCppClassName);1483      body << "  if (" << attrVar->getVar()->name << "Attr) {\n";1484    } else if (auto *propVar = dyn_cast<PropertyVariable>(firstElement)) {1485      genPropertyParser(propVar, body, opCppClassName, /*requireParse=*/false);1486      body << formatv("if ({0}PropParseResult.has_value() && "1487                      "succeeded(*{0}PropParseResult)) ",1488                      propVar->getVar()->name)1489           << " {\n";1490    } else if (auto *literal = dyn_cast<LiteralElement>(firstElement)) {1491      body << "  if (::mlir::succeeded(parser.parseOptional";1492      genLiteralParser(literal->getSpelling(), body);1493      body << ")) {\n";1494    } else if (auto *opVar = dyn_cast<OperandVariable>(firstElement)) {1495      genElementParser(opVar, body, attrTypeCtx);1496      body << "  if (!" << opVar->getVar()->name << "Operands.empty()) {\n";1497    } else if (auto *regionVar = dyn_cast<RegionVariable>(firstElement)) {1498      const NamedRegion *region = regionVar->getVar();1499      if (region->isVariadic()) {1500        genElementParser(regionVar, body, attrTypeCtx);1501        body << "  if (!" << region->name << "Regions.empty()) {\n";1502      } else {1503        body << formatv(optionalRegionParserCode, region->name);1504        body << "  if (!" << region->name << "Region->empty()) {\n  ";1505        if (hasImplicitTermTrait)1506          body << formatv(regionEnsureTerminatorParserCode, region->name);1507        else if (hasSingleBlockTrait)1508          body << formatv(regionEnsureSingleBlockParserCode, region->name);1509      }1510    } else if (auto *custom = dyn_cast<CustomDirective>(firstElement)) {1511      body << "  if (auto optResult = [&]() -> ::mlir::OptionalParseResult {\n";1512      genCustomDirectiveParser(custom, body, useProperties, opCppClassName,1513                               /*isOptional=*/true);1514      body << "    return ::mlir::success();\n"1515           << "  }(); optResult.has_value() && ::mlir::failed(*optResult)) {\n"1516           << "    return ::mlir::failure();\n"1517           << "  } else if (optResult.has_value()) {\n";1518    }1519 1520    genElementParsers(firstElement, thenElements.drop_front(),1521                      /*thenGroup=*/true);1522    body << "  }";1523 1524    // Generate the else elements.1525    auto elseElements = optional->getElseElements();1526    if (!elseElements.empty()) {1527      body << " else {\n";1528      ArrayRef<FormatElement *> elseElements =1529          optional->getElseElements(/*parseable=*/true);1530      genElementParsers(elseElements.front(), elseElements,1531                        /*thenGroup=*/false);1532      body << "  }";1533    }1534    body << "\n";1535 1536    /// OIList Directive1537  } else if (OIListElement *oilist = dyn_cast<OIListElement>(element)) {1538    for (LiteralElement *le : oilist->getLiteralElements())1539      body << "  bool " << le->getSpelling() << "Clause = false;\n";1540 1541    // Generate the parsing loop1542    body << "  while(true) {\n";1543    for (auto clause : oilist->getClauses()) {1544      LiteralElement *lelement = std::get<0>(clause);1545      ArrayRef<FormatElement *> pelement = std::get<1>(clause);1546      body << "if (succeeded(parser.parseOptional";1547      genLiteralParser(lelement->getSpelling(), body);1548      body << ")) {\n";1549      StringRef lelementName = lelement->getSpelling();1550      body << formatv(oilistParserCode, lelementName);1551      if (AttributeLikeVariable *unitVarElem =1552              oilist->getUnitVariableParsingElement(pelement)) {1553        if (isa<PropertyVariable>(unitVarElem)) {1554          body << formatv(1555              "    result.getOrAddProperties<{1}::Properties>().{0} = true;",1556              unitVarElem->getName(), opCppClassName);1557        } else if (useProperties) {1558          body << formatv(1559              "    result.getOrAddProperties<{1}::Properties>().{0} = "1560              "parser.getBuilder().getUnitAttr();",1561              unitVarElem->getName(), opCppClassName);1562        } else {1563          body << "  result.addAttribute(\"" << unitVarElem->getName()1564               << "\", UnitAttr::get(parser.getContext()));\n";1565        }1566      } else {1567        for (FormatElement *el : pelement)1568          genElementParser(el, body, attrTypeCtx);1569      }1570      body << "    } else ";1571    }1572    body << " {\n";1573    body << "    break;\n";1574    body << "  }\n";1575    body << "}\n";1576 1577    /// Literals.1578  } else if (LiteralElement *literal = dyn_cast<LiteralElement>(element)) {1579    body << "  if (parser.parse";1580    genLiteralParser(literal->getSpelling(), body);1581    body << ")\n    return ::mlir::failure();\n";1582 1583    /// Whitespaces.1584  } else if (isa<WhitespaceElement>(element)) {1585    // Nothing to parse.1586 1587    /// Arguments.1588  } else if (auto *attr = dyn_cast<AttributeVariable>(element)) {1589    bool parseAsOptional =1590        (genCtx == GenContext::Normal && attr->getVar()->attr.isOptional());1591    genAttrParser(attr, body, attrTypeCtx, parseAsOptional, useProperties,1592                  opCppClassName);1593  } else if (auto *prop = dyn_cast<PropertyVariable>(element)) {1594    genPropertyParser(prop, body, opCppClassName);1595 1596  } else if (auto *operand = dyn_cast<OperandVariable>(element)) {1597    ArgumentLengthKind lengthKind = getArgumentLengthKind(operand->getVar());1598    StringRef name = operand->getVar()->name;1599    if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)1600      body << formatv(variadicOfVariadicOperandParserCode, name);1601    else if (lengthKind == ArgumentLengthKind::Variadic)1602      body << formatv(variadicOperandParserCode, name);1603    else if (lengthKind == ArgumentLengthKind::Optional)1604      body << formatv(optionalOperandParserCode, name);1605    else1606      body << formatv(operandParserCode, name);1607 1608  } else if (auto *region = dyn_cast<RegionVariable>(element)) {1609    bool isVariadic = region->getVar()->isVariadic();1610    body << formatv(isVariadic ? regionListParserCode : regionParserCode,1611                    region->getVar()->name);1612    if (hasImplicitTermTrait)1613      body << formatv(isVariadic ? regionListEnsureTerminatorParserCode1614                                 : regionEnsureTerminatorParserCode,1615                      region->getVar()->name);1616    else if (hasSingleBlockTrait)1617      body << formatv(isVariadic ? regionListEnsureSingleBlockParserCode1618                                 : regionEnsureSingleBlockParserCode,1619                      region->getVar()->name);1620 1621  } else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {1622    bool isVariadic = successor->getVar()->isVariadic();1623    body << formatv(isVariadic ? successorListParserCode : successorParserCode,1624                    successor->getVar()->name);1625 1626    /// Directives.1627  } else if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {1628    body.indent() << "{\n";1629    body.indent() << "auto loc = parser.getCurrentLocation();(void)loc;\n"1630                  << "if (parser.parseOptionalAttrDict"1631                  << (attrDict->isWithKeyword() ? "WithKeyword" : "")1632                  << "(result.attributes))\n"1633                  << "  return ::mlir::failure();\n";1634    if (useProperties) {1635      body << "if (failed(verifyInherentAttrs(result.name, result.attributes, "1636              "[&]() {\n"1637           << "    return parser.emitError(loc) << \"'\" << "1638              "result.name.getStringRef() << \"' op \";\n"1639           << "  })))\n"1640           << "  return ::mlir::failure();\n";1641    }1642    body.unindent() << "}\n";1643    body.unindent();1644  } else if (isa<PropDictDirective>(element)) {1645    if (useProperties) {1646      body << "  if (parseProperties(parser, result))\n"1647           << "    return ::mlir::failure();\n";1648    }1649  } else if (auto *customDir = dyn_cast<CustomDirective>(element)) {1650    genCustomDirectiveParser(customDir, body, useProperties, opCppClassName);1651  } else if (isa<OperandsDirective>(element)) {1652    body << "  [[maybe_unused]] ::llvm::SMLoc allOperandLoc ="1653         << " parser.getCurrentLocation();\n"1654         << "  if (parser.parseOperandList(allOperands))\n"1655         << "    return ::mlir::failure();\n";1656 1657  } else if (isa<RegionsDirective>(element)) {1658    body << formatv(regionListParserCode, "full");1659    if (hasImplicitTermTrait)1660      body << formatv(regionListEnsureTerminatorParserCode, "full");1661    else if (hasSingleBlockTrait)1662      body << formatv(regionListEnsureSingleBlockParserCode, "full");1663 1664  } else if (isa<SuccessorsDirective>(element)) {1665    body << formatv(successorListParserCode, "full");1666 1667  } else if (auto *dir = dyn_cast<TypeDirective>(element)) {1668    ArgumentLengthKind lengthKind;1669    StringRef listName = getTypeListName(dir->getArg(), lengthKind);1670    if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {1671      body << formatv(variadicOfVariadicTypeParserCode, listName);1672    } else if (lengthKind == ArgumentLengthKind::Variadic) {1673      body << formatv(variadicTypeParserCode, listName);1674    } else if (lengthKind == ArgumentLengthKind::Optional) {1675      body << formatv(optionalTypeParserCode, listName);1676    } else {1677      const char *parserCode =1678          dir->shouldBeQualified() ? qualifiedTypeParserCode : typeParserCode;1679      TypeSwitch<FormatElement *>(dir->getArg())1680          .Case<OperandVariable, ResultVariable>([&](auto operand) {1681            body << formatv(false, parserCode,1682                            operand->getVar()->constraint.getCppType(),1683                            listName);1684          })1685          .Default([&](auto operand) {1686            body << formatv(false, parserCode, "::mlir::Type", listName);1687          });1688    }1689  } else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {1690    ArgumentLengthKind ignored;1691    body << formatv(functionalTypeParserCode,1692                    getTypeListName(dir->getInputs(), ignored),1693                    getTypeListName(dir->getResults(), ignored));1694  } else {1695    llvm_unreachable("unknown format element");1696  }1697}1698 1699void OperationFormat::genParserTypeResolution(Operator &op, MethodBody &body) {1700  // If any of type resolutions use transformed variables, make sure that the1701  // types of those variables are resolved.1702  SmallPtrSet<const NamedTypeConstraint *, 8> verifiedVariables;1703  FmtContext verifierFCtx;1704  for (TypeResolution &resolver :1705       llvm::concat<TypeResolution>(resultTypes, operandTypes)) {1706    std::optional<StringRef> transformer = resolver.getVarTransformer();1707    if (!transformer)1708      continue;1709    // Ensure that we don't verify the same variables twice.1710    const NamedTypeConstraint *variable = resolver.getVariable();1711    if (!variable || !verifiedVariables.insert(variable).second)1712      continue;1713 1714    auto constraint = variable->constraint;1715    body << "  for (::mlir::Type type : " << variable->name << "Types) {\n"1716         << "    (void)type;\n"1717         << "    if (!("1718         << tgfmt(constraint.getConditionTemplate(),1719                  &verifierFCtx.withSelf("type"))1720         << ")) {\n"1721         << formatv("      return parser.emitError(parser.getNameLoc()) << "1722                    "\"'{0}' must be {1}, but got \" << type;\n",1723                    variable->name, constraint.getSummary())1724         << "    }\n"1725         << "  }\n";1726  }1727 1728  // Initialize the set of buildable types.1729  if (!buildableTypes.empty()) {1730    FmtContext typeBuilderCtx;1731    typeBuilderCtx.withBuilder("parser.getBuilder()");1732    for (auto &it : buildableTypes)1733      body << "  ::mlir::Type odsBuildableType" << it.second << " = "1734           << tgfmt(it.first, &typeBuilderCtx) << ";\n";1735  }1736 1737  // Emit the code necessary for a type resolver.1738  auto emitTypeResolver = [&](TypeResolution &resolver, StringRef curVar) {1739    if (std::optional<int> val = resolver.getBuilderIdx()) {1740      body << "odsBuildableType" << *val;1741    } else if (const NamedTypeConstraint *var = resolver.getVariable()) {1742      if (std::optional<StringRef> tform = resolver.getVarTransformer()) {1743        FmtContext fmtContext;1744        fmtContext.addSubst("_ctxt", "parser.getContext()");1745        if (var->isVariadic())1746          fmtContext.withSelf(var->name + "Types");1747        else1748          fmtContext.withSelf(var->name + "Types[0]");1749        body << tgfmt(*tform, &fmtContext);1750      } else {1751        body << var->name << "Types";1752        if (!var->isVariadic())1753          body << "[0]";1754      }1755    } else if (const NamedAttribute *attr = resolver.getAttribute()) {1756      if (std::optional<StringRef> tform = resolver.getVarTransformer())1757        body << tgfmt(*tform,1758                      &FmtContext().withSelf(attr->name + "Attr.getType()"));1759      else1760        body << attr->name << "Attr.getType()";1761    } else {1762      body << curVar << "Types";1763    }1764  };1765 1766  // Resolve each of the result types.1767  if (!infersResultTypes) {1768    if (allResultTypes) {1769      body << "  result.addTypes(allResultTypes);\n";1770    } else {1771      for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) {1772        body << "  result.addTypes(";1773        emitTypeResolver(resultTypes[i], op.getResultName(i));1774        body << ");\n";1775      }1776    }1777  }1778 1779  // Emit the operand type resolutions.1780  genParserOperandTypeResolution(op, body, emitTypeResolver);1781 1782  // Handle return type inference once all operands have been resolved1783  if (infersResultTypes)1784    body << formatv(inferReturnTypesParserCode, op.getCppClassName());1785}1786 1787void OperationFormat::genParserOperandTypeResolution(1788    Operator &op, MethodBody &body,1789    function_ref<void(TypeResolution &, StringRef)> emitTypeResolver) {1790  // Early exit if there are no operands.1791  if (op.getNumOperands() == 0)1792    return;1793 1794  // Handle the case where all operand types are grouped together with1795  // "types(operands)".1796  if (allOperandTypes) {1797    // If `operands` was specified, use the full operand list directly.1798    if (allOperands) {1799      body << "  if (parser.resolveOperands(allOperands, allOperandTypes, "1800              "allOperandLoc, result.operands))\n"1801              "    return ::mlir::failure();\n";1802      return;1803    }1804 1805    // Otherwise, use llvm::concat to merge the disjoint operand lists together.1806    // llvm::concat does not allow the case of a single range, so guard it here.1807    body << "  if (parser.resolveOperands(";1808    if (op.getNumOperands() > 1) {1809      body << "::llvm::concat<const ::mlir::OpAsmParser::UnresolvedOperand>(";1810      llvm::interleaveComma(op.getOperands(), body, [&](auto &operand) {1811        body << operand.name << "Operands";1812      });1813      body << ")";1814    } else {1815      body << op.operand_begin()->name << "Operands";1816    }1817    body << ", allOperandTypes, parser.getNameLoc(), result.operands))\n"1818         << "    return ::mlir::failure();\n";1819    return;1820  }1821 1822  // Handle the case where all operands are grouped together with "operands".1823  if (allOperands) {1824    body << "  if (parser.resolveOperands(allOperands, ";1825 1826    // Group all of the operand types together to perform the resolution all at1827    // once. Use llvm::concat to perform the merge. llvm::concat does not allow1828    // the case of a single range, so guard it here.1829    if (op.getNumOperands() > 1) {1830      body << "::llvm::concat<const ::mlir::Type>(";1831      llvm::interleaveComma(1832          llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {1833            body << "::llvm::ArrayRef<::mlir::Type>(";1834            emitTypeResolver(operandTypes[i], op.getOperand(i).name);1835            body << ")";1836          });1837      body << ")";1838    } else {1839      emitTypeResolver(operandTypes.front(), op.getOperand(0).name);1840    }1841 1842    body << ", allOperandLoc, result.operands))\n    return "1843            "::mlir::failure();\n";1844    return;1845  }1846 1847  // The final case is the one where each of the operands types are resolved1848  // separately.1849  for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i) {1850    NamedTypeConstraint &operand = op.getOperand(i);1851    body << "  if (parser.resolveOperands(" << operand.name << "Operands, ";1852 1853    // Resolve the type of this operand.1854    TypeResolution &operandType = operandTypes[i];1855    emitTypeResolver(operandType, operand.name);1856 1857    body << ", " << operand.name1858         << "OperandsLoc, result.operands))\n    return ::mlir::failure();\n";1859  }1860}1861 1862void OperationFormat::genParserRegionResolution(Operator &op,1863                                                MethodBody &body) {1864  // Check for the case where all regions were parsed.1865  bool hasAllRegions = llvm::any_of(1866      elements, [](FormatElement *elt) { return isa<RegionsDirective>(elt); });1867  if (hasAllRegions) {1868    body << "  result.addRegions(fullRegions);\n";1869    return;1870  }1871 1872  // Otherwise, handle each region individually.1873  for (const NamedRegion &region : op.getRegions()) {1874    if (region.isVariadic())1875      body << "  result.addRegions(" << region.name << "Regions);\n";1876    else1877      body << "  result.addRegion(std::move(" << region.name << "Region));\n";1878  }1879}1880 1881void OperationFormat::genParserSuccessorResolution(Operator &op,1882                                                   MethodBody &body) {1883  // Check for the case where all successors were parsed.1884  bool hasAllSuccessors = llvm::any_of(elements, [](FormatElement *elt) {1885    return isa<SuccessorsDirective>(elt);1886  });1887  if (hasAllSuccessors) {1888    body << "  result.addSuccessors(fullSuccessors);\n";1889    return;1890  }1891 1892  // Otherwise, handle each successor individually.1893  for (const NamedSuccessor &successor : op.getSuccessors()) {1894    if (successor.isVariadic())1895      body << "  result.addSuccessors(" << successor.name << "Successors);\n";1896    else1897      body << "  result.addSuccessors(" << successor.name << "Successor);\n";1898  }1899}1900 1901void OperationFormat::genParserVariadicSegmentResolution(Operator &op,1902                                                         MethodBody &body) {1903  if (!allOperands) {1904    if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {1905      auto interleaveFn = [&](const NamedTypeConstraint &operand) {1906        // If the operand is variadic emit the parsed size.1907        if (operand.isVariableLength())1908          body << "static_cast<int32_t>(" << operand.name << "Operands.size())";1909        else1910          body << "1";1911      };1912      if (op.getDialect().usePropertiesForAttributes()) {1913        body << "::llvm::copy(::llvm::ArrayRef<int32_t>({";1914        llvm::interleaveComma(op.getOperands(), body, interleaveFn);1915        body << formatv("}), "1916                        "result.getOrAddProperties<{0}::Properties>()."1917                        "operandSegmentSizes.begin());\n",1918                        op.getCppClassName());1919      } else {1920        body << "  result.addAttribute(\"operandSegmentSizes\", "1921             << "parser.getBuilder().getDenseI32ArrayAttr({";1922        llvm::interleaveComma(op.getOperands(), body, interleaveFn);1923        body << "}));\n";1924      }1925    }1926    for (const NamedTypeConstraint &operand : op.getOperands()) {1927      if (!operand.isVariadicOfVariadic())1928        continue;1929      if (op.getDialect().usePropertiesForAttributes()) {1930        body << formatv(1931            "  result.getOrAddProperties<{0}::Properties>().{1} = "1932            "parser.getBuilder().getDenseI32ArrayAttr({2}OperandGroupSizes);\n",1933            op.getCppClassName(),1934            operand.constraint.getVariadicOfVariadicSegmentSizeAttr(),1935            operand.name);1936      } else {1937        body << formatv(1938            "  result.addAttribute(\"{0}\", "1939            "parser.getBuilder().getDenseI32ArrayAttr({1}OperandGroupSizes));"1940            "\n",1941            operand.constraint.getVariadicOfVariadicSegmentSizeAttr(),1942            operand.name);1943      }1944    }1945  }1946 1947  if (!allResultTypes &&1948      op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {1949    auto interleaveFn = [&](const NamedTypeConstraint &result) {1950      // If the result is variadic emit the parsed size.1951      if (result.isVariableLength())1952        body << "static_cast<int32_t>(" << result.name << "Types.size())";1953      else1954        body << "1";1955    };1956    if (op.getDialect().usePropertiesForAttributes()) {1957      body << "::llvm::copy(::llvm::ArrayRef<int32_t>({";1958      llvm::interleaveComma(op.getResults(), body, interleaveFn);1959      body << formatv("}), "1960                      "result.getOrAddProperties<{0}::Properties>()."1961                      "resultSegmentSizes.begin());\n",1962                      op.getCppClassName());1963    } else {1964      body << "  result.addAttribute(\"resultSegmentSizes\", "1965           << "parser.getBuilder().getDenseI32ArrayAttr({";1966      llvm::interleaveComma(op.getResults(), body, interleaveFn);1967      body << "}));\n";1968    }1969  }1970}1971 1972//===----------------------------------------------------------------------===//1973// PrinterGen1974//===----------------------------------------------------------------------===//1975 1976/// The code snippet used to generate a printer call for a region of an1977// operation that has the SingleBlockImplicitTerminator trait.1978///1979/// {0}: The name of the region.1980static const char *regionSingleBlockImplicitTerminatorPrinterCode = R"(1981  {1982    bool printTerminator = true;1983    if (auto *term = {0}.empty() ? nullptr : {0}.begin()->getTerminator()) {{1984      printTerminator = !term->getAttrDictionary().empty() ||1985                        term->getNumOperands() != 0 ||1986                        term->getNumResults() != 0;1987    }1988    _odsPrinter.printRegion({0}, /*printEntryBlockArgs=*/true,1989      /*printBlockTerminators=*/printTerminator);1990  }1991)";1992 1993/// The code snippet used to generate a printer call for an enum that has cases1994/// that can't be represented with a keyword.1995///1996/// {0}: The name of the enum attribute.1997/// {1}: The name of the enum attributes symbolToString function.1998static const char *enumAttrBeginPrinterCode = R"(1999  {2000    auto caseValue = {0}();2001    auto caseValueStr = {1}(caseValue);2002)";2003 2004/// Generate a check that an optional or default-valued attribute or property2005/// has a non-default value. For these purposes, the default value of an2006/// optional attribute is its presence, even if the attribute itself has a2007/// default value.2008static void genNonDefaultValueCheck(MethodBody &body, const Operator &op,2009                                    AttributeVariable &attrElement) {2010  Attribute attr = attrElement.getVar()->attr;2011  std::string getter = op.getGetterName(attrElement.getVar()->name);2012  bool optionalAndDefault = attr.isOptional() && attr.hasDefaultValue();2013  if (optionalAndDefault)2014    body << "(";2015  if (attr.isOptional())2016    body << getter << "Attr()";2017  if (optionalAndDefault)2018    body << " && ";2019  if (attr.hasDefaultValue()) {2020    FmtContext fctx;2021    fctx.withBuilder("::mlir::OpBuilder((*this)->getContext())");2022    body << getter << "Attr() != "2023         << tgfmt(attr.getConstBuilderTemplate(), &fctx,2024                  tgfmt(attr.getDefaultValue(), &fctx));2025  }2026  if (optionalAndDefault)2027    body << ")";2028}2029 2030static void genNonDefaultValueCheck(MethodBody &body, const Operator &op,2031                                    PropertyVariable &propElement) {2032  FmtContext fctx;2033  fctx.withBuilder("::mlir::OpBuilder((*this)->getContext())");2034  body << op.getGetterName(propElement.getVar()->name) << "() != "2035       << tgfmt(propElement.getVar()->prop.getDefaultValue(), &fctx);2036}2037 2038/// Elide the variadic segment size attributes if necessary.2039/// This pushes elided attribute names in `elidedStorage`.2040static void genVariadicSegmentElision(OperationFormat &fmt, Operator &op,2041                                      MethodBody &body,2042                                      const char *elidedStorage) {2043  if (!fmt.allOperands &&2044      op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"))2045    body << "  " << elidedStorage << ".push_back(\"operandSegmentSizes\");\n";2046  if (!fmt.allResultTypes &&2047      op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))2048    body << "  " << elidedStorage << ".push_back(\"resultSegmentSizes\");\n";2049}2050 2051/// Generate the printer for the 'prop-dict' directive.2052static void genPropDictPrinter(OperationFormat &fmt, Operator &op,2053                               MethodBody &body) {2054  body << "  ::llvm::SmallVector<::llvm::StringRef, 2> elidedProps;\n";2055 2056  genVariadicSegmentElision(fmt, op, body, "elidedProps");2057 2058  for (const NamedProperty *namedProperty : fmt.usedProperties)2059    body << "  elidedProps.push_back(\"" << namedProperty->name << "\");\n";2060  for (const NamedAttribute *namedAttr : fmt.usedAttributes)2061    body << "  elidedProps.push_back(\"" << namedAttr->name << "\");\n";2062 2063  // Add code to check attributes for equality with their default values.2064  // Default-valued attributes will not be printed when their value matches the2065  // default.2066  for (const NamedAttribute &namedAttr : op.getAttributes()) {2067    const Attribute &attr = namedAttr.attr;2068    if (!attr.isDerivedAttr() && attr.hasDefaultValue()) {2069      const StringRef &name = namedAttr.name;2070      FmtContext fctx;2071      fctx.withBuilder("odsBuilder");2072      std::string defaultValue =2073          std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2074                            tgfmt(attr.getDefaultValue(), &fctx)));2075      body << "  {\n";2076      body << "     ::mlir::Builder odsBuilder(getContext());\n";2077      body << "     ::mlir::Attribute attr = " << op.getGetterName(name)2078           << "Attr();\n";2079      body << "     if(attr && (attr == " << defaultValue << "))\n";2080      body << "       elidedProps.push_back(\"" << name << "\");\n";2081      body << "  }\n";2082    }2083  }2084  // Similarly, elide default-valued properties.2085  for (const NamedProperty &prop : op.getProperties()) {2086    if (prop.prop.hasDefaultValue()) {2087      FmtContext fctx;2088      fctx.withBuilder("odsBuilder");2089      body << "  if (" << op.getGetterName(prop.name)2090           << "() == " << tgfmt(prop.prop.getDefaultValue(), &fctx) << ") {";2091      body << "    elidedProps.push_back(\"" << prop.name << "\");\n";2092      body << "  }\n";2093    }2094  }2095 2096  if (fmt.useProperties) {2097    body << "  _odsPrinter << \" \";\n"2098         << "  printProperties(this->getContext(), _odsPrinter, "2099            "getProperties(), elidedProps);\n";2100  }2101}2102 2103/// Generate the printer for the 'attr-dict' directive.2104static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,2105                               MethodBody &body, bool withKeyword) {2106  body << "  ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;\n";2107 2108  genVariadicSegmentElision(fmt, op, body, "elidedAttrs");2109 2110  for (const StringRef key : fmt.inferredAttributes.keys())2111    body << "  elidedAttrs.push_back(\"" << key << "\");\n";2112  for (const NamedAttribute *attr : fmt.usedAttributes)2113    body << "  elidedAttrs.push_back(\"" << attr->name << "\");\n";2114 2115  // Add code to check attributes for equality with their default values.2116  // Default-valued attributes will not be printed when their value matches the2117  // default.2118  for (const NamedAttribute &namedAttr : op.getAttributes()) {2119    const Attribute &attr = namedAttr.attr;2120    if (!attr.isDerivedAttr() && attr.hasDefaultValue()) {2121      const StringRef &name = namedAttr.name;2122      FmtContext fctx;2123      fctx.withBuilder("odsBuilder");2124      std::string defaultValue =2125          std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2126                            tgfmt(attr.getDefaultValue(), &fctx)));2127      body << "  {\n";2128      body << "     ::mlir::Builder odsBuilder(getContext());\n";2129      body << "     ::mlir::Attribute attr = " << op.getGetterName(name)2130           << "Attr();\n";2131      body << "     if(attr && (attr == " << defaultValue << "))\n";2132      body << "       elidedAttrs.push_back(\"" << name << "\");\n";2133      body << "  }\n";2134    }2135  }2136  if (fmt.hasPropDict)2137    body << "  _odsPrinter.printOptionalAttrDict"2138         << (withKeyword ? "WithKeyword" : "")2139         << "(llvm::to_vector((*this)->getDiscardableAttrs()), elidedAttrs);\n";2140  else2141    body << "  _odsPrinter.printOptionalAttrDict"2142         << (withKeyword ? "WithKeyword" : "")2143         << "((*this)->getAttrs(), elidedAttrs);\n";2144}2145 2146/// Generate the printer for a literal value. `shouldEmitSpace` is true if a2147/// space should be emitted before this element. `lastWasPunctuation` is true if2148/// the previous element was a punctuation literal.2149static void genLiteralPrinter(StringRef value, MethodBody &body,2150                              bool &shouldEmitSpace, bool &lastWasPunctuation) {2151  body << "  _odsPrinter";2152 2153  // Don't insert a space for certain punctuation.2154  if (shouldEmitSpace && shouldEmitSpaceBefore(value, lastWasPunctuation))2155    body << " << ' '";2156  body << " << \"" << value << "\";\n";2157 2158  // Insert a space after certain literals.2159  shouldEmitSpace =2160      value.size() != 1 || !StringRef("<({[").contains(value.front());2161  lastWasPunctuation = value.front() != '_' && !isalpha(value.front());2162}2163 2164/// Generate the printer for a space. `shouldEmitSpace` and `lastWasPunctuation`2165/// are set to false.2166static void genSpacePrinter(bool value, MethodBody &body, bool &shouldEmitSpace,2167                            bool &lastWasPunctuation) {2168  if (value) {2169    body << "  _odsPrinter << ' ';\n";2170    lastWasPunctuation = false;2171  } else {2172    lastWasPunctuation = true;2173  }2174  shouldEmitSpace = false;2175}2176 2177/// Generate the printer for a custom directive parameter.2178static void genCustomDirectiveParameterPrinter(FormatElement *element,2179                                               const Operator &op,2180                                               MethodBody &body) {2181  if (auto *attr = dyn_cast<AttributeVariable>(element)) {2182    body << op.getGetterName(attr->getVar()->name) << "Attr()";2183 2184  } else if (isa<AttrDictDirective>(element)) {2185    body << "getOperation()->getAttrDictionary()";2186 2187  } else if (isa<PropDictDirective>(element)) {2188    body << "getProperties()";2189 2190  } else if (auto *operand = dyn_cast<OperandVariable>(element)) {2191    body << op.getGetterName(operand->getVar()->name) << "()";2192 2193  } else if (auto *region = dyn_cast<RegionVariable>(element)) {2194    body << op.getGetterName(region->getVar()->name) << "()";2195 2196  } else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {2197    body << op.getGetterName(successor->getVar()->name) << "()";2198 2199  } else if (auto *dir = dyn_cast<RefDirective>(element)) {2200    genCustomDirectiveParameterPrinter(dir->getArg(), op, body);2201 2202  } else if (auto *dir = dyn_cast<TypeDirective>(element)) {2203    auto *typeOperand = dir->getArg();2204    auto *operand = dyn_cast<OperandVariable>(typeOperand);2205    auto *var = operand ? operand->getVar()2206                        : cast<ResultVariable>(typeOperand)->getVar();2207    std::string name = op.getGetterName(var->name);2208    if (var->isVariadic())2209      body << name << "().getTypes()";2210    else if (var->isOptional())2211      body << formatv("({0}() ? {0}().getType() : ::mlir::Type())", name);2212    else2213      body << name << "().getType()";2214 2215  } else if (auto *string = dyn_cast<StringElement>(element)) {2216    FmtContext ctx;2217    ctx.withBuilder("::mlir::Builder(getContext())");2218    ctx.addSubst("_ctxt", "getContext()");2219    body << tgfmt(string->getValue(), &ctx);2220 2221  } else if (auto *property = dyn_cast<PropertyVariable>(element)) {2222    FmtContext ctx;2223    const NamedProperty *namedProperty = property->getVar();2224    ctx.addSubst("_storage", "getProperties()." + namedProperty->name);2225    body << tgfmt(namedProperty->prop.getConvertFromStorageCall(), &ctx);2226  } else {2227    llvm_unreachable("unknown custom directive parameter");2228  }2229}2230 2231/// Generate the printer for a custom directive.2232static void genCustomDirectivePrinter(CustomDirective *customDir,2233                                      const Operator &op, MethodBody &body) {2234  body << "  print" << customDir->getName() << "(_odsPrinter, *this";2235  for (FormatElement *param : customDir->getElements()) {2236    body << ", ";2237    genCustomDirectiveParameterPrinter(param, op, body);2238  }2239  body << ");\n";2240}2241 2242/// Generate the printer for a region with the given variable name.2243static void genRegionPrinter(const Twine &regionName, MethodBody &body,2244                             bool hasImplicitTermTrait) {2245  if (hasImplicitTermTrait)2246    body << formatv(regionSingleBlockImplicitTerminatorPrinterCode, regionName);2247  else2248    body << "  _odsPrinter.printRegion(" << regionName << ");\n";2249}2250static void genVariadicRegionPrinter(const Twine &regionListName,2251                                     MethodBody &body,2252                                     bool hasImplicitTermTrait) {2253  body << "    llvm::interleaveComma(" << regionListName2254       << ", _odsPrinter, [&](::mlir::Region &region) {\n      ";2255  genRegionPrinter("region", body, hasImplicitTermTrait);2256  body << "    });\n";2257}2258 2259/// Generate the C++ for an operand to a (*-)type directive.2260static MethodBody &genTypeOperandPrinter(FormatElement *arg, const Operator &op,2261                                         MethodBody &body,2262                                         bool useArrayRef = true) {2263  if (isa<OperandsDirective>(arg))2264    return body << "getOperation()->getOperandTypes()";2265  if (isa<ResultsDirective>(arg))2266    return body << "getOperation()->getResultTypes()";2267  auto *operand = dyn_cast<OperandVariable>(arg);2268  auto *var = operand ? operand->getVar() : cast<ResultVariable>(arg)->getVar();2269  if (var->isVariadicOfVariadic())2270    return body << formatv("{0}().join().getTypes()",2271                           op.getGetterName(var->name));2272  if (var->isVariadic())2273    return body << op.getGetterName(var->name) << "().getTypes()";2274  if (var->isOptional())2275    return body << formatv(2276               "({0}() ? ::llvm::ArrayRef<::mlir::Type>({0}().getType()) : "2277               "::llvm::ArrayRef<::mlir::Type>())",2278               op.getGetterName(var->name));2279  if (useArrayRef)2280    return body << "::llvm::ArrayRef<::mlir::Type>("2281                << op.getGetterName(var->name) << "().getType())";2282  return body << op.getGetterName(var->name) << "().getType()";2283}2284 2285/// Generate the printer for an enum attribute.2286static void genEnumAttrPrinter(const NamedAttribute *var, const Operator &op,2287                               MethodBody &body) {2288  Attribute baseAttr = var->attr.getBaseAttr();2289  const EnumInfo enumInfo(&baseAttr.getDef());2290  std::vector<EnumCase> cases = enumInfo.getAllCases();2291 2292  body << formatv(enumAttrBeginPrinterCode,2293                  (var->attr.isOptional() ? "*" : "") +2294                      op.getGetterName(var->name),2295                  enumInfo.getSymbolToStringFnName());2296 2297  // Get a string containing all of the cases that can't be represented with a2298  // keyword.2299  BitVector nonKeywordCases(cases.size());2300  for (auto it : llvm::enumerate(cases)) {2301    if (!canFormatStringAsKeyword(it.value().getStr()))2302      nonKeywordCases.set(it.index());2303  }2304 2305  // Otherwise if this is a bit enum attribute, don't allow cases that may2306  // overlap with other cases. For simplicity sake, only allow cases with a2307  // single bit value.2308  if (enumInfo.isBitEnum()) {2309    for (auto it : llvm::enumerate(cases)) {2310      int64_t value = it.value().getValue();2311      if (value < 0 || !llvm::isPowerOf2_64(value))2312        nonKeywordCases.set(it.index());2313    }2314  }2315 2316  // If there are any cases that can't be used with a keyword, switch on the2317  // case value to determine when to print in the string form.2318  if (nonKeywordCases.any()) {2319    body << "    switch (caseValue) {\n";2320    StringRef cppNamespace = enumInfo.getCppNamespace();2321    StringRef enumName = enumInfo.getEnumClassName();2322    for (auto it : llvm::enumerate(cases)) {2323      if (nonKeywordCases.test(it.index()))2324        continue;2325      StringRef symbol = it.value().getSymbol();2326      body << formatv("    case {0}::{1}::{2}:\n", cppNamespace, enumName,2327                      llvm::isDigit(symbol.front()) ? ("_" + symbol) : symbol);2328    }2329    body << "      _odsPrinter << caseValueStr;\n"2330            "      break;\n"2331            "    default:\n"2332            "      _odsPrinter << '\"' << caseValueStr << '\"';\n"2333            "      break;\n"2334            "    }\n"2335            "  }\n";2336    return;2337  }2338 2339  body << "    _odsPrinter << caseValueStr;\n"2340          "  }\n";2341}2342 2343/// Generate the check for the anchor of an optional group.2344static void genOptionalGroupPrinterAnchor(FormatElement *anchor,2345                                          const Operator &op,2346                                          MethodBody &body) {2347  TypeSwitch<FormatElement *>(anchor)2348      .Case<OperandVariable, ResultVariable>([&](auto *element) {2349        const NamedTypeConstraint *var = element->getVar();2350        std::string name = op.getGetterName(var->name);2351        if (var->isOptional())2352          body << name << "()";2353        else if (var->isVariadic())2354          body << "!" << name << "().empty()";2355      })2356      .Case([&](RegionVariable *element) {2357        const NamedRegion *var = element->getVar();2358        std::string name = op.getGetterName(var->name);2359        // TODO: Add a check for optional regions here when ODS supports it.2360        body << "!" << name << "().empty()";2361      })2362      .Case([&](TypeDirective *element) {2363        genOptionalGroupPrinterAnchor(element->getArg(), op, body);2364      })2365      .Case([&](FunctionalTypeDirective *element) {2366        genOptionalGroupPrinterAnchor(element->getInputs(), op, body);2367      })2368      .Case([&](AttributeVariable *element) {2369        // Consider a default-valued attribute as present if it's not the2370        // default value and an optional one present if it is set.2371        genNonDefaultValueCheck(body, op, *element);2372      })2373      .Case([&](PropertyVariable *element) {2374        genNonDefaultValueCheck(body, op, *element);2375      })2376      .Case([&](CustomDirective *ele) {2377        body << '(';2378        llvm::interleave(2379            ele->getElements(), body,2380            [&](FormatElement *child) {2381              body << '(';2382              genOptionalGroupPrinterAnchor(child, op, body);2383              body << ')';2384            },2385            " || ");2386        body << ')';2387      });2388}2389 2390static void collect(FormatElement *element,2391                    SmallVectorImpl<VariableElement *> &variables) {2392  TypeSwitch<FormatElement *>(element)2393      .Case([&](VariableElement *var) { variables.emplace_back(var); })2394      .Case([&](CustomDirective *ele) {2395        for (FormatElement *arg : ele->getElements())2396          collect(arg, variables);2397      })2398      .Case([&](OptionalElement *ele) {2399        for (FormatElement *arg : ele->getThenElements())2400          collect(arg, variables);2401        for (FormatElement *arg : ele->getElseElements())2402          collect(arg, variables);2403      })2404      .Case([&](FunctionalTypeDirective *funcType) {2405        collect(funcType->getInputs(), variables);2406        collect(funcType->getResults(), variables);2407      })2408      .Case([&](OIListElement *oilist) {2409        for (ArrayRef<FormatElement *> arg : oilist->getParsingElements())2410          for (FormatElement *arg : arg)2411            collect(arg, variables);2412      });2413}2414 2415void OperationFormat::genElementPrinter(FormatElement *element,2416                                        MethodBody &body, Operator &op,2417                                        bool &shouldEmitSpace,2418                                        bool &lastWasPunctuation) {2419  if (LiteralElement *literal = dyn_cast<LiteralElement>(element))2420    return genLiteralPrinter(literal->getSpelling(), body, shouldEmitSpace,2421                             lastWasPunctuation);2422 2423  // Emit a whitespace element.2424  if (auto *space = dyn_cast<WhitespaceElement>(element)) {2425    if (space->getValue() == "\\n") {2426      body << "  _odsPrinter.printNewline();\n";2427    } else {2428      genSpacePrinter(!space->getValue().empty(), body, shouldEmitSpace,2429                      lastWasPunctuation);2430    }2431    return;2432  }2433 2434  // Emit an optional group.2435  if (OptionalElement *optional = dyn_cast<OptionalElement>(element)) {2436    // Emit the check for the presence of the anchor element.2437    FormatElement *anchor = optional->getAnchor();2438    body << "  if (";2439    if (optional->isInverted())2440      body << "!";2441    genOptionalGroupPrinterAnchor(anchor, op, body);2442    body << ") {\n";2443    body.indent();2444 2445    // If the anchor is a unit attribute, we don't need to print it. When2446    // parsing, we will add this attribute if this group is present.2447    ArrayRef<FormatElement *> thenElements = optional->getThenElements();2448    ArrayRef<FormatElement *> elseElements = optional->getElseElements();2449    FormatElement *elidedAnchorElement = nullptr;2450    auto *anchorAttr = dyn_cast<AttributeLikeVariable>(anchor);2451    if (anchorAttr && anchorAttr != thenElements.front() &&2452        (elseElements.empty() || anchorAttr != elseElements.front()) &&2453        anchorAttr->isUnit()) {2454      elidedAnchorElement = anchorAttr;2455    }2456    auto genElementPrinters = [&](ArrayRef<FormatElement *> elements) {2457      for (FormatElement *childElement : elements) {2458        if (childElement != elidedAnchorElement) {2459          genElementPrinter(childElement, body, op, shouldEmitSpace,2460                            lastWasPunctuation);2461        }2462      }2463    };2464 2465    // Emit each of the elements.2466    genElementPrinters(thenElements);2467    body << "}";2468 2469    // Emit each of the else elements.2470    if (!elseElements.empty()) {2471      body << " else {\n";2472      genElementPrinters(elseElements);2473      body << "}";2474    }2475 2476    body.unindent() << "\n";2477    return;2478  }2479 2480  // Emit the OIList2481  if (auto *oilist = dyn_cast<OIListElement>(element)) {2482    for (auto clause : oilist->getClauses()) {2483      LiteralElement *lelement = std::get<0>(clause);2484      ArrayRef<FormatElement *> pelement = std::get<1>(clause);2485 2486      SmallVector<VariableElement *> vars;2487      for (FormatElement *el : pelement)2488        collect(el, vars);2489      body << "  if (false";2490      for (VariableElement *var : vars) {2491        TypeSwitch<FormatElement *>(var)2492            .Case([&](AttributeVariable *attrEle) {2493              body << " || (";2494              genNonDefaultValueCheck(body, op, *attrEle);2495              body << ")";2496            })2497            .Case([&](PropertyVariable *propEle) {2498              body << " || (";2499              genNonDefaultValueCheck(body, op, *propEle);2500              body << ")";2501            })2502            .Case([&](OperandVariable *ele) {2503              if (ele->getVar()->isVariadic()) {2504                body << " || " << op.getGetterName(ele->getVar()->name)2505                     << "().size()";2506              } else {2507                body << " || " << op.getGetterName(ele->getVar()->name) << "()";2508              }2509            })2510            .Case([&](ResultVariable *ele) {2511              if (ele->getVar()->isVariadic()) {2512                body << " || " << op.getGetterName(ele->getVar()->name)2513                     << "().size()";2514              } else {2515                body << " || " << op.getGetterName(ele->getVar()->name) << "()";2516              }2517            })2518            .Case([&](RegionVariable *reg) {2519              body << " || " << op.getGetterName(reg->getVar()->name) << "()";2520            });2521      }2522 2523      body << ") {\n";2524      genLiteralPrinter(lelement->getSpelling(), body, shouldEmitSpace,2525                        lastWasPunctuation);2526      if (oilist->getUnitVariableParsingElement(pelement) == nullptr) {2527        for (FormatElement *element : pelement)2528          genElementPrinter(element, body, op, shouldEmitSpace,2529                            lastWasPunctuation);2530      }2531      body << "  }\n";2532    }2533    return;2534  }2535 2536  // Emit the attribute dictionary.2537  if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {2538    genAttrDictPrinter(*this, op, body, attrDict->isWithKeyword());2539    lastWasPunctuation = false;2540    return;2541  }2542 2543  // Emit the property dictionary.2544  if (isa<PropDictDirective>(element)) {2545    genPropDictPrinter(*this, op, body);2546    lastWasPunctuation = false;2547    return;2548  }2549 2550  // Optionally insert a space before the next element. The AttrDict printer2551  // already adds a space as necessary.2552  if (shouldEmitSpace || !lastWasPunctuation)2553    body << "  _odsPrinter << ' ';\n";2554  lastWasPunctuation = false;2555  shouldEmitSpace = true;2556 2557  if (auto *attr = dyn_cast<AttributeVariable>(element)) {2558    const NamedAttribute *var = attr->getVar();2559 2560    // If we are formatting as an enum, symbolize the attribute as a string.2561    if (canFormatEnumAttr(var))2562      return genEnumAttrPrinter(var, op, body);2563 2564    // If we are formatting as a symbol name, handle it as a symbol name.2565    if (shouldFormatSymbolNameAttr(var)) {2566      body << "  _odsPrinter.printSymbolName(" << op.getGetterName(var->name)2567           << "Attr().getValue());\n";2568      return;2569    }2570 2571    // Elide the attribute type if it is buildable.2572    if (attr->getTypeBuilder())2573      body << "  _odsPrinter.printAttributeWithoutType("2574           << op.getGetterName(var->name) << "Attr());\n";2575    else if (attr->shouldBeQualified() ||2576             var->attr.getStorageType() == "::mlir::Attribute")2577      body << "  _odsPrinter.printAttribute(" << op.getGetterName(var->name)2578           << "Attr());\n";2579    else2580      body << "_odsPrinter.printStrippedAttrOrType("2581           << op.getGetterName(var->name) << "Attr());\n";2582  } else if (auto *property = dyn_cast<PropertyVariable>(element)) {2583    const NamedProperty *var = property->getVar();2584    FmtContext fmtContext;2585    fmtContext.addSubst("_printer", "_odsPrinter");2586    fmtContext.addSubst("_ctxt", "getContext()");2587    fmtContext.addSubst("_storage", "getProperties()." + var->name);2588    body << tgfmt(var->prop.getPrinterCall(), &fmtContext) << ";\n";2589  } else if (auto *operand = dyn_cast<OperandVariable>(element)) {2590    if (operand->getVar()->isVariadicOfVariadic()) {2591      body << "  ::llvm::interleaveComma("2592           << op.getGetterName(operand->getVar()->name)2593           << "(), _odsPrinter, [&](const auto &operands) { _odsPrinter << "2594              "\"(\" << operands << "2595              "\")\"; });\n";2596 2597    } else if (operand->getVar()->isOptional()) {2598      body << "  if (::mlir::Value value = "2599           << op.getGetterName(operand->getVar()->name) << "())\n"2600           << "    _odsPrinter << value;\n";2601    } else {2602      body << "  _odsPrinter << " << op.getGetterName(operand->getVar()->name)2603           << "();\n";2604    }2605  } else if (auto *region = dyn_cast<RegionVariable>(element)) {2606    const NamedRegion *var = region->getVar();2607    std::string name = op.getGetterName(var->name);2608    if (var->isVariadic()) {2609      genVariadicRegionPrinter(name + "()", body, hasImplicitTermTrait);2610    } else {2611      genRegionPrinter(name + "()", body, hasImplicitTermTrait);2612    }2613  } else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {2614    const NamedSuccessor *var = successor->getVar();2615    std::string name = op.getGetterName(var->name);2616    if (var->isVariadic())2617      body << "  ::llvm::interleaveComma(" << name << "(), _odsPrinter);\n";2618    else2619      body << "  _odsPrinter << " << name << "();\n";2620  } else if (auto *dir = dyn_cast<CustomDirective>(element)) {2621    genCustomDirectivePrinter(dir, op, body);2622  } else if (isa<OperandsDirective>(element)) {2623    body << "  _odsPrinter << getOperation()->getOperands();\n";2624  } else if (isa<RegionsDirective>(element)) {2625    genVariadicRegionPrinter("getOperation()->getRegions()", body,2626                             hasImplicitTermTrait);2627  } else if (isa<SuccessorsDirective>(element)) {2628    body << "  ::llvm::interleaveComma(getOperation()->getSuccessors(), "2629            "_odsPrinter);\n";2630  } else if (auto *dir = dyn_cast<TypeDirective>(element)) {2631    if (auto *operand = dyn_cast<OperandVariable>(dir->getArg())) {2632      if (operand->getVar()->isVariadicOfVariadic()) {2633        body << formatv(2634            "  ::llvm::interleaveComma({0}().getTypes(), _odsPrinter, "2635            "[&](::mlir::TypeRange types) {{ _odsPrinter << \"(\" << "2636            "types << \")\"; });\n",2637            op.getGetterName(operand->getVar()->name));2638        return;2639      }2640    }2641    const NamedTypeConstraint *var = nullptr;2642    {2643      if (auto *operand = dyn_cast<OperandVariable>(dir->getArg()))2644        var = operand->getVar();2645      else if (auto *operand = dyn_cast<ResultVariable>(dir->getArg()))2646        var = operand->getVar();2647    }2648    if (var && !var->isVariadicOfVariadic() && !var->isVariadic() &&2649        !var->isOptional()) {2650      StringRef cppType = var->constraint.getCppType();2651      if (dir->shouldBeQualified()) {2652        body << "   _odsPrinter << " << op.getGetterName(var->name)2653             << "().getType();\n";2654        return;2655      }2656      body << "  {\n"2657           << "    auto type = " << op.getGetterName(var->name)2658           << "().getType();\n"2659           << "    if (auto validType = ::llvm::dyn_cast<" << cppType2660           << ">(type))\n"2661           << "      _odsPrinter.printStrippedAttrOrType(validType);\n"2662           << "   else\n"2663           << "     _odsPrinter << type;\n"2664           << "  }\n";2665      return;2666    }2667    body << "  _odsPrinter << ";2668    genTypeOperandPrinter(dir->getArg(), op, body, /*useArrayRef=*/false)2669        << ";\n";2670  } else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {2671    body << "  _odsPrinter.printFunctionalType(";2672    genTypeOperandPrinter(dir->getInputs(), op, body) << ", ";2673    genTypeOperandPrinter(dir->getResults(), op, body) << ");\n";2674  } else {2675    llvm_unreachable("unknown format element");2676  }2677}2678 2679void OperationFormat::genPrinter(Operator &op, OpClass &opClass) {2680  auto *method = opClass.addMethod(2681      "void", "print",2682      MethodParameter("::mlir::OpAsmPrinter &", "_odsPrinter"));2683  auto &body = method->body();2684 2685  // Flags for if we should emit a space, and if the last element was2686  // punctuation.2687  bool shouldEmitSpace = true, lastWasPunctuation = false;2688  for (FormatElement *element : elements)2689    genElementPrinter(element, body, op, shouldEmitSpace, lastWasPunctuation);2690}2691 2692//===----------------------------------------------------------------------===//2693// OpFormatParser2694//===----------------------------------------------------------------------===//2695 2696/// Function to find an element within the given range that has the same name as2697/// 'name'.2698template <typename RangeT>2699static auto findArg(RangeT &&range, StringRef name) {2700  auto it = llvm::find_if(range, [=](auto &arg) { return arg.name == name; });2701  return it != range.end() ? &*it : nullptr;2702}2703 2704namespace {2705/// This class implements a parser for an instance of an operation assembly2706/// format.2707class OpFormatParser : public FormatParser {2708public:2709  OpFormatParser(llvm::SourceMgr &mgr, OperationFormat &format, Operator &op)2710      : FormatParser(mgr, op.getLoc()[0]), fmt(format), op(op),2711        seenOperandTypes(op.getNumOperands()),2712        seenResultTypes(op.getNumResults()) {}2713 2714protected:2715  /// Verify the format elements.2716  LogicalResult verify(SMLoc loc, ArrayRef<FormatElement *> elements) override;2717  /// Verify the arguments to a custom directive.2718  LogicalResult2719  verifyCustomDirectiveArguments(SMLoc loc,2720                                 ArrayRef<FormatElement *> arguments) override;2721  /// Verify the elements of an optional group.2722  LogicalResult verifyOptionalGroupElements(SMLoc loc,2723                                            ArrayRef<FormatElement *> elements,2724                                            FormatElement *anchor) override;2725  LogicalResult verifyOptionalGroupElement(SMLoc loc, FormatElement *element,2726                                           bool isAnchor);2727 2728  LogicalResult markQualified(SMLoc loc, FormatElement *element) override;2729 2730  /// Parse an operation variable.2731  FailureOr<FormatElement *> parseVariableImpl(SMLoc loc, StringRef name,2732                                               Context ctx) override;2733  /// Parse an operation format directive.2734  FailureOr<FormatElement *>2735  parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind, Context ctx) override;2736 2737private:2738  /// This struct represents a type resolution instance. It includes a specific2739  /// type as well as an optional transformer to apply to that type in order to2740  /// properly resolve the type of a variable.2741  struct TypeResolutionInstance {2742    ConstArgument resolver;2743    std::optional<StringRef> transformer;2744  };2745 2746  /// Verify the state of operation attributes within the format.2747  LogicalResult verifyAttributes(SMLoc loc, ArrayRef<FormatElement *> elements);2748 2749  /// Verify that attributes elements aren't followed by colon literals.2750  LogicalResult verifyAttributeColonType(SMLoc loc,2751                                         ArrayRef<FormatElement *> elements);2752  /// Verify that the attribute dictionary directive isn't followed by a region.2753  LogicalResult verifyAttrDictRegion(SMLoc loc,2754                                     ArrayRef<FormatElement *> elements);2755 2756  /// Verify the state of operation operands within the format.2757  LogicalResult2758  verifyOperands(SMLoc loc,2759                 StringMap<TypeResolutionInstance> &variableTyResolver);2760 2761  /// Verify the state of operation regions within the format.2762  LogicalResult verifyRegions(SMLoc loc);2763 2764  /// Verify the state of operation results within the format.2765  LogicalResult2766  verifyResults(SMLoc loc,2767                StringMap<TypeResolutionInstance> &variableTyResolver);2768 2769  /// Verify the state of operation successors within the format.2770  LogicalResult verifySuccessors(SMLoc loc);2771 2772  LogicalResult verifyOIListElements(SMLoc loc,2773                                     ArrayRef<FormatElement *> elements);2774 2775  /// Given the values of an `AllTypesMatch` trait, check for inferable type2776  /// resolution.2777  void handleAllTypesMatchConstraint(2778      ArrayRef<StringRef> values,2779      StringMap<TypeResolutionInstance> &variableTyResolver);2780  /// Check for inferable type resolution given all operands, and or results,2781  /// have the same type. If 'includeResults' is true, the results also have the2782  /// same type as all of the operands.2783  void handleSameTypesConstraint(2784      StringMap<TypeResolutionInstance> &variableTyResolver,2785      bool includeResults);2786  /// Check for inferable type resolution based on another operand, result, or2787  /// attribute.2788  void handleTypesMatchConstraint(2789      StringMap<TypeResolutionInstance> &variableTyResolver, const Record &def);2790 2791  /// Check for inferable type resolution based on2792  /// `ShapedTypeMatchesElementCountAndTypes` constraint.2793  void handleShapedTypeMatchesElementCountAndTypesConstraint(2794      StringMap<TypeResolutionInstance> &variableTyResolver, const Record &def);2795 2796  /// Returns an argument or attribute with the given name that has been seen2797  /// within the format.2798  ConstArgument findSeenArg(StringRef name);2799 2800  /// Parse the various different directives.2801  FailureOr<FormatElement *> parsePropDictDirective(SMLoc loc, Context context);2802  FailureOr<FormatElement *> parseAttrDictDirective(SMLoc loc, Context context,2803                                                    bool withKeyword);2804  FailureOr<FormatElement *> parseFunctionalTypeDirective(SMLoc loc,2805                                                          Context context);2806  FailureOr<FormatElement *> parseOIListDirective(SMLoc loc, Context context);2807  LogicalResult verifyOIListParsingElement(FormatElement *element, SMLoc loc);2808  FailureOr<FormatElement *> parseOperandsDirective(SMLoc loc, Context context);2809  FailureOr<FormatElement *> parseRegionsDirective(SMLoc loc, Context context);2810  FailureOr<FormatElement *> parseResultsDirective(SMLoc loc, Context context);2811  FailureOr<FormatElement *> parseSuccessorsDirective(SMLoc loc,2812                                                      Context context);2813  FailureOr<FormatElement *> parseTypeDirective(SMLoc loc, Context context);2814  FailureOr<FormatElement *> parseTypeDirectiveOperand(SMLoc loc,2815                                                       bool isRefChild = false);2816 2817  //===--------------------------------------------------------------------===//2818  // Fields2819  //===--------------------------------------------------------------------===//2820 2821  OperationFormat &fmt;2822  Operator &op;2823 2824  // The following are various bits of format state used for verification2825  // during parsing.2826  bool hasAttrDict = false;2827  bool hasPropDict = false;2828  bool hasAllRegions = false, hasAllSuccessors = false;2829  bool canInferResultTypes = false;2830  llvm::SmallBitVector seenOperandTypes, seenResultTypes;2831  llvm::SmallSetVector<const NamedAttribute *, 8> seenAttrs;2832  llvm::DenseSet<const NamedTypeConstraint *> seenOperands;2833  llvm::DenseSet<const NamedRegion *> seenRegions;2834  llvm::DenseSet<const NamedSuccessor *> seenSuccessors;2835  llvm::SmallSetVector<const NamedProperty *, 8> seenProperties;2836};2837} // namespace2838 2839LogicalResult OpFormatParser::verify(SMLoc loc,2840                                     ArrayRef<FormatElement *> elements) {2841  // Check that the attribute dictionary is in the format.2842  if (!hasAttrDict)2843    return emitError(loc, "'attr-dict' directive not found in "2844                          "custom assembly format");2845 2846  // Check for any type traits that we can use for inferring types.2847  StringMap<TypeResolutionInstance> variableTyResolver;2848  for (const Trait &trait : op.getTraits()) {2849    const Record &def = trait.getDef();2850    if (def.isSubClassOf("AllTypesMatch")) {2851      handleAllTypesMatchConstraint(def.getValueAsListOfStrings("values"),2852                                    variableTyResolver);2853    } else if (def.getName() == "SameTypeOperands") {2854      handleSameTypesConstraint(variableTyResolver, /*includeResults=*/false);2855    } else if (def.getName() == "SameOperandsAndResultType") {2856      handleSameTypesConstraint(variableTyResolver, /*includeResults=*/true);2857    } else if (def.isSubClassOf("TypesMatchWith")) {2858      handleTypesMatchConstraint(variableTyResolver, def);2859    } else if (def.isSubClassOf("ShapedTypeMatchesElementCountAndTypes")) {2860      handleShapedTypeMatchesElementCountAndTypesConstraint(variableTyResolver,2861                                                            def);2862    } else if (!op.allResultTypesKnown()) {2863      // This doesn't check the name directly to handle2864      //    DeclareOpInterfaceMethods<InferTypeOpInterface>2865      // and the like.2866      // TODO: Add hasCppInterface check.2867      if (auto name = def.getValueAsOptionalString("cppInterfaceName")) {2868        if (*name == "InferTypeOpInterface" &&2869            def.getValueAsString("cppNamespace") == "::mlir")2870          canInferResultTypes = true;2871      }2872    }2873  }2874 2875  // Verify the state of the various operation components.2876  if (failed(verifyAttributes(loc, elements)) ||2877      failed(verifyResults(loc, variableTyResolver)) ||2878      failed(verifyOperands(loc, variableTyResolver)) ||2879      failed(verifyRegions(loc)) || failed(verifySuccessors(loc)) ||2880      failed(verifyOIListElements(loc, elements)))2881    return failure();2882 2883  // Collect the set of used attributes in the format.2884  fmt.usedAttributes = std::move(seenAttrs);2885  fmt.usedProperties = std::move(seenProperties);2886 2887  // Set whether prop-dict is used in the format2888  fmt.hasPropDict = hasPropDict;2889  return success();2890}2891 2892LogicalResult2893OpFormatParser::verifyAttributes(SMLoc loc,2894                                 ArrayRef<FormatElement *> elements) {2895  // Check that there are no `:` literals after an attribute without a constant2896  // type. The attribute grammar contains an optional trailing colon type, which2897  // can lead to unexpected and generally unintended behavior. Given that, it is2898  // better to just error out here instead.2899  if (failed(verifyAttributeColonType(loc, elements)))2900    return failure();2901  // Check that there are no region variables following an attribute dicitonary.2902  // Both start with `{` and so the optional attribute dictionary can cause2903  // format ambiguities.2904  if (failed(verifyAttrDictRegion(loc, elements)))2905    return failure();2906 2907  // Check for VariadicOfVariadic variables. The segment attribute of those2908  // variables will be infered.2909  for (const NamedTypeConstraint *var : seenOperands) {2910    if (var->constraint.isVariadicOfVariadic()) {2911      fmt.inferredAttributes.insert(2912          var->constraint.getVariadicOfVariadicSegmentSizeAttr());2913    }2914  }2915 2916  return success();2917}2918 2919/// Returns whether the single format element is optionally parsed.2920static bool isOptionallyParsed(FormatElement *el) {2921  if (auto *attrVar = dyn_cast<AttributeVariable>(el)) {2922    Attribute attr = attrVar->getVar()->attr;2923    return attr.isOptional() || attr.hasDefaultValue();2924  }2925  if (auto *propVar = dyn_cast<PropertyVariable>(el)) {2926    const Property &prop = propVar->getVar()->prop;2927    return prop.hasDefaultValue() && prop.hasOptionalParser();2928  }2929  if (auto *operandVar = dyn_cast<OperandVariable>(el)) {2930    const NamedTypeConstraint *operand = operandVar->getVar();2931    return operand->isOptional() || operand->isVariadic() ||2932           operand->isVariadicOfVariadic();2933  }2934  if (auto *successorVar = dyn_cast<SuccessorVariable>(el))2935    return successorVar->getVar()->isVariadic();2936  if (auto *regionVar = dyn_cast<RegionVariable>(el))2937    return regionVar->getVar()->isVariadic();2938  return isa<WhitespaceElement, AttrDictDirective>(el);2939}2940 2941/// Scan the given range of elements from the start for an invalid format2942/// element that satisfies `isInvalid`, skipping any optionally-parsed elements.2943/// If an optional group is encountered, this function recurses into the 'then'2944/// and 'else' elements to check if they are invalid. Returns `success` if the2945/// range is known to be valid or `std::nullopt` if scanning reached the end.2946///2947/// Since the guard element of an optional group is required, this function2948/// accepts an optional element pointer to mark it as required.2949static std::optional<LogicalResult> checkRangeForElement(2950    FormatElement *base,2951    function_ref<bool(FormatElement *, FormatElement *)> isInvalid,2952    iterator_range<ArrayRef<FormatElement *>::iterator> elementRange,2953    FormatElement *optionalGuard = nullptr) {2954  for (FormatElement *element : elementRange) {2955    // If we encounter an invalid element, return an error.2956    if (isInvalid(base, element))2957      return failure();2958 2959    // Recurse on optional groups.2960    if (auto *optional = dyn_cast<OptionalElement>(element)) {2961      if (std::optional<LogicalResult> result = checkRangeForElement(2962              base, isInvalid, optional->getThenElements(),2963              // The optional group guard is required for the group.2964              optional->getThenElements().front()))2965        if (failed(*result))2966          return failure();2967      if (std::optional<LogicalResult> result = checkRangeForElement(2968              base, isInvalid, optional->getElseElements()))2969        if (failed(*result))2970          return failure();2971      // Skip the optional group.2972      continue;2973    }2974 2975    // Skip optionally parsed elements.2976    if (element != optionalGuard && isOptionallyParsed(element))2977      continue;2978 2979    // We found a closing element that is valid.2980    return success();2981  }2982  // Return std::nullopt to indicate that we reached the end.2983  return std::nullopt;2984}2985 2986/// For the given elements, check whether any attributes are followed by a colon2987/// literal, resulting in an ambiguous assembly format. Returns a non-null2988/// attribute if verification of said attribute reached the end of the range.2989/// Returns null if all attribute elements are verified.2990static FailureOr<FormatElement *> verifyAdjacentElements(2991    function_ref<bool(FormatElement *)> isBase,2992    function_ref<bool(FormatElement *, FormatElement *)> isInvalid,2993    ArrayRef<FormatElement *> elements) {2994  for (auto *it = elements.begin(), *e = elements.end(); it != e; ++it) {2995    // The current attribute being verified.2996    FormatElement *base;2997 2998    if (isBase(*it)) {2999      base = *it;3000    } else if (auto *optional = dyn_cast<OptionalElement>(*it)) {3001      // Recurse on optional groups.3002      FailureOr<FormatElement *> thenResult = verifyAdjacentElements(3003          isBase, isInvalid, optional->getThenElements());3004      if (failed(thenResult))3005        return failure();3006      FailureOr<FormatElement *> elseResult = verifyAdjacentElements(3007          isBase, isInvalid, optional->getElseElements());3008      if (failed(elseResult))3009        return failure();3010      // If either optional group has an unverified attribute, save it.3011      // Otherwise, move on to the next element.3012      if (!(base = *thenResult) && !(base = *elseResult))3013        continue;3014    } else {3015      continue;3016    }3017 3018    // Verify subsequent elements for potential ambiguities.3019    if (std::optional<LogicalResult> result =3020            checkRangeForElement(base, isInvalid, {std::next(it), e})) {3021      if (failed(*result))3022        return failure();3023    } else {3024      // Since we reached the end, return the attribute as unverified.3025      return base;3026    }3027  }3028  // All attribute elements are known to be verified.3029  return nullptr;3030}3031 3032LogicalResult3033OpFormatParser::verifyAttributeColonType(SMLoc loc,3034                                         ArrayRef<FormatElement *> elements) {3035  auto isBase = [](FormatElement *el) {3036    auto *attr = dyn_cast<AttributeVariable>(el);3037    if (!attr)3038      return false;3039    // Check only attributes without type builders or that are known to call3040    // the generic attribute parser.3041    return !attr->getTypeBuilder() &&3042           (attr->shouldBeQualified() ||3043            attr->getVar()->attr.getStorageType() == "::mlir::Attribute");3044  };3045  auto isInvalid = [&](FormatElement *base, FormatElement *el) {3046    auto *literal = dyn_cast<LiteralElement>(el);3047    if (!literal || literal->getSpelling() != ":")3048      return false;3049    // If we encounter `:`, the range is known to be invalid.3050    (void)emitError(3051        loc, formatv("format ambiguity caused by `:` literal found after "3052                     "attribute `{0}` which does not have a buildable type",3053                     cast<AttributeVariable>(base)->getVar()->name));3054    return true;3055  };3056  return verifyAdjacentElements(isBase, isInvalid, elements);3057}3058 3059LogicalResult3060OpFormatParser::verifyAttrDictRegion(SMLoc loc,3061                                     ArrayRef<FormatElement *> elements) {3062  auto isBase = [](FormatElement *el) {3063    if (auto *attrDict = dyn_cast<AttrDictDirective>(el))3064      return !attrDict->isWithKeyword();3065    return false;3066  };3067  auto isInvalid = [&](FormatElement *base, FormatElement *el) {3068    auto *region = dyn_cast<RegionVariable>(el);3069    if (!region)3070      return false;3071    (void)emitErrorAndNote(3072        loc,3073        formatv("format ambiguity caused by `attr-dict` directive "3074                "followed by region `{0}`",3075                region->getVar()->name),3076        "try using `attr-dict-with-keyword` instead");3077    return true;3078  };3079  return verifyAdjacentElements(isBase, isInvalid, elements);3080}3081 3082LogicalResult OpFormatParser::verifyOperands(3083    SMLoc loc, StringMap<TypeResolutionInstance> &variableTyResolver) {3084  // Check that all of the operands are within the format, and their types can3085  // be inferred.3086  auto &buildableTypes = fmt.buildableTypes;3087  for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i) {3088    NamedTypeConstraint &operand = op.getOperand(i);3089 3090    // Check that the operand itself is in the format.3091    if (!fmt.allOperands && !seenOperands.count(&operand)) {3092      return emitErrorAndNote(loc,3093                              "operand #" + Twine(i) + ", named '" +3094                                  operand.name + "', not found",3095                              "suggest adding a '$" + operand.name +3096                                  "' directive to the custom assembly format");3097    }3098 3099    // Check that the operand type is in the format, or that it can be inferred.3100    if (fmt.allOperandTypes || seenOperandTypes.test(i))3101      continue;3102 3103    // Check to see if we can infer this type from another variable.3104    auto varResolverIt = variableTyResolver.find(op.getOperand(i).name);3105    if (varResolverIt != variableTyResolver.end()) {3106      TypeResolutionInstance &resolver = varResolverIt->second;3107      fmt.operandTypes[i].setResolver(resolver.resolver, resolver.transformer);3108      continue;3109    }3110 3111    // Similarly to results, allow a custom builder for resolving the type if3112    // we aren't using the 'operands' directive.3113    std::optional<StringRef> builder = operand.constraint.getBuilderCall();3114    if (!builder || (fmt.allOperands && operand.isVariableLength())) {3115      return emitErrorAndNote(3116          loc,3117          "type of operand #" + Twine(i) + ", named '" + operand.name +3118              "', is not buildable and a buildable type cannot be inferred",3119          "suggest adding a type constraint to the operation or adding a "3120          "'type($" +3121              operand.name + ")' directive to the " + "custom assembly format");3122    }3123    auto it = buildableTypes.insert({*builder, buildableTypes.size()});3124    fmt.operandTypes[i].setBuilderIdx(it.first->second);3125  }3126  return success();3127}3128 3129LogicalResult OpFormatParser::verifyRegions(SMLoc loc) {3130  // Check that all of the regions are within the format.3131  if (hasAllRegions)3132    return success();3133 3134  for (unsigned i = 0, e = op.getNumRegions(); i != e; ++i) {3135    const NamedRegion &region = op.getRegion(i);3136    if (!seenRegions.count(&region)) {3137      return emitErrorAndNote(loc,3138                              "region #" + Twine(i) + ", named '" +3139                                  region.name + "', not found",3140                              "suggest adding a '$" + region.name +3141                                  "' directive to the custom assembly format");3142    }3143  }3144  return success();3145}3146 3147LogicalResult OpFormatParser::verifyResults(3148    SMLoc loc, StringMap<TypeResolutionInstance> &variableTyResolver) {3149  // If we format all of the types together, there is nothing to check.3150  if (fmt.allResultTypes)3151    return success();3152 3153  // If no result types are specified and we can infer them, infer all result3154  // types3155  if (op.getNumResults() > 0 && seenResultTypes.count() == 0 &&3156      canInferResultTypes) {3157    fmt.infersResultTypes = true;3158    return success();3159  }3160 3161  // Check that all of the result types can be inferred.3162  auto &buildableTypes = fmt.buildableTypes;3163  for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) {3164    if (seenResultTypes.test(i))3165      continue;3166 3167    // Check to see if we can infer this type from another variable.3168    auto varResolverIt = variableTyResolver.find(op.getResultName(i));3169    if (varResolverIt != variableTyResolver.end()) {3170      TypeResolutionInstance resolver = varResolverIt->second;3171      fmt.resultTypes[i].setResolver(resolver.resolver, resolver.transformer);3172      continue;3173    }3174 3175    // If the result is not variable length, allow for the case where the type3176    // has a builder that we can use.3177    NamedTypeConstraint &result = op.getResult(i);3178    std::optional<StringRef> builder = result.constraint.getBuilderCall();3179    if (!builder || result.isVariableLength()) {3180      return emitErrorAndNote(3181          loc,3182          "type of result #" + Twine(i) + ", named '" + result.name +3183              "', is not buildable and a buildable type cannot be inferred",3184          "suggest adding a type constraint to the operation or adding a "3185          "'type($" +3186              result.name + ")' directive to the " + "custom assembly format");3187    }3188    // Note in the format that this result uses the custom builder.3189    auto it = buildableTypes.insert({*builder, buildableTypes.size()});3190    fmt.resultTypes[i].setBuilderIdx(it.first->second);3191  }3192  return success();3193}3194 3195LogicalResult OpFormatParser::verifySuccessors(SMLoc loc) {3196  // Check that all of the successors are within the format.3197  if (hasAllSuccessors)3198    return success();3199 3200  for (unsigned i = 0, e = op.getNumSuccessors(); i != e; ++i) {3201    const NamedSuccessor &successor = op.getSuccessor(i);3202    if (!seenSuccessors.count(&successor)) {3203      return emitErrorAndNote(loc,3204                              "successor #" + Twine(i) + ", named '" +3205                                  successor.name + "', not found",3206                              "suggest adding a '$" + successor.name +3207                                  "' directive to the custom assembly format");3208    }3209  }3210  return success();3211}3212 3213LogicalResult3214OpFormatParser::verifyOIListElements(SMLoc loc,3215                                     ArrayRef<FormatElement *> elements) {3216  // Check that all of the successors are within the format.3217  SmallVector<StringRef> prohibitedLiterals;3218  for (FormatElement *it : elements) {3219    if (auto *oilist = dyn_cast<OIListElement>(it)) {3220      if (!prohibitedLiterals.empty()) {3221        // We just saw an oilist element in last iteration. Literals should not3222        // match.3223        for (LiteralElement *literal : oilist->getLiteralElements()) {3224          if (find(prohibitedLiterals, literal->getSpelling()) !=3225              prohibitedLiterals.end()) {3226            return emitError(3227                loc, "format ambiguity because " + literal->getSpelling() +3228                         " is used in two adjacent oilist elements.");3229          }3230        }3231      }3232      for (LiteralElement *literal : oilist->getLiteralElements())3233        prohibitedLiterals.push_back(literal->getSpelling());3234    } else if (auto *literal = dyn_cast<LiteralElement>(it)) {3235      if (find(prohibitedLiterals, literal->getSpelling()) !=3236          prohibitedLiterals.end()) {3237        return emitError(3238            loc,3239            "format ambiguity because " + literal->getSpelling() +3240                " is used both in oilist element and the adjacent literal.");3241      }3242      prohibitedLiterals.clear();3243    } else {3244      prohibitedLiterals.clear();3245    }3246  }3247  return success();3248}3249 3250void OpFormatParser::handleAllTypesMatchConstraint(3251    ArrayRef<StringRef> values,3252    StringMap<TypeResolutionInstance> &variableTyResolver) {3253  for (unsigned i = 0, e = values.size(); i != e; ++i) {3254    // Check to see if this value matches a resolved operand or result type.3255    ConstArgument arg = findSeenArg(values[i]);3256    if (!arg)3257      continue;3258 3259    // Mark this value as the type resolver for the other variables.3260    for (unsigned j = 0; j != i; ++j)3261      variableTyResolver[values[j]] = {arg, std::nullopt};3262    for (unsigned j = i + 1; j != e; ++j)3263      variableTyResolver[values[j]] = {arg, std::nullopt};3264  }3265}3266 3267void OpFormatParser::handleSameTypesConstraint(3268    StringMap<TypeResolutionInstance> &variableTyResolver,3269    bool includeResults) {3270  const NamedTypeConstraint *resolver = nullptr;3271  int resolvedIt = -1;3272 3273  // Check to see if there is an operand or result to use for the resolution.3274  if ((resolvedIt = seenOperandTypes.find_first()) != -1)3275    resolver = &op.getOperand(resolvedIt);3276  else if (includeResults && (resolvedIt = seenResultTypes.find_first()) != -1)3277    resolver = &op.getResult(resolvedIt);3278  else3279    return;3280 3281  // Set the resolvers for each operand and result.3282  for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i)3283    if (!seenOperandTypes.test(i))3284      variableTyResolver[op.getOperand(i).name] = {resolver, std::nullopt};3285  if (includeResults) {3286    for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)3287      if (!seenResultTypes.test(i))3288        variableTyResolver[op.getResultName(i)] = {resolver, std::nullopt};3289  }3290}3291 3292void OpFormatParser::handleTypesMatchConstraint(3293    StringMap<TypeResolutionInstance> &variableTyResolver, const Record &def) {3294  StringRef lhsName = def.getValueAsString("lhs");3295  StringRef rhsName = def.getValueAsString("rhs");3296  StringRef transformer = def.getValueAsString("transformer");3297  if (ConstArgument arg = findSeenArg(lhsName))3298    variableTyResolver[rhsName] = {arg, transformer};3299}3300 3301void OpFormatParser::handleShapedTypeMatchesElementCountAndTypesConstraint(3302    StringMap<TypeResolutionInstance> &variableTyResolver, const Record &def) {3303  StringRef shapedArg = def.getValueAsString("shaped");3304  StringRef elementsArg = def.getValueAsString("elements");3305 3306  // Check if the 'shaped' argument is seen, then we can infer the 'elements'3307  // types.3308  if (ConstArgument arg = findSeenArg(shapedArg)) {3309    variableTyResolver[elementsArg] = {3310        arg, "::llvm::SmallVector<::mlir::Type>(::llvm::cast<::mlir::"3311             "ShapedType>($_self).getNumElements(), "3312             "::llvm::cast<::mlir::ShapedType>($_self).getElementType())"};3313  }3314 3315  // Type inference in the opposite direction is not possible as the actual3316  // shaped type can't be inferred from the variadic elements.3317}3318 3319ConstArgument OpFormatParser::findSeenArg(StringRef name) {3320  if (const NamedTypeConstraint *arg = findArg(op.getOperands(), name))3321    return seenOperandTypes.test(arg - op.operand_begin()) ? arg : nullptr;3322  if (const NamedTypeConstraint *arg = findArg(op.getResults(), name))3323    return seenResultTypes.test(arg - op.result_begin()) ? arg : nullptr;3324  if (const NamedAttribute *attr = findArg(op.getAttributes(), name))3325    return seenAttrs.count(attr) ? attr : nullptr;3326  return nullptr;3327}3328 3329FailureOr<FormatElement *>3330OpFormatParser::parseVariableImpl(SMLoc loc, StringRef name, Context ctx) {3331  // Check that the parsed argument is something actually registered on the op.3332  // Attributes3333  if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {3334    if (ctx == TypeDirectiveContext)3335      return emitError(3336          loc, "attributes cannot be used as children to a `type` directive");3337    if (ctx == RefDirectiveContext) {3338      if (!seenAttrs.count(attr))3339        return emitError(loc, "attribute '" + name +3340                                  "' must be bound before it is referenced");3341    } else if (!seenAttrs.insert(attr)) {3342      return emitError(loc, "attribute '" + name + "' is already bound");3343    }3344 3345    return create<AttributeVariable>(attr);3346  }3347 3348  if (const NamedProperty *property = findArg(op.getProperties(), name)) {3349    if (ctx == TypeDirectiveContext)3350      return emitError(3351          loc, "properties cannot be used as children to a `type` directive");3352    if (ctx == RefDirectiveContext) {3353      if (!seenProperties.count(property))3354        return emitError(loc, "property '" + name +3355                                  "' must be bound before it is referenced");3356    } else {3357      if (!seenProperties.insert(property))3358        return emitError(loc, "property '" + name + "' is already bound");3359    }3360 3361    return create<PropertyVariable>(property);3362  }3363 3364  // Operands3365  if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {3366    if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {3367      if (fmt.allOperands || !seenOperands.insert(operand).second)3368        return emitError(loc, "operand '" + name + "' is already bound");3369    } else if (ctx == RefDirectiveContext && !seenOperands.count(operand)) {3370      return emitError(loc, "operand '" + name +3371                                "' must be bound before it is referenced");3372    }3373    return create<OperandVariable>(operand);3374  }3375  // Regions3376  if (const NamedRegion *region = findArg(op.getRegions(), name)) {3377    if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {3378      if (hasAllRegions || !seenRegions.insert(region).second)3379        return emitError(loc, "region '" + name + "' is already bound");3380    } else if (ctx == RefDirectiveContext) {3381      if (!seenRegions.count(region))3382        return emitError(loc, "region '" + name +3383                                  "' must be bound before it is referenced");3384    } else {3385      return emitError(loc, "regions can only be used at the top level "3386                            "or in a ref directive");3387    }3388    return create<RegionVariable>(region);3389  }3390  // Results.3391  if (const auto *result = findArg(op.getResults(), name)) {3392    if (ctx != TypeDirectiveContext)3393      return emitError(loc, "result variables can can only be used as a child "3394                            "to a 'type' directive");3395    return create<ResultVariable>(result);3396  }3397  // Successors.3398  if (const auto *successor = findArg(op.getSuccessors(), name)) {3399    if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {3400      if (hasAllSuccessors || !seenSuccessors.insert(successor).second)3401        return emitError(loc, "successor '" + name + "' is already bound");3402    } else if (ctx == RefDirectiveContext) {3403      if (!seenSuccessors.count(successor))3404        return emitError(loc, "successor '" + name +3405                                  "' must be bound before it is referenced");3406    } else {3407      return emitError(loc, "successors can only be used at the top level "3408                            "or in a ref directive");3409    }3410 3411    return create<SuccessorVariable>(successor);3412  }3413  return emitError(loc, "expected variable to refer to an argument, region, "3414                        "result, or successor");3415}3416 3417FailureOr<FormatElement *>3418OpFormatParser::parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind,3419                                   Context ctx) {3420  switch (kind) {3421  case FormatToken::kw_prop_dict:3422    return parsePropDictDirective(loc, ctx);3423  case FormatToken::kw_attr_dict:3424    return parseAttrDictDirective(loc, ctx,3425                                  /*withKeyword=*/false);3426  case FormatToken::kw_attr_dict_w_keyword:3427    return parseAttrDictDirective(loc, ctx,3428                                  /*withKeyword=*/true);3429  case FormatToken::kw_functional_type:3430    return parseFunctionalTypeDirective(loc, ctx);3431  case FormatToken::kw_operands:3432    return parseOperandsDirective(loc, ctx);3433  case FormatToken::kw_regions:3434    return parseRegionsDirective(loc, ctx);3435  case FormatToken::kw_results:3436    return parseResultsDirective(loc, ctx);3437  case FormatToken::kw_successors:3438    return parseSuccessorsDirective(loc, ctx);3439  case FormatToken::kw_type:3440    return parseTypeDirective(loc, ctx);3441  case FormatToken::kw_oilist:3442    return parseOIListDirective(loc, ctx);3443 3444  default:3445    return emitError(loc, "unsupported directive kind");3446  }3447}3448 3449FailureOr<FormatElement *>3450OpFormatParser::parseAttrDictDirective(SMLoc loc, Context context,3451                                       bool withKeyword) {3452  if (context == TypeDirectiveContext)3453    return emitError(loc, "'attr-dict' directive can only be used as a "3454                          "top-level directive");3455 3456  if (context == RefDirectiveContext) {3457    if (!hasAttrDict)3458      return emitError(loc, "'ref' of 'attr-dict' is not bound by a prior "3459                            "'attr-dict' directive");3460 3461    // Otherwise, this is a top-level context.3462  } else {3463    if (hasAttrDict)3464      return emitError(loc, "'attr-dict' directive has already been seen");3465    hasAttrDict = true;3466  }3467 3468  return create<AttrDictDirective>(withKeyword);3469}3470 3471FailureOr<FormatElement *>3472OpFormatParser::parsePropDictDirective(SMLoc loc, Context context) {3473  if (context == TypeDirectiveContext)3474    return emitError(loc, "'prop-dict' directive can only be used as a "3475                          "top-level directive");3476 3477  if (context == RefDirectiveContext)3478    llvm::report_fatal_error("'ref' of 'prop-dict' unsupported");3479  // Otherwise, this is a top-level context.3480 3481  if (hasPropDict)3482    return emitError(loc, "'prop-dict' directive has already been seen");3483  hasPropDict = true;3484 3485  return create<PropDictDirective>();3486}3487 3488LogicalResult OpFormatParser::verifyCustomDirectiveArguments(3489    SMLoc loc, ArrayRef<FormatElement *> arguments) {3490  for (FormatElement *argument : arguments) {3491    if (!isa<AttrDictDirective, PropDictDirective, AttributeVariable,3492             OperandVariable, PropertyVariable, RefDirective, RegionVariable,3493             SuccessorVariable, StringElement, TypeDirective>(argument)) {3494      // TODO: FormatElement should have location info attached.3495      return emitError(loc, "only variables and types may be used as "3496                            "parameters to a custom directive");3497    }3498    if (auto *type = dyn_cast<TypeDirective>(argument)) {3499      if (!isa<OperandVariable, ResultVariable>(type->getArg())) {3500        return emitError(loc, "type directives within a custom directive may "3501                              "only refer to variables");3502      }3503    }3504  }3505  return success();3506}3507 3508FailureOr<FormatElement *>3509OpFormatParser::parseFunctionalTypeDirective(SMLoc loc, Context context) {3510  if (context != TopLevelContext)3511    return emitError(3512        loc, "'functional-type' is only valid as a top-level directive");3513 3514  // Parse the main operand.3515  FailureOr<FormatElement *> inputs, results;3516  if (failed(parseToken(FormatToken::l_paren,3517                        "expected '(' before argument list")) ||3518      failed(inputs = parseTypeDirectiveOperand(loc)) ||3519      failed(parseToken(FormatToken::comma,3520                        "expected ',' after inputs argument")) ||3521      failed(results = parseTypeDirectiveOperand(loc)) ||3522      failed(3523          parseToken(FormatToken::r_paren, "expected ')' after argument list")))3524    return failure();3525  return create<FunctionalTypeDirective>(*inputs, *results);3526}3527 3528FailureOr<FormatElement *>3529OpFormatParser::parseOperandsDirective(SMLoc loc, Context context) {3530  if (context == RefDirectiveContext) {3531    if (!fmt.allOperands)3532      return emitError(loc, "'ref' of 'operands' is not bound by a prior "3533                            "'operands' directive");3534 3535  } else if (context == TopLevelContext || context == CustomDirectiveContext) {3536    if (fmt.allOperands || !seenOperands.empty())3537      return emitError(loc, "'operands' directive creates overlap in format");3538    fmt.allOperands = true;3539  }3540  return create<OperandsDirective>();3541}3542 3543FailureOr<FormatElement *>3544OpFormatParser::parseRegionsDirective(SMLoc loc, Context context) {3545  if (context == TypeDirectiveContext)3546    return emitError(loc, "'regions' is only valid as a top-level directive");3547  if (context == RefDirectiveContext) {3548    if (!hasAllRegions)3549      return emitError(loc, "'ref' of 'regions' is not bound by a prior "3550                            "'regions' directive");3551 3552    // Otherwise, this is a TopLevel directive.3553  } else {3554    if (hasAllRegions || !seenRegions.empty())3555      return emitError(loc, "'regions' directive creates overlap in format");3556    hasAllRegions = true;3557  }3558  return create<RegionsDirective>();3559}3560 3561FailureOr<FormatElement *>3562OpFormatParser::parseResultsDirective(SMLoc loc, Context context) {3563  if (context != TypeDirectiveContext)3564    return emitError(loc, "'results' directive can can only be used as a child "3565                          "to a 'type' directive");3566  return create<ResultsDirective>();3567}3568 3569FailureOr<FormatElement *>3570OpFormatParser::parseSuccessorsDirective(SMLoc loc, Context context) {3571  if (context == TypeDirectiveContext)3572    return emitError(loc,3573                     "'successors' is only valid as a top-level directive");3574  if (context == RefDirectiveContext) {3575    if (!hasAllSuccessors)3576      return emitError(loc, "'ref' of 'successors' is not bound by a prior "3577                            "'successors' directive");3578 3579    // Otherwise, this is a TopLevel directive.3580  } else {3581    if (hasAllSuccessors || !seenSuccessors.empty())3582      return emitError(loc, "'successors' directive creates overlap in format");3583    hasAllSuccessors = true;3584  }3585  return create<SuccessorsDirective>();3586}3587 3588FailureOr<FormatElement *>3589OpFormatParser::parseOIListDirective(SMLoc loc, Context context) {3590  if (failed(parseToken(FormatToken::l_paren,3591                        "expected '(' before oilist argument list")))3592    return failure();3593  std::vector<FormatElement *> literalElements;3594  std::vector<std::vector<FormatElement *>> parsingElements;3595  do {3596    FailureOr<FormatElement *> lelement = parseLiteral(context);3597    if (failed(lelement))3598      return failure();3599    literalElements.push_back(*lelement);3600    parsingElements.emplace_back();3601    std::vector<FormatElement *> &currParsingElements = parsingElements.back();3602    while (peekToken().getKind() != FormatToken::pipe &&3603           peekToken().getKind() != FormatToken::r_paren) {3604      FailureOr<FormatElement *> pelement = parseElement(context);3605      if (failed(pelement) ||3606          failed(verifyOIListParsingElement(*pelement, loc)))3607        return failure();3608      currParsingElements.push_back(*pelement);3609    }3610    if (peekToken().getKind() == FormatToken::pipe) {3611      consumeToken();3612      continue;3613    }3614    if (peekToken().getKind() == FormatToken::r_paren) {3615      consumeToken();3616      break;3617    }3618  } while (true);3619 3620  return create<OIListElement>(std::move(literalElements),3621                               std::move(parsingElements));3622}3623 3624LogicalResult OpFormatParser::verifyOIListParsingElement(FormatElement *element,3625                                                         SMLoc loc) {3626  SmallVector<VariableElement *> vars;3627  collect(element, vars);3628  for (VariableElement *elem : vars) {3629    LogicalResult res =3630        TypeSwitch<FormatElement *, LogicalResult>(elem)3631            // Only optional attributes can be within an oilist parsing group.3632            .Case([&](AttributeVariable *attrEle) {3633              if (!attrEle->getVar()->attr.isOptional() &&3634                  !attrEle->getVar()->attr.hasDefaultValue())3635                return emitError(loc, "only optional attributes can be used in "3636                                      "an oilist parsing group");3637              return success();3638            })3639            // Only optional properties can be within an oilist parsing group.3640            .Case([&](PropertyVariable *propEle) {3641              if (!propEle->getVar()->prop.hasDefaultValue())3642                return emitError(3643                    loc,3644                    "only default-valued or optional properties can be used in "3645                    "an olist parsing group");3646              return success();3647            })3648            // Only optional-like(i.e. variadic) operands can be within an3649            // oilist parsing group.3650            .Case([&](OperandVariable *ele) {3651              if (!ele->getVar()->isVariableLength())3652                return emitError(loc, "only variable length operands can be "3653                                      "used within an oilist parsing group");3654              return success();3655            })3656            // Only optional-like(i.e. variadic) results can be within an oilist3657            // parsing group.3658            .Case([&](ResultVariable *ele) {3659              if (!ele->getVar()->isVariableLength())3660                return emitError(loc, "only variable length results can be "3661                                      "used within an oilist parsing group");3662              return success();3663            })3664            .Case([&](RegionVariable *) { return success(); })3665            .Default([&](FormatElement *) {3666              return emitError(loc,3667                               "only literals, types, and variables can be "3668                               "used within an oilist group");3669            });3670    if (failed(res))3671      return failure();3672  }3673  return success();3674}3675 3676FailureOr<FormatElement *> OpFormatParser::parseTypeDirective(SMLoc loc,3677                                                              Context context) {3678  if (context == TypeDirectiveContext)3679    return emitError(loc, "'type' cannot be used as a child of another `type`");3680 3681  bool isRefChild = context == RefDirectiveContext;3682  FailureOr<FormatElement *> operand;3683  if (failed(parseToken(FormatToken::l_paren,3684                        "expected '(' before argument list")) ||3685      failed(operand = parseTypeDirectiveOperand(loc, isRefChild)) ||3686      failed(3687          parseToken(FormatToken::r_paren, "expected ')' after argument list")))3688    return failure();3689 3690  return create<TypeDirective>(*operand);3691}3692 3693LogicalResult OpFormatParser::markQualified(SMLoc loc, FormatElement *element) {3694  return TypeSwitch<FormatElement *, LogicalResult>(element)3695      .Case<AttributeVariable, TypeDirective>([](auto *element) {3696        element->setShouldBeQualified();3697        return success();3698      })3699      .Default([&](auto *element) {3700        return this->emitError(3701            loc,3702            "'qualified' directive expects an attribute or a `type` directive");3703      });3704}3705 3706FailureOr<FormatElement *>3707OpFormatParser::parseTypeDirectiveOperand(SMLoc loc, bool isRefChild) {3708  FailureOr<FormatElement *> result = parseElement(TypeDirectiveContext);3709  if (failed(result))3710    return failure();3711 3712  FormatElement *element = *result;3713  if (isa<LiteralElement>(element))3714    return emitError(3715        loc, "'type' directive operand expects variable or directive operand");3716 3717  if (auto *var = dyn_cast<OperandVariable>(element)) {3718    unsigned opIdx = var->getVar() - op.operand_begin();3719    if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.test(opIdx)))3720      return emitError(loc, "'type' of '" + var->getVar()->name +3721                                "' is already bound");3722    if (isRefChild && !(fmt.allOperandTypes || seenOperandTypes.test(opIdx)))3723      return emitError(loc, "'ref' of 'type($" + var->getVar()->name +3724                                ")' is not bound by a prior 'type' directive");3725    seenOperandTypes.set(opIdx);3726  } else if (auto *var = dyn_cast<ResultVariable>(element)) {3727    unsigned resIdx = var->getVar() - op.result_begin();3728    if (!isRefChild && (fmt.allResultTypes || seenResultTypes.test(resIdx)))3729      return emitError(loc, "'type' of '" + var->getVar()->name +3730                                "' is already bound");3731    if (isRefChild && !(fmt.allResultTypes || seenResultTypes.test(resIdx)))3732      return emitError(loc, "'ref' of 'type($" + var->getVar()->name +3733                                ")' is not bound by a prior 'type' directive");3734    seenResultTypes.set(resIdx);3735  } else if (isa<OperandsDirective>(&*element)) {3736    if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.any()))3737      return emitError(loc, "'operands' 'type' is already bound");3738    if (isRefChild && !fmt.allOperandTypes)3739      return emitError(loc, "'ref' of 'type(operands)' is not bound by a prior "3740                            "'type' directive");3741    fmt.allOperandTypes = true;3742  } else if (isa<ResultsDirective>(&*element)) {3743    if (!isRefChild && (fmt.allResultTypes || seenResultTypes.any()))3744      return emitError(loc, "'results' 'type' is already bound");3745    if (isRefChild && !fmt.allResultTypes)3746      return emitError(loc, "'ref' of 'type(results)' is not bound by a prior "3747                            "'type' directive");3748    fmt.allResultTypes = true;3749  } else {3750    return emitError(loc, "invalid argument to 'type' directive");3751  }3752  return element;3753}3754 3755LogicalResult OpFormatParser::verifyOptionalGroupElements(3756    SMLoc loc, ArrayRef<FormatElement *> elements, FormatElement *anchor) {3757  for (FormatElement *element : elements) {3758    if (failed(verifyOptionalGroupElement(loc, element, element == anchor)))3759      return failure();3760  }3761  return success();3762}3763 3764LogicalResult OpFormatParser::verifyOptionalGroupElement(SMLoc loc,3765                                                         FormatElement *element,3766                                                         bool isAnchor) {3767  return TypeSwitch<FormatElement *, LogicalResult>(element)3768      // All attributes can be within the optional group, but only optional3769      // attributes can be the anchor.3770      .Case([&](AttributeVariable *attrEle) {3771        Attribute attr = attrEle->getVar()->attr;3772        if (isAnchor && !(attr.isOptional() || attr.hasDefaultValue()))3773          return emitError(loc, "only optional or default-valued attributes "3774                                "can be used to anchor an optional group");3775        return success();3776      })3777      // All properties can be within the optional group, but only optional3778      // properties can be the anchor.3779      .Case([&](PropertyVariable *propEle) {3780        Property prop = propEle->getVar()->prop;3781        if (isAnchor && !(prop.hasDefaultValue() && prop.hasOptionalParser()))3782          return emitError(loc, "only properties with default values "3783                                "that can be optionally parsed (have the `let "3784                                "optionalParser = ...` field defined) "3785                                "can be used to anchor an optional group");3786        return success();3787      })3788      // Only optional-like(i.e. variadic) operands can be within an optional3789      // group.3790      .Case([&](OperandVariable *ele) {3791        if (!ele->getVar()->isVariableLength())3792          return emitError(loc, "only variable length operands can be used "3793                                "within an optional group");3794        return success();3795      })3796      // Only optional-like(i.e. variadic) results can be within an optional3797      // group.3798      .Case([&](ResultVariable *ele) {3799        if (!ele->getVar()->isVariableLength())3800          return emitError(loc, "only variable length results can be used "3801                                "within an optional group");3802        return success();3803      })3804      .Case([&](RegionVariable *) {3805        // TODO: When ODS has proper support for marking "optional" regions, add3806        // a check here.3807        return success();3808      })3809      .Case([&](TypeDirective *ele) {3810        return verifyOptionalGroupElement(loc, ele->getArg(),3811                                          /*isAnchor=*/false);3812      })3813      .Case([&](FunctionalTypeDirective *ele) {3814        if (failed(verifyOptionalGroupElement(loc, ele->getInputs(),3815                                              /*isAnchor=*/false)))3816          return failure();3817        return verifyOptionalGroupElement(loc, ele->getResults(),3818                                          /*isAnchor=*/false);3819      })3820      .Case([&](CustomDirective *ele) {3821        if (!isAnchor)3822          return success();3823        // Verify each child as being valid in an optional group. They are all3824        // potential anchors if the custom directive was marked as one.3825        for (FormatElement *child : ele->getElements()) {3826          if (isa<RefDirective>(child))3827            continue;3828          if (failed(verifyOptionalGroupElement(loc, child, /*isAnchor=*/true)))3829            return failure();3830        }3831        return success();3832      })3833      // Literals, whitespace, and custom directives may be used, but they can't3834      // anchor the group.3835      .Case<LiteralElement, WhitespaceElement, OptionalElement>(3836          [&](FormatElement *) {3837            if (isAnchor)3838              return emitError(loc, "only variables and types can be used "3839                                    "to anchor an optional group");3840            return success();3841          })3842      .Default([&](FormatElement *) {3843        return emitError(loc, "only literals, types, and variables can be "3844                              "used within an optional group");3845      });3846}3847 3848//===----------------------------------------------------------------------===//3849// Interface3850//===----------------------------------------------------------------------===//3851 3852void mlir::tblgen::generateOpFormat(const Operator &constOp, OpClass &opClass,3853                                    bool hasProperties) {3854  // TODO: Operator doesn't expose all necessary functionality via3855  // the const interface.3856  Operator &op = const_cast<Operator &>(constOp);3857  if (!op.hasAssemblyFormat()) {3858    // We still need to generate the parsed attribute properties setter for3859    // allowing it to be reused in custom assembly implementations.3860    OperationFormat format(op, hasProperties);3861    format.hasPropDict = true;3862    genParsedAttrPropertiesSetter(format, op, opClass);3863    return;3864  }3865 3866  // Parse the format description.3867  llvm::SourceMgr mgr;3868  mgr.AddNewSourceBuffer(3869      llvm::MemoryBuffer::getMemBuffer(op.getAssemblyFormat()), SMLoc());3870  OperationFormat format(op, hasProperties);3871  OpFormatParser parser(mgr, format, op);3872  FailureOr<std::vector<FormatElement *>> elements = parser.parse();3873  if (failed(elements)) {3874    // Exit the process if format errors are treated as fatal.3875    if (formatErrorIsFatal) {3876      // Invoke the interrupt handlers to run the file cleanup handlers.3877      llvm::sys::RunInterruptHandlers();3878      std::exit(1);3879    }3880    return;3881  }3882  format.elements = std::move(*elements);3883 3884  // Generate the printer and parser based on the parsed format.3885  format.genParser(op, opClass);3886  format.genPrinter(op, opClass);3887}3888