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 ®ion : {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 ®ion : {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 ®ion : 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 ®ionName, 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 ®ionListName,2251 MethodBody &body,2252 bool hasImplicitTermTrait) {2253 body << " llvm::interleaveComma(" << regionListName2254 << ", _odsPrinter, [&](::mlir::Region ®ion) {\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 ®ion = op.getRegion(i);3136 if (!seenRegions.count(®ion)) {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