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-exception |
6 | // |
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 | |
67 | using namespace mlir; |
68 | using namespace mlir::detail; |
69 | |
70 | //===----------------------------------------------------------------------===// |
71 | // CodeComplete |
72 | //===----------------------------------------------------------------------===// |
73 | |
74 | AsmParserCodeCompleteContext::~AsmParserCodeCompleteContext() = default; |
75 | |
76 | //===----------------------------------------------------------------------===// |
77 | // Parser |
78 | //===----------------------------------------------------------------------===// |
79 | |
80 | /// Parse a list of comma-separated items with an optional delimiter. If a |
81 | /// delimiter is provided, then an empty list is allowed. If not, then at |
82 | /// least one element will be parsed. |
83 | ParseResult |
84 | Parser::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(k: Token::l_paren)) |
92 | return success(); |
93 | [[fallthrough]]; |
94 | case Delimiter::Paren: |
95 | if (parseToken(expectedToken: Token::l_paren, message: "expected '('"+ contextMessage)) |
96 | return failure(); |
97 | // Check for empty list. |
98 | if (consumeIf(kind: Token::r_paren)) |
99 | return success(); |
100 | break; |
101 | case Delimiter::OptionalLessGreater: |
102 | // Check for absent list. |
103 | if (getToken().isNot(k: Token::less)) |
104 | return success(); |
105 | [[fallthrough]]; |
106 | case Delimiter::LessGreater: |
107 | if (parseToken(expectedToken: Token::less, message: "expected '<'"+ contextMessage)) |
108 | return success(); |
109 | // Check for empty list. |
110 | if (consumeIf(kind: Token::greater)) |
111 | return success(); |
112 | break; |
113 | case Delimiter::OptionalSquare: |
114 | if (getToken().isNot(k: Token::l_square)) |
115 | return success(); |
116 | [[fallthrough]]; |
117 | case Delimiter::Square: |
118 | if (parseToken(expectedToken: Token::l_square, message: "expected '['"+ contextMessage)) |
119 | return failure(); |
120 | // Check for empty list. |
121 | if (consumeIf(kind: Token::r_square)) |
122 | return success(); |
123 | break; |
124 | case Delimiter::OptionalBraces: |
125 | if (getToken().isNot(k: Token::l_brace)) |
126 | return success(); |
127 | [[fallthrough]]; |
128 | case Delimiter::Braces: |
129 | if (parseToken(expectedToken: Token::l_brace, message: "expected '{'"+ contextMessage)) |
130 | return failure(); |
131 | // Check for empty list. |
132 | if (consumeIf(kind: 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(kind: 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(expectedToken: Token::r_paren, message: "expected ')'"+ contextMessage); |
153 | case Delimiter::OptionalLessGreater: |
154 | case Delimiter::LessGreater: |
155 | return parseToken(expectedToken: Token::greater, message: "expected '>'"+ contextMessage); |
156 | case Delimiter::OptionalSquare: |
157 | case Delimiter::Square: |
158 | return parseToken(expectedToken: Token::r_square, message: "expected ']'"+ contextMessage); |
159 | case Delimiter::OptionalBraces: |
160 | case Delimiter::Braces: |
161 | return parseToken(expectedToken: Token::r_brace, message: "expected '}'"+ contextMessage); |
162 | } |
163 | llvm_unreachable("Unknown delimiter"); |
164 | } |
165 | |
166 | /// Parse a comma-separated list of elements, terminated with an arbitrary |
167 | /// token. This allows empty lists if allowEmptyList is true. |
168 | /// |
169 | /// abstract-list ::= rightToken // if allowEmptyList == true |
170 | /// abstract-list ::= element (',' element)* rightToken |
171 | /// |
172 | ParseResult |
173 | Parser::parseCommaSeparatedListUntil(Token::Kind rightToken, |
174 | function_ref<ParseResult()> parseElement, |
175 | bool allowEmptyList) { |
176 | // Handle the empty case. |
177 | if (getToken().is(k: rightToken)) { |
178 | if (!allowEmptyList) |
179 | return emitWrongTokenError(message: "expected list element"); |
180 | consumeToken(kind: rightToken); |
181 | return success(); |
182 | } |
183 | |
184 | if (parseCommaSeparatedList(parseElementFn: parseElement) || |
185 | parseToken(expectedToken: rightToken, message: "expected ',' or '"+ |
186 | Token::getTokenSpelling(kind: rightToken) + "'")) |
187 | return failure(); |
188 | |
189 | return success(); |
190 | } |
191 | |
192 | InFlightDiagnostic Parser::emitError(const Twine &message) { |
193 | auto loc = state.curToken.getLoc(); |
194 | if (state.curToken.isNot(k: 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(loc: SMLoc::getFromPointer(Ptr: loc.getPointer() - 1), message); |
199 | } |
200 | |
201 | InFlightDiagnostic Parser::emitError(SMLoc loc, const Twine &message) { |
202 | auto diag = mlir::emitError(loc: getEncodedSourceLocation(loc), message); |
203 | |
204 | // If we hit a parse error in response to a lexer error, then the lexer |
205 | // already reported the error. |
206 | if (getToken().is(k: Token::error)) |
207 | diag.abandon(); |
208 | return diag; |
209 | } |
210 | |
211 | /// Emit an error about a "wrong token". If the current token is at the |
212 | /// start of a source line, this will apply heuristics to back up and report |
213 | /// the error at the end of the previous line, which is where the expected |
214 | /// token is supposed to be. |
215 | InFlightDiagnostic 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(k: Token::eof)) |
220 | loc = SMLoc::getFromPointer(Ptr: 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(Chars: " \t"); |
237 | |
238 | // For tokens with no preceding source line, just emit at the original |
239 | // location. |
240 | if (startOfBuffer.empty()) |
241 | return emitError(loc: 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(loc: SMLoc::getFromPointer(Ptr: 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 a |
251 | // `//` 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(Chars: "\n\r"); |
255 | if (newLineIndex != StringRef::npos) |
256 | prevLine = prevLine.drop_front(N: newLineIndex); |
257 | |
258 | // If we find a // in the current line, then emit the diagnostic before it. |
259 | size_t commentStart = prevLine.find(Str: "//"); |
260 | if (commentStart != StringRef::npos) |
261 | startOfBuffer = startOfBuffer.drop_back(N: 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. |
267 | ParseResult Parser::parseToken(Token::Kind expectedToken, |
268 | const Twine &message) { |
269 | if (consumeIf(kind: expectedToken)) |
270 | return success(); |
271 | return emitWrongTokenError(message); |
272 | } |
273 | |
274 | /// Parses a quoted string token if present. |
275 | ParseResult Parser::parseOptionalString(std::string *string) { |
276 | if (!getToken().is(k: 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. |
286 | OptionalParseResult Parser::parseOptionalInteger(APInt &result) { |
287 | // Parse `false` and `true` keywords as 0 and 1 respectively. |
288 | if (consumeIf(kind: Token::kw_false)) { |
289 | result = false; |
290 | return success(); |
291 | } |
292 | if (consumeIf(kind: Token::kw_true)) { |
293 | result = true; |
294 | return success(); |
295 | } |
296 | |
297 | Token curToken = getToken(); |
298 | if (curToken.isNot(k1: Token::integer, k2: Token::minus)) |
299 | return std::nullopt; |
300 | |
301 | bool negative = consumeIf(kind: Token::minus); |
302 | Token curTok = getToken(); |
303 | if (parseToken(expectedToken: Token::integer, message: "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(Radix: isHex ? 0 : 10, Result&: result)) |
309 | return emitError(loc: curTok.getLoc(), message: "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(width: 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. |
323 | OptionalParseResult Parser::parseOptionalDecimalInteger(APInt &result) { |
324 | Token curToken = getToken(); |
325 | if (curToken.isNot(k1: Token::integer, k2: Token::minus)) { |
326 | return std::nullopt; |
327 | } |
328 | |
329 | bool negative = consumeIf(kind: Token::minus); |
330 | Token curTok = getToken(); |
331 | if (parseToken(expectedToken: Token::integer, message: "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 already |
337 | // moved past the entire hexidecimal encoded integer so we reset the lex |
338 | // pointer to just past the 0 we actualy want to consume. |
339 | if (spelling[0] == '0' && spelling.size() > 1 && |
340 | llvm::toLower(x: spelling[1]) == 'x') { |
341 | result = 0; |
342 | state.lex.resetPointer(newPointer: spelling.data() + 1); |
343 | consumeToken(); |
344 | return success(); |
345 | } |
346 | |
347 | if (spelling.getAsInteger(Radix: 10, Result&: result)) |
348 | return emitError(loc: curTok.getLoc(), message: "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(width: result.getBitWidth() + 1); |
353 | |
354 | // Process the negative sign if present. |
355 | if (negative) |
356 | result.negate(); |
357 | |
358 | return success(); |
359 | } |
360 | |
361 | ParseResult 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(k: Token::floatliteral)) { |
366 | auto val = tok.getFloatingPointValue(); |
367 | if (!val) |
368 | return emitError(loc: tok.getLoc()) << "floating point value too large"; |
369 | |
370 | result.emplace(args: isNegative ? -*val : *val); |
371 | bool unused; |
372 | result->convert(ToSemantics: semantics, RM: APFloat::rmNearestTiesToEven, losesInfo: &unused); |
373 | return success(); |
374 | } |
375 | |
376 | // Check for a hexadecimal float value. |
377 | if (tok.is(k: Token::integer)) |
378 | return parseFloatFromIntegerLiteral(result, tok, isNegative, semantics); |
379 | |
380 | return emitError(loc: tok.getLoc()) << "expected floating point literal"; |
381 | } |
382 | |
383 | /// Parse a floating point value from an integer literal token. |
384 | ParseResult |
385 | Parser::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(loc: tok.getLoc(), message: "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(loc: tok.getLoc(), |
398 | message: "hexadecimal float literal should not have a " |
399 | "leading minus"); |
400 | } |
401 | |
402 | APInt intValue; |
403 | tok.getSpelling().getAsInteger(Radix: isHex ? 0 : 10, Result&: intValue); |
404 | auto typeSizeInBits = APFloat::semanticsSizeInBits(semantics); |
405 | if (intValue.getActiveBits() > typeSizeInBits) { |
406 | return emitError(loc: tok.getLoc(), |
407 | message: "hexadecimal float constant out of range for type"); |
408 | } |
409 | |
410 | APInt truncatedValue(typeSizeInBits, intValue.getNumWords(), |
411 | intValue.getRawData()); |
412 | result.emplace(args: semantics, args&: truncatedValue); |
413 | return success(); |
414 | } |
415 | |
416 | ParseResult 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 | |
426 | ParseResult Parser::parseOptionalKeywordOrString(std::string *result) { |
427 | StringRef keyword; |
428 | if (succeeded(Result: parseOptionalKeyword(keyword: &keyword))) { |
429 | *result = keyword.str(); |
430 | return success(); |
431 | } |
432 | |
433 | return parseOptionalString(string: result); |
434 | } |
435 | |
436 | //===----------------------------------------------------------------------===// |
437 | // Resource Parsing |
438 | //===----------------------------------------------------------------------===// |
439 | |
440 | FailureOr<AsmDialectResourceHandle> |
441 | Parser::parseResourceHandle(const OpAsmDialectInterface *dialect, |
442 | std::string &name) { |
443 | assert(dialect && "expected valid dialect interface"); |
444 | SMLoc nameLoc = getToken().getLoc(); |
445 | if (failed(Result: parseOptionalKeywordOrString(result: &name))) |
446 | return emitError(message: "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 to |
450 | // resolve a reference to this handle. This allows for us to remap the name of |
451 | // 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(key: name); |
456 | if (failed(Result: result)) { |
457 | return emitError(loc: nameLoc) |
458 | << "unknown 'resource' key '"<< name << "' for dialect '" |
459 | << dialect->getDialect()->getNamespace() << "'"; |
460 | } |
461 | entry.first = dialect->getResourceKey(handle: *result); |
462 | entry.second = *result; |
463 | } |
464 | |
465 | name = entry.first; |
466 | return entry.second; |
467 | } |
468 | |
469 | FailureOr<AsmDialectResourceHandle> |
470 | Parser::parseResourceHandle(Dialect *dialect) { |
471 | const auto *interface = dyn_cast<OpAsmDialectInterface>(Val: dialect); |
472 | if (!interface) { |
473 | return emitError() << "dialect '"<< dialect->getNamespace() |
474 | << "' does not expect resource handles"; |
475 | } |
476 | std::string resourceName; |
477 | return parseResourceHandle(dialect: interface, name&: resourceName); |
478 | } |
479 | |
480 | //===----------------------------------------------------------------------===// |
481 | // Code Completion |
482 | //===----------------------------------------------------------------------===// |
483 | |
484 | ParseResult Parser::codeCompleteDialectName() { |
485 | state.codeCompleteContext->completeDialectName(); |
486 | return failure(); |
487 | } |
488 | |
489 | ParseResult Parser::codeCompleteOperationName(StringRef dialectName) { |
490 | // Perform some simple validation on the dialect name. This doesn't need to be |
491 | // extensive, it's more of an optimization (to avoid checking completion |
492 | // results when we know they will fail). |
493 | if (dialectName.empty() || dialectName.contains(C: '.')) |
494 | return failure(); |
495 | state.codeCompleteContext->completeOperationName(dialectName); |
496 | return failure(); |
497 | } |
498 | |
499 | ParseResult Parser::codeCompleteDialectOrElidedOpName(SMLoc loc) { |
500 | // Check to see if there is anything else on the current line. This check |
501 | // isn't strictly necessary, but it does avoid unnecessarily triggering |
502 | // completions for operations and dialects in situations where we don't want |
503 | // 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(C: *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 name |
516 | // whose dialect prefix was elided. For this we simply invoke both of the |
517 | // individual completion methods. |
518 | (void)codeCompleteDialectName(); |
519 | return codeCompleteOperationName(dialectName: state.defaultDialectStack.back()); |
520 | } |
521 | |
522 | ParseResult Parser::codeCompleteStringDialectOrOperationName(StringRef name) { |
523 | // If the name is empty, this is the start of the string and contains the |
524 | // dialect. |
525 | if (name.empty()) |
526 | return codeCompleteDialectName(); |
527 | |
528 | // Otherwise, we treat this as completing an operation name. The current name |
529 | // is used as the dialect namespace. |
530 | if (name.consume_back(Suffix: ".")) |
531 | return codeCompleteOperationName(dialectName: name); |
532 | return failure(); |
533 | } |
534 | |
535 | ParseResult Parser::codeCompleteExpectedTokens(ArrayRef<StringRef> tokens) { |
536 | state.codeCompleteContext->completeExpectedTokens(tokens, /*optional=*/false); |
537 | return failure(); |
538 | } |
539 | ParseResult Parser::codeCompleteOptionalTokens(ArrayRef<StringRef> tokens) { |
540 | state.codeCompleteContext->completeExpectedTokens(tokens, /*optional=*/true); |
541 | return failure(); |
542 | } |
543 | |
544 | Attribute Parser::codeCompleteAttribute() { |
545 | state.codeCompleteContext->completeAttribute( |
546 | aliases: state.symbols.attributeAliasDefinitions); |
547 | return {}; |
548 | } |
549 | Type Parser::codeCompleteType() { |
550 | state.codeCompleteContext->completeType(aliases: state.symbols.typeAliasDefinitions); |
551 | return {}; |
552 | } |
553 | |
554 | Attribute |
555 | Parser::codeCompleteDialectSymbol(const llvm::StringMap<Attribute> &aliases) { |
556 | state.codeCompleteContext->completeDialectAttributeOrAlias(aliases); |
557 | return {}; |
558 | } |
559 | Type Parser::codeCompleteDialectSymbol(const llvm::StringMap<Type> &aliases) { |
560 | state.codeCompleteContext->completeDialectTypeOrAlias(aliases); |
561 | return {}; |
562 | } |
563 | |
564 | //===----------------------------------------------------------------------===// |
565 | // OperationParser |
566 | //===----------------------------------------------------------------------===// |
567 | |
568 | namespace { |
569 | /// This class provides support for parsing operations and regions of |
570 | /// operations. |
571 | class OperationParser : public Parser { |
572 | public: |
573 | OperationParser(ParserState &state, ModuleOp topLevelOp); |
574 | ~OperationParser(); |
575 | |
576 | /// After parsing is finished, this function must be called to see if there |
577 | /// are any remaining issues. |
578 | ParseResult finalize(); |
579 | |
580 | //===--------------------------------------------------------------------===// |
581 | // SSA Value Handling |
582 | //===--------------------------------------------------------------------===// |
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 | ParseResult |
603 | parseOptionalSSAUseList(SmallVectorImpl<UnresolvedOperand> &results); |
604 | |
605 | /// Parse a single SSA use into 'result'. If 'allowResultNumber' is true then |
606 | /// 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. This |
611 | /// 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 it |
620 | /// has been already reference. |
621 | std::optional<SMLoc> getReferenceLoc(StringRef name, unsigned number) { |
622 | auto &values = isolatedNameScopes.back().values; |
623 | if (!values.count(Key: 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 Parsing |
632 | //===--------------------------------------------------------------------===// |
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 an |
648 | /// operation instance and populate the input operation-state 'result' with |
649 | /// those components. If any of the components is explicitly provided, then |
650 | /// 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 at |
663 | /// 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 or |
668 | /// a BlockArgument. We cannot use Value for this, because not all Operations |
669 | /// have results. |
670 | using OpOrArgument = llvm::PointerUnion<Operation *, BlockArgument>; |
671 | |
672 | /// Parse an optional trailing location and add it to the specifier Operation |
673 | /// 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: #loc42 |
680 | /// The alias may have already be defined or may be defined later, in which |
681 | /// case an OpaqueLoc is used a placeholder. The caller must ensure that the |
682 | /// 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 a |
694 | /// an object of type 'OperationName'. Otherwise, failure is returned. |
695 | FailureOr<OperationName> parseCustomOperationName(); |
696 | |
697 | //===--------------------------------------------------------------------===// |
698 | // Region Parsing |
699 | //===--------------------------------------------------------------------===// |
700 | |
701 | /// Parse a region into 'region' with the provided entry block arguments. |
702 | /// 'isIsolatedNameScope' indicates if the naming scope of this region is |
703 | /// 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 Parsing |
714 | //===--------------------------------------------------------------------===// |
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't |
726 | /// already exist. The location specified is the point of use, which allows |
727 | /// us to diagnose references to blocks that are not defined precisely. |
728 | Block *getBlockNamed(StringRef name, SMLoc loc); |
729 | |
730 | //===--------------------------------------------------------------------===// |
731 | // Code Completion |
732 | //===--------------------------------------------------------------------===// |
733 | |
734 | /// The set of various code completion methods. Every completion method |
735 | /// returns `failure` to stop the parsing process after providing completion |
736 | /// results. |
737 | |
738 | ParseResult codeCompleteSSAUse(); |
739 | ParseResult codeCompleteBlock(); |
740 | |
741 | private: |
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(Key: block, Args&: loc); |
765 | } |
766 | |
767 | /// Erase any forward reference to the given block. |
768 | bool eraseForwardRef(Block *block) { return forwardRef.back().erase(Val: 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 and |
777 | /// 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(Val: value); |
783 | } |
784 | |
785 | /// This struct represents an isolated SSA name scope. This scope may contain |
786 | /// other nested non-isolated scopes. These scopes are used for operations |
787 | /// that are known to be isolated to allow for reusing names within their |
788 | /// 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(key: def); |
793 | } |
794 | |
795 | /// Push a nested name scope. |
796 | void pushSSANameScope() { definitionsPerScope.push_back(Elt: {}); } |
797 | |
798 | /// Pop a nested name scope. |
799 | void popSSANameScope() { |
800 | for (auto &def : definitionsPerScope.pop_back_val()) |
801 | values.erase(Key: def.getKey()); |
802 | } |
803 | |
804 | /// This keeps track of all of the SSA values we are tracking for each name |
805 | /// 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 first |
816 | /// reference for each nested name scope. This is used to diagnose invalid |
817 | /// 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 of |
822 | /// 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 of |
826 | /// of the lifetime of the parser because some custom parsers may store |
827 | /// references to them in local state and use them after forward references |
828 | /// have been resolved. |
829 | DenseSet<Operation *> forwardRefOps; |
830 | |
831 | /// Deffered locations: when parsing `loc(#loc42)` we add an entry to this |
832 | /// map. After parsing the definition `#loc42 = ...` we'll patch back users |
833 | /// 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 | } // namespace |
843 | |
844 | MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(OperationParser::DeferredLocInfo *) |
845 | MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(OperationParser::DeferredLocInfo *) |
846 | |
847 | OperationParser::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: topLevelOp); |
855 | } |
856 | |
857 | OperationParser::~OperationParser() { |
858 | for (Operation *op : forwardRefOps) { |
859 | // Drop all uses of undefined forward declared reference and destroy |
860 | // 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 never |
867 | // 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 are |
875 | /// any remaining issues. |
876 | ParseResult OperationParser::finalize() { |
877 | // Check for any forward references that are left. If we find any, error |
878 | // 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(Elt: entry.second.getPointer()); |
884 | llvm::array_pod_sort(Start: errors.begin(), End: errors.end()); |
885 | |
886 | for (const char *entry : errors) { |
887 | auto loc = SMLoc::getFromPointer(Ptr: entry); |
888 | emitError(loc, message: "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(Key: locInfo.identifier); |
902 | if (!attr) |
903 | return this->emitError(locInfo.loc) |
904 | << "operation location alias was never defined"; |
905 | auto locAttr = dyn_cast<LocationAttr>(Val&: 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(callback: [&](Operation *op) { |
914 | if (failed(Result: 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(Result: 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(Result: popSSANameScope())) |
928 | return failure(); |
929 | |
930 | // Verify that the parsed operations are valid. |
931 | if (state.config.shouldVerifyAfterParse() && failed(Result: verify(op: 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 Handling |
942 | //===----------------------------------------------------------------------===// |
943 | |
944 | void OperationParser::pushSSANameScope(bool isIsolated) { |
945 | blocksByName.push_back(Elt: DenseMap<StringRef, BlockDefinition>()); |
946 | forwardRef.push_back(Elt: DenseMap<Block *, SMLoc>()); |
947 | |
948 | // Push back a new name definition scope. |
949 | if (isIsolated) |
950 | isolatedNameScopes.push_back(Elt: {}); |
951 | isolatedNameScopes.back().pushSSANameScope(); |
952 | } |
953 | |
954 | ParseResult 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(Elt: {entry.second.getPointer(), entry.first}); |
963 | // Add this block to the top-level region to allow for automatic cleanup. |
964 | topLevelOp->getRegion(index: 0).push_back(block: entry.first); |
965 | } |
966 | llvm::array_pod_sort(Start: errors.begin(), End: errors.end()); |
967 | |
968 | for (auto entry : errors) { |
969 | auto loc = SMLoc::getFromPointer(Ptr: entry.first); |
970 | emitError(loc, message: "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 | else |
981 | currentNameScope.popSSANameScope(); |
982 | |
983 | blocksByName.pop_back(); |
984 | return success(); |
985 | } |
986 | |
987 | /// Register a definition of a value with the symbol table. |
988 | ParseResult OperationParser::addDefinition(UnresolvedOperand useInfo, |
989 | Value value) { |
990 | auto &entries = getSSAValueEntry(name: useInfo.name); |
991 | |
992 | // Make sure there is a slot for this value. |
993 | if (entries.size() <= useInfo.number) |
994 | entries.resize(N: useInfo.number + 1); |
995 | |
996 | // If we already have an entry for this, check to see if it was a definition |
997 | // or a forward reference. |
998 | if (auto existing = entries[useInfo.number].value) { |
999 | if (!isForwardRefPlaceholder(value: existing)) { |
1000 | return emitError(loc: useInfo.location) |
1001 | .append(args: "redefinition of SSA value '", args&: useInfo.name, args: "'") |
1002 | .attachNote(noteLoc: getEncodedSourceLocation(loc: entries[useInfo.number].loc)) |
1003 | .append(arg: "previously defined here"); |
1004 | } |
1005 | |
1006 | if (existing.getType() != value.getType()) { |
1007 | return emitError(loc: useInfo.location) |
1008 | .append(args: "definition of SSA value '", args&: useInfo.name, args: "#", |
1009 | args&: useInfo.number, args: "' has type ", args: value.getType()) |
1010 | .attachNote(noteLoc: getEncodedSourceLocation(loc: entries[useInfo.number].loc)) |
1011 | .append(arg1: "previously used here with type ", arg2: existing.getType()); |
1012 | } |
1013 | |
1014 | // If it was a forward reference, update everything that used it to use |
1015 | // the actual definition instead, delete the forward ref, and remove it |
1016 | // from our set of forward references we track. |
1017 | existing.replaceAllUsesWith(newValue: value); |
1018 | forwardRefPlaceholders.erase(Val: existing); |
1019 | |
1020 | // If a definition of the value already exists, replace it in the assembly |
1021 | // state. |
1022 | if (state.asmState) |
1023 | state.asmState->refineDefinition(oldValue: existing, newValue: value); |
1024 | } |
1025 | |
1026 | /// Record this definition for the current scope. |
1027 | entries[useInfo.number] = {.value: value, .loc: useInfo.location}; |
1028 | recordDefinition(def: 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 | /// |
1037 | ParseResult OperationParser::parseOptionalSSAUseList( |
1038 | SmallVectorImpl<UnresolvedOperand> &results) { |
1039 | if (!getToken().isOrIsCodeCompletionFor(kind: Token::percent_identifier)) |
1040 | return success(); |
1041 | return parseCommaSeparatedList(parseElementFn: [&]() -> ParseResult { |
1042 | UnresolvedOperand result; |
1043 | if (parseSSAUse(result)) |
1044 | return failure(); |
1045 | results.push_back(Elt: result); |
1046 | return success(); |
1047 | }); |
1048 | } |
1049 | |
1050 | /// Parse a SSA operand for an operation. |
1051 | /// |
1052 | /// ssa-use ::= ssa-id |
1053 | /// |
1054 | ParseResult 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(expectedToken: Token::percent_identifier, message: "expected SSA operand")) |
1063 | return failure(); |
1064 | |
1065 | // If we have an attribute ID, it is a result number. |
1066 | if (getToken().is(k: Token::hash_identifier)) { |
1067 | if (!allowResultNumber) |
1068 | return emitError(message: "result number not allowed in argument list"); |
1069 | |
1070 | if (auto value = getToken().getHashIdentifierNumber()) |
1071 | result.number = *value; |
1072 | else |
1073 | return emitError(message: "invalid SSA value result number"); |
1074 | consumeToken(kind: Token::hash_identifier); |
1075 | } |
1076 | |
1077 | return success(); |
1078 | } |
1079 | |
1080 | /// Given an unbound reference to an SSA value and its type, return the value |
1081 | /// it specifies. This returns null on failure. |
1082 | Value OperationParser::resolveSSAUse(UnresolvedOperand useInfo, Type type) { |
1083 | auto &entries = getSSAValueEntry(name: useInfo.name); |
1084 | |
1085 | // Functor used to record the use of the given value if the assembly state |
1086 | // field is populated. |
1087 | auto maybeRecordUse = [&](Value value) { |
1088 | if (state.asmState) |
1089 | state.asmState->addUses(value, locations: 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(loc: useInfo.location, message: "use of value '") |
1101 | .append(args&: useInfo.name, |
1102 | args: "' expects different type than prior uses: ", args&: type, args: " vs ", |
1103 | args: result.getType()) |
1104 | .attachNote(noteLoc: getEncodedSourceLocation(loc: entries[useInfo.number].loc)) |
1105 | .append(arg: "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(N: useInfo.number + 1); |
1112 | |
1113 | // If the value has already been defined and this is an overly large result |
1114 | // number, diagnose that. |
1115 | if (entries[0].value && !isForwardRefPlaceholder(value: entries[0].value)) |
1116 | return (emitError(loc: useInfo.location, message: "reference to invalid result number"), |
1117 | nullptr); |
1118 | |
1119 | // Otherwise, this is a forward reference. Create a placeholder and remember |
1120 | // that we did so. |
1121 | Value result = createForwardRefPlaceholder(loc: useInfo.location, type); |
1122 | entries[useInfo.number] = {.value: result, .loc: 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 `:` type |
1129 | ParseResult OperationParser::parseSSADefOrUseAndType( |
1130 | function_ref<ParseResult(UnresolvedOperand, Type)> action) { |
1131 | UnresolvedOperand useInfo; |
1132 | if (parseSSAUse(result&: useInfo) || |
1133 | parseToken(expectedToken: Token::colon, message: "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, then |
1144 | /// followed by a type list. |
1145 | /// |
1146 | /// ssa-use-and-type-list |
1147 | /// ::= ssa-use-list ':' type-list-no-parens |
1148 | /// |
1149 | ParseResult OperationParser::parseOptionalSSAUseAndTypeList( |
1150 | SmallVectorImpl<Value> &results) { |
1151 | SmallVector<UnresolvedOperand, 4> valueIDs; |
1152 | if (parseOptionalSSAUseList(results&: 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(expectedToken: Token::colon, message: "expected ':' in operand list") || |
1161 | parseTypeListNoParens(elements&: types)) |
1162 | return failure(); |
1163 | |
1164 | if (valueIDs.size() != types.size()) |
1165 | return emitError(message: "expected ") |
1166 | << valueIDs.size() << " types to match operand list"; |
1167 | |
1168 | results.reserve(N: valueIDs.size()); |
1169 | for (unsigned i = 0, e = valueIDs.size(); i != e; ++i) { |
1170 | if (auto value = resolveSSAUse(useInfo: valueIDs[i], type: types[i])) |
1171 | results.push_back(Elt: value); |
1172 | else |
1173 | return failure(); |
1174 | } |
1175 | |
1176 | return success(); |
1177 | } |
1178 | |
1179 | /// Record that a definition was added at the current scope. |
1180 | void OperationParser::recordDefinition(StringRef def) { |
1181 | isolatedNameScopes.back().recordDefinition(def); |
1182 | } |
1183 | |
1184 | /// Get the value entry for the given SSA name. |
1185 | auto 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. |
1191 | Value OperationParser::createForwardRefPlaceholder(SMLoc loc, Type type) { |
1192 | // Forward references are always created as operations, because we just need |
1193 | // something with a def/use chain. |
1194 | // |
1195 | // We create these placeholders as having an empty name, which we know |
1196 | // cannot be created through normal user input, allowing us to distinguish |
1197 | // them. |
1198 | auto name = OperationName("builtin.unrealized_conversion_cast", getContext()); |
1199 | auto *op = Operation::create( |
1200 | location: getEncodedSourceLocation(loc), name, resultTypes: type, /*operands=*/{}, |
1201 | /*attributes=*/std::nullopt, /*properties=*/nullptr, /*successors=*/{}, |
1202 | /*numRegions=*/0); |
1203 | forwardRefPlaceholders[op->getResult(idx: 0)] = loc; |
1204 | forwardRefOps.insert(V: op); |
1205 | return op->getResult(idx: 0); |
1206 | } |
1207 | |
1208 | //===----------------------------------------------------------------------===// |
1209 | // Operation Parsing |
1210 | //===----------------------------------------------------------------------===// |
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-type |
1220 | /// custom-operation ::= bare-id custom-operation-format |
1221 | /// op-result-list ::= op-result (`,` op-result)* `=` |
1222 | /// op-result ::= ssa-id (`:` integer-literal) |
1223 | /// |
1224 | ParseResult OperationParser::parseOperation() { |
1225 | auto loc = getToken().getLoc(); |
1226 | SmallVector<ResultRecord, 1> resultIDs; |
1227 | size_t numExpectedResults = 0; |
1228 | if (getToken().is(k: 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(expectedToken: Token::percent_identifier, |
1234 | message: "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(kind: Token::colon)) { |
1240 | // Check that the next token is an integer. |
1241 | if (!getToken().is(k: Token::integer)) |
1242 | return emitWrongTokenError(message: "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 | message: "expected named operation to have at least 1 result"); |
1249 | consumeToken(kind: Token::integer); |
1250 | expectedSubResults = *val; |
1251 | } |
1252 | |
1253 | resultIDs.emplace_back(Args: nameTok.getSpelling(), Args&: expectedSubResults, |
1254 | Args: nameTok.getLoc()); |
1255 | numExpectedResults += expectedSubResults; |
1256 | return success(); |
1257 | }; |
1258 | if (parseCommaSeparatedList(parseElementFn: parseNextResult)) |
1259 | return failure(); |
1260 | |
1261 | if (parseToken(expectedToken: Token::equal, message: "expected '=' after SSA name")) |
1262 | return failure(); |
1263 | } |
1264 | |
1265 | Operation *op; |
1266 | Token nameTok = getToken(); |
1267 | if (nameTok.is(k: Token::bare_identifier) || nameTok.isKeyword()) |
1268 | op = parseCustomOperation(resultIDs); |
1269 | else if (nameTok.is(k: Token::string)) |
1270 | op = parseGenericOperation(); |
1271 | else if (nameTok.isCodeCompletionFor(kind: Token::string)) |
1272 | return codeCompleteStringDialectOrOperationName(name: nameTok.getStringValue()); |
1273 | else if (nameTok.isCodeCompletion()) |
1274 | return codeCompleteDialectOrElidedOpName(loc); |
1275 | else |
1276 | return emitWrongTokenError(message: "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, message: "cannot name an operation with no results"); |
1286 | if (numExpectedResults != op->getNumResults()) |
1287 | return emitError(loc, message: "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(N: resultIDs.size()); |
1296 | for (ResultRecord &record : resultIDs) { |
1297 | asmResultGroups.emplace_back(Args&: resultIt, Args&: std::get<2>(t&: record)); |
1298 | resultIt += std::get<1>(t&: record); |
1299 | } |
1300 | state.asmState->finalizeOperationDefinition( |
1301 | op, nameLoc: nameTok.getLocRange(), /*endLoc=*/getLastToken().getEndLoc(), |
1302 | resultGroups: 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>(Begin: 0, End: std::get<1>(t&: resIt))) { |
1309 | if (addDefinition(useInfo: {.location: std::get<2>(t&: resIt), .name: std::get<0>(t&: resIt), .number: subRes}, |
1310 | value: op->getResult(idx: 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, nameLoc: nameTok.getLocRange(), |
1319 | /*endLoc=*/getLastToken().getEndLoc()); |
1320 | } |
1321 | |
1322 | return success(); |
1323 | } |
1324 | |
1325 | /// Parse a single operation successor. |
1326 | /// |
1327 | /// successor ::= block-id |
1328 | /// |
1329 | ParseResult 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(k: Token::caret_identifier)) |
1335 | return emitWrongTokenError(message: "expected block name"); |
1336 | dest = getBlockNamed(name: getTokenSpelling(), loc: 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 | /// |
1345 | ParseResult |
1346 | OperationParser::parseSuccessors(SmallVectorImpl<Block *> &destinations) { |
1347 | if (parseToken(expectedToken: Token::l_square, message: "expected '['")) |
1348 | return failure(); |
1349 | |
1350 | auto parseElt = [this, &destinations] { |
1351 | Block *dest; |
1352 | ParseResult res = parseSuccessor(dest); |
1353 | destinations.push_back(Elt: dest); |
1354 | return res; |
1355 | }; |
1356 | return parseCommaSeparatedListUntil(rightToken: Token::r_square, parseElement: parseElt, |
1357 | /*allowEmptyList=*/false); |
1358 | } |
1359 | |
1360 | namespace { |
1361 | // RAII-style guard for cleaning up the regions in the operation state before |
1362 | // deleting them. Within the parser, regions may get deleted if parsing failed, |
1363 | // and other errors may be present, in particular undominated uses. This makes |
1364 | // sure such uses are deleted. |
1365 | struct CleanupOpStateRegions { |
1366 | ~CleanupOpStateRegions() { |
1367 | SmallVector<Region *, 4> regionsToClean; |
1368 | regionsToClean.reserve(N: 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 | } // namespace |
1377 | |
1378 | ParseResult 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(expectedToken: Token::l_paren, message: "expected '(' to start operand list") || |
1391 | parseOptionalSSAUseList(results&: opInfo) || |
1392 | parseToken(expectedToken: Token::r_paren, message: "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(k: Token::l_square)) { |
1401 | // Check if the operation is not a known terminator. |
1402 | if (!result.name.mightHaveTrait<OpTrait::IsTerminator>()) |
1403 | return emitError(message: "successors in non-terminator"); |
1404 | |
1405 | SmallVector<Block *, 2> successors; |
1406 | if (parseSuccessors(destinations&: successors)) |
1407 | return failure(); |
1408 | result.addSuccessors(newSuccessors: successors); |
1409 | } |
1410 | } else { |
1411 | result.addSuccessors(newSuccessors: *parsedSuccessors); |
1412 | } |
1413 | |
1414 | // Parse the properties, if not explicitly provided. |
1415 | if (propertiesAttribute) { |
1416 | result.propertiesAttr = *propertiesAttribute; |
1417 | } else if (consumeIf(kind: Token::less)) { |
1418 | result.propertiesAttr = parseAttribute(); |
1419 | if (!result.propertiesAttr) |
1420 | return failure(); |
1421 | if (parseToken(expectedToken: Token::greater, message: "expected '>' to close properties")) |
1422 | return failure(); |
1423 | } |
1424 | // Parse the region list, if not explicitly provided. |
1425 | if (!parsedRegions) { |
1426 | if (consumeIf(kind: Token::l_paren)) { |
1427 | do { |
1428 | // Create temporary regions with the top level region as parent. |
1429 | result.regions.emplace_back(Args: new Region(topLevelOp)); |
1430 | if (parseRegion(region&: *result.regions.back(), /*entryArguments=*/{})) |
1431 | return failure(); |
1432 | } while (consumeIf(kind: Token::comma)); |
1433 | if (parseToken(expectedToken: Token::r_paren, message: "expected ')' to end region list")) |
1434 | return failure(); |
1435 | } |
1436 | } else { |
1437 | result.addRegions(regions: *parsedRegions); |
1438 | } |
1439 | |
1440 | // Parse the attributes, if not explicitly provided. |
1441 | if (!parsedAttributes) { |
1442 | if (getToken().is(k: Token::l_brace)) { |
1443 | if (parseAttributeDict(attributes&: result.attributes)) |
1444 | return failure(); |
1445 | } |
1446 | } else { |
1447 | result.addAttributes(newAttributes: *parsedAttributes); |
1448 | } |
1449 | |
1450 | // Parse the operation type, if not explicitly provided. |
1451 | Location typeLoc = result.location; |
1452 | if (!parsedFnType) { |
1453 | if (parseToken(expectedToken: Token::colon, message: "expected ':' followed by operation type")) |
1454 | return failure(); |
1455 | |
1456 | typeLoc = getEncodedSourceLocation(loc: 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(loc: typeLoc, message: "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(loc: typeLoc, message: "expected ") |
1474 | << parsedOperandUseInfo->size() << " operand type"<< plural |
1475 | << " 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 | Elt: resolveSSAUse(useInfo: (*parsedOperandUseInfo)[i], type: operandTypes[i])); |
1482 | if (!result.operands.back()) |
1483 | return failure(); |
1484 | } |
1485 | |
1486 | return success(); |
1487 | } |
1488 | |
1489 | Operation *OperationParser::parseGenericOperation() { |
1490 | // Get location information for the operation. |
1491 | auto srcLocation = getEncodedSourceLocation(loc: getToken().getLoc()); |
1492 | |
1493 | std::string name = getToken().getStringValue(); |
1494 | if (name.empty()) |
1495 | return (emitError(message: "empty operation name is invalid"), nullptr); |
1496 | if (name.find(c: '\0') != StringRef::npos) |
1497 | return (emitError(message: "null character not allowed in operation name"), nullptr); |
1498 | |
1499 | consumeToken(kind: Token::string); |
1500 | |
1501 | OperationState result(srcLocation, name); |
1502 | CleanupOpStateRegions guard{.state: result}; |
1503 | |
1504 | // Lazy load dialects in the context as needed. |
1505 | if (!result.name.isRegistered()) { |
1506 | StringRef dialectName = StringRef(name).split(Separator: '.').first; |
1507 | if (!getContext()->getLoadedDialect(name: dialectName) && |
1508 | !getContext()->getOrLoadDialect(name: dialectName)) { |
1509 | if (!getContext()->allowsUnregisteredDialects()) { |
1510 | // Emit an error if the dialect couldn't be loaded (i.e., it was not |
1511 | // registered) and unregistered dialects aren't allowed. |
1512 | emitError(message: "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(opName: result.name); |
1526 | |
1527 | if (parseGenericOperationAfterOpName(result)) |
1528 | return nullptr; |
1529 | |
1530 | // Operation::create() is not allowed to fail, however setting the properties |
1531 | // from an attribute is a failable operation. So we save the attribute here |
1532 | // and set it on the operation post-parsing. |
1533 | Attribute properties; |
1534 | std::swap(a&: properties, b&: result.propertiesAttr); |
1535 | |
1536 | // If we don't have properties in the textual IR, but the operation now has |
1537 | // support for properties, we support some backward-compatible generic syntax |
1538 | // for the operation and as such we accept inherent attributes mixed in the |
1539 | // dictionary of discardable attributes. We pre-validate these here because |
1540 | // invalid attributes can't be casted to the properties storage and will be |
1541 | // silently dropped. For example an attribute { foo = 0 : i32 } that is |
1542 | // declared as F32Attr in ODS would have a C++ type of FloatAttr in the |
1543 | // 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 verifier |
1548 | // would be "missing foo attribute" instead of something like "expects a 32 |
1549 | // 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(Result: info->verifyInherentAttrs(attributes&: result.attributes, emitError: [&]() { |
1555 | return mlir::emitError(loc: 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(state: result); |
1563 | if (parseTrailingLocationSpecifier(opOrArgument: op)) |
1564 | return nullptr; |
1565 | |
1566 | // Try setting the properties for the operation, using a diagnostic to print |
1567 | // errors. |
1568 | if (properties) { |
1569 | auto emitError = [&]() { |
1570 | return mlir::emitError(loc: srcLocation, message: "invalid properties ") |
1571 | << properties << " for op "<< name << ": "; |
1572 | }; |
1573 | if (failed(Result: op->setPropertiesFromAttribute(attr: properties, emitError))) |
1574 | return nullptr; |
1575 | } |
1576 | |
1577 | return op; |
1578 | } |
1579 | |
1580 | Operation *OperationParser::parseGenericOperation(Block *insertBlock, |
1581 | Block::iterator insertPt) { |
1582 | Token nameToken = getToken(); |
1583 | |
1584 | OpBuilder::InsertionGuard restoreInsertionPoint(opBuilder); |
1585 | opBuilder.setInsertionPoint(block: insertBlock, insertPoint: insertPt); |
1586 | Operation *op = parseGenericOperation(); |
1587 | if (!op) |
1588 | return nullptr; |
1589 | |
1590 | // If we are populating the parser asm state, finalize this operation |
1591 | // definition. |
1592 | if (state.asmState) |
1593 | state.asmState->finalizeOperationDefinition( |
1594 | op, nameLoc: nameToken.getLocRange(), |
1595 | /*endLoc=*/getLastToken().getEndLoc()); |
1596 | return op; |
1597 | } |
1598 | |
1599 | namespace { |
1600 | class CustomOpAsmParser : public AsmParserImpl<OpAsmParser> { |
1601 | public: |
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 the |
1613 | /// 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 in |
1619 | // the attribute dictionary in the assembly, or the attribute is set |
1620 | // multiple during parsing. |
1621 | std::optional<NamedAttribute> duplicate = |
1622 | opState.attributes.findDuplicate(); |
1623 | if (duplicate) |
1624 | return emitError(loc: getNameLoc(), message: "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, parsedOperandUseInfo: parsedUnresolvedOperands, parsedSuccessors, parsedRegions, |
1649 | parsedAttributes, propertiesAttribute: parsedPropertiesAttribute, parsedFnType); |
1650 | } |
1651 | //===--------------------------------------------------------------------===// |
1652 | // Utilities |
1653 | //===--------------------------------------------------------------------===// |
1654 | |
1655 | /// Return the name of the specified result in the specified syntax, as well |
1656 | /// as the subelement in the name. For example, in this operation: |
1657 | /// |
1658 | /// %x, %y:2, %z = foo.op |
1659 | /// |
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>(t: entry)) { |
1669 | // Don't pass on the leading %. |
1670 | StringRef name = std::get<0>(t: entry).drop_front(); |
1671 | return {name, resultNo}; |
1672 | } |
1673 | resultNo -= std::get<1>(t: 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.op |
1681 | /// 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>(t: 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 Parsing |
1697 | //===--------------------------------------------------------------------===// |
1698 | |
1699 | /// Parse a single operand. |
1700 | ParseResult parseOperand(UnresolvedOperand &result, |
1701 | bool allowResultNumber = true) override { |
1702 | OperationParser::UnresolvedOperand useInfo; |
1703 | if (parser.parseSSAUse(result&: 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 | OptionalParseResult |
1712 | 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 specified |
1720 | /// 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(kind: 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(k1: Token::l_paren, k2: Token::l_square)) |
1738 | return parser.emitError(message: "unexpected delimiter"); |
1739 | return parser.emitWrongTokenError(message: "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(loc: startLoc, message: "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(Elt: value); |
1764 | return success(); |
1765 | } |
1766 | return failure(); |
1767 | } |
1768 | |
1769 | /// Parse an AffineMap of SSA ids. |
1770 | ParseResult |
1771 | 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 | else |
1784 | dimOperands.push_back(operand); |
1785 | return success(); |
1786 | }; |
1787 | |
1788 | AffineMap map; |
1789 | if (parser.parseAffineMapOfSSAIds(map, parseElement, delimiter: delimiter)) |
1790 | return failure(); |
1791 | // Add AffineMap attribute. |
1792 | if (map) { |
1793 | mapAttr = AffineMapAttr::get(map); |
1794 | attrs.push_back(newAttribute: parser.builder.getNamedAttr(name: attrName, val: 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 | ParseResult |
1805 | 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 | else |
1815 | dimOperands.push_back(operand); |
1816 | return success(); |
1817 | }; |
1818 | |
1819 | return parser.parseAffineExprOfSSAIds(expr, parseElement); |
1820 | } |
1821 | |
1822 | //===--------------------------------------------------------------------===// |
1823 | // Argument Parsing |
1824 | //===--------------------------------------------------------------------===// |
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 respective |
1831 | /// 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(context: 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 Parsing |
1869 | //===--------------------------------------------------------------------===// |
1870 | |
1871 | /// Parse a region that takes `arguments` of `argTypes` types. This |
1872 | /// 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, entryArguments: arguments, isIsolatedNameScope: 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(k: 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 is |
1894 | /// allocated and placed in `region`. If no region is present, `region` |
1895 | /// remains untouched. |
1896 | OptionalParseResult |
1897 | parseOptionalRegion(std::unique_ptr<Region> ®ion, |
1898 | ArrayRef<Argument> arguments, |
1899 | bool enableNameShadowing = false) override { |
1900 | if (parser.getToken().isNot(k: 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 Parsing |
1912 | //===--------------------------------------------------------------------===// |
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(kind: Token::caret_identifier)) |
1922 | return std::nullopt; |
1923 | return parseSuccessor(dest); |
1924 | } |
1925 | |
1926 | /// Parse a single operation successor and its operand list. |
1927 | ParseResult |
1928 | 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(results&: operands) || parseRParen())) { |
1936 | return failure(); |
1937 | } |
1938 | return success(); |
1939 | } |
1940 | |
1941 | //===--------------------------------------------------------------------===// |
1942 | // Type Parsing |
1943 | //===--------------------------------------------------------------------===// |
1944 | |
1945 | /// Parse a list of assignments of the form |
1946 | /// (%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(rightToken: Token::r_paren, parseElement: parseElt); |
1960 | } |
1961 | |
1962 | /// Parse a loc(...) specifier if present, filling in result if so. |
1963 | ParseResult |
1964 | parseOptionalLocationSpecifier(std::optional<Location> &result) override { |
1965 | // If there is a 'loc' we parse a trailing location. |
1966 | if (!parser.consumeIf(kind: Token::kw_loc)) |
1967 | return success(); |
1968 | LocationAttr directLoc; |
1969 | if (parser.parseToken(expectedToken: Token::l_paren, message: "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 a |
1975 | // 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 location |
1977 | // directly. |
1978 | if (tok.is(k: Token::hash_identifier) && !tok.getSpelling().contains(C: '.')) { |
1979 | if (parser.parseLocationAlias(loc&: directLoc)) |
1980 | return failure(); |
1981 | } else if (parser.parseLocationInstance(loc&: directLoc)) { |
1982 | return failure(); |
1983 | } |
1984 | |
1985 | if (parser.parseToken(expectedToken: Token::r_paren, message: "expected ')' in location")) |
1986 | return failure(); |
1987 | |
1988 | result = directLoc; |
1989 | return success(); |
1990 | } |
1991 | |
1992 | private: |
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 | } // namespace |
2005 | |
2006 | FailureOr<OperationName> OperationParser::parseCustomOperationName() { |
2007 | Token nameTok = getToken(); |
2008 | // Accept keywords here as they may be interpreted as a shortened operation |
2009 | // name, e.g., `dialect.keyword` can be spelled as just `keyword` within a |
2010 | // region of an operation from `dialect`. |
2011 | if (nameTok.getKind() != Token::bare_identifier && !nameTok.isKeyword()) |
2012 | return emitError(message: "expected bare identifier or keyword"); |
2013 | StringRef opName = nameTok.getSpelling(); |
2014 | if (opName.empty()) |
2015 | return (emitError(message: "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(name: opName, ctx: getContext()); |
2021 | if (opInfo) |
2022 | return *opInfo; |
2023 | |
2024 | // If the operation doesn't have a dialect prefix try using the default |
2025 | // dialect. |
2026 | auto opNameSplit = opName.split(Separator: '.'); |
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 sure |
2040 | // the operation has a chance to be registered. |
2041 | getContext()->getOrLoadDialect(name: dialectName); |
2042 | return OperationName(opName, getContext()); |
2043 | } |
2044 | |
2045 | Operation * |
2046 | OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) { |
2047 | SMLoc opLoc = getToken().getLoc(); |
2048 | StringRef originalOpName = getTokenSpelling(); |
2049 | |
2050 | FailureOr<OperationName> opNameInfo = parseCustomOperationName(); |
2051 | if (failed(Result: opNameInfo)) |
2052 | return nullptr; |
2053 | StringRef opName = opNameInfo->getStringRef(); |
2054 | |
2055 | // This is the actual hook for the custom op parsing, usually implemented by |
2056 | // the op itself (`Op::parse()`). We retrieve it either from the |
2057 | // 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(loc: opLoc) << "Dialect `"<< opNameInfo->getDialectNamespace() |
2074 | << "' not found for custom op '"<< originalOpName |
2075 | << "' "; |
2076 | if (originalOpName != opName) |
2077 | diag << " (tried '"<< opName << "' as well)"; |
2078 | auto ¬e = diag.attachNote(); |
2079 | note << "Registered dialects: "; |
2080 | llvm::interleaveComma(c: getContext()->getAvailableDialects(), os&: note, |
2081 | each_fn: [&](StringRef dialect) { note << dialect; }); |
2082 | note << " ; for more info on dialect registration see " |
2083 | "https://mlir.llvm.org/getting_started/Faq/" |
2084 | "#registered-loaded-dependent-whats-up-with-dialects-management"; |
2085 | return nullptr; |
2086 | } |
2087 | dialectHook = dialect->getParseOperationHook(opName); |
2088 | if (!dialectHook) { |
2089 | InFlightDiagnostic diag = |
2090 | emitError(loc: opLoc) << "custom op '"<< originalOpName << "' is unknown"; |
2091 | if (originalOpName != opName) |
2092 | diag << " (tried '"<< opName << "' as well)"; |
2093 | return nullptr; |
2094 | } |
2095 | parseAssemblyFn = *dialectHook; |
2096 | } |
2097 | getState().defaultDialectStack.push_back(Elt: defaultDialect); |
2098 | auto restoreDefaultDialect = llvm::make_scope_exit( |
2099 | F: [&]() { getState().defaultDialectStack.pop_back(); }); |
2100 | |
2101 | // If the custom op parser crashes, produce some indication to help |
2102 | // debugging. |
2103 | llvm::PrettyStackTraceFormat fmt("MLIR Parser: custom op parser '%s'", |
2104 | opNameInfo->getIdentifier().data()); |
2105 | |
2106 | // Get location information for the operation. |
2107 | auto srcLocation = getEncodedSourceLocation(loc: opLoc); |
2108 | OperationState opState(srcLocation, *opNameInfo); |
2109 | |
2110 | // If we are populating the parser state, start a new operation definition. |
2111 | if (state.asmState) |
2112 | state.asmState->startOperationDefinition(opName: opState.name); |
2113 | |
2114 | // Have the op implementation take a crack and parsing this. |
2115 | CleanupOpStateRegions guard{.state: opState}; |
2116 | CustomOpAsmParser opAsmParser(opLoc, resultIDs, parseAssemblyFn, |
2117 | isIsolatedFromAbove, opName, *this); |
2118 | if (opAsmParser.parseOperation(opState)) |
2119 | return nullptr; |
2120 | |
2121 | // If it emitted an error, we failed. |
2122 | if (opAsmParser.didEmitError()) |
2123 | return nullptr; |
2124 | |
2125 | Attribute properties = opState.propertiesAttr; |
2126 | opState.propertiesAttr = Attribute{}; |
2127 | |
2128 | // Otherwise, create the operation and try to parse a location for it. |
2129 | Operation *op = opBuilder.create(state: opState); |
2130 | if (parseTrailingLocationSpecifier(opOrArgument: op)) |
2131 | return nullptr; |
2132 | |
2133 | // Try setting the properties for the operation. |
2134 | if (properties) { |
2135 | auto emitError = [&]() { |
2136 | return mlir::emitError(loc: srcLocation, message: "invalid properties ") |
2137 | << properties << " for op "<< op->getName().getStringRef() |
2138 | << ": "; |
2139 | }; |
2140 | if (failed(Result: op->setPropertiesFromAttribute(attr: properties, emitError))) |
2141 | return nullptr; |
2142 | } |
2143 | return op; |
2144 | } |
2145 | |
2146 | ParseResult OperationParser::parseLocationAlias(LocationAttr &loc) { |
2147 | Token tok = getToken(); |
2148 | consumeToken(kind: Token::hash_identifier); |
2149 | StringRef identifier = tok.getSpelling().drop_front(); |
2150 | assert(!identifier.contains('.') && |
2151 | "unexpected dialect attribute token, expected alias"); |
2152 | |
2153 | if (state.asmState) |
2154 | state.asmState->addAttrAliasUses(name: identifier, locations: tok.getLocRange()); |
2155 | |
2156 | // If this alias can be resolved, do it now. |
2157 | Attribute attr = state.symbols.attributeAliasDefinitions.lookup(Key: identifier); |
2158 | if (attr) { |
2159 | if (!(loc = dyn_cast<LocationAttr>(Val&: attr))) |
2160 | return emitError(loc: tok.getLoc()) |
2161 | << "expected location, but found '"<< attr << "'"; |
2162 | } else { |
2163 | // Otherwise, remember this operation and resolve its location later. |
2164 | // In the meantime, use a special OpaqueLoc as a marker. |
2165 | loc = OpaqueLoc::get(deferredLocsReferences.size(), |
2166 | TypeID::get<DeferredLocInfo *>(), |
2167 | UnknownLoc::get(getContext())); |
2168 | deferredLocsReferences.push_back(x: DeferredLocInfo{.loc: tok.getLoc(), .identifier: identifier}); |
2169 | } |
2170 | return success(); |
2171 | } |
2172 | |
2173 | ParseResult |
2174 | OperationParser::parseTrailingLocationSpecifier(OpOrArgument opOrArgument) { |
2175 | // If there is a 'loc' we parse a trailing location. |
2176 | if (!consumeIf(kind: Token::kw_loc)) |
2177 | return success(); |
2178 | if (parseToken(expectedToken: Token::l_paren, message: "expected '(' in location")) |
2179 | return failure(); |
2180 | Token tok = getToken(); |
2181 | |
2182 | // Check to see if we are parsing a location alias. We are parsing a location |
2183 | // alias if the token is a hash identifier *without* a dot in it - the dot |
2184 | // signifies a dialect attribute. Otherwise, we parse the location directly. |
2185 | LocationAttr directLoc; |
2186 | if (tok.is(k: Token::hash_identifier) && !tok.getSpelling().contains(C: '.')) { |
2187 | if (parseLocationAlias(loc&: directLoc)) |
2188 | return failure(); |
2189 | } else if (parseLocationInstance(loc&: directLoc)) { |
2190 | return failure(); |
2191 | } |
2192 | |
2193 | if (parseToken(expectedToken: Token::r_paren, message: "expected ')' in location")) |
2194 | return failure(); |
2195 | |
2196 | if (auto *op = llvm::dyn_cast_if_present<Operation *>(Val&: opOrArgument)) |
2197 | op->setLoc(directLoc); |
2198 | else |
2199 | cast<BlockArgument>(Val&: opOrArgument).setLoc(directLoc); |
2200 | return success(); |
2201 | } |
2202 | |
2203 | //===----------------------------------------------------------------------===// |
2204 | // Region Parsing |
2205 | //===----------------------------------------------------------------------===// |
2206 | |
2207 | ParseResult OperationParser::parseRegion(Region ®ion, |
2208 | ArrayRef<Argument> entryArguments, |
2209 | bool isIsolatedNameScope) { |
2210 | // Parse the '{'. |
2211 | Token lBraceTok = getToken(); |
2212 | if (parseToken(expectedToken: Token::l_brace, message: "expected '{' to begin a region")) |
2213 | return failure(); |
2214 | |
2215 | // If we are populating the parser state, start a new region definition. |
2216 | if (state.asmState) |
2217 | state.asmState->startRegionDefinition(); |
2218 | |
2219 | // Parse the region body. |
2220 | if ((!entryArguments.empty() || getToken().isNot(k: Token::r_brace)) && |
2221 | parseRegionBody(region, startLoc: lBraceTok.getLoc(), entryArguments, |
2222 | isIsolatedNameScope)) { |
2223 | return failure(); |
2224 | } |
2225 | consumeToken(kind: Token::r_brace); |
2226 | |
2227 | // If we are populating the parser state, finalize this region. |
2228 | if (state.asmState) |
2229 | state.asmState->finalizeRegionDefinition(); |
2230 | |
2231 | return success(); |
2232 | } |
2233 | |
2234 | ParseResult OperationParser::parseRegionBody(Region ®ion, SMLoc startLoc, |
2235 | ArrayRef<Argument> entryArguments, |
2236 | bool isIsolatedNameScope) { |
2237 | auto currentPt = opBuilder.saveInsertionPoint(); |
2238 | |
2239 | // Push a new named value scope. |
2240 | pushSSANameScope(isIsolated: isIsolatedNameScope); |
2241 | |
2242 | // Parse the first block directly to allow for it to be unnamed. |
2243 | auto owningBlock = std::make_unique<Block>(); |
2244 | auto failureCleanup = llvm::make_scope_exit(F: [&] { |
2245 | if (owningBlock) { |
2246 | // If parsing failed, as indicated by the fact that `owningBlock` still |
2247 | // owns the block, drop all forward references from preceding operations |
2248 | // to definitions within the parsed block. |
2249 | owningBlock->dropAllDefinedValueUses(); |
2250 | } |
2251 | }); |
2252 | Block *block = owningBlock.get(); |
2253 | |
2254 | // If this block is not defined in the source file, add a definition for it |
2255 | // now in the assembly state. Blocks with a name will be defined when the name |
2256 | // is parsed. |
2257 | if (state.asmState && getToken().isNot(k: Token::caret_identifier)) |
2258 | state.asmState->addDefinition(block, location: startLoc); |
2259 | |
2260 | // Add arguments to the entry block if we had the form with explicit names. |
2261 | if (!entryArguments.empty() && !entryArguments[0].ssaName.name.empty()) { |
2262 | // If we had named arguments, then don't allow a block name. |
2263 | if (getToken().is(k: Token::caret_identifier)) |
2264 | return emitError(message: "invalid block name in region with named arguments"); |
2265 | |
2266 | for (auto &entryArg : entryArguments) { |
2267 | auto &argInfo = entryArg.ssaName; |
2268 | |
2269 | // Ensure that the argument was not already defined. |
2270 | if (auto defLoc = getReferenceLoc(name: argInfo.name, number: argInfo.number)) { |
2271 | return emitError(loc: argInfo.location, message: "region entry argument '"+ |
2272 | argInfo.name + |
2273 | "' is already in use") |
2274 | .attachNote(noteLoc: getEncodedSourceLocation(loc: *defLoc)) |
2275 | << "previously referenced here"; |
2276 | } |
2277 | Location loc = entryArg.sourceLoc.has_value() |
2278 | ? *entryArg.sourceLoc |
2279 | : getEncodedSourceLocation(loc: argInfo.location); |
2280 | BlockArgument arg = block->addArgument(type: entryArg.type, loc); |
2281 | |
2282 | // Add a definition of this arg to the assembly state if provided. |
2283 | if (state.asmState) |
2284 | state.asmState->addDefinition(blockArg: arg, location: argInfo.location); |
2285 | |
2286 | // Record the definition for this argument. |
2287 | if (addDefinition(useInfo: argInfo, value: arg)) |
2288 | return failure(); |
2289 | } |
2290 | } |
2291 | |
2292 | if (parseBlock(block)) |
2293 | return failure(); |
2294 | |
2295 | // Verify that no other arguments were parsed. |
2296 | if (!entryArguments.empty() && |
2297 | block->getNumArguments() > entryArguments.size()) { |
2298 | return emitError(message: "entry block arguments were already defined"); |
2299 | } |
2300 | |
2301 | // Parse the rest of the region. |
2302 | region.push_back(block: owningBlock.release()); |
2303 | while (getToken().isNot(k: Token::r_brace)) { |
2304 | Block *newBlock = nullptr; |
2305 | if (parseBlock(block&: newBlock)) |
2306 | return failure(); |
2307 | region.push_back(block: newBlock); |
2308 | } |
2309 | |
2310 | // Pop the SSA value scope for this region. |
2311 | if (popSSANameScope()) |
2312 | return failure(); |
2313 | |
2314 | // Reset the original insertion point. |
2315 | opBuilder.restoreInsertionPoint(ip: currentPt); |
2316 | return success(); |
2317 | } |
2318 | |
2319 | //===----------------------------------------------------------------------===// |
2320 | // Block Parsing |
2321 | //===----------------------------------------------------------------------===// |
2322 | |
2323 | /// Block declaration. |
2324 | /// |
2325 | /// block ::= block-label? operation* |
2326 | /// block-label ::= block-id block-arg-list? `:` |
2327 | /// block-id ::= caret-id |
2328 | /// block-arg-list ::= `(` ssa-id-and-type-list? `)` |
2329 | /// |
2330 | ParseResult OperationParser::parseBlock(Block *&block) { |
2331 | // The first block of a region may already exist, if it does the caret |
2332 | // identifier is optional. |
2333 | if (block && getToken().isNot(k: Token::caret_identifier)) |
2334 | return parseBlockBody(block); |
2335 | |
2336 | SMLoc nameLoc = getToken().getLoc(); |
2337 | auto name = getTokenSpelling(); |
2338 | if (parseToken(expectedToken: Token::caret_identifier, message: "expected block name")) |
2339 | return failure(); |
2340 | |
2341 | // Define the block with the specified name. |
2342 | auto &blockAndLoc = getBlockInfoByName(name); |
2343 | blockAndLoc.loc = nameLoc; |
2344 | |
2345 | // Use a unique pointer for in-flight block being parsed. Release ownership |
2346 | // only in the case of a successful parse. This ensures that the Block |
2347 | // allocated is released if the parse fails and control returns early. |
2348 | std::unique_ptr<Block> inflightBlock; |
2349 | auto cleanupOnFailure = llvm::make_scope_exit(F: [&] { |
2350 | if (inflightBlock) |
2351 | inflightBlock->dropAllDefinedValueUses(); |
2352 | }); |
2353 | |
2354 | // If a block has yet to be set, this is a new definition. If the caller |
2355 | // provided a block, use it. Otherwise create a new one. |
2356 | if (!blockAndLoc.block) { |
2357 | if (block) { |
2358 | blockAndLoc.block = block; |
2359 | } else { |
2360 | inflightBlock = std::make_unique<Block>(); |
2361 | blockAndLoc.block = inflightBlock.get(); |
2362 | } |
2363 | |
2364 | // Otherwise, the block has a forward declaration. Forward declarations are |
2365 | // removed once defined, so if we are defining a existing block and it is |
2366 | // not a forward declaration, then it is a redeclaration. Fail if the block |
2367 | // was already defined. |
2368 | } else if (!eraseForwardRef(block: blockAndLoc.block)) { |
2369 | return emitError(loc: nameLoc, message: "redefinition of block '") << name << "'"; |
2370 | } else { |
2371 | // This was a forward reference block that is now floating. Keep track of it |
2372 | // as inflight in case of error, so that it gets cleaned up properly. |
2373 | inflightBlock.reset(p: blockAndLoc.block); |
2374 | } |
2375 | |
2376 | // Populate the high level assembly state if necessary. |
2377 | if (state.asmState) |
2378 | state.asmState->addDefinition(block: blockAndLoc.block, location: nameLoc); |
2379 | block = blockAndLoc.block; |
2380 | |
2381 | // If an argument list is present, parse it. |
2382 | if (getToken().is(k: Token::l_paren)) |
2383 | if (parseOptionalBlockArgList(owner: block)) |
2384 | return failure(); |
2385 | if (parseToken(expectedToken: Token::colon, message: "expected ':' after block name")) |
2386 | return failure(); |
2387 | |
2388 | // Parse the body of the block. |
2389 | ParseResult res = parseBlockBody(block); |
2390 | |
2391 | // If parsing was successful, drop the inflight block. We relinquish ownership |
2392 | // back up to the caller. |
2393 | if (succeeded(Result: res)) |
2394 | (void)inflightBlock.release(); |
2395 | return res; |
2396 | } |
2397 | |
2398 | ParseResult OperationParser::parseBlockBody(Block *block) { |
2399 | // Set the insertion point to the end of the block to parse. |
2400 | opBuilder.setInsertionPointToEnd(block); |
2401 | |
2402 | // Parse the list of operations that make up the body of the block. |
2403 | while (getToken().isNot(k1: Token::caret_identifier, k2: Token::r_brace)) |
2404 | if (parseOperation()) |
2405 | return failure(); |
2406 | |
2407 | return success(); |
2408 | } |
2409 | |
2410 | /// Get the block with the specified name, creating it if it doesn't already |
2411 | /// exist. The location specified is the point of use, which allows |
2412 | /// us to diagnose references to blocks that are not defined precisely. |
2413 | Block *OperationParser::getBlockNamed(StringRef name, SMLoc loc) { |
2414 | BlockDefinition &blockDef = getBlockInfoByName(name); |
2415 | if (!blockDef.block) { |
2416 | blockDef = {.block: new Block(), .loc: loc}; |
2417 | insertForwardRef(block: blockDef.block, loc: blockDef.loc); |
2418 | } |
2419 | |
2420 | // Populate the high level assembly state if necessary. |
2421 | if (state.asmState) |
2422 | state.asmState->addUses(block: blockDef.block, locations: loc); |
2423 | |
2424 | return blockDef.block; |
2425 | } |
2426 | |
2427 | /// Parse a (possibly empty) list of SSA operands with types as block arguments |
2428 | /// enclosed in parentheses. |
2429 | /// |
2430 | /// value-id-and-type-list ::= value-id-and-type (`,` ssa-id-and-type)* |
2431 | /// block-arg-list ::= `(` value-id-and-type-list? `)` |
2432 | /// |
2433 | ParseResult OperationParser::parseOptionalBlockArgList(Block *owner) { |
2434 | if (getToken().is(k: Token::r_brace)) |
2435 | return success(); |
2436 | |
2437 | // If the block already has arguments, then we're handling the entry block. |
2438 | // Parse and register the names for the arguments, but do not add them. |
2439 | bool definingExistingArgs = owner->getNumArguments() != 0; |
2440 | unsigned nextArgument = 0; |
2441 | |
2442 | return parseCommaSeparatedList(delimiter: Delimiter::Paren, parseElementFn: [&]() -> ParseResult { |
2443 | return parseSSADefOrUseAndType( |
2444 | action: [&](UnresolvedOperand useInfo, Type type) -> ParseResult { |
2445 | BlockArgument arg; |
2446 | |
2447 | // If we are defining existing arguments, ensure that the argument |
2448 | // has already been created with the right type. |
2449 | if (definingExistingArgs) { |
2450 | // Otherwise, ensure that this argument has already been created. |
2451 | if (nextArgument >= owner->getNumArguments()) |
2452 | return emitError(message: "too many arguments specified in argument list"); |
2453 | |
2454 | // Finally, make sure the existing argument has the correct type. |
2455 | arg = owner->getArgument(i: nextArgument++); |
2456 | if (arg.getType() != type) |
2457 | return emitError(message: "argument and block argument type mismatch"); |
2458 | } else { |
2459 | auto loc = getEncodedSourceLocation(loc: useInfo.location); |
2460 | arg = owner->addArgument(type, loc); |
2461 | } |
2462 | |
2463 | // If the argument has an explicit loc(...) specifier, parse and apply |
2464 | // it. |
2465 | if (parseTrailingLocationSpecifier(opOrArgument: arg)) |
2466 | return failure(); |
2467 | |
2468 | // Mark this block argument definition in the parser state if it was |
2469 | // provided. |
2470 | if (state.asmState) |
2471 | state.asmState->addDefinition(blockArg: arg, location: useInfo.location); |
2472 | |
2473 | return addDefinition(useInfo, value: arg); |
2474 | }); |
2475 | }); |
2476 | } |
2477 | |
2478 | //===----------------------------------------------------------------------===// |
2479 | // Code Completion |
2480 | //===----------------------------------------------------------------------===// |
2481 | |
2482 | ParseResult OperationParser::codeCompleteSSAUse() { |
2483 | for (IsolatedSSANameScope &scope : isolatedNameScopes) { |
2484 | for (auto &it : scope.values) { |
2485 | if (it.second.empty()) |
2486 | continue; |
2487 | Value frontValue = it.second.front().value; |
2488 | |
2489 | std::string detailData; |
2490 | llvm::raw_string_ostream detailOS(detailData); |
2491 | |
2492 | // If the value isn't a forward reference, we also add the name of the op |
2493 | // to the detail. |
2494 | if (auto result = dyn_cast<OpResult>(Val&: frontValue)) { |
2495 | if (!forwardRefPlaceholders.count(Val: result)) |
2496 | detailOS << result.getOwner()->getName() << ": "; |
2497 | } else { |
2498 | detailOS << "arg #"<< cast<BlockArgument>(Val&: frontValue).getArgNumber() |
2499 | << ": "; |
2500 | } |
2501 | |
2502 | // Emit the type of the values to aid with completion selection. |
2503 | detailOS << frontValue.getType(); |
2504 | |
2505 | // FIXME: We should define a policy for packed values, e.g. with a limit |
2506 | // on the detail size, but it isn't clear what would be useful right now. |
2507 | // For now we just only emit the first type. |
2508 | if (it.second.size() > 1) |
2509 | detailOS << ", ..."; |
2510 | |
2511 | state.codeCompleteContext->appendSSAValueCompletion( |
2512 | name: it.getKey(), typeData: std::move(detailData)); |
2513 | } |
2514 | } |
2515 | |
2516 | return failure(); |
2517 | } |
2518 | |
2519 | ParseResult OperationParser::codeCompleteBlock() { |
2520 | // Don't provide completions if the token isn't empty, e.g. this avoids |
2521 | // weirdness when we encounter a `.` within the identifier. |
2522 | StringRef spelling = getTokenSpelling(); |
2523 | if (!(spelling.empty() || spelling == "^")) |
2524 | return failure(); |
2525 | |
2526 | for (const auto &it : blocksByName.back()) |
2527 | state.codeCompleteContext->appendBlockCompletion(name: it.getFirst()); |
2528 | return failure(); |
2529 | } |
2530 | |
2531 | //===----------------------------------------------------------------------===// |
2532 | // Top-level entity parsing. |
2533 | //===----------------------------------------------------------------------===// |
2534 | |
2535 | namespace { |
2536 | /// This parser handles entities that are only valid at the top level of the |
2537 | /// file. |
2538 | class TopLevelOperationParser : public Parser { |
2539 | public: |
2540 | explicit TopLevelOperationParser(ParserState &state) : Parser(state) {} |
2541 | |
2542 | /// Parse a set of operations into the end of the given Block. |
2543 | ParseResult parse(Block *topLevelBlock, Location parserLoc); |
2544 | |
2545 | private: |
2546 | /// Parse an attribute alias declaration. |
2547 | /// |
2548 | /// attribute-alias-def ::= '#' alias-name `=` attribute-value |
2549 | /// |
2550 | ParseResult parseAttributeAliasDef(); |
2551 | |
2552 | /// Parse a type alias declaration. |
2553 | /// |
2554 | /// type-alias-def ::= '!' alias-name `=` type |
2555 | /// |
2556 | ParseResult parseTypeAliasDef(); |
2557 | |
2558 | /// Parse a top-level file metadata dictionary. |
2559 | /// |
2560 | /// file-metadata-dict ::= '{-#' file-metadata-entry* `#-}' |
2561 | /// |
2562 | ParseResult parseFileMetadataDictionary(); |
2563 | |
2564 | /// Parse a resource metadata dictionary. |
2565 | ParseResult parseResourceFileMetadata( |
2566 | function_ref<ParseResult(StringRef, SMLoc)> parseBody); |
2567 | ParseResult parseDialectResourceFileMetadata(); |
2568 | ParseResult parseExternalResourceFileMetadata(); |
2569 | }; |
2570 | |
2571 | /// This class represents an implementation of a resource entry for the MLIR |
2572 | /// textual format. |
2573 | class ParsedResourceEntry : public AsmParsedResourceEntry { |
2574 | public: |
2575 | ParsedResourceEntry(std::string key, SMLoc keyLoc, Token value, Parser &p) |
2576 | : key(std::move(key)), keyLoc(keyLoc), value(value), p(p) {} |
2577 | ~ParsedResourceEntry() override = default; |
2578 | |
2579 | StringRef getKey() const final { return key; } |
2580 | |
2581 | InFlightDiagnostic emitError() const final { return p.emitError(loc: keyLoc); } |
2582 | |
2583 | AsmResourceEntryKind getKind() const final { |
2584 | if (value.isAny(k1: Token::kw_true, k2: Token::kw_false)) |
2585 | return AsmResourceEntryKind::Bool; |
2586 | return value.getSpelling().starts_with(Prefix: "\"0x") |
2587 | ? AsmResourceEntryKind::Blob |
2588 | : AsmResourceEntryKind::String; |
2589 | } |
2590 | |
2591 | FailureOr<bool> parseAsBool() const final { |
2592 | if (value.is(k: Token::kw_true)) |
2593 | return true; |
2594 | if (value.is(k: Token::kw_false)) |
2595 | return false; |
2596 | return p.emitError(loc: value.getLoc(), |
2597 | message: "expected 'true' or 'false' value for key '"+ key + |
2598 | "'"); |
2599 | } |
2600 | |
2601 | FailureOr<std::string> parseAsString() const final { |
2602 | if (value.isNot(k: Token::string)) |
2603 | return p.emitError(loc: value.getLoc(), |
2604 | message: "expected string value for key '"+ key + "'"); |
2605 | return value.getStringValue(); |
2606 | } |
2607 | |
2608 | FailureOr<AsmResourceBlob> |
2609 | parseAsBlob(BlobAllocatorFn allocator) const final { |
2610 | // Blob data within then textual format is represented as a hex string. |
2611 | // TODO: We could avoid an additional alloc+copy here if we pre-allocated |
2612 | // the buffer to use during hex processing. |
2613 | std::optional<std::string> blobData = |
2614 | value.is(k: Token::string) ? value.getHexStringValue() : std::nullopt; |
2615 | if (!blobData) |
2616 | return p.emitError(loc: value.getLoc(), |
2617 | message: "expected hex string blob for key '"+ key + "'"); |
2618 | |
2619 | // Extract the alignment of the blob data, which gets stored at the |
2620 | // beginning of the string. |
2621 | if (blobData->size() < sizeof(uint32_t)) { |
2622 | return p.emitError(loc: value.getLoc(), |
2623 | message: "expected hex string blob for key '"+ key + |
2624 | "' to encode alignment in first 4 bytes"); |
2625 | } |
2626 | llvm::support::ulittle32_t align; |
2627 | memcpy(dest: &align, src: blobData->data(), n: sizeof(uint32_t)); |
2628 | if (align && !llvm::isPowerOf2_32(Value: align)) { |
2629 | return p.emitError(loc: value.getLoc(), |
2630 | message: "expected hex string blob for key '"+ key + |
2631 | "' to encode alignment in first 4 bytes, but got " |
2632 | "non-power-of-2 value: "+ |
2633 | Twine(align)); |
2634 | } |
2635 | |
2636 | // Get the data portion of the blob. |
2637 | StringRef data = StringRef(*blobData).drop_front(N: sizeof(uint32_t)); |
2638 | if (data.empty()) |
2639 | return AsmResourceBlob(); |
2640 | |
2641 | // Allocate memory for the blob using the provided allocator and copy the |
2642 | // data into it. |
2643 | AsmResourceBlob blob = allocator(data.size(), align); |
2644 | assert(llvm::isAddrAligned(llvm::Align(align), blob.getData().data()) && |
2645 | blob.isMutable() && |
2646 | "blob allocator did not return a properly aligned address"); |
2647 | memcpy(dest: blob.getMutableData().data(), src: data.data(), n: data.size()); |
2648 | return blob; |
2649 | } |
2650 | |
2651 | private: |
2652 | std::string key; |
2653 | SMLoc keyLoc; |
2654 | Token value; |
2655 | Parser &p; |
2656 | }; |
2657 | } // namespace |
2658 | |
2659 | ParseResult TopLevelOperationParser::parseAttributeAliasDef() { |
2660 | assert(getToken().is(Token::hash_identifier)); |
2661 | StringRef aliasName = getTokenSpelling().drop_front(); |
2662 | |
2663 | // Check for redefinitions. |
2664 | if (state.symbols.attributeAliasDefinitions.count(Key: aliasName) > 0) |
2665 | return emitError(message: "redefinition of attribute alias id '"+ aliasName + "'"); |
2666 | |
2667 | // Make sure this isn't invading the dialect attribute namespace. |
2668 | if (aliasName.contains(C: '.')) |
2669 | return emitError(message: "attribute names with a '.' are reserved for " |
2670 | "dialect-defined names"); |
2671 | |
2672 | SMRange location = getToken().getLocRange(); |
2673 | consumeToken(kind: Token::hash_identifier); |
2674 | |
2675 | // Parse the '='. |
2676 | if (parseToken(expectedToken: Token::equal, message: "expected '=' in attribute alias definition")) |
2677 | return failure(); |
2678 | |
2679 | // Parse the attribute value. |
2680 | Attribute attr = parseAttribute(); |
2681 | if (!attr) |
2682 | return failure(); |
2683 | |
2684 | // Register this alias with the parser state. |
2685 | if (state.asmState) |
2686 | state.asmState->addAttrAliasDefinition(name: aliasName, location, value: attr); |
2687 | state.symbols.attributeAliasDefinitions[aliasName] = attr; |
2688 | return success(); |
2689 | } |
2690 | |
2691 | ParseResult TopLevelOperationParser::parseTypeAliasDef() { |
2692 | assert(getToken().is(Token::exclamation_identifier)); |
2693 | StringRef aliasName = getTokenSpelling().drop_front(); |
2694 | |
2695 | // Check for redefinitions. |
2696 | if (state.symbols.typeAliasDefinitions.count(Key: aliasName) > 0) |
2697 | return emitError(message: "redefinition of type alias id '"+ aliasName + "'"); |
2698 | |
2699 | // Make sure this isn't invading the dialect type namespace. |
2700 | if (aliasName.contains(C: '.')) |
2701 | return emitError(message: "type names with a '.' are reserved for " |
2702 | "dialect-defined names"); |
2703 | |
2704 | SMRange location = getToken().getLocRange(); |
2705 | consumeToken(kind: Token::exclamation_identifier); |
2706 | |
2707 | // Parse the '='. |
2708 | if (parseToken(expectedToken: Token::equal, message: "expected '=' in type alias definition")) |
2709 | return failure(); |
2710 | |
2711 | // Parse the type. |
2712 | Type aliasedType = parseType(); |
2713 | if (!aliasedType) |
2714 | return failure(); |
2715 | |
2716 | // Register this alias with the parser state. |
2717 | if (state.asmState) |
2718 | state.asmState->addTypeAliasDefinition(name: aliasName, location, value: aliasedType); |
2719 | state.symbols.typeAliasDefinitions.try_emplace(Key: aliasName, Args&: aliasedType); |
2720 | return success(); |
2721 | } |
2722 | |
2723 | ParseResult TopLevelOperationParser::parseFileMetadataDictionary() { |
2724 | consumeToken(kind: Token::file_metadata_begin); |
2725 | return parseCommaSeparatedListUntil( |
2726 | rightToken: Token::file_metadata_end, parseElement: [&]() -> ParseResult { |
2727 | // Parse the key of the metadata dictionary. |
2728 | SMLoc keyLoc = getToken().getLoc(); |
2729 | StringRef key; |
2730 | if (failed(Result: parseOptionalKeyword(keyword: &key))) |
2731 | return emitError(message: "expected identifier key in file " |
2732 | "metadata dictionary"); |
2733 | if (parseToken(expectedToken: Token::colon, message: "expected ':'")) |
2734 | return failure(); |
2735 | |
2736 | // Process the metadata entry. |
2737 | if (key == "dialect_resources") |
2738 | return parseDialectResourceFileMetadata(); |
2739 | if (key == "external_resources") |
2740 | return parseExternalResourceFileMetadata(); |
2741 | return emitError(loc: keyLoc, message: "unknown key '"+ key + |
2742 | "' in file metadata dictionary"); |
2743 | }); |
2744 | } |
2745 | |
2746 | ParseResult TopLevelOperationParser::parseResourceFileMetadata( |
2747 | function_ref<ParseResult(StringRef, SMLoc)> parseBody) { |
2748 | if (parseToken(expectedToken: Token::l_brace, message: "expected '{'")) |
2749 | return failure(); |
2750 | |
2751 | return parseCommaSeparatedListUntil(rightToken: Token::r_brace, parseElement: [&]() -> ParseResult { |
2752 | // Parse the top-level name entry. |
2753 | SMLoc nameLoc = getToken().getLoc(); |
2754 | StringRef name; |
2755 | if (failed(Result: parseOptionalKeyword(keyword: &name))) |
2756 | return emitError(message: "expected identifier key for 'resource' entry"); |
2757 | |
2758 | if (parseToken(expectedToken: Token::colon, message: "expected ':'") || |
2759 | parseToken(expectedToken: Token::l_brace, message: "expected '{'")) |
2760 | return failure(); |
2761 | return parseBody(name, nameLoc); |
2762 | }); |
2763 | } |
2764 | |
2765 | ParseResult TopLevelOperationParser::parseDialectResourceFileMetadata() { |
2766 | return parseResourceFileMetadata(parseBody: [&](StringRef name, |
2767 | SMLoc nameLoc) -> ParseResult { |
2768 | // Lookup the dialect and check that it can handle a resource entry. |
2769 | Dialect *dialect = getContext()->getOrLoadDialect(name); |
2770 | if (!dialect) |
2771 | return emitError(loc: nameLoc, message: "dialect '"+ name + "' is unknown"); |
2772 | const auto *handler = dyn_cast<OpAsmDialectInterface>(Val: dialect); |
2773 | if (!handler) { |
2774 | return emitError() << "unexpected 'resource' section for dialect '" |
2775 | << dialect->getNamespace() << "'"; |
2776 | } |
2777 | |
2778 | return parseCommaSeparatedListUntil(rightToken: Token::r_brace, parseElement: [&]() -> ParseResult { |
2779 | // Parse the name of the resource entry. |
2780 | SMLoc keyLoc = getToken().getLoc(); |
2781 | std::string key; |
2782 | if (failed(Result: parseResourceHandle(dialect: handler, name&: key)) || |
2783 | parseToken(expectedToken: Token::colon, message: "expected ':'")) |
2784 | return failure(); |
2785 | Token valueTok = getToken(); |
2786 | consumeToken(); |
2787 | |
2788 | ParsedResourceEntry entry(key, keyLoc, valueTok, *this); |
2789 | return handler->parseResource(entry); |
2790 | }); |
2791 | }); |
2792 | } |
2793 | |
2794 | ParseResult TopLevelOperationParser::parseExternalResourceFileMetadata() { |
2795 | return parseResourceFileMetadata(parseBody: [&](StringRef name, |
2796 | SMLoc nameLoc) -> ParseResult { |
2797 | AsmResourceParser *handler = state.config.getResourceParser(name); |
2798 | |
2799 | // TODO: Should we require handling external resources in some scenarios? |
2800 | if (!handler) { |
2801 | emitWarning(loc: getEncodedSourceLocation(loc: nameLoc)) |
2802 | << "ignoring unknown external resources for '"<< name << "'"; |
2803 | } |
2804 | |
2805 | return parseCommaSeparatedListUntil(rightToken: Token::r_brace, parseElement: [&]() -> ParseResult { |
2806 | // Parse the name of the resource entry. |
2807 | SMLoc keyLoc = getToken().getLoc(); |
2808 | std::string key; |
2809 | if (failed(Result: parseOptionalKeywordOrString(result: &key))) |
2810 | return emitError( |
2811 | message: "expected identifier key for 'external_resources' entry"); |
2812 | if (parseToken(expectedToken: Token::colon, message: "expected ':'")) |
2813 | return failure(); |
2814 | Token valueTok = getToken(); |
2815 | consumeToken(); |
2816 | |
2817 | if (!handler) |
2818 | return success(); |
2819 | ParsedResourceEntry entry(key, keyLoc, valueTok, *this); |
2820 | return handler->parseResource(entry); |
2821 | }); |
2822 | }); |
2823 | } |
2824 | |
2825 | ParseResult TopLevelOperationParser::parse(Block *topLevelBlock, |
2826 | Location parserLoc) { |
2827 | // Create a top-level operation to contain the parsed state. |
2828 | OwningOpRef<ModuleOp> topLevelOp(ModuleOp::create(parserLoc)); |
2829 | OperationParser opParser(state, topLevelOp.get()); |
2830 | while (true) { |
2831 | switch (getToken().getKind()) { |
2832 | default: |
2833 | // Parse a top-level operation. |
2834 | if (opParser.parseOperation()) |
2835 | return failure(); |
2836 | break; |
2837 | |
2838 | // If we got to the end of the file, then we're done. |
2839 | case Token::eof: { |
2840 | if (opParser.finalize()) |
2841 | return failure(); |
2842 | |
2843 | // Splice the blocks of the parsed operation over to the provided |
2844 | // top-level block. |
2845 | auto &parsedOps = topLevelOp->getBody()->getOperations(); |
2846 | auto &destOps = topLevelBlock->getOperations(); |
2847 | destOps.splice(destOps.end(), parsedOps, parsedOps.begin(), |
2848 | parsedOps.end()); |
2849 | return success(); |
2850 | } |
2851 | |
2852 | // If we got an error token, then the lexer already emitted an error, just |
2853 | // stop. Someday we could introduce error recovery if there was demand |
2854 | // for it. |
2855 | case Token::error: |
2856 | return failure(); |
2857 | |
2858 | // Parse an attribute alias. |
2859 | case Token::hash_identifier: |
2860 | if (parseAttributeAliasDef()) |
2861 | return failure(); |
2862 | break; |
2863 | |
2864 | // Parse a type alias. |
2865 | case Token::exclamation_identifier: |
2866 | if (parseTypeAliasDef()) |
2867 | return failure(); |
2868 | break; |
2869 | |
2870 | // Parse a file-level metadata dictionary. |
2871 | case Token::file_metadata_begin: |
2872 | if (parseFileMetadataDictionary()) |
2873 | return failure(); |
2874 | break; |
2875 | } |
2876 | } |
2877 | } |
2878 | |
2879 | //===----------------------------------------------------------------------===// |
2880 | |
2881 | LogicalResult |
2882 | mlir::parseAsmSourceFile(const llvm::SourceMgr &sourceMgr, Block *block, |
2883 | const ParserConfig &config, AsmParserState *asmState, |
2884 | AsmParserCodeCompleteContext *codeCompleteContext) { |
2885 | const auto *sourceBuf = sourceMgr.getMemoryBuffer(i: sourceMgr.getMainFileID()); |
2886 | |
2887 | Location parserLoc = |
2888 | FileLineColLoc::get(context: config.getContext(), fileName: sourceBuf->getBufferIdentifier(), |
2889 | /*line=*/0, /*column=*/0); |
2890 | |
2891 | SymbolState aliasState; |
2892 | ParserState state(sourceMgr, config, aliasState, asmState, |
2893 | codeCompleteContext); |
2894 | return TopLevelOperationParser(state).parse(topLevelBlock: block, parserLoc); |
2895 | } |
2896 |
Definitions
- ~AsmParserCodeCompleteContext
- parseCommaSeparatedList
- parseCommaSeparatedListUntil
- emitError
- emitError
- emitWrongTokenError
- parseToken
- parseOptionalString
- parseOptionalInteger
- parseOptionalDecimalInteger
- parseFloatFromLiteral
- parseFloatFromIntegerLiteral
- parseOptionalKeyword
- parseOptionalKeywordOrString
- parseResourceHandle
- parseResourceHandle
- codeCompleteDialectName
- codeCompleteOperationName
- codeCompleteDialectOrElidedOpName
- codeCompleteStringDialectOrOperationName
- codeCompleteExpectedTokens
- codeCompleteOptionalTokens
- codeCompleteAttribute
- codeCompleteType
- codeCompleteDialectSymbol
- codeCompleteDialectSymbol
- OperationParser
- DeferredLocInfo
- getReferenceLoc
- BlockDefinition
- ValueDefinition
- getBlockInfoByName
- insertForwardRef
- eraseForwardRef
- isForwardRefPlaceholder
- IsolatedSSANameScope
- recordDefinition
- pushSSANameScope
- popSSANameScope
- OperationParser
- ~OperationParser
- finalize
- pushSSANameScope
- popSSANameScope
- addDefinition
- parseOptionalSSAUseList
- parseSSAUse
- resolveSSAUse
- parseSSADefOrUseAndType
- parseOptionalSSAUseAndTypeList
- recordDefinition
- getSSAValueEntry
- createForwardRefPlaceholder
- parseOperation
- parseSuccessor
- parseSuccessors
- CleanupOpStateRegions
- ~CleanupOpStateRegions
- parseGenericOperationAfterOpName
- parseGenericOperation
- parseGenericOperation
- CustomOpAsmParser
- CustomOpAsmParser
- parseOperation
- parseGenericOperation
- parseCustomOperationName
- parseGenericOperationAfterOpName
- getResultName
- getNumResults
- emitError
- parseOperand
- parseOptionalOperand
- parseOperandList
- resolveOperand
- parseAffineMapOfSSAIds
- parseAffineExprOfSSAIds
- parseArgument
- parseOptionalArgument
- parseArgumentList
- parseRegion
- parseOptionalRegion
- parseOptionalRegion
- parseSuccessor
- parseOptionalSuccessor
- parseSuccessorAndUseList
- parseOptionalAssignmentList
- parseOptionalLocationSpecifier
- parseCustomOperationName
- parseCustomOperation
- parseLocationAlias
- parseTrailingLocationSpecifier
- parseRegion
- parseRegionBody
- parseBlock
- parseBlockBody
- getBlockNamed
- parseOptionalBlockArgList
- codeCompleteSSAUse
- codeCompleteBlock
- TopLevelOperationParser
- TopLevelOperationParser
- ParsedResourceEntry
- ParsedResourceEntry
- ~ParsedResourceEntry
- getKey
- emitError
- getKind
- parseAsBool
- parseAsString
- parseAsBlob
- parseAttributeAliasDef
- parseTypeAliasDef
- parseFileMetadataDictionary
- parseResourceFileMetadata
- parseDialectResourceFileMetadata
- parseExternalResourceFileMetadata
- parse
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more