//===- OpImplementation.h - Classes for implementing Op types ---*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This classes used by the implementation details of Op types. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_OPIMPLEMENTATION_H #define MLIR_IR_OPIMPLEMENTATION_H #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/DialectInterface.h" #include "mlir/IR/OpAsmSupport.h" #include "mlir/IR/OpDefinition.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/SMLoc.h" #include namespace { // reference https://stackoverflow.com/a/16000226 template struct HasStaticName : std::false_type {}; template struct HasStaticName>::value, void>::type> : std::true_type {}; } // namespace namespace mlir { class AsmParsedResourceEntry; class AsmResourceBuilder; class Builder; //===----------------------------------------------------------------------===// // AsmDialectResourceHandle //===----------------------------------------------------------------------===// /// This class represents an opaque handle to a dialect resource entry. class AsmDialectResourceHandle { public: AsmDialectResourceHandle() = default; AsmDialectResourceHandle(void *resource, TypeID resourceID, Dialect *dialect) : resource(resource), opaqueID(resourceID), dialect(dialect) {} bool operator==(const AsmDialectResourceHandle &other) const { return resource == other.resource; } /// Return an opaque pointer to the referenced resource. void *getResource() const { return resource; } /// Return the type ID of the resource. TypeID getTypeID() const { return opaqueID; } /// Return the dialect that owns the resource. Dialect *getDialect() const { return dialect; } private: /// The opaque handle to the dialect resource. void *resource = nullptr; /// The type of the resource referenced. TypeID opaqueID; /// The dialect owning the given resource. Dialect *dialect; }; /// This class represents a CRTP base class for dialect resource handles. It /// abstracts away various utilities necessary for defined derived resource /// handles. template class AsmDialectResourceHandleBase : public AsmDialectResourceHandle { public: using Dialect = DialectT; /// Construct a handle from a pointer to the resource. The given pointer /// should be guaranteed to live beyond the life of this handle. AsmDialectResourceHandleBase(ResourceT *resource, DialectT *dialect) : AsmDialectResourceHandle(resource, TypeID::get(), dialect) {} AsmDialectResourceHandleBase(AsmDialectResourceHandle handle) : AsmDialectResourceHandle(handle) { assert(handle.getTypeID() == TypeID::get()); } /// Return the resource referenced by this handle. ResourceT *getResource() { return static_cast(AsmDialectResourceHandle::getResource()); } const ResourceT *getResource() const { return const_cast(this)->getResource(); } /// Return the dialect that owns the resource. DialectT *getDialect() const { return static_cast(AsmDialectResourceHandle::getDialect()); } /// Support llvm style casting. static bool classof(const AsmDialectResourceHandle *handle) { return handle->getTypeID() == TypeID::get(); } }; inline llvm::hash_code hash_value(const AsmDialectResourceHandle ¶m) { return llvm::hash_value(param.getResource()); } //===----------------------------------------------------------------------===// // AsmPrinter //===----------------------------------------------------------------------===// /// This base class exposes generic asm printer hooks, usable across the various /// derived printers. class AsmPrinter { public: /// This class contains the internal default implementation of the base /// printer methods. class Impl; /// Initialize the printer with the given internal implementation. AsmPrinter(Impl &impl) : impl(&impl) {} virtual ~AsmPrinter(); /// Return the raw output stream used by this printer. virtual raw_ostream &getStream() const; /// Print a newline and indent the printer to the start of the current /// operation/attribute/type. /// Note: For attributes and types this method should only be used in /// custom dialects. Usage in upstream MLIR dialects is currently disallowed. virtual void printNewline(); /// Increase indentation. virtual void increaseIndent(); /// Decrease indentation. virtual void decreaseIndent(); /// Print the given floating point value in a stabilized form that can be /// roundtripped through the IR. This is the companion to the 'parseFloat' /// hook on the AsmParser. virtual void printFloat(const APFloat &value); /// Print the given integer value. This is useful to force a uint8_t/int8_t to /// be printed as an integer instead of a char. template >> void printInteger(IntT value) { // Handle int8_t/uint8_t specially to avoid printing as char if constexpr (std::is_same_v || std::is_same_v) { getStream() << static_cast(value); } else { getStream() << value; } } virtual void printType(Type type); virtual void printAttribute(Attribute attr); /// Trait to check if `AttrType` provides a `print` method. template using has_print_method = decltype(std::declval().print(std::declval())); template using detect_has_print_method = llvm::is_detected; /// Print the provided attribute in the context of an operation custom /// printer/parser: this will invoke directly the print method on the /// attribute class and skip the `#dialect.mnemonic` prefix in most cases. template ::value> *sfinae = nullptr> void printStrippedAttrOrType(AttrOrType attrOrType) { if (succeeded(printAlias(attrOrType))) return; raw_ostream &os = getStream(); uint64_t posPrior = os.tell(); attrOrType.print(*this); if (posPrior != os.tell()) return; // Fallback to printing with prefix if the above failed to write anything // to the output stream. *this << attrOrType; } /// Print the provided array of attributes or types in the context of an /// operation custom printer/parser: this will invoke directly the print /// method on the attribute class and skip the `#dialect.mnemonic` prefix in /// most cases. template ::value> *sfinae = nullptr> void printStrippedAttrOrType(ArrayRef attrOrTypes) { llvm::interleaveComma( attrOrTypes, getStream(), [this](AttrOrType attrOrType) { printStrippedAttrOrType(attrOrType); }); } /// SFINAE for printing the provided attribute in the context of an operation /// custom printer in the case where the attribute does not define a print /// method. template ::value> *sfinae = nullptr> void printStrippedAttrOrType(AttrOrType attrOrType) { *this << attrOrType; } /// Print the given attribute without its type. The corresponding parser must /// provide a valid type for the attribute. virtual void printAttributeWithoutType(Attribute attr); /// Print the given named attribute. virtual void printNamedAttribute(NamedAttribute attr); /// Print the alias for the given attribute, return failure if no alias could /// be printed. virtual LogicalResult printAlias(Attribute attr); /// Print the alias for the given type, return failure if no alias could /// be printed. virtual LogicalResult printAlias(Type type); /// Print the given string as a keyword, or a quoted and escaped string if it /// has any special or non-printable characters in it. virtual void printKeywordOrString(StringRef keyword); /// Print the given string as a quoted string, escaping any special or /// non-printable characters in it. virtual void printString(StringRef string); /// Print the given string as a symbol reference, i.e. a form representable by /// a SymbolRefAttr. A symbol reference is represented as a string prefixed /// with '@'. The reference is surrounded with ""'s and escaped if it has any /// special or non-printable characters in it. virtual void printSymbolName(StringRef symbolRef); /// Print a handle to the given dialect resource. The handle key is quoted and /// escaped if it has any special or non-printable characters in it. virtual void printResourceHandle(const AsmDialectResourceHandle &resource); /// Print an optional arrow followed by a type list. template void printOptionalArrowTypeList(TypeRange &&types) { if (types.begin() != types.end()) printArrowTypeList(types); } template void printArrowTypeList(TypeRange &&types) { auto &os = getStream() << " -> "; bool wrapped = !llvm::hasSingleElement(types) || llvm::isa((*types.begin())); if (wrapped) os << '('; llvm::interleaveComma(types, *this); if (wrapped) os << ')'; } /// Print the two given type ranges in a functional form. template void printFunctionalType(InputRangeT &&inputs, ResultRangeT &&results) { auto &os = getStream(); os << '('; llvm::interleaveComma(inputs, *this); os << ')'; printArrowTypeList(results); } void printDimensionList(ArrayRef shape); /// Class used to automatically end a cyclic region on destruction. class CyclicPrintReset { public: explicit CyclicPrintReset(AsmPrinter *printer) : printer(printer) {} ~CyclicPrintReset() { if (printer) printer->popCyclicPrinting(); } CyclicPrintReset(const CyclicPrintReset &) = delete; CyclicPrintReset &operator=(const CyclicPrintReset &) = delete; CyclicPrintReset(CyclicPrintReset &&rhs) : printer(std::exchange(rhs.printer, nullptr)) {} CyclicPrintReset &operator=(CyclicPrintReset &&rhs) { printer = std::exchange(rhs.printer, nullptr); return *this; } private: AsmPrinter *printer; }; /// Attempts to start a cyclic printing region for `attrOrType`. /// A cyclic printing region starts with this call and ends with the /// destruction of the returned `CyclicPrintReset`. During this time, /// calling `tryStartCyclicPrint` with the same attribute in any printer /// will lead to returning failure. /// /// This makes it possible to break infinite recursions when trying to print /// cyclic attributes or types by printing only immutable parameters if nested /// within itself. template FailureOr tryStartCyclicPrint(AttrOrTypeT attrOrType) { static_assert( std::is_base_of_v, AttrOrTypeT> || std::is_base_of_v, AttrOrTypeT>, "Only mutable attributes or types can be cyclic"); if (failed(pushCyclicPrinting(attrOrType.getAsOpaquePointer()))) return failure(); return CyclicPrintReset(this); } protected: /// Initialize the printer with no internal implementation. In this case, all /// virtual methods of this class must be overriden. AsmPrinter() = default; /// Pushes a new attribute or type in the form of a type erased pointer /// into an internal set. /// Returns success if the type or attribute was inserted in the set or /// failure if it was already contained. virtual LogicalResult pushCyclicPrinting(const void *opaquePointer); /// Removes the element that was last inserted with a successful call to /// `pushCyclicPrinting`. There must be exactly one `popCyclicPrinting` call /// in reverse order of all successful `pushCyclicPrinting`. virtual void popCyclicPrinting(); private: AsmPrinter(const AsmPrinter &) = delete; void operator=(const AsmPrinter &) = delete; /// The internal implementation of the printer. Impl *impl{nullptr}; }; template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, Type type) { p.printType(type); return p; } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, Attribute attr) { p.printAttribute(attr); return p; } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, const APFloat &value) { p.printFloat(value); return p; } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, float value) { return p << APFloat(value); } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, double value) { return p << APFloat(value); } // Support printing anything that isn't convertible to one of the other // streamable types, even if it isn't exactly one of them. For example, we want // to print FunctionType with the Type version above, not have it match this. template ::value && !std::is_convertible::value && !std::is_convertible::value && !std::is_convertible::value && !std::is_convertible::value && !llvm::is_one_of::value, T> * = nullptr, typename = std::enable_if_t::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, const T &other) { p.getStream() << other; return p; } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, bool value) { return p << (value ? StringRef("true") : "false"); } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, const ValueTypeRange &types) { llvm::interleaveComma(types, p); return p; } template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, const TypeRange &types) { llvm::interleaveComma(types, p); return p; } // Prevent matching the TypeRange version above for ValueRange // printing through base AsmPrinter. This is needed so that the // ValueRange printing behaviour does not change from printing // the SSA values to printing the types for the operands when // using AsmPrinter instead of OpAsmPrinter. template < typename AsmPrinterT, typename T, typename = std::enable_if_t::value && std::is_convertible::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, const T &other) = delete; template ::value>> inline AsmPrinterT &operator<<(AsmPrinterT &p, ArrayRef types) { llvm::interleaveComma(types, p); return p; } //===----------------------------------------------------------------------===// // OpAsmPrinter //===----------------------------------------------------------------------===// /// This is a pure-virtual base class that exposes the asmprinter hooks /// necessary to implement a custom print() method. class OpAsmPrinter : public AsmPrinter { public: using AsmPrinter::AsmPrinter; ~OpAsmPrinter() override; /// Print a loc(...) specifier if printing debug info is enabled. virtual void printOptionalLocationSpecifier(Location loc) = 0; /// Print a block argument in the usual format of: /// %ssaName : type {attr1=42} loc("here") /// where location printing is controlled by the standard internal option. /// You may pass omitType=true to not print a type, and pass an empty /// attribute list if you don't care for attributes. virtual void printRegionArgument(BlockArgument arg, ArrayRef argAttrs = {}, bool omitType = false) = 0; /// Print implementations for various things an operation contains. virtual void printOperand(Value value) = 0; virtual void printOperand(Value value, raw_ostream &os) = 0; /// Print a comma separated list of operands. template void printOperands(const ContainerType &container) { printOperands(container.begin(), container.end()); } /// Print a comma separated list of operands. template void printOperands(IteratorType it, IteratorType end) { llvm::interleaveComma(llvm::make_range(it, end), getStream(), [this](Value value) { printOperand(value); }); } /// Print the given successor. virtual void printSuccessor(Block *successor) = 0; /// Print the successor and its operands. virtual void printSuccessorAndUseList(Block *successor, ValueRange succOperands) = 0; /// If the specified operation has attributes, print out an attribute /// dictionary with their values. elidedAttrs allows the client to ignore /// specific well known attributes, commonly used if the attribute value is /// printed some other way (like as a fixed operand). virtual void printOptionalAttrDict(ArrayRef attrs, ArrayRef elidedAttrs = {}) = 0; /// If the specified operation has attributes, print out an attribute /// dictionary prefixed with 'attributes'. virtual void printOptionalAttrDictWithKeyword(ArrayRef attrs, ArrayRef elidedAttrs = {}) = 0; /// Prints the entire operation with the custom assembly form, if available, /// or the generic assembly form, otherwise. virtual void printCustomOrGenericOp(Operation *op) = 0; /// Print the entire operation with the default generic assembly form. /// If `printOpName` is true, then the operation name is printed (the default) /// otherwise it is omitted and the print will start with the operand list. virtual void printGenericOp(Operation *op, bool printOpName = true) = 0; /// Prints a region. /// If 'printEntryBlockArgs' is false, the arguments of the /// block are not printed. If 'printBlockTerminator' is false, the terminator /// operation of the block is not printed. If printEmptyBlock is true, then /// the block header is printed even if the block is empty. virtual void printRegion(Region &blocks, bool printEntryBlockArgs = true, bool printBlockTerminators = true, bool printEmptyBlock = false) = 0; /// Renumber the arguments for the specified region to the same names as the /// SSA values in namesToUse. This may only be used for IsolatedFromAbove /// operations. If any entry in namesToUse is null, the corresponding /// argument name is left alone. virtual void shadowRegionArgs(Region ®ion, ValueRange namesToUse) = 0; /// Prints an affine map of SSA ids, where SSA id names are used in place /// of dims/symbols. /// Operand values must come from single-result sources, and be valid /// dimensions/symbol identifiers according to mlir::isValidDim/Symbol. virtual void printAffineMapOfSSAIds(AffineMapAttr mapAttr, ValueRange operands) = 0; /// Prints an affine expression of SSA ids with SSA id names used instead of /// dims and symbols. /// Operand values must come from single-result sources, and be valid /// dimensions/symbol identifiers according to mlir::isValidDim/Symbol. virtual void printAffineExprOfSSAIds(AffineExpr expr, ValueRange dimOperands, ValueRange symOperands) = 0; /// Print the complete type of an operation in functional form. void printFunctionalType(Operation *op); using AsmPrinter::printFunctionalType; }; // Make the implementations convenient to use. inline OpAsmPrinter &operator<<(OpAsmPrinter &p, Value value) { p.printOperand(value); return p; } template ::value && !std::is_convertible::value, T> * = nullptr> inline OpAsmPrinter &operator<<(OpAsmPrinter &p, const T &values) { p.printOperands(values); return p; } inline OpAsmPrinter &operator<<(OpAsmPrinter &p, Block *value) { p.printSuccessor(value); return p; } //===----------------------------------------------------------------------===// // AsmParser //===----------------------------------------------------------------------===// /// This base class exposes generic asm parser hooks, usable across the various /// derived parsers. class AsmParser { public: AsmParser() = default; virtual ~AsmParser(); MLIRContext *getContext() const; /// Return the location of the original name token. virtual SMLoc getNameLoc() const = 0; //===--------------------------------------------------------------------===// // Utilities //===--------------------------------------------------------------------===// /// Emit a diagnostic at the specified location and return failure. virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message = {}) = 0; /// Return a builder which provides useful access to MLIRContext, global /// objects like types and attributes. virtual Builder &getBuilder() const = 0; /// Get the location of the next token and store it into the argument. This /// always succeeds. virtual SMLoc getCurrentLocation() = 0; ParseResult getCurrentLocation(SMLoc *loc) { *loc = getCurrentLocation(); return success(); } /// Re-encode the given source location as an MLIR location and return it. /// Note: This method should only be used when a `Location` is necessary, as /// the encoding process is not efficient. virtual Location getEncodedSourceLoc(SMLoc loc) = 0; //===--------------------------------------------------------------------===// // Token Parsing //===--------------------------------------------------------------------===// /// Parse a '->' token. virtual ParseResult parseArrow() = 0; /// Parse a '->' token if present virtual ParseResult parseOptionalArrow() = 0; /// Parse a `{` token. virtual ParseResult parseLBrace() = 0; /// Parse a `{` token if present. virtual ParseResult parseOptionalLBrace() = 0; /// Parse a `}` token. virtual ParseResult parseRBrace() = 0; /// Parse a `}` token if present. virtual ParseResult parseOptionalRBrace() = 0; /// Parse a `:` token. virtual ParseResult parseColon() = 0; /// Parse a `:` token if present. virtual ParseResult parseOptionalColon() = 0; /// Parse a `,` token. virtual ParseResult parseComma() = 0; /// Parse a `,` token if present. virtual ParseResult parseOptionalComma() = 0; /// Parse a `=` token. virtual ParseResult parseEqual() = 0; /// Parse a `=` token if present. virtual ParseResult parseOptionalEqual() = 0; /// Parse a '<' token. virtual ParseResult parseLess() = 0; /// Parse a '<' token if present. virtual ParseResult parseOptionalLess() = 0; /// Parse a '>' token. virtual ParseResult parseGreater() = 0; /// Parse a '>' token if present. virtual ParseResult parseOptionalGreater() = 0; /// Parse a '?' token. virtual ParseResult parseQuestion() = 0; /// Parse a '?' token if present. virtual ParseResult parseOptionalQuestion() = 0; /// Parse a '+' token. virtual ParseResult parsePlus() = 0; /// Parse a '+' token if present. virtual ParseResult parseOptionalPlus() = 0; /// Parse a '/' token. virtual ParseResult parseSlash() = 0; /// Parse a '/' token if present. virtual ParseResult parseOptionalSlash() = 0; /// Parse a '-' token. virtual ParseResult parseMinus() = 0; /// Parse a '-' token if present. virtual ParseResult parseOptionalMinus() = 0; /// Parse a '*' token. virtual ParseResult parseStar() = 0; /// Parse a '*' token if present. virtual ParseResult parseOptionalStar() = 0; /// Parse a '|' token. virtual ParseResult parseVerticalBar() = 0; /// Parse a '|' token if present. virtual ParseResult parseOptionalVerticalBar() = 0; /// Parse a quoted string token. ParseResult parseString(std::string *string) { auto loc = getCurrentLocation(); if (parseOptionalString(string)) return emitError(loc, "expected string"); return success(); } /// Parse a quoted string token if present. virtual ParseResult parseOptionalString(std::string *string) = 0; /// Parses a Base64 encoded string of bytes. virtual ParseResult parseBase64Bytes(std::vector *bytes) = 0; /// Parse a `(` token. virtual ParseResult parseLParen() = 0; /// Parse a `(` token if present. virtual ParseResult parseOptionalLParen() = 0; /// Parse a `)` token. virtual ParseResult parseRParen() = 0; /// Parse a `)` token if present. virtual ParseResult parseOptionalRParen() = 0; /// Parse a `[` token. virtual ParseResult parseLSquare() = 0; /// Parse a `[` token if present. virtual ParseResult parseOptionalLSquare() = 0; /// Parse a `]` token. virtual ParseResult parseRSquare() = 0; /// Parse a `]` token if present. virtual ParseResult parseOptionalRSquare() = 0; /// Parse a `...` token. virtual ParseResult parseEllipsis() = 0; /// Parse a `...` token if present; virtual ParseResult parseOptionalEllipsis() = 0; /// Parse a floating point value from the stream. virtual ParseResult parseFloat(double &result) = 0; /// Parse a floating point value into APFloat from the stream. virtual ParseResult parseFloat(const llvm::fltSemantics &semantics, APFloat &result) = 0; /// Parse an integer value from the stream. template ParseResult parseInteger(IntT &result) { auto loc = getCurrentLocation(); OptionalParseResult parseResult = parseOptionalInteger(result); if (!parseResult.has_value()) return emitError(loc, "expected integer value"); return *parseResult; } /// Parse a decimal integer value from the stream. template ParseResult parseDecimalInteger(IntT &result) { auto loc = getCurrentLocation(); OptionalParseResult parseResult = parseOptionalDecimalInteger(result); if (!parseResult.has_value()) return emitError(loc, "expected decimal integer value"); return *parseResult; } /// Parse an optional integer value from the stream. virtual OptionalParseResult parseOptionalInteger(APInt &result) = 0; virtual OptionalParseResult parseOptionalDecimalInteger(APInt &result) = 0; private: template OptionalParseResult parseOptionalIntegerAndCheck(IntT &result, ParseFn &&parseFn) { auto loc = getCurrentLocation(); APInt uintResult; OptionalParseResult parseResult = parseFn(uintResult); if (!parseResult.has_value() || failed(*parseResult)) return parseResult; // Try to convert to the provided integer type. sextOrTrunc is correct even // for unsigned types because parseOptionalInteger ensures the sign bit is // zero for non-negated integers. result = (IntT)uintResult.sextOrTrunc(sizeof(IntT) * CHAR_BIT).getLimitedValue(); if (APInt(uintResult.getBitWidth(), result, /*isSigned=*/std::is_signed_v, /*implicitTrunc=*/true) != uintResult) return emitError(loc, "integer value too large"); return success(); } public: template OptionalParseResult parseOptionalInteger(IntT &result) { return parseOptionalIntegerAndCheck( result, [&](APInt &result) { return parseOptionalInteger(result); }); } template OptionalParseResult parseOptionalDecimalInteger(IntT &result) { return parseOptionalIntegerAndCheck(result, [&](APInt &result) { return parseOptionalDecimalInteger(result); }); } /// These are the supported delimiters around operand lists and region /// argument lists, used by parseOperandList. enum class Delimiter { /// Zero or more operands with no delimiters. None, /// Parens surrounding zero or more operands. Paren, /// Square brackets surrounding zero or more operands. Square, /// <> brackets surrounding zero or more operands. LessGreater, /// {} brackets surrounding zero or more operands. Braces, /// Parens supporting zero or more operands, or nothing. OptionalParen, /// Square brackets supporting zero or more ops, or nothing. OptionalSquare, /// <> brackets supporting zero or more ops, or nothing. OptionalLessGreater, /// {} brackets surrounding zero or more operands, or nothing. OptionalBraces, }; /// Parse a list of comma-separated items with an optional delimiter. If a /// delimiter is provided, then an empty list is allowed. If not, then at /// least one element will be parsed. /// /// contextMessage is an optional message appended to "expected '('" sorts of /// diagnostics when parsing the delimeters. virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref parseElementFn, StringRef contextMessage = StringRef()) = 0; /// Parse a comma separated list of elements that must have at least one entry /// in it. ParseResult parseCommaSeparatedList(function_ref parseElementFn) { return parseCommaSeparatedList(Delimiter::None, parseElementFn); } //===--------------------------------------------------------------------===// // Keyword Parsing //===--------------------------------------------------------------------===// /// This class represents a StringSwitch like class that is useful for parsing /// expected keywords. On construction, unless a non-empty keyword is /// provided, it invokes `parseKeyword` and processes each of the provided /// cases statements until a match is hit. The provided `ResultT` must be /// assignable from `failure()`. template class KeywordSwitch { public: KeywordSwitch(AsmParser &parser, StringRef *keyword = nullptr) : parser(parser), loc(parser.getCurrentLocation()) { if (keyword && !keyword->empty()) this->keyword = *keyword; else if (failed(parser.parseKeywordOrCompletion(&this->keyword))) result = failure(); } /// Case that uses the provided value when true. KeywordSwitch &Case(StringLiteral str, ResultT value) { return Case(str, [&](StringRef, SMLoc) { return std::move(value); }); } KeywordSwitch &Default(ResultT value) { return Default([&](StringRef, SMLoc) { return std::move(value); }); } /// Case that invokes the provided functor when true. The parameters passed /// to the functor are the keyword, and the location of the keyword (in case /// any errors need to be emitted). template ::value>> KeywordSwitch &Case(StringLiteral str, FnT &&fn) { if (result) return *this; // If the word was empty, record this as a completion. if (keyword.empty()) parser.codeCompleteExpectedTokens(str); else if (keyword == str) result.emplace(std::move(fn(keyword, loc))); return *this; } template ::value>> KeywordSwitch &Default(FnT &&fn) { if (!result) result.emplace(fn(keyword, loc)); return *this; } /// Returns true if this switch has a value yet. bool hasValue() const { return result.has_value(); } /// Return the result of the switch. [[nodiscard]] operator ResultT() { if (!result) return parser.emitError(loc, "unexpected keyword: ") << keyword; return std::move(*result); } private: /// The parser used to construct this switch. AsmParser &parser; /// The location of the keyword, used to emit errors as necessary. SMLoc loc; /// The parsed keyword itself. StringRef keyword; /// The result of the switch statement or std::nullopt if currently unknown. std::optional result; }; /// Parse a given keyword. ParseResult parseKeyword(StringRef keyword) { return parseKeyword(keyword, ""); } virtual ParseResult parseKeyword(StringRef keyword, const Twine &msg) = 0; /// Parse a keyword into 'keyword'. ParseResult parseKeyword(StringRef *keyword) { auto loc = getCurrentLocation(); if (parseOptionalKeyword(keyword)) return emitError(loc, "expected valid keyword"); return success(); } /// Parse the given keyword if present. virtual ParseResult parseOptionalKeyword(StringRef keyword) = 0; /// Parse a keyword, if present, into 'keyword'. virtual ParseResult parseOptionalKeyword(StringRef *keyword) = 0; /// Parse a keyword, if present, and if one of the 'allowedValues', /// into 'keyword' virtual ParseResult parseOptionalKeyword(StringRef *keyword, ArrayRef allowedValues) = 0; /// Parse a string into 'string' if it is present and one of the /// 'allowedValues'. virtual ParseResult parseOptionalString(std::string *string, ArrayRef allowedValues) = 0; /// Parse a keyword or a quoted string. ParseResult parseKeywordOrString(std::string *result) { if (failed(parseOptionalKeywordOrString(result))) return emitError(getCurrentLocation()) << "expected valid keyword or string"; return success(); } /// Parse an optional keyword or string. virtual ParseResult parseOptionalKeywordOrString(std::string *result) = 0; /// Parse an optional keyword or string into `result` if it is present and one /// of the 'allowedValues'. virtual ParseResult parseOptionalKeywordOrString(std::string *result, ArrayRef allowedValues) = 0; //===--------------------------------------------------------------------===// // Attribute/Type Parsing //===--------------------------------------------------------------------===// /// Invoke the `getChecked` method of the given Attribute or Type class, using /// the provided location to emit errors in the case of failure. Note that /// unlike `OpBuilder::getType`, this method does not implicitly insert a /// context parameter. template auto getChecked(SMLoc loc, ParamsT &&...params) { return T::getChecked([&] { return emitError(loc); }, std::forward(params)...); } /// A variant of `getChecked` that uses the result of `getNameLoc` to emit /// errors. template auto getChecked(ParamsT &&...params) { return T::getChecked([&] { return emitError(getNameLoc()); }, std::forward(params)...); } //===--------------------------------------------------------------------===// // Attribute Parsing //===--------------------------------------------------------------------===// /// Parse an arbitrary attribute of a given type and return it in result. virtual ParseResult parseAttribute(Attribute &result, Type type = {}) = 0; /// Parse a custom attribute with the provided callback, unless the next /// token is `#`, in which case the generic parser is invoked. virtual ParseResult parseCustomAttributeWithFallback( Attribute &result, Type type, function_ref parseAttribute) = 0; /// Parse an attribute of a specific kind and type. template ParseResult parseAttribute(AttrType &result, Type type = {}) { SMLoc loc = getCurrentLocation(); // Parse any kind of attribute. Attribute attr; if (parseAttribute(attr, type)) return failure(); // Check for the right kind of attribute. if (!(result = llvm::dyn_cast(attr))) return emitError(loc, "invalid kind of attribute specified"); return success(); } /// Parse an arbitrary attribute and return it in result. This also adds the /// attribute to the specified attribute list with the specified name. ParseResult parseAttribute(Attribute &result, StringRef attrName, NamedAttrList &attrs) { return parseAttribute(result, Type(), attrName, attrs); } /// Parse an attribute of a specific kind and type. template ParseResult parseAttribute(AttrType &result, StringRef attrName, NamedAttrList &attrs) { return parseAttribute(result, Type(), attrName, attrs); } /// Parse an arbitrary attribute of a given type and populate it in `result`. /// This also adds the attribute to the specified attribute list with the /// specified name. template ParseResult parseAttribute(AttrType &result, Type type, StringRef attrName, NamedAttrList &attrs) { SMLoc loc = getCurrentLocation(); // Parse any kind of attribute. Attribute attr; if (parseAttribute(attr, type)) return failure(); // Check for the right kind of attribute. result = llvm::dyn_cast(attr); if (!result) return emitError(loc, "invalid kind of attribute specified"); attrs.append(attrName, result); return success(); } /// Trait to check if `AttrType` provides a `parse` method. template using has_parse_method = decltype(AttrType::parse(std::declval(), std::declval())); template using detect_has_parse_method = llvm::is_detected; /// Parse a custom attribute of a given type unless the next token is `#`, in /// which case the generic parser is invoked. The parsed attribute is /// populated in `result` and also added to the specified attribute list with /// the specified name. template std::enable_if_t::value, ParseResult> parseCustomAttributeWithFallback(AttrType &result, Type type, StringRef attrName, NamedAttrList &attrs) { SMLoc loc = getCurrentLocation(); // Parse any kind of attribute. Attribute attr; if (parseCustomAttributeWithFallback( attr, type, [&](Attribute &result, Type type) -> ParseResult { result = AttrType::parse(*this, type); if (!result) return failure(); return success(); })) return failure(); // Check for the right kind of attribute. result = llvm::dyn_cast(attr); if (!result) return emitError(loc, "invalid kind of attribute specified"); attrs.append(attrName, result); return success(); } /// SFINAE parsing method for Attribute that don't implement a parse method. template std::enable_if_t::value, ParseResult> parseCustomAttributeWithFallback(AttrType &result, Type type, StringRef attrName, NamedAttrList &attrs) { return parseAttribute(result, type, attrName, attrs); } /// Parse a custom attribute of a given type unless the next token is `#`, in /// which case the generic parser is invoked. The parsed attribute is /// populated in `result`. template std::enable_if_t::value, ParseResult> parseCustomAttributeWithFallback(AttrType &result, Type type = {}) { SMLoc loc = getCurrentLocation(); // Parse any kind of attribute. Attribute attr; if (parseCustomAttributeWithFallback( attr, type, [&](Attribute &result, Type type) -> ParseResult { result = AttrType::parse(*this, type); return success(!!result); })) return failure(); // Check for the right kind of attribute. result = llvm::dyn_cast(attr); if (!result) return emitError(loc, "invalid kind of attribute specified"); return success(); } /// SFINAE parsing method for Attribute that don't implement a parse method. template std::enable_if_t::value, ParseResult> parseCustomAttributeWithFallback(AttrType &result, Type type = {}) { return parseAttribute(result, type); } /// Parse an arbitrary optional attribute of a given type and return it in /// result. virtual OptionalParseResult parseOptionalAttribute(Attribute &result, Type type = {}) = 0; /// Parse an optional array attribute and return it in result. virtual OptionalParseResult parseOptionalAttribute(ArrayAttr &result, Type type = {}) = 0; /// Parse an optional string attribute and return it in result. virtual OptionalParseResult parseOptionalAttribute(StringAttr &result, Type type = {}) = 0; /// Parse an optional symbol ref attribute and return it in result. virtual OptionalParseResult parseOptionalAttribute(SymbolRefAttr &result, Type type = {}) = 0; /// Parse an optional attribute of a specific typed result. This overload /// handles concrete attribute types (e.g. FloatAttr) that are not covered by /// a dedicated virtual overload. It parses any attribute and then validates /// that the result is of the expected type, emitting an error if not. template < typename AttrType, typename = std::enable_if_t::value>> OptionalParseResult parseOptionalAttribute(AttrType &result, Type type = {}) { llvm::SMLoc loc = getCurrentLocation(); Attribute attr; OptionalParseResult parseResult = parseOptionalAttribute(attr, type); if (!parseResult.has_value() || failed(*parseResult)) return parseResult; result = dyn_cast(attr); if (!result) return emitError(loc) << "expected attribute of type '" << AttrType::name << "', but found attribute '" << attr << "'"; return success(); } /// Parse an optional attribute of a specific type and add it to the list with /// the specified name. template OptionalParseResult parseOptionalAttribute(AttrType &result, StringRef attrName, NamedAttrList &attrs) { return parseOptionalAttribute(result, Type(), attrName, attrs); } /// Parse an optional attribute of a specific type and add it to the list with /// the specified name. template OptionalParseResult parseOptionalAttribute(AttrType &result, Type type, StringRef attrName, NamedAttrList &attrs) { OptionalParseResult parseResult = parseOptionalAttribute(result, type); if (parseResult.has_value() && succeeded(*parseResult)) attrs.append(attrName, result); return parseResult; } /// Parse a named dictionary into 'result' if it is present. virtual ParseResult parseOptionalAttrDict(NamedAttrList &result) = 0; /// Parse a named dictionary into 'result' if the `attributes` keyword is /// present. virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result) = 0; /// Parse an affine map instance into 'map'. virtual ParseResult parseAffineMap(AffineMap &map) = 0; /// Parse an affine expr instance into 'expr' using the already computed /// mapping from symbols to affine expressions in 'symbolSet'. virtual ParseResult parseAffineExpr(ArrayRef> symbolSet, AffineExpr &expr) = 0; /// Parse an integer set instance into 'set'. virtual ParseResult parseIntegerSet(IntegerSet &set) = 0; //===--------------------------------------------------------------------===// // Identifier Parsing //===--------------------------------------------------------------------===// /// Parse an @-identifier and store it (without the '@' symbol) in a string /// attribute. ParseResult parseSymbolName(StringAttr &result) { if (failed(parseOptionalSymbolName(result))) return emitError(getCurrentLocation()) << "expected valid '@'-identifier for symbol name"; return success(); } /// Parse an @-identifier and store it (without the '@' symbol) in a string /// attribute named 'attrName'. ParseResult parseSymbolName(StringAttr &result, StringRef attrName, NamedAttrList &attrs) { if (parseSymbolName(result)) return failure(); attrs.append(attrName, result); return success(); } /// Parse an optional @-identifier and store it (without the '@' symbol) in a /// string attribute. virtual ParseResult parseOptionalSymbolName(StringAttr &result) = 0; /// Parse an optional @-identifier and store it (without the '@' symbol) in a /// string attribute named 'attrName'. ParseResult parseOptionalSymbolName(StringAttr &result, StringRef attrName, NamedAttrList &attrs) { if (succeeded(parseOptionalSymbolName(result))) { attrs.append(attrName, result); return success(); } return failure(); } //===--------------------------------------------------------------------===// // Resource Parsing //===--------------------------------------------------------------------===// /// Parse a handle to a resource within the assembly format. template FailureOr parseResourceHandle() { SMLoc handleLoc = getCurrentLocation(); // Try to load the dialect that owns the handle. auto *dialect = getContext()->getOrLoadDialect(); if (!dialect) { return emitError(handleLoc) << "dialect '" << ResourceT::Dialect::getDialectNamespace() << "' is unknown"; } FailureOr handle = parseResourceHandle(dialect); if (failed(handle)) return failure(); if (auto *result = dyn_cast(&*handle)) return std::move(*result); return emitError(handleLoc) << "provided resource handle differs from the " "expected resource type"; } //===--------------------------------------------------------------------===// // Type Parsing //===--------------------------------------------------------------------===// /// Parse a type. virtual ParseResult parseType(Type &result) = 0; /// Parse a custom type with the provided callback, unless the next /// token is `#`, in which case the generic parser is invoked. virtual ParseResult parseCustomTypeWithFallback( Type &result, function_ref parseType) = 0; /// Parse an optional type. virtual OptionalParseResult parseOptionalType(Type &result) = 0; /// Parse a type of a specific type. template ParseResult parseType(TypeT &result) { SMLoc loc = getCurrentLocation(); // Parse any kind of type. Type type; if (parseType(type)) return failure(); // Check for the right kind of type. result = llvm::dyn_cast(type); if (!result) { InFlightDiagnostic diag = emitError(loc, "invalid kind of type specified"); if constexpr (HasStaticName::value) diag << ": expected " << TypeT::name << ", but found " << type; return diag; } return success(); } /// Trait to check if `TypeT` provides a `parse` method. template using type_has_parse_method = decltype(TypeT::parse(std::declval())); template using detect_type_has_parse_method = llvm::is_detected; /// Parse a custom Type of a given type unless the next token is `#`, in /// which case the generic parser is invoked. The parsed Type is /// populated in `result`. template std::enable_if_t::value, ParseResult> parseCustomTypeWithFallback(TypeT &result) { SMLoc loc = getCurrentLocation(); // Parse any kind of Type. Type type; if (parseCustomTypeWithFallback(type, [&](Type &result) -> ParseResult { result = TypeT::parse(*this); return success(!!result); })) return failure(); // Check for the right kind of Type. result = llvm::dyn_cast(type); if (!result) { InFlightDiagnostic diag = emitError(loc, "invalid kind of type specified"); if constexpr (HasStaticName::value) diag << ": expected " << TypeT::name << ", but found " << type; return diag; } return success(); } /// SFINAE parsing method for Type that don't implement a parse method. template std::enable_if_t::value, ParseResult> parseCustomTypeWithFallback(TypeT &result) { return parseType(result); } /// Parse a type list. ParseResult parseTypeList(SmallVectorImpl &result); /// Parse an arrow followed by a type list. virtual ParseResult parseArrowTypeList(SmallVectorImpl &result) = 0; /// Parse an optional arrow followed by a type list. virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl &result) = 0; /// Parse a colon followed by a type. virtual ParseResult parseColonType(Type &result) = 0; /// Parse a colon followed by a type of a specific kind, e.g. a FunctionType. template ParseResult parseColonType(TypeType &result) { SMLoc loc = getCurrentLocation(); // Parse any kind of type. Type type; if (parseColonType(type)) return failure(); // Check for the right kind of type. result = llvm::dyn_cast(type); if (!result) { InFlightDiagnostic diag = emitError(loc, "invalid kind of type specified"); if constexpr (HasStaticName::value) diag << ": expected " << TypeType::name << ", but found " << type; return diag; } return success(); } /// Parse a colon followed by a type list, which must have at least one type. virtual ParseResult parseColonTypeList(SmallVectorImpl &result) = 0; /// Parse an optional colon followed by a type list, which if present must /// have at least one type. virtual ParseResult parseOptionalColonTypeList(SmallVectorImpl &result) = 0; /// Parse a keyword followed by a type. ParseResult parseKeywordType(const char *keyword, Type &result) { return failure(parseKeyword(keyword) || parseType(result)); } /// Add the specified type to the end of the specified type list and return /// success. This is a helper designed to allow parse methods to be simple /// and chain through || operators. ParseResult addTypeToList(Type type, SmallVectorImpl &result) { result.push_back(type); return success(); } /// Add the specified types to the end of the specified type list and return /// success. This is a helper designed to allow parse methods to be simple /// and chain through || operators. ParseResult addTypesToList(ArrayRef types, SmallVectorImpl &result) { result.append(types.begin(), types.end()); return success(); } /// Parse a dimension list of a tensor or memref type. This populates the /// dimension list, using ShapedType::kDynamic for the `?` dimensions if /// `allowDynamic` is set and errors out on `?` otherwise. Parsing the /// trailing `x` is configurable. /// /// dimension-list ::= eps | dimension (`x` dimension)* /// dimension-list-with-trailing-x ::= (dimension `x`)* /// dimension ::= `?` | decimal-literal /// /// When `allowDynamic` is not set, this is used to parse: /// /// static-dimension-list ::= eps | decimal-literal (`x` decimal-literal)* /// static-dimension-list-with-trailing-x ::= (dimension `x`)* virtual ParseResult parseDimensionList(SmallVectorImpl &dimensions, bool allowDynamic = true, bool withTrailingX = true) = 0; /// Parse an 'x' token in a dimension list, handling the case where the x is /// juxtaposed with an element type, as in "xf32", leaving the "f32" as the /// next token. virtual ParseResult parseXInDimensionList() = 0; /// Class used to automatically end a cyclic region on destruction. class CyclicParseReset { public: explicit CyclicParseReset(AsmParser *parser) : parser(parser) {} ~CyclicParseReset() { if (parser) parser->popCyclicParsing(); } CyclicParseReset(const CyclicParseReset &) = delete; CyclicParseReset &operator=(const CyclicParseReset &) = delete; CyclicParseReset(CyclicParseReset &&rhs) : parser(std::exchange(rhs.parser, nullptr)) {} CyclicParseReset &operator=(CyclicParseReset &&rhs) { parser = std::exchange(rhs.parser, nullptr); return *this; } private: AsmParser *parser; }; /// Attempts to start a cyclic parsing region for `attrOrType`. /// A cyclic parsing region starts with this call and ends with the /// destruction of the returned `CyclicParseReset`. During this time, /// calling `tryStartCyclicParse` with the same attribute in any parser /// will lead to returning failure. /// /// This makes it possible to parse cyclic attributes or types by parsing a /// short from if nested within itself. template FailureOr tryStartCyclicParse(AttrOrTypeT attrOrType) { static_assert( std::is_base_of_v, AttrOrTypeT> || std::is_base_of_v, AttrOrTypeT>, "Only mutable attributes or types can be cyclic"); if (failed(pushCyclicParsing(attrOrType.getAsOpaquePointer()))) return failure(); return CyclicParseReset(this); } protected: /// Parse a handle to a resource within the assembly format for the given /// dialect. virtual FailureOr parseResourceHandle(Dialect *dialect) = 0; /// Pushes a new attribute or type in the form of a type erased pointer /// into an internal set. /// Returns success if the type or attribute was inserted in the set or /// failure if it was already contained. virtual LogicalResult pushCyclicParsing(const void *opaquePointer) = 0; /// Removes the element that was last inserted with a successful call to /// `pushCyclicParsing`. There must be exactly one `popCyclicParsing` call /// in reverse order of all successful `pushCyclicParsing`. virtual void popCyclicParsing() = 0; //===--------------------------------------------------------------------===// // Code Completion //===--------------------------------------------------------------------===// /// Parse a keyword, or an empty string if the current location signals a code /// completion. virtual ParseResult parseKeywordOrCompletion(StringRef *keyword) = 0; /// Signal the code completion of a set of expected tokens. virtual void codeCompleteExpectedTokens(ArrayRef tokens) = 0; private: AsmParser(const AsmParser &) = delete; void operator=(const AsmParser &) = delete; }; //===----------------------------------------------------------------------===// // OpAsmParser //===----------------------------------------------------------------------===// /// The OpAsmParser has methods for interacting with the asm parser: parsing /// things from it, emitting errors etc. It has an intentionally high-level API /// that is designed to reduce/constrain syntax innovation in individual /// operations. /// /// For example, consider an op like this: /// /// %x = load %p[%1, %2] : memref<...> /// /// The "%x = load" tokens are already parsed and therefore invisible to the /// custom op parser. This can be supported by calling `parseOperandList` to /// parse the %p, then calling `parseOperandList` with a `SquareDelimiter` to /// parse the indices, then calling `parseColonTypeList` to parse the result /// type. /// class OpAsmParser : public AsmParser { public: using AsmParser::AsmParser; ~OpAsmParser() override; /// Parse a loc(...) specifier if present, filling in result if so. /// Location for BlockArgument and Operation may be deferred with an alias, in /// which case an OpaqueLoc is set and will be resolved when parsing /// completes. virtual ParseResult parseOptionalLocationSpecifier(std::optional &result) = 0; /// Return the name of the specified result in the specified syntax, as well /// as the sub-element in the name. It returns an empty string and ~0U for /// invalid result numbers. For example, in this operation: /// /// %x, %y:2, %z = foo.op /// /// getResultName(0) == {"x", 0 } /// getResultName(1) == {"y", 0 } /// getResultName(2) == {"y", 1 } /// getResultName(3) == {"z", 0 } /// getResultName(4) == {"", ~0U } virtual std::pair getResultName(unsigned resultNo) const = 0; /// Return the number of declared SSA results. This returns 4 for the foo.op /// example in the comment for `getResultName`. virtual size_t getNumResults() const = 0; // These methods emit an error and return failure or success. This allows // these to be chained together into a linear sequence of || expressions in // many cases. /// Parse an operation in its generic form. /// The parsed operation is parsed in the current context and inserted in the /// provided block and insertion point. The results produced by this operation /// aren't mapped to any named value in the parser. Returns nullptr on /// failure. virtual Operation *parseGenericOperation(Block *insertBlock, Block::iterator insertPt) = 0; /// Parse the name of an operation, in the custom form. On success, return a /// an object of type 'OperationName'. Otherwise, failure is returned. virtual FailureOr parseCustomOperationName() = 0; //===--------------------------------------------------------------------===// // Operand Parsing //===--------------------------------------------------------------------===// /// This is the representation of an operand reference. struct UnresolvedOperand { SMLoc location; // Location of the token. StringRef name; // Value name, e.g. %42 or %abc unsigned number; // Number, e.g. 12 for an operand like %xyz#12 }; /// Parse different components, viz., use-info of operand(s), successor(s), /// region(s), attribute(s) and function-type, of the generic form of an /// operation instance and populate the input operation-state 'result' with /// those components. If any of the components is explicitly provided, then /// skip parsing that component. virtual ParseResult parseGenericOperationAfterOpName( OperationState &result, std::optional> parsedOperandType = std::nullopt, std::optional> parsedSuccessors = std::nullopt, std::optional>> parsedRegions = std::nullopt, std::optional> parsedAttributes = std::nullopt, std::optional parsedPropertiesAttribute = std::nullopt, std::optional parsedFnType = std::nullopt) = 0; /// Parse a single SSA value operand name along with a result number if /// `allowResultNumber` is true. virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber = true) = 0; /// Parse a single operand if present. virtual OptionalParseResult parseOptionalOperand(UnresolvedOperand &result, bool allowResultNumber = true) = 0; /// Parse zero or more SSA comma-separated operand references with a specified /// surrounding delimiter, and an optional required operand count. virtual ParseResult parseOperandList(SmallVectorImpl &result, Delimiter delimiter = Delimiter::None, bool allowResultNumber = true, int requiredOperandCount = -1) = 0; /// Parse a specified number of comma separated operands. ParseResult parseOperandList(SmallVectorImpl &result, int requiredOperandCount, Delimiter delimiter = Delimiter::None) { return parseOperandList(result, delimiter, /*allowResultNumber=*/true, requiredOperandCount); } /// Parse zero or more trailing SSA comma-separated trailing operand /// references with a specified surrounding delimiter, and an optional /// required operand count. A leading comma is expected before the /// operands. ParseResult parseTrailingOperandList(SmallVectorImpl &result, Delimiter delimiter = Delimiter::None) { if (failed(parseOptionalComma())) return success(); // The comma is optional. return parseOperandList(result, delimiter); } /// Resolve an operand to an SSA value, emitting an error on failure. virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl &result) = 0; /// Resolve a list of operands to SSA values, emitting an error on failure, or /// appending the results to the list on success. This method should be used /// when all operands have the same type. template > ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl &result) { for (const UnresolvedOperand &operand : operands) if (resolveOperand(operand, type, result)) return failure(); return success(); } template > ParseResult resolveOperands(Operands &&operands, Type type, SMLoc loc, SmallVectorImpl &result) { return resolveOperands(std::forward(operands), type, result); } /// Resolve a list of operands and a list of operand types to SSA values, /// emitting an error and returning failure, or appending the results /// to the list on success. template < typename Operands = ArrayRef, typename Types = ArrayRef, typename = std::enable_if_t::value>> ParseResult resolveOperands(Operands &&operands, Types &&types, SMLoc loc, SmallVectorImpl &result) { size_t operandSize = llvm::range_size(operands); size_t typeSize = llvm::range_size(types); if (operandSize != typeSize) { // If no location was provided, report errors at the beginning of the op. return emitError(loc.isValid() ? loc : getNameLoc()) << "number of operands and types do not match: got " << operandSize << " operands and " << typeSize << " types"; } for (auto [operand, type] : llvm::zip_equal(operands, types)) if (resolveOperand(operand, type, result)) return failure(); return success(); } /// Parses an affine map attribute where dims and symbols are SSA operands. /// Operand values must come from single-result sources, and be valid /// dimensions/symbol identifiers according to mlir::isValidDim/Symbol. virtual ParseResult parseAffineMapOfSSAIds(SmallVectorImpl &operands, Attribute &map, StringRef attrName, NamedAttrList &attrs, Delimiter delimiter = Delimiter::Square) = 0; /// Parses an affine expression where dims and symbols are SSA operands. /// Operand values must come from single-result sources, and be valid /// dimensions/symbol identifiers according to mlir::isValidDim/Symbol. virtual ParseResult parseAffineExprOfSSAIds(SmallVectorImpl &dimOperands, SmallVectorImpl &symbOperands, AffineExpr &expr) = 0; //===--------------------------------------------------------------------===// // Argument Parsing //===--------------------------------------------------------------------===// struct Argument { UnresolvedOperand ssaName; // SourceLoc, SSA name, result #. Type type; // Type. DictionaryAttr attrs; // Attributes if present. std::optional sourceLoc; // Source location specifier if present. }; /// Parse a single argument with the following syntax: /// /// `%ssaName : !type { optionalAttrDict} loc(optionalSourceLoc)` /// /// If `allowType` is false or `allowAttrs` are false then the respective /// parts of the grammar are not parsed. virtual ParseResult parseArgument(Argument &result, bool allowType = false, bool allowAttrs = false) = 0; /// Parse a single argument if present. virtual OptionalParseResult parseOptionalArgument(Argument &result, bool allowType = false, bool allowAttrs = false) = 0; /// Parse zero or more arguments with a specified surrounding delimiter. virtual ParseResult parseArgumentList(SmallVectorImpl &result, Delimiter delimiter = Delimiter::None, bool allowType = false, bool allowAttrs = false) = 0; //===--------------------------------------------------------------------===// // Region Parsing //===--------------------------------------------------------------------===// /// Parses a region. Any parsed blocks are appended to 'region' and must be /// moved to the op regions after the op is created. The first block of the /// region takes 'arguments'. /// /// If 'enableNameShadowing' is set to true, the argument names are allowed to /// shadow the names of other existing SSA values defined above the region /// scope. 'enableNameShadowing' can only be set to true for regions attached /// to operations that are 'IsolatedFromAbove'. virtual ParseResult parseRegion(Region ®ion, ArrayRef arguments = {}, bool enableNameShadowing = false) = 0; /// Parses a region if present. virtual OptionalParseResult parseOptionalRegion(Region ®ion, ArrayRef arguments = {}, bool enableNameShadowing = false) = 0; /// Parses a region if present. If the region is present, a new region is /// allocated and placed in `region`. If no region is present or on failure, /// `region` remains untouched. virtual OptionalParseResult parseOptionalRegion(std::unique_ptr ®ion, ArrayRef arguments = {}, bool enableNameShadowing = false) = 0; //===--------------------------------------------------------------------===// // Successor Parsing //===--------------------------------------------------------------------===// /// Parse a single operation successor. virtual ParseResult parseSuccessor(Block *&dest) = 0; /// Parse an optional operation successor. virtual OptionalParseResult parseOptionalSuccessor(Block *&dest) = 0; /// Parse a single operation successor and its operand list. virtual ParseResult parseSuccessorAndUseList(Block *&dest, SmallVectorImpl &operands) = 0; //===--------------------------------------------------------------------===// // Type Parsing //===--------------------------------------------------------------------===// /// Parse a list of assignments of the form /// (%x1 = %y1, %x2 = %y2, ...) ParseResult parseAssignmentList(SmallVectorImpl &lhs, SmallVectorImpl &rhs) { OptionalParseResult result = parseOptionalAssignmentList(lhs, rhs); if (!result.has_value()) return emitError(getCurrentLocation(), "expected '('"); return result.value(); } virtual OptionalParseResult parseOptionalAssignmentList(SmallVectorImpl &lhs, SmallVectorImpl &rhs) = 0; }; //===--------------------------------------------------------------------===// // Custom printers and parsers. //===--------------------------------------------------------------------===// // Handles custom(...) in TableGen. void printDimensionList(OpAsmPrinter &printer, Operation *op, ArrayRef dimensions); ParseResult parseDimensionList(OpAsmParser &parser, DenseI64ArrayAttr &dimensions); } // namespace mlir //===--------------------------------------------------------------------===// // Operation OpAsm interface. //===--------------------------------------------------------------------===// /// The OpAsmOpInterface, see OpAsmInterface.td for more details. #include "mlir/IR/OpAsmOpInterface.h.inc" //===--------------------------------------------------------------------===// // Dialect OpAsm interface. //===--------------------------------------------------------------------===// /// The OpAsmDialectInterface, see OpAsmDialectInterface.td #include "mlir/IR/OpAsmDialectInterface.h.inc" namespace llvm { template <> struct DenseMapInfo { static unsigned getHashValue(const mlir::AsmDialectResourceHandle &handle) { return DenseMapInfo::getHashValue(handle.getResource()); } static bool isEqual(const mlir::AsmDialectResourceHandle &lhs, const mlir::AsmDialectResourceHandle &rhs) { return lhs.getResource() == rhs.getResource(); } }; } // namespace llvm #endif