2932 lines · cpp
1//===- Parser.cpp - MLIR Parser Implementation ----------------------------===//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// This file implements the parser for the MLIR textual form.10//11//===----------------------------------------------------------------------===//12 13#include "Parser.h"14#include "AsmParserImpl.h"15#include "mlir/AsmParser/AsmParser.h"16#include "mlir/AsmParser/AsmParserState.h"17#include "mlir/AsmParser/CodeComplete.h"18#include "mlir/IR/AffineExpr.h"19#include "mlir/IR/AffineMap.h"20#include "mlir/IR/AsmState.h"21#include "mlir/IR/Attributes.h"22#include "mlir/IR/BuiltinAttributes.h"23#include "mlir/IR/BuiltinOps.h"24#include "mlir/IR/BuiltinTypes.h"25#include "mlir/IR/Diagnostics.h"26#include "mlir/IR/Dialect.h"27#include "mlir/IR/Location.h"28#include "mlir/IR/OpDefinition.h"29#include "mlir/IR/OpImplementation.h"30#include "mlir/IR/OperationSupport.h"31#include "mlir/IR/OwningOpRef.h"32#include "mlir/IR/Region.h"33#include "mlir/IR/Value.h"34#include "mlir/IR/Verifier.h"35#include "mlir/IR/Visitors.h"36#include "mlir/Support/LLVM.h"37#include "mlir/Support/TypeID.h"38#include "llvm/ADT/APFloat.h"39#include "llvm/ADT/DenseMap.h"40#include "llvm/ADT/PointerUnion.h"41#include "llvm/ADT/STLExtras.h"42#include "llvm/ADT/ScopeExit.h"43#include "llvm/ADT/Sequence.h"44#include "llvm/ADT/StringExtras.h"45#include "llvm/ADT/StringMap.h"46#include "llvm/ADT/StringSet.h"47#include "llvm/Support/Alignment.h"48#include "llvm/Support/Casting.h"49#include "llvm/Support/Endian.h"50#include "llvm/Support/ErrorHandling.h"51#include "llvm/Support/MathExtras.h"52#include "llvm/Support/PrettyStackTrace.h"53#include "llvm/Support/SourceMgr.h"54#include "llvm/Support/raw_ostream.h"55#include <algorithm>56#include <cassert>57#include <cstddef>58#include <cstdint>59#include <cstring>60#include <memory>61#include <optional>62#include <string>63#include <tuple>64#include <utility>65#include <vector>66 67using namespace mlir;68using namespace mlir::detail;69 70//===----------------------------------------------------------------------===//71// CodeComplete72//===----------------------------------------------------------------------===//73 74AsmParserCodeCompleteContext::~AsmParserCodeCompleteContext() = default;75 76//===----------------------------------------------------------------------===//77// Parser78//===----------------------------------------------------------------------===//79 80/// Parse a list of comma-separated items with an optional delimiter. If a81/// delimiter is provided, then an empty list is allowed. If not, then at82/// least one element will be parsed.83ParseResult84Parser::parseCommaSeparatedList(Delimiter delimiter,85 function_ref<ParseResult()> parseElementFn,86 StringRef contextMessage) {87 switch (delimiter) {88 case Delimiter::None:89 break;90 case Delimiter::OptionalParen:91 if (getToken().isNot(Token::l_paren))92 return success();93 [[fallthrough]];94 case Delimiter::Paren:95 if (parseToken(Token::l_paren, "expected '('" + contextMessage))96 return failure();97 // Check for empty list.98 if (consumeIf(Token::r_paren))99 return success();100 break;101 case Delimiter::OptionalLessGreater:102 // Check for absent list.103 if (getToken().isNot(Token::less))104 return success();105 [[fallthrough]];106 case Delimiter::LessGreater:107 if (parseToken(Token::less, "expected '<'" + contextMessage))108 return success();109 // Check for empty list.110 if (consumeIf(Token::greater))111 return success();112 break;113 case Delimiter::OptionalSquare:114 if (getToken().isNot(Token::l_square))115 return success();116 [[fallthrough]];117 case Delimiter::Square:118 if (parseToken(Token::l_square, "expected '['" + contextMessage))119 return failure();120 // Check for empty list.121 if (consumeIf(Token::r_square))122 return success();123 break;124 case Delimiter::OptionalBraces:125 if (getToken().isNot(Token::l_brace))126 return success();127 [[fallthrough]];128 case Delimiter::Braces:129 if (parseToken(Token::l_brace, "expected '{'" + contextMessage))130 return failure();131 // Check for empty list.132 if (consumeIf(Token::r_brace))133 return success();134 break;135 }136 137 // Non-empty case starts with an element.138 if (parseElementFn())139 return failure();140 141 // Otherwise we have a list of comma separated elements.142 while (consumeIf(Token::comma)) {143 if (parseElementFn())144 return failure();145 }146 147 switch (delimiter) {148 case Delimiter::None:149 return success();150 case Delimiter::OptionalParen:151 case Delimiter::Paren:152 return parseToken(Token::r_paren, "expected ')'" + contextMessage);153 case Delimiter::OptionalLessGreater:154 case Delimiter::LessGreater:155 return parseToken(Token::greater, "expected '>'" + contextMessage);156 case Delimiter::OptionalSquare:157 case Delimiter::Square:158 return parseToken(Token::r_square, "expected ']'" + contextMessage);159 case Delimiter::OptionalBraces:160 case Delimiter::Braces:161 return parseToken(Token::r_brace, "expected '}'" + contextMessage);162 }163 llvm_unreachable("Unknown delimiter");164}165 166/// Parse a comma-separated list of elements, terminated with an arbitrary167/// token. This allows empty lists if allowEmptyList is true.168///169/// abstract-list ::= rightToken // if allowEmptyList == true170/// abstract-list ::= element (',' element)* rightToken171///172ParseResult173Parser::parseCommaSeparatedListUntil(Token::Kind rightToken,174 function_ref<ParseResult()> parseElement,175 bool allowEmptyList) {176 // Handle the empty case.177 if (getToken().is(rightToken)) {178 if (!allowEmptyList)179 return emitWrongTokenError("expected list element");180 consumeToken(rightToken);181 return success();182 }183 184 if (parseCommaSeparatedList(parseElement) ||185 parseToken(rightToken, "expected ',' or '" +186 Token::getTokenSpelling(rightToken) + "'"))187 return failure();188 189 return success();190}191 192InFlightDiagnostic Parser::emitError(const Twine &message) {193 auto loc = state.curToken.getLoc();194 if (state.curToken.isNot(Token::eof))195 return emitError(loc, message);196 197 // If the error is to be emitted at EOF, move it back one character.198 return emitError(SMLoc::getFromPointer(loc.getPointer() - 1), message);199}200 201InFlightDiagnostic Parser::emitError(SMLoc loc, const Twine &message) {202 auto diag = mlir::emitError(getEncodedSourceLocation(loc), message);203 204 // If we hit a parse error in response to a lexer error, then the lexer205 // already reported the error.206 if (getToken().is(Token::error))207 diag.abandon();208 return diag;209}210 211/// Emit an error about a "wrong token". If the current token is at the212/// start of a source line, this will apply heuristics to back up and report213/// the error at the end of the previous line, which is where the expected214/// token is supposed to be.215InFlightDiagnostic Parser::emitWrongTokenError(const Twine &message) {216 auto loc = state.curToken.getLoc();217 218 // If the error is to be emitted at EOF, move it back one character.219 if (state.curToken.is(Token::eof))220 loc = SMLoc::getFromPointer(loc.getPointer() - 1);221 222 // This is the location we were originally asked to report the error at.223 auto originalLoc = loc;224 225 // Determine if the token is at the start of the current line.226 const char *bufferStart = state.lex.getBufferBegin();227 const char *curPtr = loc.getPointer();228 229 // Use this StringRef to keep track of what we are going to back up through,230 // it provides nicer string search functions etc.231 StringRef startOfBuffer(bufferStart, curPtr - bufferStart);232 233 // Back up over entirely blank lines.234 while (true) {235 // Back up until we see a \n, but don't look past the buffer start.236 startOfBuffer = startOfBuffer.rtrim(" \t");237 238 // For tokens with no preceding source line, just emit at the original239 // location.240 if (startOfBuffer.empty())241 return emitError(originalLoc, message);242 243 // If we found something that isn't the end of line, then we're done.244 if (startOfBuffer.back() != '\n' && startOfBuffer.back() != '\r')245 return emitError(SMLoc::getFromPointer(startOfBuffer.end()), message);246 247 // Drop the \n so we emit the diagnostic at the end of the line.248 startOfBuffer = startOfBuffer.drop_back();249 250 // Check to see if the preceding line has a comment on it. We assume that a251 // `//` is the start of a comment, which is mostly correct.252 // TODO: This will do the wrong thing for // in a string literal.253 auto prevLine = startOfBuffer;254 size_t newLineIndex = prevLine.find_last_of("\n\r");255 if (newLineIndex != StringRef::npos)256 prevLine = prevLine.drop_front(newLineIndex);257 258 // If we find a // in the current line, then emit the diagnostic before it.259 size_t commentStart = prevLine.find("//");260 if (commentStart != StringRef::npos)261 startOfBuffer = startOfBuffer.drop_back(prevLine.size() - commentStart);262 }263}264 265/// Consume the specified token if present and return success. On failure,266/// output a diagnostic and return failure.267ParseResult Parser::parseToken(Token::Kind expectedToken,268 const Twine &message) {269 if (consumeIf(expectedToken))270 return success();271 return emitWrongTokenError(message);272}273 274/// Parses a quoted string token if present.275ParseResult Parser::parseOptionalString(std::string *string) {276 if (!getToken().is(Token::string))277 return failure();278 279 if (string)280 *string = getToken().getStringValue();281 consumeToken();282 return success();283}284 285/// Parse an optional integer value from the stream.286OptionalParseResult Parser::parseOptionalInteger(APInt &result) {287 // Parse `false` and `true` keywords as 0 and 1 respectively.288 if (consumeIf(Token::kw_false)) {289 result = false;290 return success();291 }292 if (consumeIf(Token::kw_true)) {293 result = true;294 return success();295 }296 297 Token curToken = getToken();298 if (curToken.isNot(Token::integer, Token::minus))299 return std::nullopt;300 301 bool negative = consumeIf(Token::minus);302 Token curTok = getToken();303 if (parseToken(Token::integer, "expected integer value"))304 return failure();305 306 StringRef spelling = curTok.getSpelling();307 bool isHex = spelling.size() > 1 && spelling[1] == 'x';308 if (spelling.getAsInteger(isHex ? 0 : 10, result))309 return emitError(curTok.getLoc(), "integer value too large");310 311 // Make sure we have a zero at the top so we return the right signedness.312 if (result.isNegative())313 result = result.zext(result.getBitWidth() + 1);314 315 // Process the negative sign if present.316 if (negative)317 result.negate();318 319 return success();320}321 322/// Parse an optional integer value only in decimal format from the stream.323OptionalParseResult Parser::parseOptionalDecimalInteger(APInt &result) {324 Token curToken = getToken();325 if (curToken.isNot(Token::integer, Token::minus)) {326 return std::nullopt;327 }328 329 bool negative = consumeIf(Token::minus);330 Token curTok = getToken();331 if (parseToken(Token::integer, "expected integer value")) {332 return failure();333 }334 335 StringRef spelling = curTok.getSpelling();336 // If the integer is in hexadecimal return only the 0. The lexer has already337 // moved past the entire hexidecimal encoded integer so we reset the lex338 // pointer to just past the 0 we actualy want to consume.339 if (spelling[0] == '0' && spelling.size() > 1 &&340 llvm::toLower(spelling[1]) == 'x') {341 result = 0;342 state.lex.resetPointer(spelling.data() + 1);343 consumeToken();344 return success();345 }346 347 if (spelling.getAsInteger(10, result))348 return emitError(curTok.getLoc(), "integer value too large");349 350 // Make sure we have a zero at the top so we return the right signedness.351 if (result.isNegative())352 result = result.zext(result.getBitWidth() + 1);353 354 // Process the negative sign if present.355 if (negative)356 result.negate();357 358 return success();359}360 361ParseResult Parser::parseFloatFromLiteral(std::optional<APFloat> &result,362 const Token &tok, bool isNegative,363 const llvm::fltSemantics &semantics) {364 // Check for a floating point value.365 if (tok.is(Token::floatliteral)) {366 auto val = tok.getFloatingPointValue();367 if (!val)368 return emitError(tok.getLoc()) << "floating point value too large";369 370 result.emplace(isNegative ? -*val : *val);371 bool unused;372 result->convert(semantics, APFloat::rmNearestTiesToEven, &unused);373 return success();374 }375 376 // Check for a hexadecimal float value.377 if (tok.is(Token::integer))378 return parseFloatFromIntegerLiteral(result, tok, isNegative, semantics);379 380 return emitError(tok.getLoc()) << "expected floating point literal";381}382 383/// Parse a floating point value from an integer literal token.384ParseResult385Parser::parseFloatFromIntegerLiteral(std::optional<APFloat> &result,386 const Token &tok, bool isNegative,387 const llvm::fltSemantics &semantics) {388 StringRef spelling = tok.getSpelling();389 bool isHex = spelling.size() > 1 && spelling[1] == 'x';390 if (!isHex) {391 return emitError(tok.getLoc(), "unexpected decimal integer literal for a "392 "floating point value")393 .attachNote()394 << "add a trailing dot to make the literal a float";395 }396 if (isNegative) {397 return emitError(tok.getLoc(),398 "hexadecimal float literal should not have a "399 "leading minus");400 }401 402 APInt intValue;403 tok.getSpelling().getAsInteger(isHex ? 0 : 10, intValue);404 auto typeSizeInBits = APFloat::semanticsSizeInBits(semantics);405 if (intValue.getActiveBits() > typeSizeInBits) {406 return emitError(tok.getLoc(),407 "hexadecimal float constant out of range for type");408 }409 410 APInt truncatedValue(typeSizeInBits,411 ArrayRef(intValue.getRawData(), intValue.getNumWords()));412 result.emplace(semantics, truncatedValue);413 return success();414}415 416ParseResult Parser::parseOptionalKeyword(StringRef *keyword) {417 // Check that the current token is a keyword.418 if (!isCurrentTokenAKeyword())419 return failure();420 421 *keyword = getTokenSpelling();422 consumeToken();423 return success();424}425 426ParseResult Parser::parseOptionalKeywordOrString(std::string *result) {427 StringRef keyword;428 if (succeeded(parseOptionalKeyword(&keyword))) {429 *result = keyword.str();430 return success();431 }432 433 return parseOptionalString(result);434}435 436//===----------------------------------------------------------------------===//437// Resource Parsing438//===----------------------------------------------------------------------===//439 440FailureOr<AsmDialectResourceHandle>441Parser::parseResourceHandle(const OpAsmDialectInterface *dialect,442 std::string &name) {443 assert(dialect && "expected valid dialect interface");444 SMLoc nameLoc = getToken().getLoc();445 if (failed(parseOptionalKeywordOrString(&name)))446 return emitError("expected identifier key for 'resource' entry");447 auto &resources = getState().symbols.dialectResources;448 449 // If this is the first time encountering this handle, ask the dialect to450 // resolve a reference to this handle. This allows for us to remap the name of451 // the handle if necessary.452 std::pair<std::string, AsmDialectResourceHandle> &entry =453 resources[dialect][name];454 if (entry.first.empty()) {455 FailureOr<AsmDialectResourceHandle> result = dialect->declareResource(name);456 if (failed(result)) {457 return emitError(nameLoc)458 << "unknown 'resource' key '" << name << "' for dialect '"459 << dialect->getDialect()->getNamespace() << "'";460 }461 entry.first = dialect->getResourceKey(*result);462 entry.second = *result;463 }464 465 name = entry.first;466 return entry.second;467}468 469FailureOr<AsmDialectResourceHandle>470Parser::parseResourceHandle(Dialect *dialect) {471 const auto *interface = dyn_cast<OpAsmDialectInterface>(dialect);472 if (!interface) {473 return emitError() << "dialect '" << dialect->getNamespace()474 << "' does not expect resource handles";475 }476 std::string resourceName;477 return parseResourceHandle(interface, resourceName);478}479 480//===----------------------------------------------------------------------===//481// Code Completion482//===----------------------------------------------------------------------===//483 484ParseResult Parser::codeCompleteDialectName() {485 state.codeCompleteContext->completeDialectName();486 return failure();487}488 489ParseResult Parser::codeCompleteOperationName(StringRef dialectName) {490 // Perform some simple validation on the dialect name. This doesn't need to be491 // extensive, it's more of an optimization (to avoid checking completion492 // results when we know they will fail).493 if (dialectName.empty() || dialectName.contains('.'))494 return failure();495 state.codeCompleteContext->completeOperationName(dialectName);496 return failure();497}498 499ParseResult Parser::codeCompleteDialectOrElidedOpName(SMLoc loc) {500 // Check to see if there is anything else on the current line. This check501 // isn't strictly necessary, but it does avoid unnecessarily triggering502 // completions for operations and dialects in situations where we don't want503 // them (e.g. at the end of an operation).504 auto shouldIgnoreOpCompletion = [&]() {505 const char *bufBegin = state.lex.getBufferBegin();506 const char *it = loc.getPointer() - 1;507 for (; it > bufBegin && *it != '\n'; --it)508 if (!StringRef(" \t\r").contains(*it))509 return true;510 return false;511 };512 if (shouldIgnoreOpCompletion())513 return failure();514 515 // The completion here is either for a dialect name, or an operation name516 // whose dialect prefix was elided. For this we simply invoke both of the517 // individual completion methods.518 (void)codeCompleteDialectName();519 return codeCompleteOperationName(state.defaultDialectStack.back());520}521 522ParseResult Parser::codeCompleteStringDialectOrOperationName(StringRef name) {523 // If the name is empty, this is the start of the string and contains the524 // dialect.525 if (name.empty())526 return codeCompleteDialectName();527 528 // Otherwise, we treat this as completing an operation name. The current name529 // is used as the dialect namespace.530 if (name.consume_back("."))531 return codeCompleteOperationName(name);532 return failure();533}534 535ParseResult Parser::codeCompleteExpectedTokens(ArrayRef<StringRef> tokens) {536 state.codeCompleteContext->completeExpectedTokens(tokens, /*optional=*/false);537 return failure();538}539ParseResult Parser::codeCompleteOptionalTokens(ArrayRef<StringRef> tokens) {540 state.codeCompleteContext->completeExpectedTokens(tokens, /*optional=*/true);541 return failure();542}543 544Attribute Parser::codeCompleteAttribute() {545 state.codeCompleteContext->completeAttribute(546 state.symbols.attributeAliasDefinitions);547 return {};548}549Type Parser::codeCompleteType() {550 state.codeCompleteContext->completeType(state.symbols.typeAliasDefinitions);551 return {};552}553 554Attribute555Parser::codeCompleteDialectSymbol(const llvm::StringMap<Attribute> &aliases) {556 state.codeCompleteContext->completeDialectAttributeOrAlias(aliases);557 return {};558}559Type Parser::codeCompleteDialectSymbol(const llvm::StringMap<Type> &aliases) {560 state.codeCompleteContext->completeDialectTypeOrAlias(aliases);561 return {};562}563 564//===----------------------------------------------------------------------===//565// OperationParser566//===----------------------------------------------------------------------===//567 568namespace {569/// This class provides support for parsing operations and regions of570/// operations.571class OperationParser : public Parser {572public:573 OperationParser(ParserState &state, ModuleOp topLevelOp);574 ~OperationParser();575 576 /// After parsing is finished, this function must be called to see if there577 /// are any remaining issues.578 ParseResult finalize();579 580 //===--------------------------------------------------------------------===//581 // SSA Value Handling582 //===--------------------------------------------------------------------===//583 584 using UnresolvedOperand = OpAsmParser::UnresolvedOperand;585 using Argument = OpAsmParser::Argument;586 587 struct DeferredLocInfo {588 SMLoc loc;589 StringRef identifier;590 };591 592 /// Push a new SSA name scope to the parser.593 void pushSSANameScope(bool isIsolated);594 595 /// Pop the last SSA name scope from the parser.596 ParseResult popSSANameScope();597 598 /// Register a definition of a value with the symbol table.599 ParseResult addDefinition(UnresolvedOperand useInfo, Value value);600 601 /// Parse an optional list of SSA uses into 'results'.602 ParseResult603 parseOptionalSSAUseList(SmallVectorImpl<UnresolvedOperand> &results);604 605 /// Parse a single SSA use into 'result'. If 'allowResultNumber' is true then606 /// we allow #42 syntax.607 ParseResult parseSSAUse(UnresolvedOperand &result,608 bool allowResultNumber = true);609 610 /// Given a reference to an SSA value and its type, return a reference. This611 /// returns null on failure.612 Value resolveSSAUse(UnresolvedOperand useInfo, Type type);613 614 ParseResult parseSSADefOrUseAndType(615 function_ref<ParseResult(UnresolvedOperand, Type)> action);616 617 ParseResult parseOptionalSSAUseAndTypeList(SmallVectorImpl<Value> &results);618 619 /// Return the location of the value identified by its name and number if it620 /// has been already reference.621 std::optional<SMLoc> getReferenceLoc(StringRef name, unsigned number) {622 auto &values = isolatedNameScopes.back().values;623 if (!values.count(name) || number >= values[name].size())624 return {};625 if (values[name][number].value)626 return values[name][number].loc;627 return {};628 }629 630 //===--------------------------------------------------------------------===//631 // Operation Parsing632 //===--------------------------------------------------------------------===//633 634 /// Parse an operation instance.635 ParseResult parseOperation();636 637 /// Parse a single operation successor.638 ParseResult parseSuccessor(Block *&dest);639 640 /// Parse a comma-separated list of operation successors in brackets.641 ParseResult parseSuccessors(SmallVectorImpl<Block *> &destinations);642 643 /// Parse an operation instance that is in the generic form.644 Operation *parseGenericOperation();645 646 /// Parse different components, viz., use-info of operand(s), successor(s),647 /// region(s), attribute(s) and function-type, of the generic form of an648 /// operation instance and populate the input operation-state 'result' with649 /// those components. If any of the components is explicitly provided, then650 /// skip parsing that component.651 ParseResult parseGenericOperationAfterOpName(652 OperationState &result,653 std::optional<ArrayRef<UnresolvedOperand>> parsedOperandUseInfo =654 std::nullopt,655 std::optional<ArrayRef<Block *>> parsedSuccessors = std::nullopt,656 std::optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions =657 std::nullopt,658 std::optional<ArrayRef<NamedAttribute>> parsedAttributes = std::nullopt,659 std::optional<Attribute> propertiesAttribute = std::nullopt,660 std::optional<FunctionType> parsedFnType = std::nullopt);661 662 /// Parse an operation instance that is in the generic form and insert it at663 /// the provided insertion point.664 Operation *parseGenericOperation(Block *insertBlock,665 Block::iterator insertPt);666 667 /// This type is used to keep track of things that are either an Operation or668 /// a BlockArgument. We cannot use Value for this, because not all Operations669 /// have results.670 using OpOrArgument = llvm::PointerUnion<Operation *, BlockArgument>;671 672 /// Parse an optional trailing location and add it to the specifier Operation673 /// or `UnresolvedOperand` if present.674 ///675 /// trailing-location ::= (`loc` (`(` location `)` | attribute-alias))?676 ///677 ParseResult parseTrailingLocationSpecifier(OpOrArgument opOrArgument);678 679 /// Parse a location alias, that is a sequence looking like: #loc42680 /// The alias may have already be defined or may be defined later, in which681 /// case an OpaqueLoc is used a placeholder. The caller must ensure that the682 /// token is actually an alias, which means it must not contain a dot.683 ParseResult parseLocationAlias(LocationAttr &loc);684 685 /// This is the structure of a result specifier in the assembly syntax,686 /// including the name, number of results, and location.687 using ResultRecord = std::tuple<StringRef, unsigned, SMLoc>;688 689 /// Parse an operation instance that is in the op-defined custom form.690 /// resultInfo specifies information about the "%name =" specifiers.691 Operation *parseCustomOperation(ArrayRef<ResultRecord> resultIDs);692 693 /// Parse the name of an operation, in the custom form. On success, return a694 /// an object of type 'OperationName'. Otherwise, failure is returned.695 FailureOr<OperationName> parseCustomOperationName();696 697 //===--------------------------------------------------------------------===//698 // Region Parsing699 //===--------------------------------------------------------------------===//700 701 /// Parse a region into 'region' with the provided entry block arguments.702 /// 'isIsolatedNameScope' indicates if the naming scope of this region is703 /// isolated from those above.704 ParseResult parseRegion(Region ®ion, ArrayRef<Argument> entryArguments,705 bool isIsolatedNameScope = false);706 707 /// Parse a region body into 'region'.708 ParseResult parseRegionBody(Region ®ion, SMLoc startLoc,709 ArrayRef<Argument> entryArguments,710 bool isIsolatedNameScope);711 712 //===--------------------------------------------------------------------===//713 // Block Parsing714 //===--------------------------------------------------------------------===//715 716 /// Parse a new block into 'block'.717 ParseResult parseBlock(Block *&block);718 719 /// Parse a list of operations into 'block'.720 ParseResult parseBlockBody(Block *block);721 722 /// Parse a (possibly empty) list of block arguments.723 ParseResult parseOptionalBlockArgList(Block *owner);724 725 /// Get the block with the specified name, creating it if it doesn't726 /// already exist. The location specified is the point of use, which allows727 /// us to diagnose references to blocks that are not defined precisely.728 Block *getBlockNamed(StringRef name, SMLoc loc);729 730 //===--------------------------------------------------------------------===//731 // Code Completion732 //===--------------------------------------------------------------------===//733 734 /// The set of various code completion methods. Every completion method735 /// returns `failure` to stop the parsing process after providing completion736 /// results.737 738 ParseResult codeCompleteSSAUse();739 ParseResult codeCompleteBlock();740 741private:742 /// This class represents a definition of a Block.743 struct BlockDefinition {744 /// A pointer to the defined Block.745 Block *block;746 /// The location that the Block was defined at.747 SMLoc loc;748 };749 /// This class represents a definition of a Value.750 struct ValueDefinition {751 /// A pointer to the defined Value.752 Value value;753 /// The location that the Value was defined at.754 SMLoc loc;755 };756 757 /// Returns the info for a block at the current scope for the given name.758 BlockDefinition &getBlockInfoByName(StringRef name) {759 return blocksByName.back()[name];760 }761 762 /// Insert a new forward reference to the given block.763 void insertForwardRef(Block *block, SMLoc loc) {764 forwardRef.back().try_emplace(block, loc);765 }766 767 /// Erase any forward reference to the given block.768 bool eraseForwardRef(Block *block) { return forwardRef.back().erase(block); }769 770 /// Record that a definition was added at the current scope.771 void recordDefinition(StringRef def);772 773 /// Get the value entry for the given SSA name.774 SmallVectorImpl<ValueDefinition> &getSSAValueEntry(StringRef name);775 776 /// Create a forward reference placeholder value with the given location and777 /// result type.778 Value createForwardRefPlaceholder(SMLoc loc, Type type);779 780 /// Return true if this is a forward reference.781 bool isForwardRefPlaceholder(Value value) {782 return forwardRefPlaceholders.count(value);783 }784 785 /// This struct represents an isolated SSA name scope. This scope may contain786 /// other nested non-isolated scopes. These scopes are used for operations787 /// that are known to be isolated to allow for reusing names within their788 /// regions, even if those names are used above.789 struct IsolatedSSANameScope {790 /// Record that a definition was added at the current scope.791 void recordDefinition(StringRef def) {792 definitionsPerScope.back().insert(def);793 }794 795 /// Push a nested name scope.796 void pushSSANameScope() { definitionsPerScope.push_back({}); }797 798 /// Pop a nested name scope.799 void popSSANameScope() {800 for (auto &def : definitionsPerScope.pop_back_val())801 values.erase(def.getKey());802 }803 804 /// This keeps track of all of the SSA values we are tracking for each name805 /// scope, indexed by their name. This has one entry per result number.806 llvm::StringMap<SmallVector<ValueDefinition, 1>> values;807 808 /// This keeps track of all of the values defined by a specific name scope.809 SmallVector<llvm::StringSet<>, 2> definitionsPerScope;810 };811 812 /// A list of isolated name scopes.813 SmallVector<IsolatedSSANameScope, 2> isolatedNameScopes;814 815 /// This keeps track of the block names as well as the location of the first816 /// reference for each nested name scope. This is used to diagnose invalid817 /// block references and memorize them.818 SmallVector<DenseMap<StringRef, BlockDefinition>, 2> blocksByName;819 SmallVector<DenseMap<Block *, SMLoc>, 2> forwardRef;820 821 /// These are all of the placeholders we've made along with the location of822 /// their first reference, to allow checking for use of undefined values.823 DenseMap<Value, SMLoc> forwardRefPlaceholders;824 825 /// Operations that define the placeholders. These are kept until the end of826 /// of the lifetime of the parser because some custom parsers may store827 /// references to them in local state and use them after forward references828 /// have been resolved.829 DenseSet<Operation *> forwardRefOps;830 831 /// Deffered locations: when parsing `loc(#loc42)` we add an entry to this832 /// map. After parsing the definition `#loc42 = ...` we'll patch back users833 /// of this location.834 std::vector<DeferredLocInfo> deferredLocsReferences;835 836 /// The builder used when creating parsed operation instances.837 OpBuilder opBuilder;838 839 /// The top level operation that holds all of the parsed operations.840 Operation *topLevelOp;841};842} // namespace843 844MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(OperationParser::DeferredLocInfo *)845MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(OperationParser::DeferredLocInfo *)846 847OperationParser::OperationParser(ParserState &state, ModuleOp topLevelOp)848 : Parser(state), opBuilder(topLevelOp.getRegion()), topLevelOp(topLevelOp) {849 // The top level operation starts a new name scope.850 pushSSANameScope(/*isIsolated=*/true);851 852 // If we are populating the parser state, prepare it for parsing.853 if (state.asmState)854 state.asmState->initialize(topLevelOp);855}856 857OperationParser::~OperationParser() {858 for (Operation *op : forwardRefOps) {859 // Drop all uses of undefined forward declared reference and destroy860 // defining operation.861 op->dropAllUses();862 op->destroy();863 }864 for (const auto &scope : forwardRef) {865 for (const auto &fwd : scope) {866 // Delete all blocks that were created as forward references but never867 // included into a region.868 fwd.first->dropAllUses();869 delete fwd.first;870 }871 }872}873 874/// After parsing is finished, this function must be called to see if there are875/// any remaining issues.876ParseResult OperationParser::finalize() {877 // Check for any forward references that are left. If we find any, error878 // out.879 if (!forwardRefPlaceholders.empty()) {880 SmallVector<const char *, 4> errors;881 // Iteration over the map isn't deterministic, so sort by source location.882 for (auto entry : forwardRefPlaceholders)883 errors.push_back(entry.second.getPointer());884 llvm::array_pod_sort(errors.begin(), errors.end());885 886 for (const char *entry : errors) {887 auto loc = SMLoc::getFromPointer(entry);888 emitError(loc, "use of undeclared SSA value name");889 }890 return failure();891 }892 893 // Resolve the locations of any deferred operations.894 auto &attributeAliases = state.symbols.attributeAliasDefinitions;895 auto locID = TypeID::get<DeferredLocInfo *>();896 auto resolveLocation = [&, this](auto &opOrArgument) -> LogicalResult {897 auto fwdLoc = dyn_cast<OpaqueLoc>(opOrArgument.getLoc());898 if (!fwdLoc || fwdLoc.getUnderlyingTypeID() != locID)899 return success();900 auto locInfo = deferredLocsReferences[fwdLoc.getUnderlyingLocation()];901 Attribute attr = attributeAliases.lookup(locInfo.identifier);902 if (!attr)903 return this->emitError(locInfo.loc)904 << "operation location alias was never defined";905 auto locAttr = dyn_cast<LocationAttr>(attr);906 if (!locAttr)907 return this->emitError(locInfo.loc)908 << "expected location, but found '" << attr << "'";909 opOrArgument.setLoc(locAttr);910 return success();911 };912 913 auto walkRes = topLevelOp->walk([&](Operation *op) {914 if (failed(resolveLocation(*op)))915 return WalkResult::interrupt();916 for (Region ®ion : op->getRegions())917 for (Block &block : region.getBlocks())918 for (BlockArgument arg : block.getArguments())919 if (failed(resolveLocation(arg)))920 return WalkResult::interrupt();921 return WalkResult::advance();922 });923 if (walkRes.wasInterrupted())924 return failure();925 926 // Pop the top level name scope.927 if (failed(popSSANameScope()))928 return failure();929 930 // Verify that the parsed operations are valid.931 if (state.config.shouldVerifyAfterParse() && failed(verify(topLevelOp)))932 return failure();933 934 // If we are populating the parser state, finalize the top-level operation.935 if (state.asmState)936 state.asmState->finalize(topLevelOp);937 return success();938}939 940//===----------------------------------------------------------------------===//941// SSA Value Handling942//===----------------------------------------------------------------------===//943 944void OperationParser::pushSSANameScope(bool isIsolated) {945 blocksByName.push_back(DenseMap<StringRef, BlockDefinition>());946 forwardRef.push_back(DenseMap<Block *, SMLoc>());947 948 // Push back a new name definition scope.949 if (isIsolated)950 isolatedNameScopes.push_back({});951 isolatedNameScopes.back().pushSSANameScope();952}953 954ParseResult OperationParser::popSSANameScope() {955 auto forwardRefInCurrentScope = forwardRef.pop_back_val();956 957 // Verify that all referenced blocks were defined.958 if (!forwardRefInCurrentScope.empty()) {959 SmallVector<std::pair<const char *, Block *>, 4> errors;960 // Iteration over the map isn't deterministic, so sort by source location.961 for (auto entry : forwardRefInCurrentScope) {962 errors.push_back({entry.second.getPointer(), entry.first});963 // Add this block to the top-level region to allow for automatic cleanup.964 topLevelOp->getRegion(0).push_back(entry.first);965 }966 llvm::array_pod_sort(errors.begin(), errors.end());967 968 for (auto entry : errors) {969 auto loc = SMLoc::getFromPointer(entry.first);970 emitError(loc, "reference to an undefined block");971 }972 return failure();973 }974 975 // Pop the next nested namescope. If there is only one internal namescope,976 // just pop the isolated scope.977 auto ¤tNameScope = isolatedNameScopes.back();978 if (currentNameScope.definitionsPerScope.size() == 1)979 isolatedNameScopes.pop_back();980 else981 currentNameScope.popSSANameScope();982 983 blocksByName.pop_back();984 return success();985}986 987/// Register a definition of a value with the symbol table.988ParseResult OperationParser::addDefinition(UnresolvedOperand useInfo,989 Value value) {990 auto &entries = getSSAValueEntry(useInfo.name);991 992 // Make sure there is a slot for this value.993 if (entries.size() <= useInfo.number)994 entries.resize(useInfo.number + 1);995 996 // If we already have an entry for this, check to see if it was a definition997 // or a forward reference.998 if (auto existing = entries[useInfo.number].value) {999 if (!isForwardRefPlaceholder(existing)) {1000 return emitError(useInfo.location)1001 .append("redefinition of SSA value '", useInfo.name, "'")1002 .attachNote(getEncodedSourceLocation(entries[useInfo.number].loc))1003 .append("previously defined here");1004 }1005 1006 if (existing.getType() != value.getType()) {1007 return emitError(useInfo.location)1008 .append("definition of SSA value '", useInfo.name, "#",1009 useInfo.number, "' has type ", value.getType())1010 .attachNote(getEncodedSourceLocation(entries[useInfo.number].loc))1011 .append("previously used here with type ", existing.getType());1012 }1013 1014 // If it was a forward reference, update everything that used it to use1015 // the actual definition instead, delete the forward ref, and remove it1016 // from our set of forward references we track.1017 existing.replaceAllUsesWith(value);1018 forwardRefPlaceholders.erase(existing);1019 1020 // If a definition of the value already exists, replace it in the assembly1021 // state.1022 if (state.asmState)1023 state.asmState->refineDefinition(existing, value);1024 }1025 1026 /// Record this definition for the current scope.1027 entries[useInfo.number] = {value, useInfo.location};1028 recordDefinition(useInfo.name);1029 return success();1030}1031 1032/// Parse a (possibly empty) list of SSA operands.1033///1034/// ssa-use-list ::= ssa-use (`,` ssa-use)*1035/// ssa-use-list-opt ::= ssa-use-list?1036///1037ParseResult OperationParser::parseOptionalSSAUseList(1038 SmallVectorImpl<UnresolvedOperand> &results) {1039 if (!getToken().isOrIsCodeCompletionFor(Token::percent_identifier))1040 return success();1041 return parseCommaSeparatedList([&]() -> ParseResult {1042 UnresolvedOperand result;1043 if (parseSSAUse(result))1044 return failure();1045 results.push_back(result);1046 return success();1047 });1048}1049 1050/// Parse a SSA operand for an operation.1051///1052/// ssa-use ::= ssa-id1053///1054ParseResult OperationParser::parseSSAUse(UnresolvedOperand &result,1055 bool allowResultNumber) {1056 if (getToken().isCodeCompletion())1057 return codeCompleteSSAUse();1058 1059 result.name = getTokenSpelling();1060 result.number = 0;1061 result.location = getToken().getLoc();1062 if (parseToken(Token::percent_identifier, "expected SSA operand"))1063 return failure();1064 1065 // If we have an attribute ID, it is a result number.1066 if (getToken().is(Token::hash_identifier)) {1067 if (!allowResultNumber)1068 return emitError("result number not allowed in argument list");1069 1070 if (auto value = getToken().getHashIdentifierNumber())1071 result.number = *value;1072 else1073 return emitError("invalid SSA value result number");1074 consumeToken(Token::hash_identifier);1075 }1076 1077 return success();1078}1079 1080/// Given an unbound reference to an SSA value and its type, return the value1081/// it specifies. This returns null on failure.1082Value OperationParser::resolveSSAUse(UnresolvedOperand useInfo, Type type) {1083 auto &entries = getSSAValueEntry(useInfo.name);1084 1085 // Functor used to record the use of the given value if the assembly state1086 // field is populated.1087 auto maybeRecordUse = [&](Value value) {1088 if (state.asmState)1089 state.asmState->addUses(value, useInfo.location);1090 return value;1091 };1092 1093 // If we have already seen a value of this name, return it.1094 if (useInfo.number < entries.size() && entries[useInfo.number].value) {1095 Value result = entries[useInfo.number].value;1096 // Check that the type matches the other uses.1097 if (result.getType() == type)1098 return maybeRecordUse(result);1099 1100 emitError(useInfo.location, "use of value '")1101 .append(useInfo.name,1102 "' expects different type than prior uses: ", type, " vs ",1103 result.getType())1104 .attachNote(getEncodedSourceLocation(entries[useInfo.number].loc))1105 .append("prior use here");1106 return nullptr;1107 }1108 1109 // Make sure we have enough slots for this.1110 if (entries.size() <= useInfo.number)1111 entries.resize(useInfo.number + 1);1112 1113 // If the value has already been defined and this is an overly large result1114 // number, diagnose that.1115 if (entries[0].value && !isForwardRefPlaceholder(entries[0].value))1116 return (emitError(useInfo.location, "reference to invalid result number"),1117 nullptr);1118 1119 // Otherwise, this is a forward reference. Create a placeholder and remember1120 // that we did so.1121 Value result = createForwardRefPlaceholder(useInfo.location, type);1122 entries[useInfo.number] = {result, useInfo.location};1123 return maybeRecordUse(result);1124}1125 1126/// Parse an SSA use with an associated type.1127///1128/// ssa-use-and-type ::= ssa-use `:` type1129ParseResult OperationParser::parseSSADefOrUseAndType(1130 function_ref<ParseResult(UnresolvedOperand, Type)> action) {1131 UnresolvedOperand useInfo;1132 if (parseSSAUse(useInfo) ||1133 parseToken(Token::colon, "expected ':' and type for SSA operand"))1134 return failure();1135 1136 auto type = parseType();1137 if (!type)1138 return failure();1139 1140 return action(useInfo, type);1141}1142 1143/// Parse a (possibly empty) list of SSA operands, followed by a colon, then1144/// followed by a type list.1145///1146/// ssa-use-and-type-list1147/// ::= ssa-use-list ':' type-list-no-parens1148///1149ParseResult OperationParser::parseOptionalSSAUseAndTypeList(1150 SmallVectorImpl<Value> &results) {1151 SmallVector<UnresolvedOperand, 4> valueIDs;1152 if (parseOptionalSSAUseList(valueIDs))1153 return failure();1154 1155 // If there were no operands, then there is no colon or type lists.1156 if (valueIDs.empty())1157 return success();1158 1159 SmallVector<Type, 4> types;1160 if (parseToken(Token::colon, "expected ':' in operand list") ||1161 parseTypeListNoParens(types))1162 return failure();1163 1164 if (valueIDs.size() != types.size())1165 return emitError("expected ")1166 << valueIDs.size() << " types to match operand list";1167 1168 results.reserve(valueIDs.size());1169 for (unsigned i = 0, e = valueIDs.size(); i != e; ++i) {1170 if (auto value = resolveSSAUse(valueIDs[i], types[i]))1171 results.push_back(value);1172 else1173 return failure();1174 }1175 1176 return success();1177}1178 1179/// Record that a definition was added at the current scope.1180void OperationParser::recordDefinition(StringRef def) {1181 isolatedNameScopes.back().recordDefinition(def);1182}1183 1184/// Get the value entry for the given SSA name.1185auto OperationParser::getSSAValueEntry(StringRef name)1186 -> SmallVectorImpl<ValueDefinition> & {1187 return isolatedNameScopes.back().values[name];1188}1189 1190/// Create and remember a new placeholder for a forward reference.1191Value OperationParser::createForwardRefPlaceholder(SMLoc loc, Type type) {1192 // Forward references are always created as operations, because we just need1193 // something with a def/use chain.1194 //1195 // We create these placeholders as having an empty name, which we know1196 // cannot be created through normal user input, allowing us to distinguish1197 // them.1198 auto name = OperationName("builtin.unrealized_conversion_cast", getContext());1199 auto *op = Operation::create(1200 getEncodedSourceLocation(loc), name, type, /*operands=*/{},1201 /*attributes=*/NamedAttrList(), /*properties=*/nullptr,1202 /*successors=*/{}, /*numRegions=*/0);1203 forwardRefPlaceholders[op->getResult(0)] = loc;1204 forwardRefOps.insert(op);1205 return op->getResult(0);1206}1207 1208//===----------------------------------------------------------------------===//1209// Operation Parsing1210//===----------------------------------------------------------------------===//1211 1212/// Parse an operation.1213///1214/// operation ::= op-result-list?1215/// (generic-operation | custom-operation)1216/// trailing-location?1217/// generic-operation ::= string-literal `(` ssa-use-list? `)`1218/// successor-list? (`(` region-list `)`)?1219/// attribute-dict? `:` function-type1220/// custom-operation ::= bare-id custom-operation-format1221/// op-result-list ::= op-result (`,` op-result)* `=`1222/// op-result ::= ssa-id (`:` integer-literal)1223///1224ParseResult OperationParser::parseOperation() {1225 auto loc = getToken().getLoc();1226 SmallVector<ResultRecord, 1> resultIDs;1227 size_t numExpectedResults = 0;1228 if (getToken().is(Token::percent_identifier)) {1229 // Parse the group of result ids.1230 auto parseNextResult = [&]() -> ParseResult {1231 // Parse the next result id.1232 Token nameTok = getToken();1233 if (parseToken(Token::percent_identifier,1234 "expected valid ssa identifier"))1235 return failure();1236 1237 // If the next token is a ':', we parse the expected result count.1238 size_t expectedSubResults = 1;1239 if (consumeIf(Token::colon)) {1240 // Check that the next token is an integer.1241 if (!getToken().is(Token::integer))1242 return emitWrongTokenError("expected integer number of results");1243 1244 // Check that number of results is > 0.1245 auto val = getToken().getUInt64IntegerValue();1246 if (!val || *val < 1)1247 return emitError(1248 "expected named operation to have at least 1 result");1249 consumeToken(Token::integer);1250 expectedSubResults = *val;1251 }1252 1253 resultIDs.emplace_back(nameTok.getSpelling(), expectedSubResults,1254 nameTok.getLoc());1255 numExpectedResults += expectedSubResults;1256 return success();1257 };1258 if (parseCommaSeparatedList(parseNextResult))1259 return failure();1260 1261 if (parseToken(Token::equal, "expected '=' after SSA name"))1262 return failure();1263 }1264 1265 Operation *op;1266 Token nameTok = getToken();1267 if (nameTok.is(Token::bare_identifier) || nameTok.isKeyword())1268 op = parseCustomOperation(resultIDs);1269 else if (nameTok.is(Token::string))1270 op = parseGenericOperation();1271 else if (nameTok.isCodeCompletionFor(Token::string))1272 return codeCompleteStringDialectOrOperationName(nameTok.getStringValue());1273 else if (nameTok.isCodeCompletion())1274 return codeCompleteDialectOrElidedOpName(loc);1275 else1276 return emitWrongTokenError("expected operation name in quotes");1277 1278 // If parsing of the basic operation failed, then this whole thing fails.1279 if (!op)1280 return failure();1281 1282 // If the operation had a name, register it.1283 if (!resultIDs.empty()) {1284 if (op->getNumResults() == 0)1285 return emitError(loc, "cannot name an operation with no results");1286 if (numExpectedResults != op->getNumResults())1287 return emitError(loc, "operation defines ")1288 << op->getNumResults() << " results but was provided "1289 << numExpectedResults << " to bind";1290 1291 // Add this operation to the assembly state if it was provided to populate.1292 if (state.asmState) {1293 unsigned resultIt = 0;1294 SmallVector<std::pair<unsigned, SMLoc>> asmResultGroups;1295 asmResultGroups.reserve(resultIDs.size());1296 for (ResultRecord &record : resultIDs) {1297 asmResultGroups.emplace_back(resultIt, std::get<2>(record));1298 resultIt += std::get<1>(record);1299 }1300 state.asmState->finalizeOperationDefinition(1301 op, nameTok.getLocRange(), /*endLoc=*/getLastToken().getEndLoc(),1302 asmResultGroups);1303 }1304 1305 // Add definitions for each of the result groups.1306 unsigned opResI = 0;1307 for (ResultRecord &resIt : resultIDs) {1308 for (unsigned subRes : llvm::seq<unsigned>(0, std::get<1>(resIt))) {1309 if (addDefinition({std::get<2>(resIt), std::get<0>(resIt), subRes},1310 op->getResult(opResI++)))1311 return failure();1312 }1313 }1314 1315 // Add this operation to the assembly state if it was provided to populate.1316 } else if (state.asmState) {1317 state.asmState->finalizeOperationDefinition(1318 op, nameTok.getLocRange(),1319 /*endLoc=*/getLastToken().getEndLoc());1320 }1321 1322 return success();1323}1324 1325/// Parse a single operation successor.1326///1327/// successor ::= block-id1328///1329ParseResult OperationParser::parseSuccessor(Block *&dest) {1330 if (getToken().isCodeCompletion())1331 return codeCompleteBlock();1332 1333 // Verify branch is identifier and get the matching block.1334 if (!getToken().is(Token::caret_identifier))1335 return emitWrongTokenError("expected block name");1336 dest = getBlockNamed(getTokenSpelling(), getToken().getLoc());1337 consumeToken();1338 return success();1339}1340 1341/// Parse a comma-separated list of operation successors in brackets.1342///1343/// successor-list ::= `[` successor (`,` successor )* `]`1344///1345ParseResult1346OperationParser::parseSuccessors(SmallVectorImpl<Block *> &destinations) {1347 if (parseToken(Token::l_square, "expected '['"))1348 return failure();1349 1350 auto parseElt = [this, &destinations] {1351 Block *dest;1352 ParseResult res = parseSuccessor(dest);1353 destinations.push_back(dest);1354 return res;1355 };1356 return parseCommaSeparatedListUntil(Token::r_square, parseElt,1357 /*allowEmptyList=*/false);1358}1359 1360namespace {1361// RAII-style guard for cleaning up the regions in the operation state before1362// deleting them. Within the parser, regions may get deleted if parsing failed,1363// and other errors may be present, in particular undominated uses. This makes1364// sure such uses are deleted.1365struct CleanupOpStateRegions {1366 ~CleanupOpStateRegions() {1367 SmallVector<Region *, 4> regionsToClean;1368 regionsToClean.reserve(state.regions.size());1369 for (auto ®ion : state.regions)1370 if (region)1371 for (auto &block : *region)1372 block.dropAllDefinedValueUses();1373 }1374 OperationState &state;1375};1376} // namespace1377 1378ParseResult OperationParser::parseGenericOperationAfterOpName(1379 OperationState &result,1380 std::optional<ArrayRef<UnresolvedOperand>> parsedOperandUseInfo,1381 std::optional<ArrayRef<Block *>> parsedSuccessors,1382 std::optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions,1383 std::optional<ArrayRef<NamedAttribute>> parsedAttributes,1384 std::optional<Attribute> propertiesAttribute,1385 std::optional<FunctionType> parsedFnType) {1386 1387 // Parse the operand list, if not explicitly provided.1388 SmallVector<UnresolvedOperand, 8> opInfo;1389 if (!parsedOperandUseInfo) {1390 if (parseToken(Token::l_paren, "expected '(' to start operand list") ||1391 parseOptionalSSAUseList(opInfo) ||1392 parseToken(Token::r_paren, "expected ')' to end operand list")) {1393 return failure();1394 }1395 parsedOperandUseInfo = opInfo;1396 }1397 1398 // Parse the successor list, if not explicitly provided.1399 if (!parsedSuccessors) {1400 if (getToken().is(Token::l_square)) {1401 // Check if the operation is not a known terminator.1402 if (!result.name.mightHaveTrait<OpTrait::IsTerminator>())1403 return emitError("successors in non-terminator");1404 1405 SmallVector<Block *, 2> successors;1406 if (parseSuccessors(successors))1407 return failure();1408 result.addSuccessors(successors);1409 }1410 } else {1411 result.addSuccessors(*parsedSuccessors);1412 }1413 1414 // Parse the properties, if not explicitly provided.1415 if (propertiesAttribute) {1416 result.propertiesAttr = *propertiesAttribute;1417 } else if (consumeIf(Token::less)) {1418 result.propertiesAttr = parseAttribute();1419 if (!result.propertiesAttr)1420 return failure();1421 if (parseToken(Token::greater, "expected '>' to close properties"))1422 return failure();1423 }1424 // Parse the region list, if not explicitly provided.1425 if (!parsedRegions) {1426 if (consumeIf(Token::l_paren)) {1427 do {1428 // Create temporary regions with the top level region as parent.1429 result.regions.emplace_back(new Region(topLevelOp));1430 if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))1431 return failure();1432 } while (consumeIf(Token::comma));1433 if (parseToken(Token::r_paren, "expected ')' to end region list"))1434 return failure();1435 }1436 } else {1437 result.addRegions(*parsedRegions);1438 }1439 1440 // Parse the attributes, if not explicitly provided.1441 if (!parsedAttributes) {1442 if (getToken().is(Token::l_brace)) {1443 if (parseAttributeDict(result.attributes))1444 return failure();1445 }1446 } else {1447 result.addAttributes(*parsedAttributes);1448 }1449 1450 // Parse the operation type, if not explicitly provided.1451 Location typeLoc = result.location;1452 if (!parsedFnType) {1453 if (parseToken(Token::colon, "expected ':' followed by operation type"))1454 return failure();1455 1456 typeLoc = getEncodedSourceLocation(getToken().getLoc());1457 auto type = parseType();1458 if (!type)1459 return failure();1460 auto fnType = dyn_cast<FunctionType>(type);1461 if (!fnType)1462 return mlir::emitError(typeLoc, "expected function type");1463 1464 parsedFnType = fnType;1465 }1466 1467 result.addTypes(parsedFnType->getResults());1468 1469 // Check that we have the right number of types for the operands.1470 ArrayRef<Type> operandTypes = parsedFnType->getInputs();1471 if (operandTypes.size() != parsedOperandUseInfo->size()) {1472 auto plural = "s"[parsedOperandUseInfo->size() == 1];1473 return mlir::emitError(typeLoc, "expected ")1474 << parsedOperandUseInfo->size() << " operand type" << plural1475 << " but had " << operandTypes.size();1476 }1477 1478 // Resolve all of the operands.1479 for (unsigned i = 0, e = parsedOperandUseInfo->size(); i != e; ++i) {1480 result.operands.push_back(1481 resolveSSAUse((*parsedOperandUseInfo)[i], operandTypes[i]));1482 if (!result.operands.back())1483 return failure();1484 }1485 1486 return success();1487}1488 1489Operation *OperationParser::parseGenericOperation() {1490 // Get location information for the operation.1491 auto srcLocation = getEncodedSourceLocation(getToken().getLoc());1492 1493 std::string name = getToken().getStringValue();1494 if (name.empty())1495 return (emitError("empty operation name is invalid"), nullptr);1496 if (name.find('\0') != StringRef::npos)1497 return (emitError("null character not allowed in operation name"), nullptr);1498 1499 consumeToken(Token::string);1500 1501 OperationState result(srcLocation, name);1502 CleanupOpStateRegions guard{result};1503 1504 // Lazy load dialects in the context as needed.1505 if (!result.name.isRegistered()) {1506 StringRef dialectName = StringRef(name).split('.').first;1507 if (!getContext()->getLoadedDialect(dialectName) &&1508 !getContext()->getOrLoadDialect(dialectName)) {1509 if (!getContext()->allowsUnregisteredDialects()) {1510 // Emit an error if the dialect couldn't be loaded (i.e., it was not1511 // registered) and unregistered dialects aren't allowed.1512 emitError("operation being parsed with an unregistered dialect. If "1513 "this is intended, please use -allow-unregistered-dialect "1514 "with the MLIR tool used");1515 return nullptr;1516 }1517 } else {1518 // Reload the OperationName now that the dialect is loaded.1519 result.name = OperationName(name, getContext());1520 }1521 }1522 1523 // If we are populating the parser state, start a new operation definition.1524 if (state.asmState)1525 state.asmState->startOperationDefinition(result.name);1526 1527 if (parseGenericOperationAfterOpName(result))1528 return nullptr;1529 1530 // Operation::create() is not allowed to fail, however setting the properties1531 // from an attribute is a failable operation. So we save the attribute here1532 // and set it on the operation post-parsing.1533 Attribute properties;1534 std::swap(properties, result.propertiesAttr);1535 1536 // If we don't have properties in the textual IR, but the operation now has1537 // support for properties, we support some backward-compatible generic syntax1538 // for the operation and as such we accept inherent attributes mixed in the1539 // dictionary of discardable attributes. We pre-validate these here because1540 // invalid attributes can't be casted to the properties storage and will be1541 // silently dropped. For example an attribute { foo = 0 : i32 } that is1542 // declared as F32Attr in ODS would have a C++ type of FloatAttr in the1543 // properties array. When setting it we would do something like:1544 //1545 // properties.foo = dyn_cast<FloatAttr>(fooAttr);1546 //1547 // which would end up with a null Attribute. The diagnostic from the verifier1548 // would be "missing foo attribute" instead of something like "expects a 321549 // bits float attribute but got a 32 bits integer attribute".1550 if (!properties && !result.getRawProperties()) {1551 std::optional<RegisteredOperationName> info =1552 result.name.getRegisteredInfo();1553 if (info) {1554 if (failed(info->verifyInherentAttrs(result.attributes, [&]() {1555 return mlir::emitError(srcLocation) << "'" << name << "' op ";1556 })))1557 return nullptr;1558 }1559 }1560 1561 // Create the operation and try to parse a location for it.1562 Operation *op = opBuilder.create(result);1563 if (parseTrailingLocationSpecifier(op))1564 return nullptr;1565 1566 // Try setting the properties for the operation, using a diagnostic to print1567 // errors.1568 if (properties) {1569 auto emitError = [&]() {1570 return mlir::emitError(srcLocation, "invalid properties ")1571 << properties << " for op " << name << ": ";1572 };1573 if (failed(op->setPropertiesFromAttribute(properties, emitError)))1574 return nullptr;1575 }1576 1577 return op;1578}1579 1580Operation *OperationParser::parseGenericOperation(Block *insertBlock,1581 Block::iterator insertPt) {1582 Token nameToken = getToken();1583 1584 OpBuilder::InsertionGuard restoreInsertionPoint(opBuilder);1585 opBuilder.setInsertionPoint(insertBlock, insertPt);1586 Operation *op = parseGenericOperation();1587 if (!op)1588 return nullptr;1589 1590 // If we are populating the parser asm state, finalize this operation1591 // definition.1592 if (state.asmState)1593 state.asmState->finalizeOperationDefinition(1594 op, nameToken.getLocRange(),1595 /*endLoc=*/getLastToken().getEndLoc());1596 return op;1597}1598 1599namespace {1600class CustomOpAsmParser : public AsmParserImpl<OpAsmParser> {1601public:1602 CustomOpAsmParser(1603 SMLoc nameLoc, ArrayRef<OperationParser::ResultRecord> resultIDs,1604 function_ref<ParseResult(OpAsmParser &, OperationState &)> parseAssembly,1605 bool isIsolatedFromAbove, StringRef opName, OperationParser &parser)1606 : AsmParserImpl<OpAsmParser>(nameLoc, parser), resultIDs(resultIDs),1607 parseAssembly(parseAssembly), isIsolatedFromAbove(isIsolatedFromAbove),1608 opName(opName), parser(parser) {1609 (void)isIsolatedFromAbove; // Only used in assert, silence unused warning.1610 }1611 1612 /// Parse an instance of the operation described by 'opDefinition' into the1613 /// provided operation state.1614 ParseResult parseOperation(OperationState &opState) {1615 if (parseAssembly(*this, opState))1616 return failure();1617 // Verify that the parsed attributes does not have duplicate attributes.1618 // This can happen if an attribute set during parsing is also specified in1619 // the attribute dictionary in the assembly, or the attribute is set1620 // multiple during parsing.1621 std::optional<NamedAttribute> duplicate =1622 opState.attributes.findDuplicate();1623 if (duplicate)1624 return emitError(getNameLoc(), "attribute '")1625 << duplicate->getName().getValue()1626 << "' occurs more than once in the attribute list";1627 return success();1628 }1629 1630 Operation *parseGenericOperation(Block *insertBlock,1631 Block::iterator insertPt) final {1632 return parser.parseGenericOperation(insertBlock, insertPt);1633 }1634 1635 FailureOr<OperationName> parseCustomOperationName() final {1636 return parser.parseCustomOperationName();1637 }1638 1639 ParseResult parseGenericOperationAfterOpName(1640 OperationState &result,1641 std::optional<ArrayRef<UnresolvedOperand>> parsedUnresolvedOperands,1642 std::optional<ArrayRef<Block *>> parsedSuccessors,1643 std::optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions,1644 std::optional<ArrayRef<NamedAttribute>> parsedAttributes,1645 std::optional<Attribute> parsedPropertiesAttribute,1646 std::optional<FunctionType> parsedFnType) final {1647 return parser.parseGenericOperationAfterOpName(1648 result, parsedUnresolvedOperands, parsedSuccessors, parsedRegions,1649 parsedAttributes, parsedPropertiesAttribute, parsedFnType);1650 }1651 //===--------------------------------------------------------------------===//1652 // Utilities1653 //===--------------------------------------------------------------------===//1654 1655 /// Return the name of the specified result in the specified syntax, as well1656 /// as the subelement in the name. For example, in this operation:1657 ///1658 /// %x, %y:2, %z = foo.op1659 ///1660 /// getResultName(0) == {"x", 0 }1661 /// getResultName(1) == {"y", 0 }1662 /// getResultName(2) == {"y", 1 }1663 /// getResultName(3) == {"z", 0 }1664 std::pair<StringRef, unsigned>1665 getResultName(unsigned resultNo) const override {1666 // Scan for the resultID that contains this result number.1667 for (const auto &entry : resultIDs) {1668 if (resultNo < std::get<1>(entry)) {1669 // Don't pass on the leading %.1670 StringRef name = std::get<0>(entry).drop_front();1671 return {name, resultNo};1672 }1673 resultNo -= std::get<1>(entry);1674 }1675 1676 // Invalid result number.1677 return {"", ~0U};1678 }1679 1680 /// Return the number of declared SSA results. This returns 4 for the foo.op1681 /// example in the comment for getResultName.1682 size_t getNumResults() const override {1683 size_t count = 0;1684 for (auto &entry : resultIDs)1685 count += std::get<1>(entry);1686 return count;1687 }1688 1689 /// Emit a diagnostic at the specified location and return failure.1690 InFlightDiagnostic emitError(SMLoc loc, const Twine &message) override {1691 return AsmParserImpl<OpAsmParser>::emitError(loc, "custom op '" + opName +1692 "' " + message);1693 }1694 1695 //===--------------------------------------------------------------------===//1696 // Operand Parsing1697 //===--------------------------------------------------------------------===//1698 1699 /// Parse a single operand.1700 ParseResult parseOperand(UnresolvedOperand &result,1701 bool allowResultNumber = true) override {1702 OperationParser::UnresolvedOperand useInfo;1703 if (parser.parseSSAUse(useInfo, allowResultNumber))1704 return failure();1705 1706 result = {useInfo.location, useInfo.name, useInfo.number};1707 return success();1708 }1709 1710 /// Parse a single operand if present.1711 OptionalParseResult1712 parseOptionalOperand(UnresolvedOperand &result,1713 bool allowResultNumber = true) override {1714 if (parser.getToken().isOrIsCodeCompletionFor(Token::percent_identifier))1715 return parseOperand(result, allowResultNumber);1716 return std::nullopt;1717 }1718 1719 /// Parse zero or more SSA comma-separated operand references with a specified1720 /// surrounding delimiter, and an optional required operand count.1721 ParseResult parseOperandList(SmallVectorImpl<UnresolvedOperand> &result,1722 Delimiter delimiter = Delimiter::None,1723 bool allowResultNumber = true,1724 int requiredOperandCount = -1) override {1725 // The no-delimiter case has some special handling for better diagnostics.1726 if (delimiter == Delimiter::None) {1727 // parseCommaSeparatedList doesn't handle the missing case for "none",1728 // so we handle it custom here.1729 Token tok = parser.getToken();1730 if (!tok.isOrIsCodeCompletionFor(Token::percent_identifier)) {1731 // If we didn't require any operands or required exactly zero (weird)1732 // then this is success.1733 if (requiredOperandCount == -1 || requiredOperandCount == 0)1734 return success();1735 1736 // Otherwise, try to produce a nice error message.1737 if (tok.isAny(Token::l_paren, Token::l_square))1738 return parser.emitError("unexpected delimiter");1739 return parser.emitWrongTokenError("expected operand");1740 }1741 }1742 1743 auto parseOneOperand = [&]() -> ParseResult {1744 return parseOperand(result.emplace_back(), allowResultNumber);1745 };1746 1747 auto startLoc = parser.getToken().getLoc();1748 if (parseCommaSeparatedList(delimiter, parseOneOperand, " in operand list"))1749 return failure();1750 1751 // Check that we got the expected # of elements.1752 if (requiredOperandCount != -1 &&1753 result.size() != static_cast<size_t>(requiredOperandCount))1754 return emitError(startLoc, "expected ")1755 << requiredOperandCount << " operands";1756 return success();1757 }1758 1759 /// Resolve an operand to an SSA value, emitting an error on failure.1760 ParseResult resolveOperand(const UnresolvedOperand &operand, Type type,1761 SmallVectorImpl<Value> &result) override {1762 if (auto value = parser.resolveSSAUse(operand, type)) {1763 result.push_back(value);1764 return success();1765 }1766 return failure();1767 }1768 1769 /// Parse an AffineMap of SSA ids.1770 ParseResult1771 parseAffineMapOfSSAIds(SmallVectorImpl<UnresolvedOperand> &operands,1772 Attribute &mapAttr, StringRef attrName,1773 NamedAttrList &attrs, Delimiter delimiter) override {1774 SmallVector<UnresolvedOperand, 2> dimOperands;1775 SmallVector<UnresolvedOperand, 1> symOperands;1776 1777 auto parseElement = [&](bool isSymbol) -> ParseResult {1778 UnresolvedOperand operand;1779 if (parseOperand(operand))1780 return failure();1781 if (isSymbol)1782 symOperands.push_back(operand);1783 else1784 dimOperands.push_back(operand);1785 return success();1786 };1787 1788 AffineMap map;1789 if (parser.parseAffineMapOfSSAIds(map, parseElement, delimiter))1790 return failure();1791 // Add AffineMap attribute.1792 if (map) {1793 mapAttr = AffineMapAttr::get(map);1794 attrs.push_back(parser.builder.getNamedAttr(attrName, mapAttr));1795 }1796 1797 // Add dim operands before symbol operands in 'operands'.1798 operands.assign(dimOperands.begin(), dimOperands.end());1799 operands.append(symOperands.begin(), symOperands.end());1800 return success();1801 }1802 1803 /// Parse an AffineExpr of SSA ids.1804 ParseResult1805 parseAffineExprOfSSAIds(SmallVectorImpl<UnresolvedOperand> &dimOperands,1806 SmallVectorImpl<UnresolvedOperand> &symbOperands,1807 AffineExpr &expr) override {1808 auto parseElement = [&](bool isSymbol) -> ParseResult {1809 UnresolvedOperand operand;1810 if (parseOperand(operand))1811 return failure();1812 if (isSymbol)1813 symbOperands.push_back(operand);1814 else1815 dimOperands.push_back(operand);1816 return success();1817 };1818 1819 return parser.parseAffineExprOfSSAIds(expr, parseElement);1820 }1821 1822 //===--------------------------------------------------------------------===//1823 // Argument Parsing1824 //===--------------------------------------------------------------------===//1825 1826 /// Parse a single argument with the following syntax:1827 ///1828 /// `%ssaname : !type { optionalAttrDict} loc(optionalSourceLoc)`1829 ///1830 /// If `allowType` is false or `allowAttrs` are false then the respective1831 /// parts of the grammar are not parsed.1832 ParseResult parseArgument(Argument &result, bool allowType = false,1833 bool allowAttrs = false) override {1834 NamedAttrList attrs;1835 if (parseOperand(result.ssaName, /*allowResultNumber=*/false) ||1836 (allowType && parseColonType(result.type)) ||1837 (allowAttrs && parseOptionalAttrDict(attrs)) ||1838 parseOptionalLocationSpecifier(result.sourceLoc))1839 return failure();1840 result.attrs = attrs.getDictionary(getContext());1841 return success();1842 }1843 1844 /// Parse a single argument if present.1845 OptionalParseResult parseOptionalArgument(Argument &result, bool allowType,1846 bool allowAttrs) override {1847 if (parser.getToken().is(Token::percent_identifier))1848 return parseArgument(result, allowType, allowAttrs);1849 return std::nullopt;1850 }1851 1852 ParseResult parseArgumentList(SmallVectorImpl<Argument> &result,1853 Delimiter delimiter, bool allowType,1854 bool allowAttrs) override {1855 // The no-delimiter case has some special handling for the empty case.1856 if (delimiter == Delimiter::None &&1857 parser.getToken().isNot(Token::percent_identifier))1858 return success();1859 1860 auto parseOneArgument = [&]() -> ParseResult {1861 return parseArgument(result.emplace_back(), allowType, allowAttrs);1862 };1863 return parseCommaSeparatedList(delimiter, parseOneArgument,1864 " in argument list");1865 }1866 1867 //===--------------------------------------------------------------------===//1868 // Region Parsing1869 //===--------------------------------------------------------------------===//1870 1871 /// Parse a region that takes `arguments` of `argTypes` types. This1872 /// effectively defines the SSA values of `arguments` and assigns their type.1873 ParseResult parseRegion(Region ®ion, ArrayRef<Argument> arguments,1874 bool enableNameShadowing) override {1875 // Try to parse the region.1876 (void)isIsolatedFromAbove;1877 assert((!enableNameShadowing || isIsolatedFromAbove) &&1878 "name shadowing is only allowed on isolated regions");1879 if (parser.parseRegion(region, arguments, enableNameShadowing))1880 return failure();1881 return success();1882 }1883 1884 /// Parses a region if present.1885 OptionalParseResult parseOptionalRegion(Region ®ion,1886 ArrayRef<Argument> arguments,1887 bool enableNameShadowing) override {1888 if (parser.getToken().isNot(Token::l_brace))1889 return std::nullopt;1890 return parseRegion(region, arguments, enableNameShadowing);1891 }1892 1893 /// Parses a region if present. If the region is present, a new region is1894 /// allocated and placed in `region`. If no region is present, `region`1895 /// remains untouched.1896 OptionalParseResult1897 parseOptionalRegion(std::unique_ptr<Region> ®ion,1898 ArrayRef<Argument> arguments,1899 bool enableNameShadowing = false) override {1900 if (parser.getToken().isNot(Token::l_brace))1901 return std::nullopt;1902 std::unique_ptr<Region> newRegion = std::make_unique<Region>();1903 if (parseRegion(*newRegion, arguments, enableNameShadowing))1904 return failure();1905 1906 region = std::move(newRegion);1907 return success();1908 }1909 1910 //===--------------------------------------------------------------------===//1911 // Successor Parsing1912 //===--------------------------------------------------------------------===//1913 1914 /// Parse a single operation successor.1915 ParseResult parseSuccessor(Block *&dest) override {1916 return parser.parseSuccessor(dest);1917 }1918 1919 /// Parse an optional operation successor and its operand list.1920 OptionalParseResult parseOptionalSuccessor(Block *&dest) override {1921 if (!parser.getToken().isOrIsCodeCompletionFor(Token::caret_identifier))1922 return std::nullopt;1923 return parseSuccessor(dest);1924 }1925 1926 /// Parse a single operation successor and its operand list.1927 ParseResult1928 parseSuccessorAndUseList(Block *&dest,1929 SmallVectorImpl<Value> &operands) override {1930 if (parseSuccessor(dest))1931 return failure();1932 1933 // Handle optional arguments.1934 if (succeeded(parseOptionalLParen()) &&1935 (parser.parseOptionalSSAUseAndTypeList(operands) || parseRParen())) {1936 return failure();1937 }1938 return success();1939 }1940 1941 //===--------------------------------------------------------------------===//1942 // Type Parsing1943 //===--------------------------------------------------------------------===//1944 1945 /// Parse a list of assignments of the form1946 /// (%x1 = %y1, %x2 = %y2, ...).1947 OptionalParseResult parseOptionalAssignmentList(1948 SmallVectorImpl<Argument> &lhs,1949 SmallVectorImpl<UnresolvedOperand> &rhs) override {1950 if (failed(parseOptionalLParen()))1951 return std::nullopt;1952 1953 auto parseElt = [&]() -> ParseResult {1954 if (parseArgument(lhs.emplace_back()) || parseEqual() ||1955 parseOperand(rhs.emplace_back()))1956 return failure();1957 return success();1958 };1959 return parser.parseCommaSeparatedListUntil(Token::r_paren, parseElt);1960 }1961 1962 /// Parse a loc(...) specifier if present, filling in result if so.1963 ParseResult1964 parseOptionalLocationSpecifier(std::optional<Location> &result) override {1965 // If there is a 'loc' we parse a trailing location.1966 if (!parser.consumeIf(Token::kw_loc))1967 return success();1968 LocationAttr directLoc;1969 if (parser.parseToken(Token::l_paren, "expected '(' in location"))1970 return failure();1971 1972 Token tok = parser.getToken();1973 1974 // Check to see if we are parsing a location alias. We are parsing a1975 // location alias if the token is a hash identifier *without* a dot in it -1976 // the dot signifies a dialect attribute. Otherwise, we parse the location1977 // directly.1978 if (tok.is(Token::hash_identifier) && !tok.getSpelling().contains('.')) {1979 if (parser.parseLocationAlias(directLoc))1980 return failure();1981 } else if (parser.parseLocationInstance(directLoc)) {1982 return failure();1983 }1984 1985 if (parser.parseToken(Token::r_paren, "expected ')' in location"))1986 return failure();1987 1988 result = directLoc;1989 return success();1990 }1991 1992private:1993 /// Information about the result name specifiers.1994 ArrayRef<OperationParser::ResultRecord> resultIDs;1995 1996 /// The abstract information of the operation.1997 function_ref<ParseResult(OpAsmParser &, OperationState &)> parseAssembly;1998 bool isIsolatedFromAbove;1999 StringRef opName;2000 2001 /// The backing operation parser.2002 OperationParser &parser;2003};2004} // namespace2005 2006FailureOr<OperationName> OperationParser::parseCustomOperationName() {2007 Token nameTok = getToken();2008 // Accept keywords here as they may be interpreted as a shortened operation2009 // name, e.g., `dialect.keyword` can be spelled as just `keyword` within a2010 // region of an operation from `dialect`.2011 if (nameTok.getKind() != Token::bare_identifier && !nameTok.isKeyword())2012 return emitError("expected bare identifier or keyword");2013 StringRef opName = nameTok.getSpelling();2014 if (opName.empty())2015 return (emitError("empty operation name is invalid"), failure());2016 consumeToken();2017 2018 // Check to see if this operation name is already registered.2019 std::optional<RegisteredOperationName> opInfo =2020 RegisteredOperationName::lookup(opName, getContext());2021 if (opInfo)2022 return *opInfo;2023 2024 // If the operation doesn't have a dialect prefix try using the default2025 // dialect.2026 auto opNameSplit = opName.split('.');2027 StringRef dialectName = opNameSplit.first;2028 std::string opNameStorage;2029 if (opNameSplit.second.empty()) {2030 // If the name didn't have a prefix, check for a code completion request.2031 if (getToken().isCodeCompletion() && opName.back() == '.')2032 return codeCompleteOperationName(dialectName);2033 2034 dialectName = getState().defaultDialectStack.back();2035 opNameStorage = (dialectName + "." + opName).str();2036 opName = opNameStorage;2037 }2038 2039 // Try to load the dialect before returning the operation name to make sure2040 // the operation has a chance to be registered.2041 getContext()->getOrLoadDialect(dialectName);2042 return OperationName(opName, getContext());2043}2044 2045Operation *2046OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {2047 SMLoc opLoc = getToken().getLoc();2048 StringRef originalOpName = getTokenSpelling();2049 2050 FailureOr<OperationName> opNameInfo = parseCustomOperationName();2051 if (failed(opNameInfo))2052 return nullptr;2053 StringRef opName = opNameInfo->getStringRef();2054 2055 // This is the actual hook for the custom op parsing, usually implemented by2056 // the op itself (`Op::parse()`). We retrieve it either from the2057 // RegisteredOperationName or from the Dialect.2058 OperationName::ParseAssemblyFn parseAssemblyFn;2059 bool isIsolatedFromAbove = false;2060 2061 StringRef defaultDialect = "";2062 if (auto opInfo = opNameInfo->getRegisteredInfo()) {2063 parseAssemblyFn = opInfo->getParseAssemblyFn();2064 isIsolatedFromAbove = opInfo->hasTrait<OpTrait::IsIsolatedFromAbove>();2065 auto *iface = opInfo->getInterface<OpAsmOpInterface>();2066 if (iface && !iface->getDefaultDialect().empty())2067 defaultDialect = iface->getDefaultDialect();2068 } else {2069 std::optional<Dialect::ParseOpHook> dialectHook;2070 Dialect *dialect = opNameInfo->getDialect();2071 if (!dialect) {2072 InFlightDiagnostic diag =2073 emitError(opLoc) << "Dialect `" << opNameInfo->getDialectNamespace()2074 << "' not found for custom op '" << originalOpName2075 << "' ";2076 if (originalOpName != opName)2077 diag << " (tried '" << opName << "' as well)";2078 auto ¬e = diag.attachNote();2079 note << "Available dialects: ";2080 std::vector<StringRef> registered = getContext()->getAvailableDialects();2081 auto loaded = getContext()->getLoadedDialects();2082 2083 // Merge the sorted lists of registered and loaded dialects.2084 SmallVector<std::pair<StringRef, bool>> mergedDialects;2085 auto regIt = registered.begin(), regEnd = registered.end();2086 auto loadIt = loaded.rbegin(), loadEnd = loaded.rend();2087 bool isRegistered = false;2088 bool isOnlyLoaded = true;2089 while (regIt != regEnd && loadIt != loadEnd) {2090 StringRef reg = *regIt;2091 StringRef load = (*loadIt)->getNamespace();2092 if (load < reg) {2093 mergedDialects.emplace_back(load, isOnlyLoaded);2094 ++loadIt;2095 } else {2096 mergedDialects.emplace_back(reg, isRegistered);2097 ++regIt;2098 if (reg == load)2099 ++loadIt;2100 }2101 }2102 for (; regIt != regEnd; ++regIt)2103 mergedDialects.emplace_back(*regIt, isRegistered);2104 for (; loadIt != loadEnd; ++loadIt)2105 mergedDialects.emplace_back((*loadIt)->getNamespace(), isOnlyLoaded);2106 2107 bool loadedUnregistered = false;2108 llvm::interleaveComma(mergedDialects, note, [&](auto &pair) {2109 note << pair.first;2110 if (pair.second) {2111 loadedUnregistered = true;2112 note << " (*)";2113 }2114 });2115 note << " ";2116 if (loadedUnregistered)2117 note << "(* corresponding to loaded but unregistered dialects)";2118 note << "; for more info on dialect registration see "2119 "https://mlir.llvm.org/getting_started/Faq/"2120 "#registered-loaded-dependent-whats-up-with-dialects-management";2121 return nullptr;2122 }2123 dialectHook = dialect->getParseOperationHook(opName);2124 if (!dialectHook) {2125 InFlightDiagnostic diag =2126 emitError(opLoc) << "custom op '" << originalOpName << "' is unknown";2127 if (originalOpName != opName)2128 diag << " (tried '" << opName << "' as well)";2129 return nullptr;2130 }2131 parseAssemblyFn = *dialectHook;2132 }2133 getState().defaultDialectStack.push_back(defaultDialect);2134 auto restoreDefaultDialect = llvm::make_scope_exit(2135 [&]() { getState().defaultDialectStack.pop_back(); });2136 2137 // If the custom op parser crashes, produce some indication to help2138 // debugging.2139 llvm::PrettyStackTraceFormat fmt("MLIR Parser: custom op parser '%s'",2140 opNameInfo->getIdentifier().data());2141 2142 // Get location information for the operation.2143 auto srcLocation = getEncodedSourceLocation(opLoc);2144 OperationState opState(srcLocation, *opNameInfo);2145 2146 // If we are populating the parser state, start a new operation definition.2147 if (state.asmState)2148 state.asmState->startOperationDefinition(opState.name);2149 2150 // Have the op implementation take a crack and parsing this.2151 CleanupOpStateRegions guard{opState};2152 CustomOpAsmParser opAsmParser(opLoc, resultIDs, parseAssemblyFn,2153 isIsolatedFromAbove, opName, *this);2154 if (opAsmParser.parseOperation(opState))2155 return nullptr;2156 2157 // If it emitted an error, we failed.2158 if (opAsmParser.didEmitError())2159 return nullptr;2160 2161 Attribute properties = opState.propertiesAttr;2162 opState.propertiesAttr = Attribute{};2163 2164 // Otherwise, create the operation and try to parse a location for it.2165 Operation *op = opBuilder.create(opState);2166 if (parseTrailingLocationSpecifier(op))2167 return nullptr;2168 2169 // Try setting the properties for the operation.2170 if (properties) {2171 auto emitError = [&]() {2172 return mlir::emitError(srcLocation, "invalid properties ")2173 << properties << " for op " << op->getName().getStringRef()2174 << ": ";2175 };2176 if (failed(op->setPropertiesFromAttribute(properties, emitError)))2177 return nullptr;2178 }2179 return op;2180}2181 2182ParseResult OperationParser::parseLocationAlias(LocationAttr &loc) {2183 Token tok = getToken();2184 consumeToken(Token::hash_identifier);2185 StringRef identifier = tok.getSpelling().drop_front();2186 assert(!identifier.contains('.') &&2187 "unexpected dialect attribute token, expected alias");2188 2189 if (state.asmState)2190 state.asmState->addAttrAliasUses(identifier, tok.getLocRange());2191 2192 // If this alias can be resolved, do it now.2193 Attribute attr = state.symbols.attributeAliasDefinitions.lookup(identifier);2194 if (attr) {2195 if (!(loc = dyn_cast<LocationAttr>(attr)))2196 return emitError(tok.getLoc())2197 << "expected location, but found '" << attr << "'";2198 } else {2199 // Otherwise, remember this operation and resolve its location later.2200 // In the meantime, use a special OpaqueLoc as a marker.2201 loc = OpaqueLoc::get(deferredLocsReferences.size(),2202 TypeID::get<DeferredLocInfo *>(),2203 UnknownLoc::get(getContext()));2204 deferredLocsReferences.push_back(DeferredLocInfo{tok.getLoc(), identifier});2205 }2206 return success();2207}2208 2209ParseResult2210OperationParser::parseTrailingLocationSpecifier(OpOrArgument opOrArgument) {2211 // If there is a 'loc' we parse a trailing location.2212 if (!consumeIf(Token::kw_loc))2213 return success();2214 if (parseToken(Token::l_paren, "expected '(' in location"))2215 return failure();2216 Token tok = getToken();2217 2218 // Check to see if we are parsing a location alias. We are parsing a location2219 // alias if the token is a hash identifier *without* a dot in it - the dot2220 // signifies a dialect attribute. Otherwise, we parse the location directly.2221 LocationAttr directLoc;2222 if (tok.is(Token::hash_identifier) && !tok.getSpelling().contains('.')) {2223 if (parseLocationAlias(directLoc))2224 return failure();2225 } else if (parseLocationInstance(directLoc)) {2226 return failure();2227 }2228 2229 if (parseToken(Token::r_paren, "expected ')' in location"))2230 return failure();2231 2232 if (auto *op = llvm::dyn_cast_if_present<Operation *>(opOrArgument))2233 op->setLoc(directLoc);2234 else2235 cast<BlockArgument>(opOrArgument).setLoc(directLoc);2236 return success();2237}2238 2239//===----------------------------------------------------------------------===//2240// Region Parsing2241//===----------------------------------------------------------------------===//2242 2243ParseResult OperationParser::parseRegion(Region ®ion,2244 ArrayRef<Argument> entryArguments,2245 bool isIsolatedNameScope) {2246 // Parse the '{'.2247 Token lBraceTok = getToken();2248 if (parseToken(Token::l_brace, "expected '{' to begin a region"))2249 return failure();2250 2251 // If we are populating the parser state, start a new region definition.2252 if (state.asmState)2253 state.asmState->startRegionDefinition();2254 2255 // Parse the region body.2256 if ((!entryArguments.empty() || getToken().isNot(Token::r_brace)) &&2257 parseRegionBody(region, lBraceTok.getLoc(), entryArguments,2258 isIsolatedNameScope)) {2259 return failure();2260 }2261 consumeToken(Token::r_brace);2262 2263 // If we are populating the parser state, finalize this region.2264 if (state.asmState)2265 state.asmState->finalizeRegionDefinition();2266 2267 return success();2268}2269 2270ParseResult OperationParser::parseRegionBody(Region ®ion, SMLoc startLoc,2271 ArrayRef<Argument> entryArguments,2272 bool isIsolatedNameScope) {2273 auto currentPt = opBuilder.saveInsertionPoint();2274 2275 // Push a new named value scope.2276 pushSSANameScope(isIsolatedNameScope);2277 2278 // Parse the first block directly to allow for it to be unnamed.2279 auto owningBlock = std::make_unique<Block>();2280 auto failureCleanup = llvm::make_scope_exit([&] {2281 if (owningBlock) {2282 // If parsing failed, as indicated by the fact that `owningBlock` still2283 // owns the block, drop all forward references from preceding operations2284 // to definitions within the parsed block.2285 owningBlock->dropAllDefinedValueUses();2286 }2287 });2288 Block *block = owningBlock.get();2289 2290 // If this block is not defined in the source file, add a definition for it2291 // now in the assembly state. Blocks with a name will be defined when the name2292 // is parsed.2293 if (state.asmState && getToken().isNot(Token::caret_identifier))2294 state.asmState->addDefinition(block, startLoc);2295 2296 // Add arguments to the entry block if we had the form with explicit names.2297 if (!entryArguments.empty() && !entryArguments[0].ssaName.name.empty()) {2298 // If we had named arguments, then don't allow a block name.2299 if (getToken().is(Token::caret_identifier))2300 return emitError("invalid block name in region with named arguments");2301 2302 for (auto &entryArg : entryArguments) {2303 auto &argInfo = entryArg.ssaName;2304 2305 // Ensure that the argument was not already defined.2306 if (auto defLoc = getReferenceLoc(argInfo.name, argInfo.number)) {2307 return emitError(argInfo.location, "region entry argument '" +2308 argInfo.name +2309 "' is already in use")2310 .attachNote(getEncodedSourceLocation(*defLoc))2311 << "previously referenced here";2312 }2313 Location loc = entryArg.sourceLoc.has_value()2314 ? *entryArg.sourceLoc2315 : getEncodedSourceLocation(argInfo.location);2316 BlockArgument arg = block->addArgument(entryArg.type, loc);2317 2318 // Add a definition of this arg to the assembly state if provided.2319 if (state.asmState)2320 state.asmState->addDefinition(arg, argInfo.location);2321 2322 // Record the definition for this argument.2323 if (addDefinition(argInfo, arg))2324 return failure();2325 }2326 }2327 2328 if (parseBlock(block))2329 return failure();2330 2331 // Verify that no other arguments were parsed.2332 if (!entryArguments.empty() &&2333 block->getNumArguments() > entryArguments.size()) {2334 return emitError("entry block arguments were already defined");2335 }2336 2337 // Parse the rest of the region.2338 region.push_back(owningBlock.release());2339 while (getToken().isNot(Token::r_brace)) {2340 Block *newBlock = nullptr;2341 if (parseBlock(newBlock))2342 return failure();2343 region.push_back(newBlock);2344 }2345 2346 // Pop the SSA value scope for this region.2347 if (popSSANameScope())2348 return failure();2349 2350 // Reset the original insertion point.2351 opBuilder.restoreInsertionPoint(currentPt);2352 return success();2353}2354 2355//===----------------------------------------------------------------------===//2356// Block Parsing2357//===----------------------------------------------------------------------===//2358 2359/// Block declaration.2360///2361/// block ::= block-label? operation*2362/// block-label ::= block-id block-arg-list? `:`2363/// block-id ::= caret-id2364/// block-arg-list ::= `(` ssa-id-and-type-list? `)`2365///2366ParseResult OperationParser::parseBlock(Block *&block) {2367 // The first block of a region may already exist, if it does the caret2368 // identifier is optional.2369 if (block && getToken().isNot(Token::caret_identifier))2370 return parseBlockBody(block);2371 2372 SMLoc nameLoc = getToken().getLoc();2373 auto name = getTokenSpelling();2374 if (parseToken(Token::caret_identifier, "expected block name"))2375 return failure();2376 2377 // Define the block with the specified name.2378 auto &blockAndLoc = getBlockInfoByName(name);2379 blockAndLoc.loc = nameLoc;2380 2381 // Use a unique pointer for in-flight block being parsed. Release ownership2382 // only in the case of a successful parse. This ensures that the Block2383 // allocated is released if the parse fails and control returns early.2384 std::unique_ptr<Block> inflightBlock;2385 auto cleanupOnFailure = llvm::make_scope_exit([&] {2386 if (inflightBlock)2387 inflightBlock->dropAllDefinedValueUses();2388 });2389 2390 // If a block has yet to be set, this is a new definition. If the caller2391 // provided a block, use it. Otherwise create a new one.2392 if (!blockAndLoc.block) {2393 if (block) {2394 blockAndLoc.block = block;2395 } else {2396 inflightBlock = std::make_unique<Block>();2397 blockAndLoc.block = inflightBlock.get();2398 }2399 2400 // Otherwise, the block has a forward declaration. Forward declarations are2401 // removed once defined, so if we are defining a existing block and it is2402 // not a forward declaration, then it is a redeclaration. Fail if the block2403 // was already defined.2404 } else if (!eraseForwardRef(blockAndLoc.block)) {2405 return emitError(nameLoc, "redefinition of block '") << name << "'";2406 } else {2407 // This was a forward reference block that is now floating. Keep track of it2408 // as inflight in case of error, so that it gets cleaned up properly.2409 inflightBlock.reset(blockAndLoc.block);2410 }2411 2412 // Populate the high level assembly state if necessary.2413 if (state.asmState)2414 state.asmState->addDefinition(blockAndLoc.block, nameLoc);2415 block = blockAndLoc.block;2416 2417 // If an argument list is present, parse it.2418 if (getToken().is(Token::l_paren))2419 if (parseOptionalBlockArgList(block))2420 return failure();2421 if (parseToken(Token::colon, "expected ':' after block name"))2422 return failure();2423 2424 // Parse the body of the block.2425 ParseResult res = parseBlockBody(block);2426 2427 // If parsing was successful, drop the inflight block. We relinquish ownership2428 // back up to the caller.2429 if (succeeded(res))2430 (void)inflightBlock.release();2431 return res;2432}2433 2434ParseResult OperationParser::parseBlockBody(Block *block) {2435 // Set the insertion point to the end of the block to parse.2436 opBuilder.setInsertionPointToEnd(block);2437 2438 // Parse the list of operations that make up the body of the block.2439 while (getToken().isNot(Token::caret_identifier, Token::r_brace))2440 if (parseOperation())2441 return failure();2442 2443 return success();2444}2445 2446/// Get the block with the specified name, creating it if it doesn't already2447/// exist. The location specified is the point of use, which allows2448/// us to diagnose references to blocks that are not defined precisely.2449Block *OperationParser::getBlockNamed(StringRef name, SMLoc loc) {2450 BlockDefinition &blockDef = getBlockInfoByName(name);2451 if (!blockDef.block) {2452 blockDef = {new Block(), loc};2453 insertForwardRef(blockDef.block, blockDef.loc);2454 }2455 2456 // Populate the high level assembly state if necessary.2457 if (state.asmState)2458 state.asmState->addUses(blockDef.block, loc);2459 2460 return blockDef.block;2461}2462 2463/// Parse a (possibly empty) list of SSA operands with types as block arguments2464/// enclosed in parentheses.2465///2466/// value-id-and-type-list ::= value-id-and-type (`,` ssa-id-and-type)*2467/// block-arg-list ::= `(` value-id-and-type-list? `)`2468///2469ParseResult OperationParser::parseOptionalBlockArgList(Block *owner) {2470 if (getToken().is(Token::r_brace))2471 return success();2472 2473 // If the block already has arguments, then we're handling the entry block.2474 // Parse and register the names for the arguments, but do not add them.2475 bool definingExistingArgs = owner->getNumArguments() != 0;2476 unsigned nextArgument = 0;2477 2478 return parseCommaSeparatedList(Delimiter::Paren, [&]() -> ParseResult {2479 return parseSSADefOrUseAndType(2480 [&](UnresolvedOperand useInfo, Type type) -> ParseResult {2481 BlockArgument arg;2482 2483 // If we are defining existing arguments, ensure that the argument2484 // has already been created with the right type.2485 if (definingExistingArgs) {2486 // Otherwise, ensure that this argument has already been created.2487 if (nextArgument >= owner->getNumArguments())2488 return emitError("too many arguments specified in argument list");2489 2490 // Finally, make sure the existing argument has the correct type.2491 arg = owner->getArgument(nextArgument++);2492 if (arg.getType() != type)2493 return emitError("argument and block argument type mismatch");2494 } else {2495 auto loc = getEncodedSourceLocation(useInfo.location);2496 arg = owner->addArgument(type, loc);2497 }2498 2499 // If the argument has an explicit loc(...) specifier, parse and apply2500 // it.2501 if (parseTrailingLocationSpecifier(arg))2502 return failure();2503 2504 // Mark this block argument definition in the parser state if it was2505 // provided.2506 if (state.asmState)2507 state.asmState->addDefinition(arg, useInfo.location);2508 2509 return addDefinition(useInfo, arg);2510 });2511 });2512}2513 2514//===----------------------------------------------------------------------===//2515// Code Completion2516//===----------------------------------------------------------------------===//2517 2518ParseResult OperationParser::codeCompleteSSAUse() {2519 for (IsolatedSSANameScope &scope : isolatedNameScopes) {2520 for (auto &it : scope.values) {2521 if (it.second.empty())2522 continue;2523 Value frontValue = it.second.front().value;2524 2525 std::string detailData;2526 llvm::raw_string_ostream detailOS(detailData);2527 2528 // If the value isn't a forward reference, we also add the name of the op2529 // to the detail.2530 if (auto result = dyn_cast<OpResult>(frontValue)) {2531 if (!forwardRefPlaceholders.count(result))2532 detailOS << result.getOwner()->getName() << ": ";2533 } else {2534 detailOS << "arg #" << cast<BlockArgument>(frontValue).getArgNumber()2535 << ": ";2536 }2537 2538 // Emit the type of the values to aid with completion selection.2539 detailOS << frontValue.getType();2540 2541 // FIXME: We should define a policy for packed values, e.g. with a limit2542 // on the detail size, but it isn't clear what would be useful right now.2543 // For now we just only emit the first type.2544 if (it.second.size() > 1)2545 detailOS << ", ...";2546 2547 state.codeCompleteContext->appendSSAValueCompletion(2548 it.getKey(), std::move(detailData));2549 }2550 }2551 2552 return failure();2553}2554 2555ParseResult OperationParser::codeCompleteBlock() {2556 // Don't provide completions if the token isn't empty, e.g. this avoids2557 // weirdness when we encounter a `.` within the identifier.2558 StringRef spelling = getTokenSpelling();2559 if (!(spelling.empty() || spelling == "^"))2560 return failure();2561 2562 for (const auto &it : blocksByName.back())2563 state.codeCompleteContext->appendBlockCompletion(it.getFirst());2564 return failure();2565}2566 2567//===----------------------------------------------------------------------===//2568// Top-level entity parsing.2569//===----------------------------------------------------------------------===//2570 2571namespace {2572/// This parser handles entities that are only valid at the top level of the2573/// file.2574class TopLevelOperationParser : public Parser {2575public:2576 explicit TopLevelOperationParser(ParserState &state) : Parser(state) {}2577 2578 /// Parse a set of operations into the end of the given Block.2579 ParseResult parse(Block *topLevelBlock, Location parserLoc);2580 2581private:2582 /// Parse an attribute alias declaration.2583 ///2584 /// attribute-alias-def ::= '#' alias-name `=` attribute-value2585 ///2586 ParseResult parseAttributeAliasDef();2587 2588 /// Parse a type alias declaration.2589 ///2590 /// type-alias-def ::= '!' alias-name `=` type2591 ///2592 ParseResult parseTypeAliasDef();2593 2594 /// Parse a top-level file metadata dictionary.2595 ///2596 /// file-metadata-dict ::= '{-#' file-metadata-entry* `#-}'2597 ///2598 ParseResult parseFileMetadataDictionary();2599 2600 /// Parse a resource metadata dictionary.2601 ParseResult parseResourceFileMetadata(2602 function_ref<ParseResult(StringRef, SMLoc)> parseBody);2603 ParseResult parseDialectResourceFileMetadata();2604 ParseResult parseExternalResourceFileMetadata();2605};2606 2607/// This class represents an implementation of a resource entry for the MLIR2608/// textual format.2609class ParsedResourceEntry : public AsmParsedResourceEntry {2610public:2611 ParsedResourceEntry(std::string key, SMLoc keyLoc, Token value, Parser &p)2612 : key(std::move(key)), keyLoc(keyLoc), value(value), p(p) {}2613 ~ParsedResourceEntry() override = default;2614 2615 StringRef getKey() const final { return key; }2616 2617 InFlightDiagnostic emitError() const final { return p.emitError(keyLoc); }2618 2619 AsmResourceEntryKind getKind() const final {2620 if (value.isAny(Token::kw_true, Token::kw_false))2621 return AsmResourceEntryKind::Bool;2622 return value.getSpelling().starts_with("\"0x")2623 ? AsmResourceEntryKind::Blob2624 : AsmResourceEntryKind::String;2625 }2626 2627 FailureOr<bool> parseAsBool() const final {2628 if (value.is(Token::kw_true))2629 return true;2630 if (value.is(Token::kw_false))2631 return false;2632 return p.emitError(value.getLoc(),2633 "expected 'true' or 'false' value for key '" + key +2634 "'");2635 }2636 2637 FailureOr<std::string> parseAsString() const final {2638 if (value.isNot(Token::string))2639 return p.emitError(value.getLoc(),2640 "expected string value for key '" + key + "'");2641 return value.getStringValue();2642 }2643 2644 FailureOr<AsmResourceBlob>2645 parseAsBlob(BlobAllocatorFn allocator) const final {2646 // Blob data within then textual format is represented as a hex string.2647 // TODO: We could avoid an additional alloc+copy here if we pre-allocated2648 // the buffer to use during hex processing.2649 std::optional<std::string> blobData =2650 value.is(Token::string) ? value.getHexStringValue() : std::nullopt;2651 if (!blobData)2652 return p.emitError(value.getLoc(),2653 "expected hex string blob for key '" + key + "'");2654 2655 // Extract the alignment of the blob data, which gets stored at the2656 // beginning of the string.2657 if (blobData->size() < sizeof(uint32_t)) {2658 return p.emitError(value.getLoc(),2659 "expected hex string blob for key '" + key +2660 "' to encode alignment in first 4 bytes");2661 }2662 llvm::support::ulittle32_t align;2663 memcpy(&align, blobData->data(), sizeof(uint32_t));2664 if (align && !llvm::isPowerOf2_32(align)) {2665 return p.emitError(value.getLoc(),2666 "expected hex string blob for key '" + key +2667 "' to encode alignment in first 4 bytes, but got "2668 "non-power-of-2 value: " +2669 Twine(align));2670 }2671 2672 // Get the data portion of the blob.2673 StringRef data = StringRef(*blobData).drop_front(sizeof(uint32_t));2674 if (data.empty())2675 return AsmResourceBlob();2676 2677 // Allocate memory for the blob using the provided allocator and copy the2678 // data into it.2679 AsmResourceBlob blob = allocator(data.size(), align);2680 assert(llvm::isAddrAligned(llvm::Align(align), blob.getData().data()) &&2681 blob.isMutable() &&2682 "blob allocator did not return a properly aligned address");2683 memcpy(blob.getMutableData().data(), data.data(), data.size());2684 return blob;2685 }2686 2687private:2688 std::string key;2689 SMLoc keyLoc;2690 Token value;2691 Parser &p;2692};2693} // namespace2694 2695ParseResult TopLevelOperationParser::parseAttributeAliasDef() {2696 assert(getToken().is(Token::hash_identifier));2697 StringRef aliasName = getTokenSpelling().drop_front();2698 2699 // Check for redefinitions.2700 if (state.symbols.attributeAliasDefinitions.count(aliasName) > 0)2701 return emitError("redefinition of attribute alias id '" + aliasName + "'");2702 2703 // Make sure this isn't invading the dialect attribute namespace.2704 if (aliasName.contains('.'))2705 return emitError("attribute names with a '.' are reserved for "2706 "dialect-defined names");2707 2708 SMRange location = getToken().getLocRange();2709 consumeToken(Token::hash_identifier);2710 2711 // Parse the '='.2712 if (parseToken(Token::equal, "expected '=' in attribute alias definition"))2713 return failure();2714 2715 // Parse the attribute value.2716 Attribute attr = parseAttribute();2717 if (!attr)2718 return failure();2719 2720 // Register this alias with the parser state.2721 if (state.asmState)2722 state.asmState->addAttrAliasDefinition(aliasName, location, attr);2723 state.symbols.attributeAliasDefinitions[aliasName] = attr;2724 return success();2725}2726 2727ParseResult TopLevelOperationParser::parseTypeAliasDef() {2728 assert(getToken().is(Token::exclamation_identifier));2729 StringRef aliasName = getTokenSpelling().drop_front();2730 2731 // Check for redefinitions.2732 if (state.symbols.typeAliasDefinitions.count(aliasName) > 0)2733 return emitError("redefinition of type alias id '" + aliasName + "'");2734 2735 // Make sure this isn't invading the dialect type namespace.2736 if (aliasName.contains('.'))2737 return emitError("type names with a '.' are reserved for "2738 "dialect-defined names");2739 2740 SMRange location = getToken().getLocRange();2741 consumeToken(Token::exclamation_identifier);2742 2743 // Parse the '='.2744 if (parseToken(Token::equal, "expected '=' in type alias definition"))2745 return failure();2746 2747 // Parse the type.2748 Type aliasedType = parseType();2749 if (!aliasedType)2750 return failure();2751 2752 // Register this alias with the parser state.2753 if (state.asmState)2754 state.asmState->addTypeAliasDefinition(aliasName, location, aliasedType);2755 state.symbols.typeAliasDefinitions.try_emplace(aliasName, aliasedType);2756 return success();2757}2758 2759ParseResult TopLevelOperationParser::parseFileMetadataDictionary() {2760 consumeToken(Token::file_metadata_begin);2761 return parseCommaSeparatedListUntil(2762 Token::file_metadata_end, [&]() -> ParseResult {2763 // Parse the key of the metadata dictionary.2764 SMLoc keyLoc = getToken().getLoc();2765 StringRef key;2766 if (failed(parseOptionalKeyword(&key)))2767 return emitError("expected identifier key in file "2768 "metadata dictionary");2769 if (parseToken(Token::colon, "expected ':'"))2770 return failure();2771 2772 // Process the metadata entry.2773 if (key == "dialect_resources")2774 return parseDialectResourceFileMetadata();2775 if (key == "external_resources")2776 return parseExternalResourceFileMetadata();2777 return emitError(keyLoc, "unknown key '" + key +2778 "' in file metadata dictionary");2779 });2780}2781 2782ParseResult TopLevelOperationParser::parseResourceFileMetadata(2783 function_ref<ParseResult(StringRef, SMLoc)> parseBody) {2784 if (parseToken(Token::l_brace, "expected '{'"))2785 return failure();2786 2787 return parseCommaSeparatedListUntil(Token::r_brace, [&]() -> ParseResult {2788 // Parse the top-level name entry.2789 SMLoc nameLoc = getToken().getLoc();2790 StringRef name;2791 if (failed(parseOptionalKeyword(&name)))2792 return emitError("expected identifier key for 'resource' entry");2793 2794 if (parseToken(Token::colon, "expected ':'") ||2795 parseToken(Token::l_brace, "expected '{'"))2796 return failure();2797 return parseBody(name, nameLoc);2798 });2799}2800 2801ParseResult TopLevelOperationParser::parseDialectResourceFileMetadata() {2802 return parseResourceFileMetadata([&](StringRef name,2803 SMLoc nameLoc) -> ParseResult {2804 // Lookup the dialect and check that it can handle a resource entry.2805 Dialect *dialect = getContext()->getOrLoadDialect(name);2806 if (!dialect)2807 return emitError(nameLoc, "dialect '" + name + "' is unknown");2808 const auto *handler = dyn_cast<OpAsmDialectInterface>(dialect);2809 if (!handler) {2810 return emitError() << "unexpected 'resource' section for dialect '"2811 << dialect->getNamespace() << "'";2812 }2813 2814 return parseCommaSeparatedListUntil(Token::r_brace, [&]() -> ParseResult {2815 // Parse the name of the resource entry.2816 SMLoc keyLoc = getToken().getLoc();2817 std::string key;2818 if (failed(parseResourceHandle(handler, key)) ||2819 parseToken(Token::colon, "expected ':'"))2820 return failure();2821 Token valueTok = getToken();2822 consumeToken();2823 2824 ParsedResourceEntry entry(key, keyLoc, valueTok, *this);2825 return handler->parseResource(entry);2826 });2827 });2828}2829 2830ParseResult TopLevelOperationParser::parseExternalResourceFileMetadata() {2831 return parseResourceFileMetadata([&](StringRef name,2832 SMLoc nameLoc) -> ParseResult {2833 AsmResourceParser *handler = state.config.getResourceParser(name);2834 2835 // TODO: Should we require handling external resources in some scenarios?2836 if (!handler) {2837 emitWarning(getEncodedSourceLocation(nameLoc))2838 << "ignoring unknown external resources for '" << name << "'";2839 }2840 2841 return parseCommaSeparatedListUntil(Token::r_brace, [&]() -> ParseResult {2842 // Parse the name of the resource entry.2843 SMLoc keyLoc = getToken().getLoc();2844 std::string key;2845 if (failed(parseOptionalKeywordOrString(&key)))2846 return emitError(2847 "expected identifier key for 'external_resources' entry");2848 if (parseToken(Token::colon, "expected ':'"))2849 return failure();2850 Token valueTok = getToken();2851 consumeToken();2852 2853 if (!handler)2854 return success();2855 ParsedResourceEntry entry(key, keyLoc, valueTok, *this);2856 return handler->parseResource(entry);2857 });2858 });2859}2860 2861ParseResult TopLevelOperationParser::parse(Block *topLevelBlock,2862 Location parserLoc) {2863 // Create a top-level operation to contain the parsed state.2864 OwningOpRef<ModuleOp> topLevelOp(ModuleOp::create(parserLoc));2865 OperationParser opParser(state, topLevelOp.get());2866 while (true) {2867 switch (getToken().getKind()) {2868 default:2869 // Parse a top-level operation.2870 if (opParser.parseOperation())2871 return failure();2872 break;2873 2874 // If we got to the end of the file, then we're done.2875 case Token::eof: {2876 if (opParser.finalize())2877 return failure();2878 2879 // Splice the blocks of the parsed operation over to the provided2880 // top-level block.2881 auto &parsedOps = topLevelOp->getBody()->getOperations();2882 auto &destOps = topLevelBlock->getOperations();2883 destOps.splice(destOps.end(), parsedOps, parsedOps.begin(),2884 parsedOps.end());2885 return success();2886 }2887 2888 // If we got an error token, then the lexer already emitted an error, just2889 // stop. Someday we could introduce error recovery if there was demand2890 // for it.2891 case Token::error:2892 return failure();2893 2894 // Parse an attribute alias.2895 case Token::hash_identifier:2896 if (parseAttributeAliasDef())2897 return failure();2898 break;2899 2900 // Parse a type alias.2901 case Token::exclamation_identifier:2902 if (parseTypeAliasDef())2903 return failure();2904 break;2905 2906 // Parse a file-level metadata dictionary.2907 case Token::file_metadata_begin:2908 if (parseFileMetadataDictionary())2909 return failure();2910 break;2911 }2912 }2913}2914 2915//===----------------------------------------------------------------------===//2916 2917LogicalResult2918mlir::parseAsmSourceFile(const llvm::SourceMgr &sourceMgr, Block *block,2919 const ParserConfig &config, AsmParserState *asmState,2920 AsmParserCodeCompleteContext *codeCompleteContext) {2921 const auto *sourceBuf = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());2922 2923 Location parserLoc =2924 FileLineColLoc::get(config.getContext(), sourceBuf->getBufferIdentifier(),2925 /*line=*/0, /*column=*/0);2926 2927 SymbolState aliasState;2928 ParserState state(sourceMgr, config, aliasState, asmState,2929 codeCompleteContext);2930 return TopLevelOperationParser(state).parse(block, parserLoc);2931}2932