CIRCT 24.0.0git
Loading...
Searching...
No Matches
FIRParser.cpp
Go to the documentation of this file.
1//===- FIRParser.cpp - .fir to FIRRTL dialect parser ----------------------===//
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 implements a .fir file parser.
10//
11//===----------------------------------------------------------------------===//
12
14#include "FIRLexer.h"
24#include "circt/Support/LLVM.h"
25#include "mlir/IR/BuiltinOps.h"
26#include "mlir/IR/BuiltinTypes.h"
27#include "mlir/IR/Diagnostics.h"
28#include "mlir/IR/ImplicitLocOpBuilder.h"
29#include "mlir/IR/PatternMatch.h"
30#include "mlir/IR/Threading.h"
31#include "mlir/IR/Verifier.h"
32#include "mlir/Support/Timing.h"
33#include "mlir/Tools/mlir-translate/Translation.h"
34#include "llvm/ADT/PointerEmbeddedInt.h"
35#include "llvm/ADT/STLExtras.h"
36#include "llvm/ADT/SmallPtrSet.h"
37#include "llvm/ADT/StringExtras.h"
38#include "llvm/ADT/StringSet.h"
39#include "llvm/ADT/StringSwitch.h"
40#include "llvm/ADT/TypeSwitch.h"
41#include "llvm/Support/JSON.h"
42#include "llvm/Support/LogicalResult.h"
43#include "llvm/Support/SourceMgr.h"
44#include "llvm/Support/raw_ostream.h"
45#include <memory>
46#include <utility>
47
48using namespace circt;
49using namespace firrtl;
50using namespace chirrtl;
51
52using llvm::SMLoc;
53using llvm::SourceMgr;
54using mlir::LocationAttr;
55
56namespace json = llvm::json;
57
58//===----------------------------------------------------------------------===//
59// SharedParserConstants
60//===----------------------------------------------------------------------===//
61
62namespace {
63
64/// This class refers to immutable values and annotations maintained globally by
65/// the parser which can be referred to by any active parser, even those running
66/// in parallel. This is shared by all active parsers.
67struct SharedParserConstants {
68 SharedParserConstants(MLIRContext *context, FIRParserOptions options)
69 : context(context), options(options),
70 emptyArrayAttr(ArrayAttr::get(context, {})),
71 loIdentifier(StringAttr::get(context, "lo")),
72 hiIdentifier(StringAttr::get(context, "hi")),
73 amountIdentifier(StringAttr::get(context, "amount")),
74 placeholderInnerRef(
75 hw::InnerRefAttr::get(StringAttr::get(context, "module"),
76 StringAttr::get(context, "placeholder"))) {}
77
78 /// The context we're parsing into.
79 MLIRContext *const context;
80
81 // Options that control the behavior of the parser.
82 const FIRParserOptions options;
83
84 /// A map from identifiers to type aliases.
85 llvm::StringMap<FIRRTLType> aliasMap;
86
87 /// A map from identifiers to class ops.
88 llvm::DenseMap<StringRef, ClassLike> classMap;
89
90 /// A map from identifiers to domain ops.
91 llvm::DenseMap<StringRef, DomainOp> domainMap;
92
93 /// An empty array attribute.
94 const ArrayAttr emptyArrayAttr;
95
96 /// Cached identifiers used in primitives.
97 const StringAttr loIdentifier, hiIdentifier, amountIdentifier;
98
99 /// Cached placeholder inner-ref used until fixed up.
100 const hw::InnerRefAttr placeholderInnerRef;
101
102private:
103 SharedParserConstants(const SharedParserConstants &) = delete;
104 void operator=(const SharedParserConstants &) = delete;
105};
106
107} // end anonymous namespace
108
109//===----------------------------------------------------------------------===//
110// FIRParser
111//===----------------------------------------------------------------------===//
112
113namespace {
114/// This class implements logic common to all levels of the parser, including
115/// things like types and helper logic.
116struct FIRParser {
117 FIRParser(SharedParserConstants &constants, FIRLexer &lexer,
118 FIRVersion version)
119 : version(version), constants(constants), lexer(lexer),
120 locatorFilenameCache(constants.loIdentifier /*arbitrary non-null id*/) {
121 }
122
123 // Helper methods to get stuff from the shared parser constants.
124 SharedParserConstants &getConstants() const { return constants; }
125 MLIRContext *getContext() const { return constants.context; }
126
127 FIRLexer &getLexer() { return lexer; }
128
129 /// Return the indentation level of the specified token.
130 std::optional<unsigned> getIndentation() const {
131 return lexer.getIndentation(getToken());
132 }
133
134 /// Return the current token the parser is inspecting.
135 const FIRToken &getToken() const { return lexer.getToken(); }
136 StringRef getTokenSpelling() const { return getToken().getSpelling(); }
137
138 //===--------------------------------------------------------------------===//
139 // Error Handling
140 //===--------------------------------------------------------------------===//
141
142 /// Emit an error and return failure.
143 InFlightDiagnostic emitError(const Twine &message = {}) {
144 return emitError(getToken().getLoc(), message);
145 }
146 InFlightDiagnostic emitError(SMLoc loc, const Twine &message = {});
147
148 /// Emit a warning.
149 InFlightDiagnostic emitWarning(const Twine &message = {}) {
150 return emitWarning(getToken().getLoc(), message);
151 }
152
153 InFlightDiagnostic emitWarning(SMLoc loc, const Twine &message = {});
154
155 //===--------------------------------------------------------------------===//
156 // Location Handling
157 //===--------------------------------------------------------------------===//
158
159 class LocWithInfo;
160
161 /// Encode the specified source location information into an attribute for
162 /// attachment to the IR.
163 Location translateLocation(llvm::SMLoc loc) {
164 return lexer.translateLocation(loc);
165 }
166
167 /// Parse an @info marker if present. If so, fill in the specified Location,
168 /// if not, ignore it.
169 ParseResult parseOptionalInfoLocator(LocationAttr &result);
170
171 /// Parse an optional name that may appear in Stop, Printf, or Verification
172 /// statements.
173 ParseResult parseOptionalName(StringAttr &name);
174
175 //===--------------------------------------------------------------------===//
176 // Version and Feature Checking
177 //===--------------------------------------------------------------------===//
178
179 ParseResult requireFeature(FIRVersion minimum, StringRef feature) {
180 return requireFeature(minimum, feature, getToken().getLoc());
181 }
182
183 ParseResult requireFeature(FIRVersion minimum, StringRef feature, SMLoc loc) {
184 if (version < minimum)
185 return emitError(loc)
186 << feature << " are a FIRRTL " << minimum
187 << "+ feature, but the specified FIRRTL version was " << version;
188 return success();
189 }
190
191 ParseResult removedFeature(FIRVersion removedVersion, StringRef feature) {
192 return removedFeature(removedVersion, feature, getToken().getLoc());
193 }
194
195 ParseResult removedFeature(FIRVersion removedVersion, StringRef feature,
196 SMLoc loc) {
197 if (version >= removedVersion)
198 return emitError(loc)
199 << feature << " were removed in FIRRTL " << removedVersion
200 << ", but the specified FIRRTL version was " << version;
201 return success();
202 }
203
204 //===--------------------------------------------------------------------===//
205 // Annotation Parsing
206 //===--------------------------------------------------------------------===//
207
208 /// Parse a non-standard inline Annotation JSON blob if present. This uses
209 /// the info-like encoding of %[<JSON Blob>].
210 ParseResult parseOptionalAnnotations(SMLoc &loc, StringRef &result);
211
212 //===--------------------------------------------------------------------===//
213 // Token Parsing
214 //===--------------------------------------------------------------------===//
215
216 /// If the current token has the specified kind, consume it and return true.
217 /// If not, return false.
218 bool consumeIf(FIRToken::Kind kind) {
219 if (getToken().isNot(kind))
220 return false;
221 consumeToken(kind);
222 return true;
223 }
224
225 /// Advance the current lexer onto the next token.
226 ///
227 /// This returns the consumed token.
228 FIRToken consumeToken() {
229 FIRToken consumedToken = getToken();
230 assert(consumedToken.isNot(FIRToken::eof, FIRToken::error) &&
231 "shouldn't advance past EOF or errors");
232 lexer.lexToken();
233 return consumedToken;
234 }
235
236 /// Advance the current lexer onto the next token, asserting what the expected
237 /// current token is. This is preferred to the above method because it leads
238 /// to more self-documenting code with better checking.
239 ///
240 /// This returns the consumed token.
241 FIRToken consumeToken(FIRToken::Kind kind) {
242 FIRToken consumedToken = getToken();
243 assert(consumedToken.is(kind) && "consumed an unexpected token");
244 consumeToken();
245 return consumedToken;
246 }
247
248 /// Capture the current token's spelling into the specified value. This
249 /// always succeeds.
250 ParseResult parseGetSpelling(StringRef &spelling) {
251 spelling = getTokenSpelling();
252 return success();
253 }
254
255 /// Consume the specified token if present and return success. On failure,
256 /// output a diagnostic and return failure.
257 ParseResult parseToken(FIRToken::Kind expectedToken, const Twine &message);
258
259 /// Parse a comma-separated list of elements, terminated with an arbitrary
260 /// token.
261 ParseResult parseListUntil(FIRToken::Kind rightToken,
262 const std::function<ParseResult()> &parseElement);
263
264 //===--------------------------------------------------------------------===//
265 // Common Parser Rules
266 //===--------------------------------------------------------------------===//
267
268 /// Parse 'intLit' into the specified value.
269 ParseResult parseIntLit(APInt &result, const Twine &message);
270 ParseResult parseIntLit(int64_t &result, const Twine &message);
271 ParseResult parseIntLit(int32_t &result, const Twine &message);
272
273 // Parse 'verLit' into specified value
274 ParseResult parseVersionLit(const Twine &message);
275
276 // Parse 'intLit' '>' assuming '<' was already consumed.
277 ParseResult parseWidth(int32_t &result);
278
279 // Parse the 'id' grammar, which is an identifier or an allowed keyword.
280 ParseResult parseId(StringRef &result, const Twine &message);
281 ParseResult parseId(StringAttr &result, const Twine &message);
282 ParseResult parseFieldId(StringRef &result, const Twine &message);
283 ParseResult parseFieldIdSeq(SmallVectorImpl<StringRef> &result,
284 const Twine &message);
285 ParseResult parseEnumType(FIRRTLType &result);
286 ParseResult parseListType(FIRRTLType &result);
287 ParseResult parseRegistryType(FIRRTLType &result);
288 ParseResult parseType(FIRRTLType &result, const Twine &message);
289 // Parse a property type specifically.
290 ParseResult parsePropertyType(PropertyType &result, const Twine &message);
291
292 ParseResult parseRUW(RUWBehavior &result);
293 ParseResult parseOptionalRUW(RUWBehavior &result);
294
295 ParseResult parseParameter(StringAttr &resultName, Attribute &resultValue,
296 SMLoc &resultLoc, bool allowAggregates = false);
297 ParseResult parseParameterValue(Attribute &resultValue,
298 bool allowAggregates = false);
299
300 /// The version of FIRRTL to use for this parser.
301 FIRVersion version;
302
303private:
304 FIRParser(const FIRParser &) = delete;
305 void operator=(const FIRParser &) = delete;
306
307 /// FIRParser is subclassed and reinstantiated. Do not add additional
308 /// non-trivial state here, add it to SharedParserConstants.
309 SharedParserConstants &constants;
310 FIRLexer &lexer;
311
312 /// This is a single-entry cache for filenames in locators.
313 StringAttr locatorFilenameCache;
314 /// This is a single-entry cache for FileLineCol locations.
315 FileLineColLoc fileLineColLocCache;
316};
317
318} // end anonymous namespace
319
320//===----------------------------------------------------------------------===//
321// Error Handling
322//===----------------------------------------------------------------------===//
323
324InFlightDiagnostic FIRParser::emitError(SMLoc loc, const Twine &message) {
325 auto diag = mlir::emitError(translateLocation(loc), message);
326
327 // If we hit a parse error in response to a lexer error, then the lexer
328 // already reported the error.
329 if (getToken().is(FIRToken::error))
330 diag.abandon();
331 return diag;
332}
333
334InFlightDiagnostic FIRParser::emitWarning(SMLoc loc, const Twine &message) {
335 return mlir::emitWarning(translateLocation(loc), message);
336}
337
338//===----------------------------------------------------------------------===//
339// Token Parsing
340//===----------------------------------------------------------------------===//
341
342/// Consume the specified token if present and return success. On failure,
343/// output a diagnostic and return failure.
344ParseResult FIRParser::parseToken(FIRToken::Kind expectedToken,
345 const Twine &message) {
346 if (consumeIf(expectedToken))
347 return success();
348 return emitError(message);
349}
350
351/// Parse a comma-separated list of zero or more elements, terminated with an
352/// arbitrary token.
353ParseResult
354FIRParser::parseListUntil(FIRToken::Kind rightToken,
355 const std::function<ParseResult()> &parseElement) {
356 if (consumeIf(rightToken))
357 return success();
358
359 if (parseElement())
360 return failure();
361
362 while (consumeIf(FIRToken::comma)) {
363 if (parseElement())
364 return failure();
365 }
366
367 if (parseToken(rightToken, "expected ','"))
368 return failure();
369
370 return success();
371}
372
373//===--------------------------------------------------------------------===//
374// Location Processing
375//===--------------------------------------------------------------------===//
376
377/// This helper class is used to handle Info records, which specify higher level
378/// symbolic source location, that may be missing from the file. If the higher
379/// level source information is missing, we fall back to the location in the
380/// .fir file.
382public:
383 explicit LocWithInfo(SMLoc firLoc, FIRParser *parser)
384 : parser(parser), firLoc(firLoc) {}
385
386 SMLoc getFIRLoc() const { return firLoc; }
387
388 Location getLoc() {
389 if (infoLoc)
390 return *infoLoc;
391 auto result = parser->translateLocation(firLoc);
392 infoLoc = result;
393 return result;
394 }
395
396 /// Parse an @info marker if present and update our location.
397 ParseResult parseOptionalInfo() {
398 LocationAttr loc;
399 if (failed(parser->parseOptionalInfoLocator(loc)))
400 return failure();
401 if (loc) {
403 switch (parser->constants.options.infoLocatorHandling) {
404 case ILH::IgnoreInfo:
405 assert(0 && "Should not return info locations if ignoring");
406 break;
407 case ILH::PreferInfo:
408 infoLoc = loc;
409 break;
410 case ILH::FusedInfo:
411 infoLoc = FusedLoc::get(loc.getContext(),
412 {loc, parser->translateLocation(firLoc)});
413 break;
414 }
415 }
416 return success();
417 }
418
419 /// If we didn't parse an info locator for the specified value, this sets a
420 /// default, overriding a fall back to a location in the .fir file.
421 void setDefaultLoc(Location loc) {
422 if (!infoLoc)
423 infoLoc = loc;
424 }
425
426private:
427 FIRParser *const parser;
428
429 /// This is the designated location in the .fir file for use when there is no
430 /// @ info marker.
431 SMLoc firLoc;
432
433 /// This is the location specified by the @ marker if present.
434 std::optional<Location> infoLoc;
435};
436
437/// Parse an @info marker if present. If so, fill in the specified Location,
438/// if not, ignore it.
439ParseResult FIRParser::parseOptionalInfoLocator(LocationAttr &result) {
440 if (getToken().isNot(FIRToken::fileinfo))
441 return success();
442
443 auto loc = getToken().getLoc();
444
445 auto spelling = getTokenSpelling();
446 consumeToken(FIRToken::fileinfo);
447
448 auto locationPair = maybeStringToLocation(
449 spelling,
450 constants.options.infoLocatorHandling ==
451 FIRParserOptions::InfoLocHandling::IgnoreInfo,
452 locatorFilenameCache, fileLineColLocCache, getContext());
453
454 // If parsing failed, then indicate that a weird info was found.
455 if (!locationPair.first) {
456 mlir::emitWarning(translateLocation(loc),
457 "ignoring unknown @ info record format");
458 return success();
459 }
460
461 // If the parsing succeeded, but we are supposed to drop locators, then just
462 // return.
463 if (locationPair.first && constants.options.infoLocatorHandling ==
464 FIRParserOptions::InfoLocHandling::IgnoreInfo)
465 return success();
466
467 // Otherwise, set the location attribute and return.
468 result = *locationPair.second;
469 return success();
470}
471
472/// Parse an optional trailing name that may show up on assert, assume, cover,
473/// stop, or printf.
474///
475/// optional_name ::= ( ':' id )?
476ParseResult FIRParser::parseOptionalName(StringAttr &name) {
477
478 if (getToken().isNot(FIRToken::colon)) {
479 name = StringAttr::get(getContext(), "");
480 return success();
481 }
482
483 consumeToken(FIRToken::colon);
484 StringRef nameRef;
485 if (parseId(nameRef, "expected result name"))
486 return failure();
487
488 name = StringAttr::get(getContext(), nameRef);
489
490 return success();
491}
492
493//===--------------------------------------------------------------------===//
494// Annotation Handling
495//===--------------------------------------------------------------------===//
496
497/// Parse a non-standard inline Annotation JSON blob if present. This uses
498/// the info-like encoding of %[<JSON Blob>].
499ParseResult FIRParser::parseOptionalAnnotations(SMLoc &loc, StringRef &result) {
500
501 if (getToken().isNot(FIRToken::inlineannotation))
502 return success();
503
504 loc = getToken().getLoc();
505
506 result = getTokenSpelling().drop_front(2).drop_back(1);
507 consumeToken(FIRToken::inlineannotation);
508
509 return success();
510}
511
512//===--------------------------------------------------------------------===//
513// Common Parser Rules
514//===--------------------------------------------------------------------===//
515
516/// intLit ::= UnsignedInt
517/// ::= SignedInt
518/// ::= HexLit
519/// ::= OctalLit
520/// ::= BinaryLit
521/// HexLit ::= '"' 'h' ( '+' | '-' )? ( HexDigit )+ '"'
522/// OctalLit ::= '"' 'o' ( '+' | '-' )? ( OctalDigit )+ '"'
523/// BinaryLit ::= '"' 'b' ( '+' | '-' )? ( BinaryDigit )+ '"'
524///
525ParseResult FIRParser::parseIntLit(APInt &result, const Twine &message) {
526 auto spelling = getTokenSpelling();
527 bool isNegative = false;
528 switch (getToken().getKind()) {
529 case FIRToken::signed_integer:
530 isNegative = spelling[0] == '-';
531 assert(spelling[0] == '+' || spelling[0] == '-');
532 spelling = spelling.drop_front();
533 [[fallthrough]];
534 case FIRToken::integer:
535 if (spelling.getAsInteger(10, result))
536 return emitError(message), failure();
537
538 // Make sure that the returned APInt has a zero at the top so clients don't
539 // confuse it with a negative number.
540 if (result.isNegative())
541 result = result.zext(result.getBitWidth() + 1);
542
543 if (isNegative)
544 result = -result;
545
546 // If this was parsed as >32 bits, but can be represented in 32 bits,
547 // truncate off the extra width. This is important for extmodules which
548 // like parameters to be 32-bits, and insulates us from some arbitraryness
549 // in StringRef::getAsInteger.
550 if (result.getBitWidth() > 32 && result.getSignificantBits() <= 32)
551 result = result.trunc(32);
552
553 consumeToken();
554 return success();
555 case FIRToken::radix_specified_integer: {
556 if (requireFeature({2, 4, 0}, "radix-specified integer literals"))
557 return failure();
558 if (spelling[0] == '-') {
559 isNegative = true;
560 spelling = spelling.drop_front();
561 }
562 unsigned base = llvm::StringSwitch<unsigned>(spelling.take_front(2))
563 .Case("0b", 2)
564 .Case("0o", 8)
565 .Case("0d", 10)
566 .Case("0h", 16);
567 spelling = spelling.drop_front(2);
568 if (spelling.getAsInteger(base, result))
569 return emitError("invalid character in integer literal"), failure();
570 if (result.isNegative())
571 result = result.zext(result.getBitWidth() + 1);
572 if (isNegative)
573 result = -result;
574 consumeToken();
575 return success();
576 }
577 case FIRToken::string: {
578 if (FIRVersion(3, 0, 0) <= version)
579 return emitError(
580 "String-encoded integer literals are unsupported after FIRRTL 3.0.0");
581
582 // Drop the quotes.
583 assert(spelling.front() == '"' && spelling.back() == '"');
584 spelling = spelling.drop_back().drop_front();
585
586 // Decode the base.
587 unsigned base;
588 switch (spelling.empty() ? ' ' : spelling.front()) {
589 case 'h':
590 base = 16;
591 break;
592 case 'o':
593 base = 8;
594 break;
595 case 'b':
596 base = 2;
597 break;
598 default:
599 return emitError("expected base specifier (h/o/b) in integer literal"),
600 failure();
601 }
602 spelling = spelling.drop_front();
603
604 // Handle the optional sign.
605 bool isNegative = false;
606 if (!spelling.empty() && spelling.front() == '+')
607 spelling = spelling.drop_front();
608 else if (!spelling.empty() && spelling.front() == '-') {
609 isNegative = true;
610 spelling = spelling.drop_front();
611 }
612
613 // Parse the digits.
614 if (spelling.empty())
615 return emitError("expected digits in integer literal"), failure();
616
617 if (spelling.getAsInteger(base, result))
618 return emitError("invalid character in integer literal"), failure();
619
620 // We just parsed the positive version of this number. Make sure it has
621 // a zero at the top so clients don't confuse it with a negative number and
622 // so the negation (in the case of a negative sign) doesn't overflow.
623 if (result.isNegative())
624 result = result.zext(result.getBitWidth() + 1);
625
626 if (isNegative)
627 result = -result;
628
629 consumeToken(FIRToken::string);
630 return success();
631 }
632
633 default:
634 return emitError("expected integer literal"), failure();
635 }
636}
637
638ParseResult FIRParser::parseIntLit(int64_t &result, const Twine &message) {
639 APInt value;
640 auto loc = getToken().getLoc();
641 if (parseIntLit(value, message))
642 return failure();
643
644 result = (int64_t)value.getLimitedValue(INT64_MAX);
645 if (result != value)
646 return emitError(loc, "value is too big to handle"), failure();
647 return success();
648}
649
650ParseResult FIRParser::parseIntLit(int32_t &result, const Twine &message) {
651 APInt value;
652 auto loc = getToken().getLoc();
653 if (parseIntLit(value, message))
654 return failure();
655
656 result = (int32_t)value.getLimitedValue(INT32_MAX);
657 if (result != value)
658 return emitError(loc, "value is too big to handle"), failure();
659 return success();
660}
661
662/// versionLit ::= version
663/// deconstruct a version literal into parts and returns those.
664ParseResult FIRParser::parseVersionLit(const Twine &message) {
665 auto spelling = getTokenSpelling();
666 if (getToken().getKind() != FIRToken::version)
667 return emitError(message), failure();
668 auto ver = FIRVersion::fromString(spelling);
669 if (!ver)
670 return emitError("failed to parse version string"), failure();
671 version = *ver;
672 if (version < minimumFIRVersion)
673 return emitError() << "FIRRTL version must be >=" << minimumFIRVersion,
674 failure();
675 consumeToken(FIRToken::version);
676 return success();
677}
678
679/// Parse a width specifier: intLit '>'
680/// This is used when the '<' has already been consumed.
681ParseResult FIRParser::parseWidth(int32_t &result) {
682 auto widthLoc = getToken().getLoc();
683 if (parseIntLit(result, "expected width") ||
684 parseToken(FIRToken::greater, "expected '>'"))
685 return failure();
686 if (result < 0)
687 return emitError(widthLoc, "invalid width specifier"), failure();
688 return success();
689}
690
691/// id ::= Id | keywordAsId
692///
693/// Parse the 'id' grammar, which is an identifier or an allowed keyword. On
694/// success, this returns the identifier in the result attribute.
695ParseResult FIRParser::parseId(StringRef &result, const Twine &message) {
696 switch (getToken().getKind()) {
697 // The most common case is an identifier.
698 case FIRToken::identifier:
699 case FIRToken::literal_identifier:
700// Otherwise it may be a keyword that we're allowing in an id position.
701#define TOK_KEYWORD(spelling) case FIRToken::kw_##spelling:
702#include "FIRTokenKinds.def"
703
704 // Yep, this is a valid identifier or literal identifier. Turn it into an
705 // attribute. If it is a literal identifier, then drop the leading and
706 // trailing '`' (backticks).
707 if (getToken().getKind() == FIRToken::literal_identifier)
708 result = getTokenSpelling().drop_front().drop_back();
709 else
710 result = getTokenSpelling();
711 consumeToken();
712 return success();
713
714 default:
715 emitError(message);
716 return failure();
717 }
718}
719
720ParseResult FIRParser::parseId(StringAttr &result, const Twine &message) {
721 StringRef name;
722 if (parseId(name, message))
723 return failure();
724
725 result = StringAttr::get(getContext(), name);
726 return success();
727}
728
729/// fieldId ::= Id
730/// ::= RelaxedId
731/// ::= UnsignedInt
732/// ::= keywordAsId
733///
734ParseResult FIRParser::parseFieldId(StringRef &result, const Twine &message) {
735 // Handle the UnsignedInt case.
736 result = getTokenSpelling();
737 if (consumeIf(FIRToken::integer))
738 return success();
739
740 // FIXME: Handle RelaxedId
741
742 // Otherwise, it must be Id or keywordAsId.
743 if (parseId(result, message))
744 return failure();
745
746 return success();
747}
748
749/// fieldId ::= Id
750/// ::= Float
751/// ::= version
752/// ::= UnsignedInt
753/// ::= keywordAsId
754///
755ParseResult FIRParser::parseFieldIdSeq(SmallVectorImpl<StringRef> &result,
756 const Twine &message) {
757 // Handle the UnsignedInt case.
758 StringRef tmp = getTokenSpelling();
759
760 if (consumeIf(FIRToken::integer)) {
761 result.push_back(tmp);
762 return success();
763 }
764
765 if (consumeIf(FIRToken::floatingpoint)) {
766 // form a.b
767 // Both a and b could have more floating point stuff, but just ignore that
768 // for now.
769 auto [a, b] = tmp.split(".");
770 result.push_back(a);
771 result.push_back(b);
772 return success();
773 }
774
775 if (consumeIf(FIRToken::version)) {
776 // form a.b.c
777 auto [a, d] = tmp.split(".");
778 auto [b, c] = d.split(".");
779 result.push_back(a);
780 result.push_back(b);
781 result.push_back(c);
782 return success();
783 }
784
785 // Otherwise, it must be Id or keywordAsId.
786 if (parseId(tmp, message))
787 return failure();
788 result.push_back(tmp);
789 return success();
790}
791
792/// enum-field ::= Id ( '=' int )? ( ':' type )? ;
793/// enum-type ::= '{|' enum-field* '|}'
794ParseResult FIRParser::parseEnumType(FIRRTLType &result) {
795 if (parseToken(FIRToken::l_brace_bar,
796 "expected leading '{|' in enumeration type"))
797 return failure();
798 SmallVector<StringAttr> names;
799 SmallVector<APInt> values;
800 SmallVector<FIRRTLBaseType> types;
801 SmallVector<SMLoc> locs;
802 if (parseListUntil(FIRToken::r_brace_bar, [&]() -> ParseResult {
803 auto fieldLoc = getToken().getLoc();
804 locs.push_back(fieldLoc);
805
806 // Parse the name of the tag.
807 StringRef nameStr;
808 if (parseId(nameStr, "expected valid identifier for enumeration tag"))
809 return failure();
810 auto name = StringAttr::get(getContext(), nameStr);
811 names.push_back(name);
812
813 // Parse the integer value if it exists. If its the first element of the
814 // enum, it implicitly has a value of 0, otherwise it has the previous
815 // value + 1.
816 APInt value;
817 if (consumeIf(FIRToken::equal)) {
818 if (parseIntLit(value, "expected integer value for enumeration tag"))
819 return failure();
820 if (value.isNegative())
821 return emitError(fieldLoc, "enum tag value must be non-negative");
822 } else if (values.empty()) {
823 // This is the first enum variant, so it defaults to 0.
824 value = APInt(1, 0);
825 } else {
826 // This value is not specified, so it defaults to the previous value
827 // + 1.
828 auto &prev = values.back();
829 if (prev.isMaxValue())
830 value = prev.zext(prev.getBitWidth() + 1);
831 else
832 value = prev;
833 ++value;
834 }
835 values.push_back(std::move(value));
836
837 // Parse an optional type ascription.
838 FIRRTLBaseType type;
839 if (consumeIf(FIRToken::colon)) {
840 FIRRTLType parsedType;
841 if (parseType(parsedType, "expected enumeration type"))
842 return failure();
843 type = type_dyn_cast<FIRRTLBaseType>(parsedType);
844 if (!type)
845 return emitError(fieldLoc, "field must be a base type");
846 } else {
847 // If there is no type specified, default to UInt<0>.
848 type = UIntType::get(getContext(), 0);
849 }
850 types.push_back(type);
851
852 auto r = type.getRecursiveTypeProperties();
853 if (!r.isPassive)
854 return emitError(fieldLoc) << "enum field " << name << " not passive";
855 if (r.containsAnalog)
856 return emitError(fieldLoc)
857 << "enum field " << name << " contains analog";
858 if (r.hasUninferredWidth)
859 return emitError(fieldLoc)
860 << "enum field " << name << " has uninferred width";
861 if (r.hasUninferredReset)
862 return emitError(fieldLoc)
863 << "enum field " << name << " has uninferred reset";
864 return success();
865 }))
866 return failure();
867
868 // Verify that the names of each variant are unique.
869 SmallPtrSet<StringAttr, 4> nameSet;
870 for (auto [name, loc] : llvm::zip(names, locs))
871 if (!nameSet.insert(name).second)
872 return emitError(loc,
873 "duplicate variant name in enum: " + name.getValue());
874
875 // Find the bitwidth of the enum.
876 unsigned bitwidth = 0;
877 for (auto &value : values)
878 bitwidth = std::max(bitwidth, value.getActiveBits());
879 auto tagType =
880 IntegerType::get(getContext(), bitwidth, IntegerType::Unsigned);
881
882 // Extend all tag values to the same width, and check that they are all
883 // unique.
884 SmallPtrSet<IntegerAttr, 4> valueSet;
885 SmallVector<FEnumType::EnumElement, 4> elements;
886 for (auto [name, value, type, loc] : llvm::zip(names, values, types, locs)) {
887 auto tagValue = value.zextOrTrunc(bitwidth);
888 auto attr = IntegerAttr::get(tagType, tagValue);
889 // Verify that the names of each variant are unique.
890 if (!valueSet.insert(attr).second)
891 return emitError(loc, "duplicate variant value in enum: ") << attr;
892 elements.push_back({name, attr, type});
893 }
894
895 llvm::sort(elements);
896 result = FEnumType::get(getContext(), elements);
897 return success();
898}
899
900ParseResult FIRParser::parsePropertyType(PropertyType &result,
901 const Twine &message) {
902 auto loc = getToken().getLoc();
903
904 FIRRTLType type;
905 if (parseType(type, message))
906 return failure();
907 auto prop = type_dyn_cast<PropertyType>(type);
908 if (!prop)
909 return emitError(loc, "expected property type");
910 result = prop;
911 return success();
912}
913
914/// list-type ::= 'List' '<' type '>'
915ParseResult FIRParser::parseListType(FIRRTLType &result) {
916 consumeToken(FIRToken::kw_List);
917
919 if (parseToken(FIRToken::less, "expected '<' in List type") ||
920 parsePropertyType(elementType, "expected List element type") ||
921 parseToken(FIRToken::greater, "expected '>' in List type"))
922 return failure();
923
924 result = ListType::get(getContext(), elementType);
925 return success();
926}
927
928/// registry-type ::= 'Registry' '<' type '>'
929ParseResult FIRParser::parseRegistryType(FIRRTLType &result) {
930 consumeToken(FIRToken::kw_Registry);
931
933 if (parseToken(FIRToken::less, "expected '<' in Registry type") ||
934 parsePropertyType(elementType, "expected Registry element type") ||
935 parseToken(FIRToken::greater, "expected '>' in Registry type"))
936 return failure();
937
938 result = RegistryType::get(getContext(), elementType);
939 return success();
940}
941
942/// type ::= 'Clock'
943/// ::= 'Reset'
944/// ::= 'AsyncReset'
945/// ::= 'UInt' optional-width
946/// ::= 'SInt' optional-width
947/// ::= 'Analog' optional-width
948/// ::= 'Domain'
949/// ::= {' field* '}'
950/// ::= type '[' intLit ']'
951/// ::= 'Probe' '<' type '>'
952/// ::= 'RWProbe' '<' type '>'
953/// ::= 'const' type
954/// ::= 'String'
955/// ::= list-type
956/// ::= registry-type
957/// ::= id
958///
959/// field: 'flip'? fieldId ':' type
960///
961// NOLINTNEXTLINE(misc-no-recursion)
962ParseResult FIRParser::parseType(FIRRTLType &result, const Twine &message) {
963 switch (getToken().getKind()) {
964 default:
965 return emitError(message), failure();
966
967 case FIRToken::kw_Clock:
968 consumeToken(FIRToken::kw_Clock);
969 result = ClockType::get(getContext());
970 break;
971
972 case FIRToken::kw_Inst: {
973 if (requireFeature({6, 0, 0}, "Inst types"))
974 return failure();
975
976 consumeToken(FIRToken::kw_Inst);
977 if (parseToken(FIRToken::less, "expected < in Inst type"))
978 return failure();
979
980 auto loc = getToken().getLoc();
981 StringRef id;
982 if (parseId(id, "expected class name in Inst type"))
983 return failure();
984
985 // Look up the class that is being referenced.
986 const auto &classMap = getConstants().classMap;
987 auto lookup = classMap.find(id);
988 if (lookup == classMap.end())
989 return emitError(loc) << "unknown class '" << id << "'";
990
991 auto classOp = lookup->second;
992
993 if (parseToken(FIRToken::greater, "expected > in Inst type"))
994 return failure();
995
996 result = classOp.getInstanceType();
997 break;
998 }
999
1000 case FIRToken::kw_AnyRef: {
1001 if (requireFeature({6, 0, 0}, "AnyRef types"))
1002 return failure();
1003
1004 consumeToken(FIRToken::kw_AnyRef);
1005 result = AnyRefType::get(getContext());
1006 break;
1007 }
1008
1009 case FIRToken::kw_Reset:
1010 consumeToken(FIRToken::kw_Reset);
1011 result = ResetType::get(getContext());
1012 break;
1013
1014 case FIRToken::kw_AsyncReset:
1015 consumeToken(FIRToken::kw_AsyncReset);
1016 result = AsyncResetType::get(getContext());
1017 break;
1018
1019 case FIRToken::kw_UInt:
1020 consumeToken(FIRToken::kw_UInt);
1021 // Width is not present since langle_UInt would have been lexed instead.
1022 result = UIntType::get(getContext(), -1);
1023 break;
1024
1025 case FIRToken::kw_SInt:
1026 consumeToken(FIRToken::kw_SInt);
1027 // Width is not present since langle_SInt would have been lexed instead.
1028 result = SIntType::get(getContext(), -1);
1029 break;
1030
1031 case FIRToken::kw_Analog:
1032 consumeToken(FIRToken::kw_Analog);
1033 // Width is not present since langle_Analog would have been lexed instead.
1034 result = AnalogType::get(getContext(), -1);
1035 break;
1036
1037 case FIRToken::langle_UInt:
1038 case FIRToken::langle_SInt:
1039 case FIRToken::langle_Analog: {
1040 // The '<' has already been consumed by the lexer, so we need to parse
1041 // the mandatory width and the trailing '>'.
1042 auto kind = getToken().getKind();
1043 consumeToken();
1044
1045 int32_t width;
1046 if (parseWidth(width))
1047 return failure();
1048
1049 if (kind == FIRToken::langle_SInt)
1050 result = SIntType::get(getContext(), width);
1051 else if (kind == FIRToken::langle_UInt)
1052 result = UIntType::get(getContext(), width);
1053 else {
1054 assert(kind == FIRToken::langle_Analog);
1055 result = AnalogType::get(getContext(), width);
1056 }
1057 break;
1058 }
1059
1060 case FIRToken::kw_Domain: {
1061 if (requireFeature(missingSpecFIRVersion, "domains"))
1062 return failure();
1063 consumeToken();
1064
1065 // Parse: Domain of SymbolName
1066 auto loc = getToken().getLoc();
1067 StringRef domainKindStr;
1068 if (parseToken(FIRToken::kw_of, "expected 'of' after Domain type") ||
1069 parseId(domainKindStr, "expected domain kind"))
1070 return failure();
1071
1072 // Look up the domain to get its fields
1073 const auto &domainMap = getConstants().domainMap;
1074 auto lookup = domainMap.find(domainKindStr);
1075 if (lookup == domainMap.end())
1076 return emitError(loc) << "unknown domain '" << domainKindStr << "'";
1077
1078 result = DomainType::getFromDomainOp(lookup->second);
1079 break;
1080 }
1081
1082 case FIRToken::kw_Probe:
1083 case FIRToken::kw_RWProbe: {
1084 auto kind = getToken().getKind();
1085 auto loc = getToken().getLoc();
1086 consumeToken();
1087
1088 // Inner Type
1089 FIRRTLType type;
1090 if (parseToken(FIRToken::less, "expected '<' in reference type") ||
1091 parseType(type, "expected probe data type"))
1092 return failure();
1093
1094 SmallVector<StringRef> layers;
1095 if (consumeIf(FIRToken::comma)) {
1096 if (requireFeature({4, 0, 0}, "colored probes"))
1097 return failure();
1098 // Probe Color
1099 do {
1100 StringRef layer;
1101 loc = getToken().getLoc();
1102 if (parseId(layer, "expected layer name"))
1103 return failure();
1104 layers.push_back(layer);
1105 } while (consumeIf(FIRToken::period));
1106 }
1107
1108 if (!consumeIf(FIRToken::greater))
1109 return emitError(loc, "expected '>' to end reference type");
1110
1111 bool forceable = kind == FIRToken::kw_RWProbe;
1112
1113 auto innerType = type_dyn_cast<FIRRTLBaseType>(type);
1114 if (!innerType)
1115 return emitError(loc, "invalid probe inner type, must be base-type");
1116
1117 if (!innerType.isPassive())
1118 return emitError(loc, "probe inner type must be passive");
1119
1120 if (forceable && innerType.containsConst())
1121 return emitError(loc, "rwprobe cannot contain const");
1122
1123 SymbolRefAttr layer;
1124 if (!layers.empty()) {
1125 auto nestedLayers =
1126 llvm::map_range(ArrayRef(layers).drop_front(), [&](StringRef a) {
1127 return FlatSymbolRefAttr::get(getContext(), a);
1128 });
1129 layer = SymbolRefAttr::get(getContext(), layers.front(),
1130 llvm::to_vector(nestedLayers));
1131 }
1132
1133 result = RefType::get(innerType, forceable, layer);
1134 break;
1135 }
1136
1137 case FIRToken::l_brace: {
1138 consumeToken(FIRToken::l_brace);
1139
1140 SmallVector<OpenBundleType::BundleElement, 4> elements;
1141 SmallPtrSet<StringAttr, 4> nameSet;
1142 bool bundleCompatible = true;
1143 if (parseListUntil(FIRToken::r_brace, [&]() -> ParseResult {
1144 bool isFlipped = consumeIf(FIRToken::kw_flip);
1145
1146 auto loc = getToken().getLoc();
1147 StringRef fieldNameStr;
1148 if (parseFieldId(fieldNameStr, "expected bundle field name") ||
1149 parseToken(FIRToken::colon, "expected ':' in bundle"))
1150 return failure();
1151 auto fieldName = StringAttr::get(getContext(), fieldNameStr);
1152
1153 // Verify that the names of each field are unique.
1154 if (!nameSet.insert(fieldName).second)
1155 return emitError(loc, "duplicate field name in bundle: " +
1156 fieldName.getValue());
1157
1158 FIRRTLType type;
1159 if (parseType(type, "expected bundle field type"))
1160 return failure();
1161
1162 elements.push_back({fieldName, isFlipped, type});
1163 bundleCompatible &= isa<BundleType::ElementType>(type);
1164
1165 return success();
1166 }))
1167 return failure();
1168
1169 // Try to emit base-only bundle.
1170 if (bundleCompatible) {
1171 auto bundleElements = llvm::map_range(elements, [](auto element) {
1172 return BundleType::BundleElement{
1173 element.name, element.isFlip,
1174 cast<BundleType::ElementType>(element.type)};
1175 });
1176 result = BundleType::get(getContext(), llvm::to_vector(bundleElements));
1177 } else
1178 result = OpenBundleType::get(getContext(), elements);
1179 break;
1180 }
1181
1182 case FIRToken::l_brace_bar: {
1183 if (parseEnumType(result))
1184 return failure();
1185 break;
1186 }
1187
1188 case FIRToken::identifier: {
1189 StringRef id;
1190 auto loc = getToken().getLoc();
1191 if (parseId(id, "expected a type alias name"))
1192 return failure();
1193 auto it = constants.aliasMap.find(id);
1194 if (it == constants.aliasMap.end()) {
1195 emitError(loc) << "type identifier `" << id << "` is not declared";
1196 return failure();
1197 }
1198 result = it->second;
1199 break;
1200 }
1201
1202 case FIRToken::kw_const: {
1203 consumeToken(FIRToken::kw_const);
1204 auto nextToken = getToken();
1205 auto loc = nextToken.getLoc();
1206
1207 // Guard against multiple 'const' specifications
1208 if (nextToken.is(FIRToken::kw_const))
1209 return emitError(loc, "'const' can only be specified once on a type");
1210
1211 if (failed(parseType(result, message)))
1212 return failure();
1213
1214 auto baseType = type_dyn_cast<FIRRTLBaseType>(result);
1215 if (!baseType)
1216 return emitError(loc, "only hardware types can be 'const'");
1217
1218 result = baseType.getConstType(true);
1219 return success();
1220 }
1221
1222 case FIRToken::kw_String:
1223 if (requireFeature({3, 1, 0}, "Strings"))
1224 return failure();
1225 consumeToken(FIRToken::kw_String);
1226 result = StringType::get(getContext());
1227 break;
1228 case FIRToken::kw_Integer:
1229 if (requireFeature({3, 1, 0}, "Integers"))
1230 return failure();
1231 consumeToken(FIRToken::kw_Integer);
1232 result = FIntegerType::get(getContext());
1233 break;
1234 case FIRToken::kw_Bool:
1235 if (requireFeature({6, 0, 0}, "Bools"))
1236 return failure();
1237 consumeToken(FIRToken::kw_Bool);
1238 result = BoolType::get(getContext());
1239 break;
1240 case FIRToken::kw_Double:
1241 if (requireFeature({6, 0, 0}, "Doubles"))
1242 return failure();
1243 consumeToken(FIRToken::kw_Double);
1244 result = DoubleType::get(getContext());
1245 break;
1246 case FIRToken::kw_Path:
1247 if (requireFeature({6, 0, 0}, "Paths"))
1248 return failure();
1249 consumeToken(FIRToken::kw_Path);
1250 result = PathType::get(getContext());
1251 break;
1252 case FIRToken::kw_List:
1253 if (requireFeature({4, 0, 0}, "Lists") || parseListType(result))
1254 return failure();
1255 break;
1256
1257 case FIRToken::langle_List: {
1258 // The '<' has already been consumed by the lexer, so we need to parse
1259 // the element type and the trailing '>'.
1260 if (requireFeature({4, 0, 0}, "Lists"))
1261 return failure();
1262 consumeToken();
1263
1265 if (parsePropertyType(elementType, "expected List element type") ||
1266 parseToken(FIRToken::greater, "expected '>' in List type"))
1267 return failure();
1268
1269 result = ListType::get(getContext(), elementType);
1270 break;
1271 }
1272
1273 case FIRToken::kw_Registry:
1274 if (requireFeature(missingSpecFIRVersion, "Registries") ||
1275 parseRegistryType(result))
1276 return failure();
1277 break;
1278
1279 case FIRToken::langle_Registry: {
1280 // The '<' has already been consumed by the lexer, so we need to parse
1281 // the element type and the trailing '>'.
1282 if (requireFeature(missingSpecFIRVersion, "Registries"))
1283 return failure();
1284 consumeToken();
1285
1287 if (parsePropertyType(elementType, "expected Registry element type") ||
1288 parseToken(FIRToken::greater, "expected '>' in Registry type"))
1289 return failure();
1290
1291 result = RegistryType::get(getContext(), elementType);
1292 break;
1293 }
1294 }
1295
1296 // Handle postfix vector sizes.
1297 while (consumeIf(FIRToken::l_square)) {
1298 auto sizeLoc = getToken().getLoc();
1299 int64_t size;
1300 if (parseIntLit(size, "expected width") ||
1301 parseToken(FIRToken::r_square, "expected ]"))
1302 return failure();
1303
1304 if (size < 0)
1305 return emitError(sizeLoc, "invalid size specifier"), failure();
1306
1307 auto baseType = type_dyn_cast<FIRRTLBaseType>(result);
1308 if (baseType)
1309 result = FVectorType::get(baseType, size);
1310 else
1311 result = OpenVectorType::get(result, size);
1312 }
1313
1314 return success();
1315}
1316
1317/// ruw ::= 'old' | 'new' | 'undefined'
1318ParseResult FIRParser::parseRUW(RUWBehavior &result) {
1319 switch (getToken().getKind()) {
1320
1321 case FIRToken::kw_old:
1322 result = RUWBehavior::Old;
1323 consumeToken(FIRToken::kw_old);
1324 break;
1325 case FIRToken::kw_new:
1326 result = RUWBehavior::New;
1327 consumeToken(FIRToken::kw_new);
1328 break;
1329 case FIRToken::kw_undefined:
1330 result = RUWBehavior::Undefined;
1331 consumeToken(FIRToken::kw_undefined);
1332 break;
1333 default:
1334 return failure();
1335 }
1336
1337 return success();
1338}
1339
1340/// ruw ::= 'old' | 'new' | 'undefined'
1341ParseResult FIRParser::parseOptionalRUW(RUWBehavior &result) {
1342 switch (getToken().getKind()) {
1343 default:
1344 break;
1345
1346 case FIRToken::kw_old:
1347 result = RUWBehavior::Old;
1348 consumeToken(FIRToken::kw_old);
1349 break;
1350 case FIRToken::kw_new:
1351 result = RUWBehavior::New;
1352 consumeToken(FIRToken::kw_new);
1353 break;
1354 case FIRToken::kw_undefined:
1355 result = RUWBehavior::Undefined;
1356 consumeToken(FIRToken::kw_undefined);
1357 break;
1358 }
1359
1360 return success();
1361}
1362
1363/// param ::= id '=' param-value
1364ParseResult FIRParser::parseParameter(StringAttr &resultName,
1365 Attribute &resultValue, SMLoc &resultLoc,
1366 bool allowAggregates) {
1367 auto loc = getToken().getLoc();
1368
1369 // Parse the name of the parameter.
1370 StringRef name;
1371 if (parseId(name, "expected parameter name") ||
1372 parseToken(FIRToken::equal, "expected '=' in parameter"))
1373 return failure();
1374
1375 // Parse the value of the parameter.
1376 Attribute value;
1377 if (parseParameterValue(value, allowAggregates))
1378 return failure();
1379
1380 resultName = StringAttr::get(getContext(), name);
1381 resultValue = value;
1382 resultLoc = loc;
1383 return success();
1384}
1385
1386/// param-value ::= intLit
1387/// ::= StringLit
1388/// ::= floatingpoint
1389/// ::= VerbatimStringLit
1390/// ::= '[' (param-value)','* ']' (if allowAggregates)
1391/// ::= '{' (id '=' param)','* '}' (if allowAggregates)
1392ParseResult FIRParser::parseParameterValue(Attribute &value,
1393 bool allowAggregates) {
1394 mlir::Builder builder(getContext());
1395 switch (getToken().getKind()) {
1396
1397 // param-value ::= intLit
1398 case FIRToken::integer:
1399 case FIRToken::signed_integer: {
1400 APInt result;
1401 if (parseIntLit(result, "invalid integer parameter"))
1402 return failure();
1403
1404 // If the integer parameter is less than 32-bits, sign extend this to a
1405 // 32-bit value. This needs to eventually emit as a 32-bit value in
1406 // Verilog and we want to get the size correct immediately.
1407 if (result.getBitWidth() < 32)
1408 result = result.sext(32);
1409
1410 value = builder.getIntegerAttr(
1411 builder.getIntegerType(result.getBitWidth(), result.isSignBitSet()),
1412 result);
1413 return success();
1414 }
1415
1416 // param-value ::= StringLit
1417 case FIRToken::string: {
1418 // Drop the double quotes and unescape.
1419 value = builder.getStringAttr(getToken().getStringValue());
1420 consumeToken(FIRToken::string);
1421 return success();
1422 }
1423
1424 // param-value ::= VerbatimStringLit
1425 case FIRToken::verbatim_string: {
1426 // Drop the single quotes and unescape the ones inside.
1427 auto text = builder.getStringAttr(getToken().getVerbatimStringValue());
1428 value = hw::ParamVerbatimAttr::get(text);
1429 consumeToken(FIRToken::verbatim_string);
1430 return success();
1431 }
1432
1433 // param-value ::= floatingpoint
1434 case FIRToken::floatingpoint: {
1435 double v;
1436 if (!llvm::to_float(getTokenSpelling(), v))
1437 return emitError("invalid float parameter syntax"), failure();
1438
1439 value = builder.getF64FloatAttr(v);
1440 consumeToken(FIRToken::floatingpoint);
1441 return success();
1442 }
1443
1444 // param-value ::= '[' (param)','* ']'
1445 case FIRToken::l_square: {
1446 if (!allowAggregates)
1447 return emitError("expected non-aggregate parameter value");
1448 consumeToken();
1449
1450 SmallVector<Attribute> elements;
1451 auto parseElement = [&] {
1452 return parseParameterValue(elements.emplace_back(),
1453 /*allowAggregates=*/true);
1454 };
1455 if (parseListUntil(FIRToken::r_square, parseElement))
1456 return failure();
1457
1458 value = builder.getArrayAttr(elements);
1459 return success();
1460 }
1461
1462 // param-value ::= '{' (id '=' param)','* '}'
1463 case FIRToken::l_brace: {
1464 if (!allowAggregates)
1465 return emitError("expected non-aggregate parameter value");
1466 consumeToken();
1467
1468 NamedAttrList fields;
1469 auto parseField = [&]() -> ParseResult {
1470 StringAttr fieldName;
1471 Attribute fieldValue;
1472 SMLoc fieldLoc;
1473 if (parseParameter(fieldName, fieldValue, fieldLoc,
1474 /*allowAggregates=*/true))
1475 return failure();
1476 if (fields.set(fieldName, fieldValue))
1477 return emitError(fieldLoc)
1478 << "redefinition of parameter '" << fieldName.getValue() << "'";
1479 return success();
1480 };
1481 if (parseListUntil(FIRToken::r_brace, parseField))
1482 return failure();
1483
1484 value = fields.getDictionary(getContext());
1485 return success();
1486 }
1487
1488 default:
1489 return emitError("expected parameter value");
1490 }
1491}
1492
1493//===----------------------------------------------------------------------===//
1494// FIRModuleContext
1495//===----------------------------------------------------------------------===//
1496
1497// Entries in a symbol table are either an mlir::Value for the operation that
1498// defines the value or an unbundled ID tracking the index in the
1499// UnbundledValues list.
1500using UnbundledID = llvm::PointerEmbeddedInt<unsigned, 31>;
1501using SymbolValueEntry = llvm::PointerUnion<Value, UnbundledID>;
1502
1504 llvm::StringMap<std::pair<SMLoc, SymbolValueEntry>, llvm::BumpPtrAllocator>;
1505using ModuleSymbolTableEntry = ModuleSymbolTable::MapEntryTy;
1506
1507using UnbundledValueEntry = SmallVector<std::pair<Attribute, Value>>;
1508using UnbundledValuesList = std::vector<UnbundledValueEntry>;
1509namespace {
1510/// This structure is used to track which entries are added while inside a scope
1511/// and remove them upon exiting the scope.
1512struct UnbundledValueRestorer {
1513 UnbundledValuesList &list;
1514 size_t startingSize;
1515 UnbundledValueRestorer(UnbundledValuesList &list) : list(list) {
1516 startingSize = list.size();
1517 }
1518 ~UnbundledValueRestorer() { list.resize(startingSize); }
1519};
1520} // namespace
1521
1522using SubaccessCache = llvm::DenseMap<std::pair<Value, unsigned>, Value>;
1523
1524namespace {
1525/// This struct provides context information that is global to the module we're
1526/// currently parsing into.
1527struct FIRModuleContext : public FIRParser {
1528 explicit FIRModuleContext(Block *topLevelBlock,
1529 SharedParserConstants &constants, FIRLexer &lexer,
1530 FIRVersion version)
1531 : FIRParser(constants, lexer, version), topLevelBlock(topLevelBlock) {}
1532
1533 /// Get a cached constant.
1534 template <typename OpTy = ConstantOp, typename... Args>
1535 Value getCachedConstant(ImplicitLocOpBuilder &builder, Attribute attr,
1536 Type type, Args &&...args) {
1537 auto &result = constantCache[{attr, type}];
1538 if (result)
1539 return result;
1540
1541 // Make sure to insert constants at the top level of the module to maintain
1542 // dominance.
1543 OpBuilder::InsertPoint savedIP;
1544
1545 // Find the insertion point.
1546 if (builder.getInsertionBlock() != topLevelBlock) {
1547 savedIP = builder.saveInsertionPoint();
1548 auto *block = builder.getInsertionBlock();
1549 while (true) {
1550 auto *op = block->getParentOp();
1551 if (!op || !op->getBlock()) {
1552 // We are inserting into an unknown region.
1553 builder.setInsertionPointToEnd(topLevelBlock);
1554 break;
1555 }
1556 if (op->getBlock() == topLevelBlock) {
1557 builder.setInsertionPoint(op);
1558 break;
1559 }
1560 block = op->getBlock();
1561 }
1562 }
1563
1564 result = OpTy::create(builder, type, std::forward<Args>(args)...);
1565
1566 if (savedIP.isSet())
1567 builder.setInsertionPoint(savedIP.getBlock(), savedIP.getPoint());
1568
1569 return result;
1570 }
1571
1572 //===--------------------------------------------------------------------===//
1573 // SubaccessCache
1574
1575 /// This returns a reference with the assumption that the caller will fill in
1576 /// the cached value. We keep track of inserted subaccesses so that we can
1577 /// remove them when we exit a scope.
1578 Value &getCachedSubaccess(Value value, unsigned index) {
1579 auto &result = subaccessCache[{value, index}];
1580 if (!result) {
1581 // The outer most block won't be in the map.
1582 auto it = scopeMap.find(value.getParentBlock());
1583 if (it != scopeMap.end())
1584 it->second->scopedSubaccesses.push_back({result, index});
1585 }
1586 return result;
1587 }
1588
1589 //===--------------------------------------------------------------------===//
1590 // SymbolTable
1591
1592 /// Add a symbol entry with the specified name, returning failure if the name
1593 /// is already defined.
1594 ParseResult addSymbolEntry(StringRef name, SymbolValueEntry entry, SMLoc loc,
1595 bool insertNameIntoGlobalScope = false);
1596 ParseResult addSymbolEntry(StringRef name, Value value, SMLoc loc,
1597 bool insertNameIntoGlobalScope = false) {
1598 return addSymbolEntry(name, SymbolValueEntry(value), loc,
1599 insertNameIntoGlobalScope);
1600 }
1601
1602 // Removes a symbol from symbolTable (Workaround since symbolTable is private)
1603 void removeSymbolEntry(StringRef name);
1604
1605 /// Resolved a symbol table entry to a value. Emission of error is optional.
1606 ParseResult resolveSymbolEntry(Value &result, SymbolValueEntry &entry,
1607 SMLoc loc, bool fatal = true);
1608
1609 /// Resolved a symbol table entry if it is an expanded bundle e.g. from an
1610 /// instance. Emission of error is optional.
1611 ParseResult resolveSymbolEntry(Value &result, SymbolValueEntry &entry,
1612 StringRef field, SMLoc loc);
1613
1614 /// Look up the specified name, emitting an error and returning failure if the
1615 /// name is unknown.
1616 ParseResult lookupSymbolEntry(SymbolValueEntry &result, StringRef name,
1617 SMLoc loc);
1618
1619 UnbundledValueEntry &getUnbundledEntry(unsigned index) {
1620 assert(index < unbundledValues.size());
1621 return unbundledValues[index];
1622 }
1623
1624 /// This contains one entry for each value in FIRRTL that is represented as a
1625 /// bundle type in the FIRRTL spec but for which we represent as an exploded
1626 /// set of elements in the FIRRTL dialect.
1627 UnbundledValuesList unbundledValues;
1628
1629 /// Provide a symbol table scope that automatically pops all the entries off
1630 /// the symbol table when the scope is exited.
1631 struct ContextScope {
1632 friend struct FIRModuleContext;
1633 ContextScope(FIRModuleContext &moduleContext, Block *block)
1634 : moduleContext(moduleContext), block(block),
1635 previousScope(moduleContext.currentScope) {
1636 moduleContext.currentScope = this;
1637 moduleContext.scopeMap[block] = this;
1638 }
1639 ~ContextScope() {
1640 // Mark all entries in this scope as being invalid. We track validity
1641 // through the SMLoc field instead of deleting entries.
1642 for (auto *entryPtr : scopedDecls)
1643 entryPtr->second.first = SMLoc();
1644 // Erase the scoped subacceses from the cache. If the block is deleted we
1645 // could resuse the memory, although the chances are quite small.
1646 for (auto subaccess : scopedSubaccesses)
1647 moduleContext.subaccessCache.erase(subaccess);
1648 // Erase this context from the map.
1649 moduleContext.scopeMap.erase(block);
1650 // Reset to the previous scope.
1651 moduleContext.currentScope = previousScope;
1652 }
1653
1654 private:
1655 void operator=(const ContextScope &) = delete;
1656 ContextScope(const ContextScope &) = delete;
1657
1658 FIRModuleContext &moduleContext;
1659 Block *block;
1660 ContextScope *previousScope;
1661 std::vector<ModuleSymbolTableEntry *> scopedDecls;
1662 std::vector<std::pair<Value, unsigned>> scopedSubaccesses;
1663 };
1664
1665private:
1666 /// The top level block in which we insert cached constants.
1667 Block *topLevelBlock;
1668
1669 /// The expression-oriented nature of firrtl syntax produces tons of constant
1670 /// nodes which are obviously redundant. Instead of literally producing them
1671 /// in the parser, do an implicit CSE to reduce parse time and silliness in
1672 /// the resulting IR.
1673 llvm::DenseMap<std::pair<Attribute, Type>, Value> constantCache;
1674
1675 /// This symbol table holds the names of ports, wires, and other local decls.
1676 /// This is scoped because conditional statements introduce subscopes.
1677 ModuleSymbolTable symbolTable;
1678
1679 /// This is a cache of subindex and subfield operations so we don't constantly
1680 /// recreate large chains of them. This maps a bundle value + index to the
1681 /// subaccess result.
1682 SubaccessCache subaccessCache;
1683
1684 /// This maps a block to related ContextScope.
1685 DenseMap<Block *, ContextScope *> scopeMap;
1686
1687 /// If non-null, all new entries added to the symbol table are added to this
1688 /// list. This allows us to "pop" the entries by resetting them to null when
1689 /// scope is exited.
1690 ContextScope *currentScope = nullptr;
1691};
1692
1693} // end anonymous namespace
1694
1695// Removes a symbol from symbolTable (Workaround since symbolTable is private)
1696void FIRModuleContext::removeSymbolEntry(StringRef name) {
1697 symbolTable.erase(name);
1698}
1699
1700/// Add a symbol entry with the specified name, returning failure if the name
1701/// is already defined.
1702///
1703/// When 'insertNameIntoGlobalScope' is true, we don't allow the name to be
1704/// popped. This is a workaround for (firrtl scala bug) that should eventually
1705/// be fixed.
1706ParseResult FIRModuleContext::addSymbolEntry(StringRef name,
1707 SymbolValueEntry entry, SMLoc loc,
1708 bool insertNameIntoGlobalScope) {
1709 // Do a lookup by trying to do an insertion. Do so in a way that we can tell
1710 // if we hit a missing element (SMLoc is null).
1711 auto [entryIt, inserted] =
1712 symbolTable.try_emplace(name, SMLoc(), SymbolValueEntry());
1713
1714 // If insertion failed, the name already exists
1715 if (!inserted) {
1716 if (entryIt->second.first.isValid()) {
1717 // Valid activeSMLoc: active symbol in current scope redeclared
1718 emitError(loc, "redefinition of name '" + name + "' ")
1719 .attachNote(translateLocation(entryIt->second.first))
1720 << "previous definition here.";
1721 } else {
1722 // Invalid activeSMLoc: symbol from a completed scope redeclared
1723 emitError(loc, "redefinition of name '" + name + "' ")
1724 << "- FIRRTL has flat namespace and requires all "
1725 << "declarations in a module to have unique names.";
1726 }
1727 return failure();
1728 }
1729
1730 // If we didn't have a hit, then record the location, and remember that this
1731 // was new to this scope.
1732 entryIt->second = {loc, entry};
1733 if (currentScope && !insertNameIntoGlobalScope)
1734 currentScope->scopedDecls.push_back(&*entryIt);
1735 return success();
1736}
1737
1738/// Look up the specified name, emitting an error and returning null if the
1739/// name is unknown.
1740ParseResult FIRModuleContext::lookupSymbolEntry(SymbolValueEntry &result,
1741 StringRef name, SMLoc loc) {
1742 auto &entry = symbolTable[name];
1743 if (!entry.first.isValid())
1744 return emitError(loc, "use of unknown declaration '" + name + "'");
1745 result = entry.second;
1746 assert(result && "name in symbol table without definition");
1747 return success();
1748}
1749
1750ParseResult FIRModuleContext::resolveSymbolEntry(Value &result,
1751 SymbolValueEntry &entry,
1752 SMLoc loc, bool fatal) {
1753 if (!isa<Value>(entry)) {
1754 if (fatal)
1755 emitError(loc, "bundle value should only be used from subfield");
1756 return failure();
1757 }
1758 result = cast<Value>(entry);
1759 return success();
1760}
1761
1762ParseResult FIRModuleContext::resolveSymbolEntry(Value &result,
1763 SymbolValueEntry &entry,
1764 StringRef fieldName,
1765 SMLoc loc) {
1766 if (!isa<UnbundledID>(entry)) {
1767 emitError(loc, "value should not be used from subfield");
1768 return failure();
1769 }
1770
1771 auto fieldAttr = StringAttr::get(getContext(), fieldName);
1772
1773 unsigned unbundledId = cast<UnbundledID>(entry) - 1;
1774 assert(unbundledId < unbundledValues.size());
1775 UnbundledValueEntry &ubEntry = unbundledValues[unbundledId];
1776 for (auto elt : ubEntry) {
1777 if (elt.first == fieldAttr) {
1778 result = elt.second;
1779 break;
1780 }
1781 }
1782 if (!result) {
1783 emitError(loc, "use of invalid field name '")
1784 << fieldName << "' on bundle value";
1785 return failure();
1786 }
1787
1788 return success();
1789}
1790
1791//===----------------------------------------------------------------------===//
1792// FIRStmtParser
1793//===----------------------------------------------------------------------===//
1794
1795namespace {
1796/// This class is used when building expression nodes for a statement: we need
1797/// to parse a bunch of expressions and build MLIR operations for them, and then
1798/// we see the locator that specifies the location for those operations
1799/// afterward.
1800///
1801/// It is wasteful in time and memory to create a bunch of temporary FileLineCol
1802/// location's that point into the .fir file when they're destined to get
1803/// overwritten by a location specified by a Locator. To avoid this, we create
1804/// all of the operations with a temporary location on them, then remember the
1805/// [Operation*, SMLoc] pair for the newly created operation.
1806///
1807/// At the end of the operation we'll see a Locator (or not). If we see a
1808/// locator, we apply it to all the operations we've parsed and we're done. If
1809/// not, we lazily create the locators in the .fir file.
1810struct LazyLocationListener : public OpBuilder::Listener {
1811 LazyLocationListener(OpBuilder &builder) : builder(builder) {
1812 assert(builder.getListener() == nullptr);
1813 builder.setListener(this);
1814 }
1815
1816 ~LazyLocationListener() {
1817 assert(subOps.empty() && "didn't process parsed operations");
1818 assert(builder.getListener() == this);
1819 builder.setListener(nullptr);
1820 }
1821
1822 void startStatement() {
1823 assert(!isActive && "Already processing a statement");
1824 isActive = true;
1825 }
1826
1827 /// Compute the location an operation created at `loc` should be given,
1828 /// or that a diagnostic about it should be emitted on, honoring the
1829 /// the @info handling policy. This will only translate the SMLoc when the
1830 /// policy actually needs it, since that will intern a location into the
1831 /// context permanently.
1832 Location getLoc(FIRParser &parser, SMLoc loc) {
1834 switch (parser.getConstants().options.infoLocatorHandling) {
1835 case ILH::IgnoreInfo:
1836 // Shouldn't have an infoLoc, but if we do ignore it.
1837 break;
1838 case ILH::PreferInfo:
1839 if (infoLoc)
1840 return infoLoc;
1841 break;
1842 case ILH::FusedInfo:
1843 if (infoLoc)
1844 return FusedLoc::get(infoLoc.getContext(),
1845 {infoLoc, parser.translateLocation(loc)});
1846 break;
1847 }
1848 return parser.translateLocation(loc);
1849 }
1850
1851 /// This is called when done with each statement. This applies the locations
1852 /// to each statement.
1853 void endStatement(FIRParser &parser) {
1854 assert(isActive && "Not parsing a statement");
1855
1856 // Apply a location to each subop, following user preference to use the
1857 // @info location, a fused location, or the location in the .fir file.
1858 for (auto opAndSMLoc : subOps)
1859 opAndSMLoc.first->setLoc(getLoc(parser, opAndSMLoc.second));
1860
1861 // Reset our state.
1862 isActive = false;
1863 infoLoc = LocationAttr();
1864 currentSMLoc = SMLoc();
1865 subOps.clear();
1866 }
1867
1868 /// Specify the location to be used for the next operations that are created.
1869 void setLoc(SMLoc loc) { currentSMLoc = loc; }
1870
1871 /// When a @Info locator is parsed, this method captures it.
1872 void setInfoLoc(LocationAttr loc) {
1873 assert(!infoLoc && "Info location multiply specified");
1874 infoLoc = loc;
1875 }
1876
1877 // Notification handler for when an operation is inserted into the builder.
1878 /// `op` is the operation that was inserted.
1879 void notifyOperationInserted(Operation *op,
1880 mlir::IRRewriter::InsertPoint) override {
1881 assert(currentSMLoc != SMLoc() && "No .fir file location specified");
1882 assert(isActive && "Not parsing a statement");
1883 subOps.push_back({op, currentSMLoc});
1884 }
1885
1886private:
1887 /// This is set to true while parsing a statement. It is used for assertions.
1888 bool isActive = false;
1889
1890 /// This is the current position in the source file that the next operation
1891 /// will be parsed into.
1892 SMLoc currentSMLoc;
1893
1894 /// This is the @ location attribute for the current statement, or null if
1895 /// not set.
1896 LocationAttr infoLoc;
1897
1898 /// This is the builder we're installed into.
1899 OpBuilder &builder;
1900
1901 /// This is the set of operations we've enqueued along with their location in
1902 /// the source file.
1903 SmallVector<std::pair<Operation *, SMLoc>, 8> subOps;
1904
1905 void operator=(const LazyLocationListener &) = delete;
1906 LazyLocationListener(const LazyLocationListener &) = delete;
1907};
1908} // end anonymous namespace
1909
1910namespace {
1911/// This class tracks inner-ref users and their intended targets,
1912/// (presently there must be just one) for post-processing at a point
1913/// where adding the symbols is safe without risk of races.
1914struct InnerSymFixups {
1915 /// Add a fixup to be processed later.
1916 void add(hw::InnerRefUserOpInterface user, hw::InnerSymTarget target) {
1917 fixups.push_back({user, target});
1918 }
1919
1920 /// Resolve all stored fixups, if any. Not expected to fail,
1921 /// as checking should primarily occur during original parsing.
1922 LogicalResult resolve(hw::InnerSymbolNamespaceCollection &isnc);
1923
1924private:
1925 struct Fixup {
1926 hw::InnerRefUserOpInterface innerRefUser;
1927 hw::InnerSymTarget target;
1928 };
1929 SmallVector<Fixup, 0> fixups;
1930};
1931} // end anonymous namespace
1932
1933LogicalResult
1934InnerSymFixups::resolve(hw::InnerSymbolNamespaceCollection &isnc) {
1935 for (auto &f : fixups) {
1936 auto ref = getInnerRefTo(
1937 f.target, [&isnc](FModuleLike module) -> hw::InnerSymbolNamespace & {
1938 return isnc.get(module);
1939 });
1940 assert(ref && "unable to resolve inner symbol target");
1941
1942 // Per-op fixup logic. Only RWProbeOp's presently.
1943 auto result =
1944 TypeSwitch<Operation *, LogicalResult>(f.innerRefUser.getOperation())
1945 .Case<RWProbeOp>([ref](RWProbeOp op) {
1946 op.setTargetAttr(ref);
1947 return success();
1948 })
1949 .Default([](auto *op) {
1950 return op->emitError("unknown inner-ref user requiring fixup");
1951 });
1952 if (failed(result))
1953 return failure();
1954 }
1955 return success();
1956}
1957
1958namespace {
1959/// This class implements logic and state for parsing statements, suites, and
1960/// similar module body constructs.
1961struct FIRStmtParser : public FIRParser {
1962 explicit FIRStmtParser(Block &blockToInsertInto,
1963 FIRModuleContext &moduleContext,
1964 InnerSymFixups &innerSymFixups,
1965 const SymbolTable &circuitSymTbl, FIRVersion version,
1966 SymbolRefAttr layerSym = {})
1967 : FIRParser(moduleContext.getConstants(), moduleContext.getLexer(),
1968 version),
1969 builder(UnknownLoc::get(getContext()), getContext()),
1970 locationProcessor(this->builder), moduleContext(moduleContext),
1971 innerSymFixups(innerSymFixups), layerSym(layerSym),
1972 circuitSymTbl(circuitSymTbl) {
1973 builder.setInsertionPointToEnd(&blockToInsertInto);
1974 }
1975
1976 ParseResult parseSimpleStmt(unsigned stmtIndent);
1977 ParseResult parseSimpleStmtBlock(unsigned indent);
1978
1979private:
1980 ParseResult parseSimpleStmtImpl(unsigned stmtIndent);
1981
1982 /// Attach invalid values to every element of the value.
1983 void emitInvalidate(Value val, Flow flow);
1984
1985 // The FIRRTL specification describes Invalidates as a statement with
1986 // implicit connect semantics. The FIRRTL dialect models it as a primitive
1987 // that returns an "Invalid Value", followed by an explicit connect to make
1988 // the representation simpler and more consistent.
1989 void emitInvalidate(Value val) { emitInvalidate(val, foldFlow(val)); }
1990
1991 /// Parse an @info marker if present and inform locationProcessor about it.
1992 ParseResult parseOptionalInfo() {
1993 LocationAttr loc;
1994 if (failed(parseOptionalInfoLocator(loc)))
1995 return failure();
1996 locationProcessor.setInfoLoc(loc);
1997 return success();
1998 }
1999
2000 // Exp Parsing
2001 ParseResult parseExpImpl(Value &result, const Twine &message,
2002 bool isLeadingStmt);
2003 ParseResult parseExp(Value &result, const Twine &message) {
2004 return parseExpImpl(result, message, /*isLeadingStmt:*/ false);
2005 }
2006 ParseResult parseExpLeadingStmt(Value &result, const Twine &message) {
2007 return parseExpImpl(result, message, /*isLeadingStmt:*/ true);
2008 }
2009 ParseResult parseEnumExp(Value &result);
2010 ParseResult parsePathExp(Value &result);
2011 ParseResult parseDomainExp(Value &result);
2012 ParseResult parseRefExp(Value &result, const Twine &message);
2013 ParseResult parseStaticRefExp(Value &result, const Twine &message);
2014 ParseResult parseRWProbeStaticRefExp(FieldRef &refResult, Type &type,
2015 const Twine &message);
2016
2017 // Generic intrinsic parsing.
2018 ParseResult parseIntrinsic(Value &result, bool isStatement);
2019 ParseResult parseIntrinsicStmt() {
2020 Value unused;
2021 return parseIntrinsic(unused, /*isStatement=*/true);
2022 }
2023 ParseResult parseIntrinsicExp(Value &result) {
2024 return parseIntrinsic(result, /*isStatement=*/false);
2025 }
2026 ParseResult parseOptionalParams(ArrayAttr &resultParameters);
2027
2028 template <typename subop>
2029 FailureOr<Value> emitCachedSubAccess(Value base, unsigned indexNo, SMLoc loc);
2030 ParseResult parseOptionalExpPostscript(Value &result,
2031 bool allowDynamic = true);
2032 ParseResult parsePostFixFieldId(Value &result);
2033 ParseResult parsePostFixIntSubscript(Value &result);
2034 ParseResult parsePostFixDynamicSubscript(Value &result);
2035 ParseResult
2036 parseIntegerLiteralExp(Value &result, bool isSigned,
2037 std::optional<int32_t> allocatedWidth = {});
2038 ParseResult parseListExp(Value &result);
2039 ParseResult parseListConcatExp(Value &result);
2040 ParseResult parseCatExp(Value &result);
2041 ParseResult parseStringConcatExp(Value &result);
2042 ParseResult parsePropEqExp(Value &result);
2043 ParseResult parseUnsafeDomainCast(Value &result);
2044 ParseResult parseUnknownProperty(Value &result);
2045
2046 template <typename T, size_t M, size_t N, size_t... Ms, size_t... Ns>
2047 ParseResult parsePrim(std::index_sequence<Ms...>, std::index_sequence<Ns...>,
2048 Value &result) {
2049 auto loc = getToken().getLoc();
2050 locationProcessor.setLoc(loc);
2051 consumeToken();
2052
2053 auto vals = std::array<Value, M>();
2054 auto ints = std::array<int64_t, N>();
2055
2056 // Parse all the values.
2057 bool first = true;
2058 for (size_t i = 0; i < M; ++i) {
2059 if (!first)
2060 if (parseToken(FIRToken::comma, "expected ','"))
2061 return failure();
2062 if (parseExp(vals[i], "expected expression in primitive operand"))
2063 return failure();
2064 first = false;
2065 }
2066
2067 // Parse all the attributes.
2068 for (size_t i = 0; i < N; ++i) {
2069 if (!first)
2070 if (parseToken(FIRToken::comma, "expected ','"))
2071 return failure();
2072 if (parseIntLit(ints[i], "expected integer in primitive operand"))
2073 return failure();
2074 first = false;
2075 }
2076
2077 if (parseToken(FIRToken::r_paren, "expected ')'"))
2078 return failure();
2079
2080 // Infer the type.
2081 auto type = T::inferReturnType(cast<FIRRTLType>(vals[Ms].getType())...,
2082 ints[Ns]..., {});
2083 if (!type) {
2084 // Only call translateLocation on an error case, it is expensive.
2085 T::inferReturnType(cast<FIRRTLType>(vals[Ms].getType())..., ints[Ns]...,
2086 translateLocation(loc));
2087 return failure();
2088 }
2089
2090 // Create the operation.
2091 auto op = T::create(builder, type, vals[Ms]..., ints[Ns]...);
2092 result = op.getResult();
2093 return success();
2094 }
2095
2096 template <typename T, unsigned M, unsigned N>
2097 ParseResult parsePrimExp(Value &result) {
2098 auto ms = std::make_index_sequence<M>();
2099 auto ns = std::make_index_sequence<N>();
2100 return parsePrim<T, M, N>(ms, ns, result);
2101 }
2102
2103 std::optional<ParseResult> parseExpWithLeadingKeyword(FIRToken keyword);
2104
2105 // Stmt Parsing
2106 ParseResult parseSubBlock(Block &blockToInsertInto, unsigned indent,
2107 SymbolRefAttr layerSym);
2108 ParseResult parseAttach();
2109 ParseResult parseMemPort(MemDirAttr direction);
2110
2111 // Parse a format string and build operations for FIRRTL "special"
2112 // substitutions. Set `formatStringResult` to the validated format string and
2113 // `operands` to the list of actual operands.
2114 ParseResult parseFormatString(SMLoc formatStringLoc, StringRef formatString,
2115 ArrayRef<Value> specOperands,
2116 StringAttr &formatStringResult,
2117 SmallVectorImpl<Value> &operands);
2118 ParseResult parsePrintf();
2119 ParseResult parseFPrintf();
2120 ParseResult parseFFlush();
2121 ParseResult parseSkip();
2122 ParseResult parseStop();
2123 ParseResult parseAssert();
2124 ParseResult parseAssume();
2125 ParseResult parseCover();
2126 ParseResult parseWhen(unsigned whenIndent);
2127 ParseResult parseMatch(unsigned matchIndent);
2128 ParseResult parseDomainInstantiation();
2129 ParseResult parseDomainDefine();
2130 ParseResult parseRefDefine();
2131 ParseResult parseRefForce();
2132 ParseResult parseRefForceInitial();
2133 ParseResult parseRefRelease();
2134 ParseResult parseRefReleaseInitial();
2135 ParseResult parseRefRead(Value &result);
2136 ParseResult parseProbe(Value &result);
2137 ParseResult parsePropAssert();
2138 ParseResult parsePropAssign();
2139 ParseResult parseRWProbe(Value &result);
2140 ParseResult parseLeadingExpStmt(Value lhs);
2141 ParseResult parseConnect();
2142 ParseResult parseInvalidate();
2143 ParseResult parseLayerBlockOrGroup(unsigned indent);
2144
2145 // Declarations
2146 ParseResult parseInstance();
2147 ParseResult parseInstanceChoice();
2148 ParseResult parseObject();
2149 ParseResult parseCombMem();
2150 ParseResult parseSeqMem();
2151 ParseResult parseMem(unsigned memIndent);
2152 ParseResult parseNode();
2153 ParseResult parseWire();
2154 ParseResult parseRegister(unsigned regIndent);
2155 ParseResult parseRegisterWithReset();
2156 ParseResult parseContract(unsigned blockIndent);
2157
2158 // Helper to fetch a module referenced by an instance-like statement.
2159 FModuleLike getReferencedModule(SMLoc loc, StringRef moduleName);
2160
2161 // The builder to build into.
2162 ImplicitLocOpBuilder builder;
2163 LazyLocationListener locationProcessor;
2164
2165 // Extra information maintained across a module.
2166 FIRModuleContext &moduleContext;
2167
2168 /// Inner symbol users to fixup after parsing.
2169 InnerSymFixups &innerSymFixups;
2170
2171 // An optional symbol that contains the current layer block that we are in.
2172 // This is used to construct a nested symbol for a layer block operation.
2173 SymbolRefAttr layerSym;
2174
2175 const SymbolTable &circuitSymTbl;
2176};
2177
2178} // end anonymous namespace
2179
2180/// Attach invalid values to every element of the value.
2181// NOLINTNEXTLINE(misc-no-recursion)
2182void FIRStmtParser::emitInvalidate(Value val, Flow flow) {
2183 auto tpe = type_dyn_cast<FIRRTLBaseType>(val.getType());
2184 // Invalidate does nothing for non-base types.
2185 // When aggregates-of-refs are supported, instead check 'containsReference'
2186 // below.
2187 if (!tpe)
2188 return;
2189
2190 auto props = tpe.getRecursiveTypeProperties();
2191 if (props.isPassive && !props.containsAnalog) {
2192 if (flow == Flow::Source)
2193 return;
2194 emitConnect(builder, val, InvalidValueOp::create(builder, tpe),
2195 getConstants().options.warnOnTruncation);
2196 return;
2197 }
2198
2199 // Recurse until we hit passive leaves. Connect any leaves which have sink or
2200 // duplex flow.
2201 //
2202 // TODO: This is very similar to connect expansion in the LowerTypes pass
2203 // works. Find a way to unify this with methods common to LowerTypes or to
2204 // have LowerTypes to the actual work here, e.g., emitting a partial connect
2205 // to only the leaf sources.
2206 TypeSwitch<FIRRTLType>(tpe)
2207 .Case<BundleType>([&](auto tpe) {
2208 for (size_t i = 0, e = tpe.getNumElements(); i < e; ++i) {
2209 auto &subfield = moduleContext.getCachedSubaccess(val, i);
2210 if (!subfield) {
2211 OpBuilder::InsertionGuard guard(builder);
2212 builder.setInsertionPointAfterValue(val);
2213 subfield = SubfieldOp::create(builder, val, i);
2214 }
2215 emitInvalidate(subfield,
2216 tpe.getElement(i).isFlip ? swapFlow(flow) : flow);
2217 }
2218 })
2219 .Case<FVectorType>([&](auto tpe) {
2220 auto tpex = tpe.getElementType();
2221 for (size_t i = 0, e = tpe.getNumElements(); i != e; ++i) {
2222 auto &subindex = moduleContext.getCachedSubaccess(val, i);
2223 if (!subindex) {
2224 OpBuilder::InsertionGuard guard(builder);
2225 builder.setInsertionPointAfterValue(val);
2226 subindex = SubindexOp::create(builder, tpex, val, i);
2227 }
2228 emitInvalidate(subindex, flow);
2229 }
2230 });
2231}
2232
2233//===-------------------------------
2234// FIRStmtParser Expression Parsing.
2235
2236/// Parse the 'exp' grammar, returning all of the suboperations in the
2237/// specified vector, and the ultimate SSA value in value.
2238///
2239/// exp ::= id // Ref
2240/// ::= prim
2241/// ::= integer-literal-exp
2242/// ::= enum-exp
2243/// ::= list-exp
2244/// ::= 'String(' stringLit ')'
2245/// ::= exp '.' fieldId
2246/// ::= exp '[' intLit ']'
2247/// XX ::= exp '.' DoubleLit // TODO Workaround for #470
2248/// ::= exp '[' exp ']'
2249///
2250///
2251/// If 'isLeadingStmt' is true, then this is being called to parse the first
2252/// expression in a statement. We can handle some weird cases due to this if
2253/// we end up parsing the whole statement. In that case we return success, but
2254/// set the 'result' value to null.
2255// NOLINTNEXTLINE(misc-no-recursion)
2256ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message,
2257 bool isLeadingStmt) {
2258 auto token = getToken();
2259 auto kind = token.getKind();
2260 switch (kind) {
2261 case FIRToken::lp_integer_add:
2262 case FIRToken::lp_integer_mul:
2263 case FIRToken::lp_integer_shr:
2264 case FIRToken::lp_integer_shl:
2265 if (requireFeature({4, 0, 0}, "Integer arithmetic expressions"))
2266 return failure();
2267 break;
2268 default:
2269 break;
2270 }
2271
2272 switch (kind) {
2273 // Handle all primitive's.
2274#define TOK_LPKEYWORD_PRIM(SPELLING, CLASS, NUMOPERANDS, NUMATTRIBUTES, \
2275 VERSION, FEATURE) \
2276 case FIRToken::lp_##SPELLING: \
2277 if (requireFeature(VERSION, FEATURE)) \
2278 return failure(); \
2279 if (parsePrimExp<CLASS, NUMOPERANDS, NUMATTRIBUTES>(result)) \
2280 return failure(); \
2281 break;
2282#include "FIRTokenKinds.def"
2283
2284 case FIRToken::l_brace_bar:
2285 if (isLeadingStmt)
2286 return emitError("unexpected enumeration as start of statement");
2287 if (parseEnumExp(result))
2288 return failure();
2289 break;
2290 case FIRToken::lp_read:
2291 if (isLeadingStmt)
2292 return emitError("unexpected read() as start of statement");
2293 if (parseRefRead(result))
2294 return failure();
2295 break;
2296 case FIRToken::lp_probe:
2297 if (isLeadingStmt)
2298 return emitError("unexpected probe() as start of statement");
2299 if (parseProbe(result))
2300 return failure();
2301 break;
2302 case FIRToken::lp_rwprobe:
2303 if (isLeadingStmt)
2304 return emitError("unexpected rwprobe() as start of statement");
2305 if (parseRWProbe(result))
2306 return failure();
2307 break;
2308
2309 case FIRToken::langle_UInt:
2310 case FIRToken::langle_SInt: {
2311 // The '<' has already been consumed by the lexer, so we need to parse
2312 // the mandatory width and '>'.
2313 bool isSigned = getToken().is(FIRToken::langle_SInt);
2314 consumeToken();
2315 int32_t width;
2316 if (parseWidth(width))
2317 return failure();
2318
2319 // Now parse the '(' intLit ')' part.
2320 if (parseIntegerLiteralExp(result, isSigned, width))
2321 return failure();
2322 break;
2323 }
2324
2325 case FIRToken::lp_UInt:
2326 if (parseIntegerLiteralExp(result, /*isSigned=*/false))
2327 return failure();
2328 break;
2329 case FIRToken::lp_SInt:
2330 if (parseIntegerLiteralExp(result, /*isSigned=*/true))
2331 return failure();
2332 break;
2333 case FIRToken::lp_String: {
2334 if (requireFeature({3, 1, 0}, "Strings"))
2335 return failure();
2336 locationProcessor.setLoc(getToken().getLoc());
2337 consumeToken(FIRToken::lp_String);
2338 StringRef spelling;
2339 if (parseGetSpelling(spelling) ||
2340 parseToken(FIRToken::string,
2341 "expected string literal in String expression") ||
2342 parseToken(FIRToken::r_paren, "expected ')' in String expression"))
2343 return failure();
2344 auto attr = builder.getStringAttr(FIRToken::getStringValue(spelling));
2345 result = moduleContext.getCachedConstant<StringConstantOp>(
2346 builder, attr, builder.getType<StringType>(), attr);
2347 break;
2348 }
2349 case FIRToken::lp_Integer: {
2350 if (requireFeature({3, 1, 0}, "Integers"))
2351 return failure();
2352 locationProcessor.setLoc(getToken().getLoc());
2353 consumeToken(FIRToken::lp_Integer);
2354 APInt value;
2355 if (parseIntLit(value, "expected integer literal in Integer expression") ||
2356 parseToken(FIRToken::r_paren, "expected ')' in Integer expression"))
2357 return failure();
2358 APSInt apint(value, /*isUnsigned=*/false);
2359 result = moduleContext.getCachedConstant<FIntegerConstantOp>(
2360 builder, IntegerAttr::get(getContext(), apint),
2361 builder.getType<FIntegerType>(), apint);
2362 break;
2363 }
2364 case FIRToken::lp_Bool: {
2365 if (requireFeature({6, 0, 0}, "Bools"))
2366 return failure();
2367 locationProcessor.setLoc(getToken().getLoc());
2368 consumeToken(FIRToken::lp_Bool);
2369 bool value;
2370 if (consumeIf(FIRToken::kw_true))
2371 value = true;
2372 else if (consumeIf(FIRToken::kw_false))
2373 value = false;
2374 else
2375 return emitError("expected true or false in Bool expression");
2376 if (parseToken(FIRToken::r_paren, "expected ')' in Bool expression"))
2377 return failure();
2378 auto attr = builder.getBoolAttr(value);
2379 result = moduleContext.getCachedConstant<BoolConstantOp>(
2380 builder, attr, builder.getType<BoolType>(), value);
2381 break;
2382 }
2383 case FIRToken::lp_Double: {
2384 if (requireFeature({6, 0, 0}, "Doubles"))
2385 return failure();
2386 locationProcessor.setLoc(getToken().getLoc());
2387 consumeToken(FIRToken::lp_Double);
2388 auto spelling = getTokenSpelling();
2389 if (parseToken(FIRToken::floatingpoint,
2390 "expected floating point in Double expression") ||
2391 parseToken(FIRToken::r_paren, "expected ')' in Double expression"))
2392 return failure();
2393 // NaN, INF, exponent, hex, integer?
2394 // This uses `strtod` internally, FWIW. See `man 3 strtod`.
2395 double d;
2396 if (!llvm::to_float(spelling, d))
2397 return emitError("invalid double");
2398 auto attr = builder.getF64FloatAttr(d);
2399 result = moduleContext.getCachedConstant<DoubleConstantOp>(
2400 builder, attr, builder.getType<DoubleType>(), attr);
2401 break;
2402 }
2403 case FIRToken::lp_List:
2404 case FIRToken::langle_List: {
2405 if (requireFeature({4, 0, 0}, "Lists"))
2406 return failure();
2407 if (isLeadingStmt)
2408 return emitError("unexpected List<>() as start of statement");
2409 if (parseListExp(result))
2410 return failure();
2411 break;
2412 }
2413
2414 case FIRToken::lp_list_concat: {
2415 if (isLeadingStmt)
2416 return emitError("unexpected list_create() as start of statement");
2417 if (requireFeature({4, 0, 0}, "List concat") || parseListConcatExp(result))
2418 return failure();
2419 break;
2420 }
2421
2422 case FIRToken::lp_path:
2423 if (isLeadingStmt)
2424 return emitError("unexpected path() as start of statement");
2425 if (requireFeature({6, 0, 0}, "Paths") || parsePathExp(result))
2426 return failure();
2427 break;
2428
2429 case FIRToken::lp_intrinsic:
2430 if (requireFeature({4, 0, 0}, "generic intrinsics") ||
2431 parseIntrinsicExp(result))
2432 return failure();
2433 break;
2434
2435 case FIRToken::lp_cat:
2436 if (parseCatExp(result))
2437 return failure();
2438 break;
2439
2440 case FIRToken::lp_string_concat:
2441 if (parseStringConcatExp(result))
2442 return failure();
2443 break;
2444
2445 case FIRToken::lp_prop_eq:
2446 if (requireFeature({6, 0, 0}, "property equality") ||
2447 parsePropEqExp(result))
2448 return failure();
2449 break;
2450
2451 case FIRToken::lp_unsafe_domain_cast:
2452 if (requireFeature(nextFIRVersion, "unsafe_domain_cast") ||
2453 parseUnsafeDomainCast(result))
2454 return failure();
2455 break;
2456 case FIRToken::lp_Unknown:
2457 if (requireFeature(nextFIRVersion, "unknown property expressions") ||
2458 parseUnknownProperty(result))
2459 return failure();
2460 break;
2461
2462 // Otherwise there are a bunch of keywords that are treated as identifiers
2463 // try them.
2464 case FIRToken::identifier: // exp ::= id
2465 case FIRToken::literal_identifier:
2466 case FIRToken::kw_UInt:
2467 case FIRToken::kw_SInt:
2468 case FIRToken::kw_String:
2469 case FIRToken::kw_Integer:
2470 case FIRToken::kw_Bool:
2471 case FIRToken::kw_Double:
2472 case FIRToken::kw_List:
2473 default: {
2474 StringRef name;
2475 auto loc = getToken().getLoc();
2476 SymbolValueEntry symtabEntry;
2477 if (parseId(name, message) ||
2478 moduleContext.lookupSymbolEntry(symtabEntry, name, loc))
2479 return failure();
2480
2481 // If we looked up a normal value, then we're done.
2482 if (!moduleContext.resolveSymbolEntry(result, symtabEntry, loc, false))
2483 break;
2484
2485 assert(isa<UnbundledID>(symtabEntry) && "should be an instance");
2486
2487 // Otherwise we referred to an implicitly bundled value. We *must* be in
2488 // the midst of processing a field ID reference or 'is invalid'. If not,
2489 // this is an error.
2490 if (isLeadingStmt && consumeIf(FIRToken::kw_is)) {
2491 if (parseToken(FIRToken::kw_invalid, "expected 'invalid'") ||
2492 parseOptionalInfo())
2493 return failure();
2494
2495 locationProcessor.setLoc(loc);
2496 // Invalidate all of the results of the bundled value.
2497 unsigned unbundledId = cast<UnbundledID>(symtabEntry) - 1;
2498 UnbundledValueEntry &ubEntry =
2499 moduleContext.getUnbundledEntry(unbundledId);
2500 for (auto elt : ubEntry)
2501 emitInvalidate(elt.second);
2502
2503 // Signify that we parsed the whole statement.
2504 result = Value();
2505 return success();
2506 }
2507
2508 // Handle the normal "instance.x" reference.
2509 StringRef fieldName;
2510 if (parseToken(FIRToken::period, "expected '.' in field reference") ||
2511 parseFieldId(fieldName, "expected field name") ||
2512 moduleContext.resolveSymbolEntry(result, symtabEntry, fieldName, loc))
2513 return failure();
2514 break;
2515 }
2516 }
2517 // Don't add code here, the common cases of these switch statements will be
2518 // merged. This allows for fixing up primops after they have been created.
2519 switch (kind) {
2520 case FIRToken::lp_shr:
2521 // For FIRRTL versions earlier than 4.0.0, insert pad(_, 1) around any
2522 // unsigned shr This ensures the minimum width is 1 (but can be greater)
2523 if (version < FIRVersion(4, 0, 0) && type_isa<UIntType>(result.getType()))
2524 result = PadPrimOp::create(builder, result, 1);
2525 break;
2526 default:
2527 break;
2528 }
2529
2530 return parseOptionalExpPostscript(result);
2531}
2532
2533/// Parse the postfix productions of expression after the leading expression
2534/// has been parsed.
2535///
2536/// exp ::= exp '.' fieldId
2537/// ::= exp '[' intLit ']'
2538/// XX ::= exp '.' DoubleLit // TODO Workaround for #470
2539/// ::= exp '[' exp ']'
2540ParseResult FIRStmtParser::parseOptionalExpPostscript(Value &result,
2541 bool allowDynamic) {
2542
2543 // Handle postfix expressions.
2544 while (true) {
2545 // Subfield: exp ::= exp '.' fieldId
2546 if (consumeIf(FIRToken::period)) {
2547 if (parsePostFixFieldId(result))
2548 return failure();
2549
2550 continue;
2551 }
2552
2553 // Subindex: exp ::= exp '[' intLit ']' | exp '[' exp ']'
2554 if (consumeIf(FIRToken::l_square)) {
2555 if (getToken().isAny(FIRToken::integer, FIRToken::string)) {
2556 if (parsePostFixIntSubscript(result))
2557 return failure();
2558 continue;
2559 }
2560 if (!allowDynamic)
2561 return emitError("subaccess not allowed here");
2562 if (parsePostFixDynamicSubscript(result))
2563 return failure();
2564
2565 continue;
2566 }
2567
2568 return success();
2569 }
2570}
2571
2572template <typename subop>
2573FailureOr<Value>
2574FIRStmtParser::emitCachedSubAccess(Value base, unsigned indexNo, SMLoc loc) {
2575 // Check if we already have created this Subindex op.
2576 auto &value = moduleContext.getCachedSubaccess(base, indexNo);
2577 if (value)
2578 return value;
2579
2580 // Make sure the field name matches up with the input value's type and
2581 // compute the result type for the expression.
2582 auto baseType = cast<FIRRTLType>(base.getType());
2583 auto resultType = subop::inferReturnType(baseType, indexNo, {});
2584 if (!resultType) {
2585 // Emit the error at the right location. translateLocation is expensive.
2586 (void)subop::inferReturnType(baseType, indexNo, translateLocation(loc));
2587 return failure();
2588 }
2589
2590 // Create the result operation, inserting at the location of the declaration.
2591 // This will cache the subfield operation for further uses.
2592 locationProcessor.setLoc(loc);
2593 OpBuilder::InsertionGuard guard(builder);
2594 builder.setInsertionPointAfterValue(base);
2595 auto op = subop::create(builder, resultType, base, indexNo);
2596
2597 // Insert the newly created operation into the cache.
2598 return value = op.getResult();
2599}
2600
2601/// exp ::= exp '.' fieldId
2602///
2603/// The "exp '.'" part of the production has already been parsed.
2604///
2605ParseResult FIRStmtParser::parsePostFixFieldId(Value &result) {
2606 auto loc = getToken().getLoc();
2607 SmallVector<StringRef, 3> fields;
2608 if (parseFieldIdSeq(fields, "expected field name"))
2609 return failure();
2610 for (auto fieldName : fields) {
2611 std::optional<unsigned> indexV;
2612 auto type = result.getType();
2613 if (auto refTy = type_dyn_cast<RefType>(type))
2614 type = refTy.getType();
2615 if (auto bundle = type_dyn_cast<BundleType>(type))
2616 indexV = bundle.getElementIndex(fieldName);
2617 else if (auto bundle = type_dyn_cast<OpenBundleType>(type))
2618 indexV = bundle.getElementIndex(fieldName);
2619 else if (auto klass = type_dyn_cast<ClassType>(type))
2620 indexV = klass.getElementIndex(fieldName);
2621 else if (auto domain = type_dyn_cast<DomainType>(type))
2622 indexV = domain.getFieldIndex(fieldName);
2623 else
2624 return emitError(loc,
2625 "subfield requires bundle, object, or domain operand ");
2626 if (!indexV)
2627 return emitError(loc, "unknown field '" + fieldName + "' in type ")
2628 << result.getType();
2629 auto indexNo = *indexV;
2630
2631 FailureOr<Value> subResult;
2632 if (type_isa<RefType>(result.getType()))
2633 subResult = emitCachedSubAccess<RefSubOp>(result, indexNo, loc);
2634 else if (type_isa<ClassType>(type))
2635 subResult = emitCachedSubAccess<ObjectSubfieldOp>(result, indexNo, loc);
2636 else if (type_isa<DomainType>(type))
2637 subResult = emitCachedSubAccess<DomainSubfieldOp>(result, indexNo, loc);
2638 else if (type_isa<BundleType>(type))
2639 subResult = emitCachedSubAccess<SubfieldOp>(result, indexNo, loc);
2640 else
2641 subResult = emitCachedSubAccess<OpenSubfieldOp>(result, indexNo, loc);
2642
2643 if (failed(subResult))
2644 return failure();
2645 result = *subResult;
2646 }
2647 return success();
2648}
2649
2650/// exp ::= exp '[' intLit ']'
2651///
2652/// The "exp '['" part of the production has already been parsed.
2653///
2654ParseResult FIRStmtParser::parsePostFixIntSubscript(Value &result) {
2655 auto loc = getToken().getLoc();
2656 int32_t indexNo;
2657 if (parseIntLit(indexNo, "expected index") ||
2658 parseToken(FIRToken::r_square, "expected ']'"))
2659 return failure();
2660
2661 if (indexNo < 0)
2662 return emitError(loc, "invalid index specifier"), failure();
2663
2664 FailureOr<Value> subResult;
2665 if (type_isa<RefType>(result.getType()))
2666 subResult = emitCachedSubAccess<RefSubOp>(result, indexNo, loc);
2667 else if (type_isa<FVectorType>(result.getType()))
2668 subResult = emitCachedSubAccess<SubindexOp>(result, indexNo, loc);
2669 else
2670 subResult = emitCachedSubAccess<OpenSubindexOp>(result, indexNo, loc);
2671
2672 if (failed(subResult))
2673 return failure();
2674 result = *subResult;
2675 return success();
2676}
2677
2678/// exp ::= exp '[' exp ']'
2679///
2680/// The "exp '['" part of the production has already been parsed.
2681///
2682ParseResult FIRStmtParser::parsePostFixDynamicSubscript(Value &result) {
2683 auto loc = getToken().getLoc();
2684 Value index;
2685 if (parseExp(index, "expected subscript index expression") ||
2686 parseToken(FIRToken::r_square, "expected ']' in subscript"))
2687 return failure();
2688
2689 // If the index expression is a flip type, strip it off.
2690 auto indexType = type_dyn_cast<FIRRTLBaseType>(index.getType());
2691 if (!indexType)
2692 return emitError("expected base type for index expression");
2693 indexType = indexType.getPassiveType();
2694 locationProcessor.setLoc(loc);
2695
2696 // Make sure the index expression is valid and compute the result type for the
2697 // expression.
2698 auto resultType =
2699 SubaccessOp::inferReturnType(result.getType(), index.getType(), {});
2700 if (!resultType) {
2701 // Emit the error at the right location. translateLocation is expensive.
2702 (void)SubaccessOp::inferReturnType(result.getType(), index.getType(),
2703 translateLocation(loc));
2704 return failure();
2705 }
2706
2707 // Create the result operation.
2708 auto op = SubaccessOp::create(builder, resultType, result, index);
2709 result = op.getResult();
2710 return success();
2711}
2712
2713/// integer-literal-exp ::= 'UInt' optional-width '(' intLit ')'
2714/// ::= 'SInt' optional-width '(' intLit ')'
2715///
2716/// If allocatedWidth is provided, it means the width was already parsed
2717/// (e.g., from a langle_UInt token) and should be used instead of parsing
2718/// it from the token stream.
2719ParseResult
2720FIRStmtParser::parseIntegerLiteralExp(Value &result, bool isSigned,
2721 std::optional<int32_t> allocatedWidth) {
2722 auto loc = getToken().getLoc();
2723
2724 // Determine if '(' was already consumed by the lexer.
2725 bool hasLParen = getToken().isAny(FIRToken::lp_UInt, FIRToken::lp_SInt);
2726 if (hasLParen)
2727 consumeToken();
2728
2729 // Parse a width specifier if not already provided.
2730 int32_t width = allocatedWidth.value_or(-1);
2731 APInt value;
2732
2733 // If we consumed an lp_ token, the '(' was already consumed by the lexer.
2734 // Otherwise, we need to parse it.
2735 if (!hasLParen &&
2736 parseToken(FIRToken::l_paren, "expected '(' in integer expression"))
2737 return failure();
2738
2739 if (parseIntLit(value, "expected integer value") ||
2740 parseToken(FIRToken::r_paren, "expected ')' in integer expression"))
2741 return failure();
2742
2743 // Construct an integer attribute of the right width.
2744 // Literals are parsed as 'const' types.
2745 auto type = IntType::get(builder.getContext(), isSigned, width, true);
2746
2747 IntegerType::SignednessSemantics signedness =
2748 isSigned ? IntegerType::Signed : IntegerType::Unsigned;
2749 if (width == 0) {
2750 if (!value.isZero())
2751 return emitError(loc, "zero bit constant must be zero");
2752 value = value.trunc(0);
2753 } else if (width != -1) {
2754 // Convert to the type's width, checking value fits in destination width.
2755 bool valueFits = isSigned ? value.isSignedIntN(width) : value.isIntN(width);
2756 if (!valueFits)
2757 return emitError(loc, "initializer too wide for declared width");
2758 value = isSigned ? value.sextOrTrunc(width) : value.zextOrTrunc(width);
2759 }
2760
2761 Type attrType =
2762 IntegerType::get(type.getContext(), value.getBitWidth(), signedness);
2763 auto attr = builder.getIntegerAttr(attrType, value);
2764
2765 locationProcessor.setLoc(loc);
2766 result = moduleContext.getCachedConstant(builder, attr, type, attr);
2767 return success();
2768}
2769
2770/// list-exp ::= list-type '(' exp* ')'
2771ParseResult FIRStmtParser::parseListExp(Value &result) {
2772 auto loc = getToken().getLoc();
2773 bool hasLAngle = getToken().is(FIRToken::langle_List);
2774 bool hasLParen = getToken().is(FIRToken::lp_List);
2775 consumeToken();
2776
2778 // If we consumed a langle_ token, the '<' was already consumed by the lexer.
2779 if (!hasLAngle && parseToken(FIRToken::less, "expected '<' in List type"))
2780 return failure();
2781
2782 if (parsePropertyType(elementType, "expected List element type") ||
2783 parseToken(FIRToken::greater, "expected '>' in List type"))
2784 return failure();
2785
2786 auto listType = ListType::get(getContext(), elementType);
2787
2788 // If we consumed an lp_ token, the '(' was already consumed by the lexer.
2789 if (!hasLParen &&
2790 parseToken(FIRToken::l_paren, "expected '(' in List expression"))
2791 return failure();
2792
2793 SmallVector<Value, 3> operands;
2794 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
2795 Value operand;
2796 locationProcessor.setLoc(loc);
2797 if (parseExp(operand, "expected expression in List expression"))
2798 return failure();
2799
2800 if (operand.getType() != elementType) {
2801 if (!isa<AnyRefType>(elementType) ||
2802 !isa<ClassType>(operand.getType()))
2803 return emitError(loc, "unexpected expression of type ")
2804 << operand.getType() << " in List expression of type "
2805 << elementType;
2806 operand = ObjectAnyRefCastOp::create(builder, operand);
2807 }
2808
2809 operands.push_back(operand);
2810 return success();
2811 }))
2812 return failure();
2813
2814 locationProcessor.setLoc(loc);
2815 result = ListCreateOp::create(builder, listType, operands);
2816 return success();
2817}
2818
2819/// list-concat-exp ::= 'list_concat' '(' exp* ')'
2820ParseResult FIRStmtParser::parseListConcatExp(Value &result) {
2821 consumeToken(FIRToken::lp_list_concat);
2822
2823 auto loc = getToken().getLoc();
2824 ListType type;
2825 SmallVector<Value, 3> operands;
2826 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
2827 Value operand;
2828 locationProcessor.setLoc(loc);
2829 if (parseExp(operand, "expected expression in List concat expression"))
2830 return failure();
2831
2832 if (!type_isa<ListType>(operand.getType()))
2833 return emitError(loc, "unexpected expression of type ")
2834 << operand.getType() << " in List concat expression";
2835
2836 if (!type)
2837 type = type_cast<ListType>(operand.getType());
2838
2839 if (operand.getType() != type)
2840 return emitError(loc, "unexpected expression of type ")
2841 << operand.getType() << " in List concat expression of type "
2842 << type;
2843
2844 operands.push_back(operand);
2845 return success();
2846 }))
2847 return failure();
2848
2849 if (operands.empty())
2850 return emitError(loc, "need at least one List to concatenate");
2851
2852 locationProcessor.setLoc(loc);
2853 result = ListConcatOp::create(builder, type, operands);
2854 return success();
2855}
2856
2857/// cat-exp ::= 'cat(' exp* ')'
2858ParseResult FIRStmtParser::parseCatExp(Value &result) {
2859 consumeToken(FIRToken::lp_cat);
2860
2861 auto loc = getToken().getLoc();
2862 SmallVector<Value, 3> operands;
2863 std::optional<bool> isSigned;
2864 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
2865 Value operand;
2866 locationProcessor.setLoc(loc);
2867 auto operandLoc = getToken().getLoc();
2868 if (parseExp(operand, "expected expression in cat expression"))
2869 return failure();
2870 if (!type_isa<IntType>(operand.getType())) {
2871 auto diag = emitError(loc, "all operands must be Int type");
2872 diag.attachNote(translateLocation(operandLoc))
2873 << "non-integer operand is here";
2874 return failure();
2875 }
2876 if (!isSigned)
2877 isSigned = type_isa<SIntType>(operand.getType());
2878 else if (type_isa<SIntType>(operand.getType()) != *isSigned) {
2879 auto diag = emitError(loc, "all operands must have same signedness");
2880 diag.attachNote(translateLocation(operandLoc))
2881 << "operand with different signedness is here";
2882 return failure();
2883 }
2884
2885 operands.push_back(operand);
2886 return success();
2887 }))
2888 return failure();
2889
2890 if (operands.size() != 2) {
2891 if (requireFeature({6, 0, 0}, "variadic cat", loc))
2892 return failure();
2893 }
2894
2895 locationProcessor.setLoc(loc);
2896 result = CatPrimOp::create(builder, operands);
2897 return success();
2898}
2899
2900/// string_concat-exp ::= 'string_concat(' exp* ')'
2901ParseResult FIRStmtParser::parseStringConcatExp(Value &result) {
2902 consumeToken(FIRToken::lp_string_concat);
2903
2904 auto loc = getToken().getLoc();
2905 SmallVector<Value, 3> operands;
2906 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
2907 Value operand;
2908 locationProcessor.setLoc(loc);
2909 if (parseExp(operand,
2910 "expected expression in string_concat expression"))
2911 return failure();
2912 if (!type_isa<StringType>(operand.getType()))
2913 return emitError(loc, "all operands must be String type");
2914 operands.push_back(operand);
2915 return success();
2916 }))
2917 return failure();
2918
2919 if (operands.empty())
2920 return emitError(loc, "need at least one String to concatenate");
2921
2922 locationProcessor.setLoc(loc);
2923 auto type = StringType::get(builder.getContext());
2924 result = builder.create<StringConcatOp>(type, operands);
2925 return success();
2926}
2927
2928/// prop_eq-exp ::= 'prop_eq(' expr ',' expr ')'
2929ParseResult FIRStmtParser::parsePropEqExp(Value &result) {
2930 consumeToken(FIRToken::lp_prop_eq);
2931
2932 auto loc = getToken().getLoc();
2933 Value lhs, rhs;
2934 locationProcessor.setLoc(loc);
2935 if (parseExp(lhs, "expected lhs expression in prop_eq expression") ||
2936 parseToken(FIRToken::comma, "expected ','") ||
2937 parseExp(rhs, "expected rhs expression in prop_eq expression") ||
2938 parseToken(FIRToken::r_paren, "expected ')'"))
2939 return failure();
2940
2941 auto isValidType = [](Type t) {
2942 return type_isa<StringType>(t) || type_isa<BoolType>(t) ||
2943 type_isa<FIntegerType>(t);
2944 };
2945 if (!isValidType(lhs.getType()))
2946 return emitError(loc,
2947 "lhs of prop_eq must be String, Bool, or Integer type");
2948 if (!isValidType(rhs.getType()))
2949 return emitError(loc,
2950 "rhs of prop_eq must be String, Bool, or Integer type");
2951 if (lhs.getType() != rhs.getType())
2952 return emitError(loc, "prop_eq operands must have the same type");
2953
2954 locationProcessor.setLoc(loc);
2955 result = PropEqOp::create(builder, lhs, rhs);
2956 return success();
2957}
2958
2959ParseResult FIRStmtParser::parseUnsafeDomainCast(Value &result) {
2960 consumeToken(FIRToken::lp_unsafe_domain_cast);
2961
2962 auto loc = getToken().getLoc();
2963 Value input;
2964 if (parseExp(input, "expected input"))
2965 return failure();
2966
2967 SmallVector<Value> domains;
2968 if (consumeIf(FIRToken::comma)) {
2969 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
2970 Value domain;
2971 if (parseExp(domain, "expected domain"))
2972 return failure();
2973 domains.push_back(domain);
2974 return success();
2975 }))
2976 return failure();
2977 } else if (parseToken(FIRToken::r_paren, "expected closing parenthesis")) {
2978 return failure();
2979 }
2980
2981 locationProcessor.setLoc(loc);
2982 result = UnsafeDomainCastOp::create(builder, input, domains);
2983 return success();
2984}
2985
2986ParseResult FIRStmtParser::parseUnknownProperty(Value &result) {
2987 auto loc = getToken().getLoc();
2988 consumeToken(FIRToken::lp_Unknown);
2989 // The '(' has already been consumed by the lexer.
2990
2991 PropertyType type;
2992 if (parsePropertyType(type, "expected property type") ||
2993 parseToken(FIRToken::r_paren, "expected ')' in unknown property"))
2994 return failure();
2995
2996 locationProcessor.setLoc(loc);
2997 result = UnknownValueOp::create(builder, type);
2998 return success();
2999}
3000
3001/// The .fir grammar has the annoying property where:
3002/// 1) some statements start with keywords
3003/// 2) some start with an expression
3004/// 3) it allows the 'reference' expression to either be an identifier or a
3005/// keyword.
3006///
3007/// One example of this is something like, where this is not a register decl:
3008/// reg <- thing
3009///
3010/// Solving this requires lookahead to the second token. We handle it by
3011/// factoring the lookahead inline into the code to keep the parser fast.
3012///
3013/// As such, statements that start with a leading keyword call this method to
3014/// check to see if the keyword they consumed was actually the start of an
3015/// expression. If so, they parse the expression-based statement and return the
3016/// parser result. If not, they return None and the statement is parsed like
3017/// normal.
3018std::optional<ParseResult>
3019FIRStmtParser::parseExpWithLeadingKeyword(FIRToken keyword) {
3020 switch (getToken().getKind()) {
3021 default:
3022 // This isn't part of an expression, and isn't part of a statement.
3023 return std::nullopt;
3024
3025 case FIRToken::period: // exp `.` identifier
3026 case FIRToken::l_square: // exp `[` index `]`
3027 case FIRToken::kw_is: // exp is invalid
3028 case FIRToken::less_equal: // exp <= thing
3029 break;
3030 }
3031
3032 Value lhs;
3033 SymbolValueEntry symtabEntry;
3034 auto loc = keyword.getLoc();
3035
3036 if (moduleContext.lookupSymbolEntry(symtabEntry, keyword.getSpelling(), loc))
3037 return ParseResult(failure());
3038
3039 // If we have a '.', we might have a symbol or an expanded port. If we
3040 // resolve to a symbol, use that, otherwise check for expanded bundles of
3041 // other ops.
3042 // Non '.' ops take the plain symbol path.
3043 if (moduleContext.resolveSymbolEntry(lhs, symtabEntry, loc, false)) {
3044 // Ok if the base name didn't resolve by itself, it might be part of an
3045 // expanded dot reference. That doesn't work then we fail.
3046 if (!consumeIf(FIRToken::period))
3047 return ParseResult(failure());
3048
3049 StringRef fieldName;
3050 if (parseFieldId(fieldName, "expected field name") ||
3051 moduleContext.resolveSymbolEntry(lhs, symtabEntry, fieldName, loc))
3052 return ParseResult(failure());
3053 }
3054
3055 // Parse any further trailing things like "mem.x.y".
3056 if (parseOptionalExpPostscript(lhs))
3057 return ParseResult(failure());
3058
3059 return parseLeadingExpStmt(lhs);
3060}
3061//===-----------------------------
3062// FIRStmtParser Statement Parsing
3063
3064/// simple_stmt_block ::= simple_stmt*
3065ParseResult FIRStmtParser::parseSimpleStmtBlock(unsigned indent) {
3066 while (true) {
3067 // The outer level parser can handle these tokens.
3068 if (getToken().isAny(FIRToken::eof, FIRToken::error))
3069 return success();
3070
3071 auto subIndent = getIndentation();
3072 if (!subIndent.has_value())
3073 return emitError("expected statement to be on its own line"), failure();
3074
3075 if (*subIndent <= indent)
3076 return success();
3077
3078 // Let the statement parser handle this.
3079 if (parseSimpleStmt(*subIndent))
3080 return failure();
3081 }
3082}
3083
3084ParseResult FIRStmtParser::parseSimpleStmt(unsigned stmtIndent) {
3085 locationProcessor.startStatement();
3086 auto result = parseSimpleStmtImpl(stmtIndent);
3087 locationProcessor.endStatement(*this);
3088 return result;
3089}
3090
3091/// simple_stmt ::= stmt
3092///
3093/// stmt ::= attach
3094/// ::= memport
3095/// ::= printf
3096/// ::= skip
3097/// ::= stop
3098/// ::= when
3099/// ::= leading-exp-stmt
3100/// ::= define
3101/// ::= propassert
3102/// ::= propassign
3103///
3104/// stmt ::= instance
3105/// ::= cmem | smem | mem
3106/// ::= node | wire
3107/// ::= register
3108/// ::= contract
3109///
3110ParseResult FIRStmtParser::parseSimpleStmtImpl(unsigned stmtIndent) {
3111 auto kind = getToken().getKind();
3112 /// Massage the kind based on the FIRRTL Version.
3113 switch (kind) {
3114 case FIRToken::kw_invalidate:
3115 case FIRToken::kw_connect:
3116 case FIRToken::kw_regreset:
3117 /// The "invalidate", "connect", and "regreset" keywords were added
3118 /// in 3.0.0.
3119 if (version < FIRVersion(3, 0, 0))
3120 kind = FIRToken::identifier;
3121 break;
3122 default:
3123 break;
3124 };
3125 switch (kind) {
3126 // Statements.
3127 case FIRToken::kw_attach:
3128 return parseAttach();
3129 case FIRToken::kw_infer:
3130 return parseMemPort(MemDirAttr::Infer);
3131 case FIRToken::kw_read:
3132 return parseMemPort(MemDirAttr::Read);
3133 case FIRToken::kw_write:
3134 return parseMemPort(MemDirAttr::Write);
3135 case FIRToken::kw_rdwr:
3136 return parseMemPort(MemDirAttr::ReadWrite);
3137 case FIRToken::kw_connect:
3138 return parseConnect();
3139 case FIRToken::kw_propassert:
3140 if (requireFeature({6, 0, 0}, "property assertions"))
3141 return failure();
3142 return parsePropAssert();
3143 case FIRToken::kw_propassign:
3144 if (requireFeature({3, 1, 0}, "properties"))
3145 return failure();
3146 return parsePropAssign();
3147 case FIRToken::kw_invalidate:
3148 return parseInvalidate();
3149 case FIRToken::lp_printf:
3150 return parsePrintf();
3151 case FIRToken::lp_fprintf:
3152 return parseFPrintf();
3153 case FIRToken::lp_fflush:
3154 return parseFFlush();
3155 case FIRToken::kw_skip:
3156 return parseSkip();
3157 case FIRToken::lp_stop:
3158 return parseStop();
3159 case FIRToken::lp_assert:
3160 return parseAssert();
3161 case FIRToken::lp_assume:
3162 return parseAssume();
3163 case FIRToken::lp_cover:
3164 return parseCover();
3165 case FIRToken::kw_when:
3166 return parseWhen(stmtIndent);
3167 case FIRToken::kw_match:
3168 return parseMatch(stmtIndent);
3169 case FIRToken::kw_domain:
3170 // In module context, 'domain' is only valid for domain instantiation
3171 return parseDomainInstantiation();
3172 case FIRToken::kw_domain_define:
3173 return parseDomainDefine();
3174 case FIRToken::kw_define:
3175 return parseRefDefine();
3176 case FIRToken::lp_force:
3177 return parseRefForce();
3178 case FIRToken::lp_force_initial:
3179 return parseRefForceInitial();
3180 case FIRToken::lp_release:
3181 return parseRefRelease();
3182 case FIRToken::lp_release_initial:
3183 return parseRefReleaseInitial();
3184 case FIRToken::kw_group:
3185 if (requireFeature({3, 2, 0}, "optional groups") ||
3186 removedFeature({3, 3, 0}, "optional groups"))
3187 return failure();
3188 return parseLayerBlockOrGroup(stmtIndent);
3189 case FIRToken::kw_layerblock:
3190 if (requireFeature({3, 3, 0}, "layers"))
3191 return failure();
3192 return parseLayerBlockOrGroup(stmtIndent);
3193 case FIRToken::lp_intrinsic:
3194 if (requireFeature({4, 0, 0}, "generic intrinsics"))
3195 return failure();
3196 return parseIntrinsicStmt();
3197 default: {
3198 // Statement productions that start with an expression.
3199 Value lhs;
3200 if (parseExpLeadingStmt(lhs, "unexpected token in module"))
3201 return failure();
3202 // We use parseExp in a special mode that can complete the entire stmt
3203 // at once in unusual cases. If this happened, then we are done.
3204 if (!lhs)
3205 return success();
3206
3207 return parseLeadingExpStmt(lhs);
3208 }
3209
3210 // Declarations
3211 case FIRToken::kw_inst:
3212 return parseInstance();
3213 case FIRToken::kw_instchoice:
3214 return parseInstanceChoice();
3215 case FIRToken::kw_object:
3216 return parseObject();
3217 case FIRToken::kw_cmem:
3218 return parseCombMem();
3219 case FIRToken::kw_smem:
3220 return parseSeqMem();
3221 case FIRToken::kw_mem:
3222 return parseMem(stmtIndent);
3223 case FIRToken::kw_node:
3224 return parseNode();
3225 case FIRToken::kw_wire:
3226 return parseWire();
3227 case FIRToken::kw_reg:
3228 return parseRegister(stmtIndent);
3229 case FIRToken::kw_regreset:
3230 return parseRegisterWithReset();
3231 case FIRToken::kw_contract:
3232 return parseContract(stmtIndent);
3233 }
3234}
3235
3236ParseResult FIRStmtParser::parseSubBlock(Block &blockToInsertInto,
3237 unsigned indent,
3238 SymbolRefAttr layerSym) {
3239 // Declarations within the suite are scoped to within the suite.
3240 auto suiteScope = std::make_unique<FIRModuleContext::ContextScope>(
3241 moduleContext, &blockToInsertInto);
3242
3243 // After parsing the when region, we can release any new entries in
3244 // unbundledValues since the symbol table entries that refer to them will be
3245 // gone.
3246 UnbundledValueRestorer x(moduleContext.unbundledValues);
3247
3248 // We parse the substatements into their own parser, so they get inserted
3249 // into the specified 'when' region.
3250 auto subParser = std::make_unique<FIRStmtParser>(
3251 blockToInsertInto, moduleContext, innerSymFixups, circuitSymTbl, version,
3252 layerSym);
3253
3254 // Figure out whether the body is a single statement or a nested one.
3255 auto stmtIndent = getIndentation();
3256
3257 // Parsing a single statment is straightforward.
3258 if (!stmtIndent.has_value())
3259 return subParser->parseSimpleStmt(indent);
3260
3261 if (*stmtIndent <= indent)
3262 return emitError("statement must be indented more than previous statement"),
3263 failure();
3264
3265 // Parse a block of statements that are indented more than the when.
3266 return subParser->parseSimpleStmtBlock(indent);
3267}
3268
3269/// attach ::= 'attach' '(' exp+ ')' info?
3270ParseResult FIRStmtParser::parseAttach() {
3271 auto startTok = consumeToken(FIRToken::kw_attach);
3272
3273 // If this was actually the start of a connect or something else handle that.
3274 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
3275 return *isExpr;
3276
3277 if (parseToken(FIRToken::l_paren, "expected '(' after attach"))
3278 return failure();
3279
3280 SmallVector<Value, 4> operands;
3281 operands.push_back({});
3282 if (parseExp(operands.back(), "expected operand in attach"))
3283 return failure();
3284
3285 while (consumeIf(FIRToken::comma)) {
3286 operands.push_back({});
3287 if (parseExp(operands.back(), "expected operand in attach"))
3288 return failure();
3289 }
3290 if (parseToken(FIRToken::r_paren, "expected close paren"))
3291 return failure();
3292
3293 if (parseOptionalInfo())
3294 return failure();
3295
3296 locationProcessor.setLoc(startTok.getLoc());
3297 AttachOp::create(builder, operands);
3298 return success();
3299}
3300
3301/// stmt ::= mdir 'mport' id '=' id '[' exp ']' exp info?
3302/// mdir ::= 'infer' | 'read' | 'write' | 'rdwr'
3303///
3304ParseResult FIRStmtParser::parseMemPort(MemDirAttr direction) {
3305 auto startTok = consumeToken();
3306 auto startLoc = startTok.getLoc();
3307
3308 // If this was actually the start of a connect or something else handle
3309 // that.
3310 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
3311 return *isExpr;
3312
3313 StringRef id;
3314 StringRef memName;
3315 SymbolValueEntry memorySym;
3316 Value memory, indexExp, clock;
3317 if (parseToken(FIRToken::kw_mport, "expected 'mport' in memory port") ||
3318 parseId(id, "expected result name") ||
3319 parseToken(FIRToken::equal, "expected '=' in memory port") ||
3320 parseId(memName, "expected memory name") ||
3321 moduleContext.lookupSymbolEntry(memorySym, memName, startLoc) ||
3322 moduleContext.resolveSymbolEntry(memory, memorySym, startLoc) ||
3323 parseToken(FIRToken::l_square, "expected '[' in memory port") ||
3324 parseExp(indexExp, "expected index expression") ||
3325 parseToken(FIRToken::r_square, "expected ']' in memory port") ||
3326 parseToken(FIRToken::comma, "expected ','") ||
3327 parseExp(clock, "expected clock expression") || parseOptionalInfo())
3328 return failure();
3329
3330 auto memVType = type_dyn_cast<CMemoryType>(memory.getType());
3331 if (!memVType)
3332 return emitError(startLoc,
3333 "memory port should have behavioral memory type");
3334 auto resultType = memVType.getElementType();
3335
3336 ArrayAttr annotations = getConstants().emptyArrayAttr;
3337 locationProcessor.setLoc(startLoc);
3338
3339 // Create the memory port at the location of the cmemory.
3340 Value memoryPort, memoryData;
3341 {
3342 OpBuilder::InsertionGuard guard(builder);
3343 builder.setInsertionPointAfterValue(memory);
3344 auto memoryPortOp = MemoryPortOp::create(
3345 builder, resultType, CMemoryPortType::get(getContext()), memory,
3346 direction, id, annotations);
3347 memoryData = memoryPortOp.getResult(0);
3348 memoryPort = memoryPortOp.getResult(1);
3349 }
3350
3351 // Create a memory port access in the current scope.
3352 MemoryPortAccessOp::create(builder, memoryPort, indexExp, clock);
3353
3354 return moduleContext.addSymbolEntry(id, memoryData, startLoc, true);
3355}
3356
3357// Parse a format string and build operations for FIRRTL "special"
3358// substitutions. Set `formatStringResult` to the validated format string and
3359// `operands` to the list of actual operands.
3360ParseResult FIRStmtParser::parseFormatString(SMLoc formatStringLoc,
3361 StringRef formatString,
3362 ArrayRef<Value> specOperands,
3363 StringAttr &formatStringResult,
3364 SmallVectorImpl<Value> &operands) {
3365 // For FIRRTL versions < 5.0.0, don't process special substitutions
3366 if (version < FIRVersion(5, 0, 0)) {
3367 operands.append(specOperands.begin(), specOperands.end());
3368 formatStringResult =
3369 builder.getStringAttr(FIRToken::getStringValue(formatString));
3370 return success();
3371 }
3372
3373 // Use the utility function to parse the format string
3374 auto loc = translateLocation(formatStringLoc);
3376 builder, loc, FIRToken::getStringValue(formatString), specOperands,
3377 formatStringResult, operands);
3378 return result;
3379}
3380
3381/// printf ::= 'printf(' exp exp StringLit exp* ')' name? info?
3382ParseResult FIRStmtParser::parsePrintf() {
3383 auto startTok = consumeToken(FIRToken::lp_printf);
3384
3385 Value clock, condition;
3386 StringRef formatString;
3387 if (parseExp(clock, "expected clock expression in printf") ||
3388 parseToken(FIRToken::comma, "expected ','") ||
3389 parseExp(condition, "expected condition in printf") ||
3390 parseToken(FIRToken::comma, "expected ','"))
3391 return failure();
3392
3393 auto formatStringLoc = getToken().getLoc();
3394 if (parseGetSpelling(formatString) ||
3395 parseToken(FIRToken::string, "expected format string in printf"))
3396 return failure();
3397
3398 SmallVector<Value, 4> specOperands;
3399 while (consumeIf(FIRToken::comma)) {
3400 specOperands.push_back({});
3401 if (parseExp(specOperands.back(), "expected operand in printf"))
3402 return failure();
3403 }
3404
3405 StringAttr name;
3406 if (parseToken(FIRToken::r_paren, "expected ')'") ||
3407 parseOptionalName(name) || parseOptionalInfo())
3408 return failure();
3409
3410 locationProcessor.setLoc(startTok.getLoc());
3411
3412 StringAttr formatStrUnescaped;
3413 SmallVector<Value> operands;
3414 if (parseFormatString(formatStringLoc, formatString, specOperands,
3415 formatStrUnescaped, operands))
3416 return failure();
3417
3418 PrintFOp::create(builder, clock, condition, formatStrUnescaped, operands,
3419 name);
3420 return success();
3421}
3422
3423/// fprintf ::= 'fprintf(' exp exp StringLit StringLit exp* ')' name? info?
3424ParseResult FIRStmtParser::parseFPrintf() {
3425 if (requireFeature({6, 0, 0}, "fprintf"))
3426 return failure();
3427 auto startTok = consumeToken(FIRToken::lp_fprintf);
3428
3429 Value clock, condition;
3430 StringRef outputFile, formatString;
3431 if (parseExp(clock, "expected clock expression in fprintf") ||
3432 parseToken(FIRToken::comma, "expected ','") ||
3433 parseExp(condition, "expected condition in fprintf") ||
3434 parseToken(FIRToken::comma, "expected ','"))
3435 return failure();
3436
3437 auto outputFileLoc = getToken().getLoc();
3438 if (parseGetSpelling(outputFile) ||
3439 parseToken(FIRToken::string, "expected output file in fprintf"))
3440 return failure();
3441
3442 SmallVector<Value, 4> outputFileSpecOperands;
3443 while (consumeIf(FIRToken::comma)) {
3444 // Stop parsing operands when we see the format string.
3445 if (getToken().getKind() == FIRToken::string)
3446 break;
3447 outputFileSpecOperands.push_back({});
3448 if (parseExp(outputFileSpecOperands.back(), "expected operand in fprintf"))
3449 return failure();
3450 }
3451
3452 auto formatStringLoc = getToken().getLoc();
3453 if (parseGetSpelling(formatString) ||
3454 parseToken(FIRToken::string, "expected format string in printf"))
3455 return failure();
3456
3457 SmallVector<Value, 4> specOperands;
3458 while (consumeIf(FIRToken::comma)) {
3459 specOperands.push_back({});
3460 if (parseExp(specOperands.back(), "expected operand in fprintf"))
3461 return failure();
3462 }
3463
3464 StringAttr name;
3465 if (parseToken(FIRToken::r_paren, "expected ')'") ||
3466 parseOptionalName(name) || parseOptionalInfo())
3467 return failure();
3468
3469 locationProcessor.setLoc(startTok.getLoc());
3470
3471 StringAttr outputFileNameStrUnescaped;
3472 SmallVector<Value> outputFileOperands;
3473 if (parseFormatString(outputFileLoc, outputFile, outputFileSpecOperands,
3474 outputFileNameStrUnescaped, outputFileOperands))
3475 return failure();
3476
3477 StringAttr formatStrUnescaped;
3478 SmallVector<Value> operands;
3479 if (parseFormatString(formatStringLoc, formatString, specOperands,
3480 formatStrUnescaped, operands))
3481 return failure();
3482
3483 FPrintFOp::create(builder, clock, condition, outputFileNameStrUnescaped,
3484 outputFileOperands, formatStrUnescaped, operands, name);
3485 return success();
3486}
3487
3488/// fflush ::= 'fflush(' exp exp (StringLit exp*)? ')' info?
3489ParseResult FIRStmtParser::parseFFlush() {
3490 if (requireFeature({6, 0, 0}, "fflush"))
3491 return failure();
3492
3493 auto startTok = consumeToken(FIRToken::lp_fflush);
3494
3495 Value clock, condition;
3496 if (parseExp(clock, "expected clock expression in 'fflush'") ||
3497 parseToken(FIRToken::comma, "expected ','") ||
3498 parseExp(condition, "expected condition in 'fflush'"))
3499 return failure();
3500
3501 locationProcessor.setLoc(startTok.getLoc());
3502 StringAttr outputFileNameStrUnescaped;
3503 SmallVector<Value> outputFileOperands;
3504 // Parse file name if present.
3505 if (consumeIf(FIRToken::comma)) {
3506 SmallVector<Value, 4> outputFileSpecOperands;
3507 auto outputFileLoc = getToken().getLoc();
3508 StringRef outputFile;
3509 if (parseGetSpelling(outputFile) ||
3510 parseToken(FIRToken::string, "expected output file in fflush"))
3511 return failure();
3512
3513 while (consumeIf(FIRToken::comma)) {
3514 outputFileSpecOperands.push_back({});
3515 if (parseExp(outputFileSpecOperands.back(), "expected operand in fflush"))
3516 return failure();
3517 }
3518
3519 if (parseFormatString(outputFileLoc, outputFile, outputFileSpecOperands,
3520 outputFileNameStrUnescaped, outputFileOperands))
3521 return failure();
3522 }
3523
3524 if (parseToken(FIRToken::r_paren, "expected ')' in 'fflush'") ||
3525 parseOptionalInfo())
3526 return failure();
3527
3528 FFlushOp::create(builder, clock, condition, outputFileNameStrUnescaped,
3529 outputFileOperands);
3530 return success();
3531}
3532
3533/// skip ::= 'skip' info?
3534ParseResult FIRStmtParser::parseSkip() {
3535 auto startTok = consumeToken(FIRToken::kw_skip);
3536
3537 // If this was actually the start of a connect or something else handle
3538 // that.
3539 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
3540 return *isExpr;
3541
3542 if (parseOptionalInfo())
3543 return failure();
3544
3545 locationProcessor.setLoc(startTok.getLoc());
3546 SkipOp::create(builder);
3547 return success();
3548}
3549
3550/// stop ::= 'stop(' exp exp intLit ')' info?
3551ParseResult FIRStmtParser::parseStop() {
3552 auto startTok = consumeToken(FIRToken::lp_stop);
3553
3554 Value clock, condition;
3555 int64_t exitCode;
3556 StringAttr name;
3557 if (parseExp(clock, "expected clock expression in 'stop'") ||
3558 parseToken(FIRToken::comma, "expected ','") ||
3559 parseExp(condition, "expected condition in 'stop'") ||
3560 parseToken(FIRToken::comma, "expected ','") ||
3561 parseIntLit(exitCode, "expected exit code in 'stop'") ||
3562 parseToken(FIRToken::r_paren, "expected ')' in 'stop'") ||
3563 parseOptionalName(name) || parseOptionalInfo())
3564 return failure();
3565
3566 locationProcessor.setLoc(startTok.getLoc());
3567 StopOp::create(builder, clock, condition, builder.getI32IntegerAttr(exitCode),
3568 name);
3569 return success();
3570}
3571
3572/// assert ::= 'assert(' exp exp exp StringLit exp*')' info?
3573ParseResult FIRStmtParser::parseAssert() {
3574 auto startTok = consumeToken(FIRToken::lp_assert);
3575
3576 Value clock, predicate, enable;
3577 StringRef formatString;
3578 StringAttr name;
3579 if (parseExp(clock, "expected clock expression in 'assert'") ||
3580 parseToken(FIRToken::comma, "expected ','") ||
3581 parseExp(predicate, "expected predicate in 'assert'") ||
3582 parseToken(FIRToken::comma, "expected ','") ||
3583 parseExp(enable, "expected enable in 'assert'") ||
3584 parseToken(FIRToken::comma, "expected ','") ||
3585 parseGetSpelling(formatString) ||
3586 parseToken(FIRToken::string, "expected format string in 'assert'"))
3587 return failure();
3588
3589 SmallVector<Value, 4> operands;
3590 while (!consumeIf(FIRToken::r_paren)) {
3591 operands.push_back({});
3592 if (parseToken(FIRToken::comma, "expected ','") ||
3593 parseExp(operands.back(), "expected operand in 'assert'"))
3594 return failure();
3595 }
3596
3597 if (parseOptionalName(name) || parseOptionalInfo())
3598 return failure();
3599
3600 locationProcessor.setLoc(startTok.getLoc());
3601 auto formatStrUnescaped = FIRToken::getStringValue(formatString);
3602 AssertOp::create(builder, clock, predicate, enable, formatStrUnescaped,
3603 operands, name.getValue());
3604 return success();
3605}
3606
3607/// assume ::= 'assume(' exp exp exp StringLit exp* ')' info?
3608ParseResult FIRStmtParser::parseAssume() {
3609 auto startTok = consumeToken(FIRToken::lp_assume);
3610
3611 Value clock, predicate, enable;
3612 StringRef formatString;
3613 StringAttr name;
3614 if (parseExp(clock, "expected clock expression in 'assume'") ||
3615 parseToken(FIRToken::comma, "expected ','") ||
3616 parseExp(predicate, "expected predicate in 'assume'") ||
3617 parseToken(FIRToken::comma, "expected ','") ||
3618 parseExp(enable, "expected enable in 'assume'") ||
3619 parseToken(FIRToken::comma, "expected ','") ||
3620 parseGetSpelling(formatString) ||
3621 parseToken(FIRToken::string, "expected format string in 'assume'"))
3622 return failure();
3623
3624 SmallVector<Value, 4> operands;
3625 while (!consumeIf(FIRToken::r_paren)) {
3626 operands.push_back({});
3627 if (parseToken(FIRToken::comma, "expected ','") ||
3628 parseExp(operands.back(), "expected operand in 'assume'"))
3629 return failure();
3630 }
3631
3632 if (parseOptionalName(name) || parseOptionalInfo())
3633 return failure();
3634
3635 locationProcessor.setLoc(startTok.getLoc());
3636 auto formatStrUnescaped = FIRToken::getStringValue(formatString);
3637 AssumeOp::create(builder, clock, predicate, enable, formatStrUnescaped,
3638 operands, name.getValue());
3639 return success();
3640}
3641
3642/// cover ::= 'cover(' exp exp exp StringLit ')' info?
3643ParseResult FIRStmtParser::parseCover() {
3644 auto startTok = consumeToken(FIRToken::lp_cover);
3645
3646 Value clock, predicate, enable;
3647 StringRef message;
3648 StringAttr name;
3649 if (parseExp(clock, "expected clock expression in 'cover'") ||
3650 parseToken(FIRToken::comma, "expected ','") ||
3651 parseExp(predicate, "expected predicate in 'cover'") ||
3652 parseToken(FIRToken::comma, "expected ','") ||
3653 parseExp(enable, "expected enable in 'cover'") ||
3654 parseToken(FIRToken::comma, "expected ','") ||
3655 parseGetSpelling(message) ||
3656 parseToken(FIRToken::string, "expected message in 'cover'") ||
3657 parseToken(FIRToken::r_paren, "expected ')' in 'cover'") ||
3658 parseOptionalName(name) || parseOptionalInfo())
3659 return failure();
3660
3661 locationProcessor.setLoc(startTok.getLoc());
3662 auto messageUnescaped = FIRToken::getStringValue(message);
3663 CoverOp::create(builder, clock, predicate, enable, messageUnescaped,
3664 ValueRange{}, name.getValue());
3665 return success();
3666}
3667
3668/// when ::= 'when' exp ':' info? suite? ('else' ( when | ':' info? suite?)
3669/// )? suite ::= simple_stmt | INDENT simple_stmt+ DEDENT
3670ParseResult FIRStmtParser::parseWhen(unsigned whenIndent) {
3671 auto startTok = consumeToken(FIRToken::kw_when);
3672
3673 // If this was actually the start of a connect or something else handle
3674 // that.
3675 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
3676 return *isExpr;
3677
3678 Value condition;
3679 if (parseExp(condition, "expected condition in 'when'") ||
3680 parseToken(FIRToken::colon, "expected ':' in when") ||
3681 parseOptionalInfo())
3682 return failure();
3683
3684 locationProcessor.setLoc(startTok.getLoc());
3685 // Create the IR representation for the when.
3686 auto whenStmt = WhenOp::create(builder, condition, /*createElse*/ false);
3687
3688 // Parse the 'then' body into the 'then' region.
3689 if (parseSubBlock(whenStmt.getThenBlock(), whenIndent, layerSym))
3690 return failure();
3691
3692 // If the else is present, handle it otherwise we're done.
3693 if (getToken().isNot(FIRToken::kw_else))
3694 return success();
3695
3696 // If the 'else' is less indented than the when, then it must belong to some
3697 // containing 'when'.
3698 auto elseIndent = getIndentation();
3699 if (elseIndent && *elseIndent < whenIndent)
3700 return success();
3701
3702 consumeToken(FIRToken::kw_else);
3703
3704 // Create an else block to parse into.
3705 whenStmt.createElseRegion();
3706
3707 // If we have the ':' form, then handle it.
3708
3709 // Syntactic shorthand 'else when'. This uses the same indentation level as
3710 // the outer 'when'.
3711 if (getToken().is(FIRToken::kw_when)) {
3712 // We create a sub parser for the else block.
3713 auto subParser = std::make_unique<FIRStmtParser>(
3714 whenStmt.getElseBlock(), moduleContext, innerSymFixups, circuitSymTbl,
3715 version, layerSym);
3716
3717 return subParser->parseSimpleStmt(whenIndent);
3718 }
3719
3720 // Parse the 'else' body into the 'else' region.
3721 LocationAttr elseLoc; // ignore the else locator.
3722 if (parseToken(FIRToken::colon, "expected ':' after 'else'") ||
3723 parseOptionalInfoLocator(elseLoc) ||
3724 parseSubBlock(whenStmt.getElseBlock(), whenIndent, layerSym))
3725 return failure();
3726
3727 // TODO(firrtl spec): There is no reason for the 'else :' grammar to take an
3728 // info. It doesn't appear to be generated either.
3729 return success();
3730}
3731
3732/// enum-exp ::= enum-type '(' Id ( ',' exp )? ')'
3733ParseResult FIRStmtParser::parseEnumExp(Value &value) {
3734 auto startLoc = getToken().getLoc();
3735 locationProcessor.setLoc(startLoc);
3736 FIRRTLType type;
3737 if (parseEnumType(type))
3738 return failure();
3739
3740 // Check that the input type is a legal enumeration.
3741 auto enumType = type_dyn_cast<FEnumType>(type);
3742 if (!enumType)
3743 return emitError(startLoc,
3744 "expected enumeration type in enumeration expression");
3745
3746 StringRef tag;
3747 if (parseToken(FIRToken::l_paren, "expected '(' in enumeration expression") ||
3748 parseId(tag, "expected enumeration tag"))
3749 return failure();
3750
3751 Value input;
3752 if (consumeIf(FIRToken::r_paren)) {
3753 // If the payload is not specified, we create a 0 bit unsigned integer
3754 // constant.
3755 auto type = IntType::get(builder.getContext(), false, 0, true);
3756 Type attrType = IntegerType::get(getContext(), 0, IntegerType::Unsigned);
3757 auto attr = builder.getIntegerAttr(attrType, APInt(0, 0, false));
3758 input = ConstantOp::create(builder, type, attr);
3759 } else {
3760 // Otherwise we parse an expression.
3761 if (parseToken(FIRToken::comma, "expected ','") ||
3762 parseExp(input, "expected expression in enumeration value") ||
3763 parseToken(FIRToken::r_paren, "expected closing ')'"))
3764 return failure();
3765 }
3766
3767 value = FEnumCreateOp::create(builder, enumType, tag, input);
3768 return success();
3769}
3770
3771/// match ::= 'match' exp ':' info?
3772/// (INDENT ( Id ( '(' Id ')' )? ':'
3773/// (INDENT simple_stmt* DEDENT )?
3774/// )* DEDENT)?
3775ParseResult FIRStmtParser::parseMatch(unsigned matchIndent) {
3776 auto startTok = consumeToken(FIRToken::kw_match);
3777
3778 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
3779 return *isExpr;
3780
3781 Value input;
3782 if (parseExp(input, "expected expression in 'match'") ||
3783 parseToken(FIRToken::colon, "expected ':' in 'match'") ||
3784 parseOptionalInfo())
3785 return failure();
3786
3787 auto enumType = type_dyn_cast<FEnumType>(input.getType());
3788 if (!enumType)
3789 return mlir::emitError(
3790 input.getLoc(),
3791 "expected enumeration type for 'match' statement, but got ")
3792 << input.getType();
3793
3794 locationProcessor.setLoc(startTok.getLoc());
3795
3796 SmallVector<Attribute> tags;
3797 SmallVector<std::unique_ptr<Region>> regions;
3798 while (true) {
3799 auto tagLoc = getToken().getLoc();
3800
3801 // Only consume the keyword if the indentation is correct.
3802 auto caseIndent = getIndentation();
3803 if (!caseIndent || *caseIndent <= matchIndent)
3804 break;
3805
3806 // Parse the tag.
3807 StringRef tagSpelling;
3808 if (parseId(tagSpelling, "expected enumeration tag in match statement"))
3809 return failure();
3810 auto tagIndex = enumType.getElementIndex(tagSpelling);
3811 if (!tagIndex)
3812 return emitError(tagLoc, "tag ")
3813 << tagSpelling << " not a member of enumeration " << enumType;
3814 auto tag = IntegerAttr::get(IntegerType::get(getContext(), 32), *tagIndex);
3815 tags.push_back(tag);
3816
3817 // Add a new case to the match operation.
3818 auto *caseBlock = &regions.emplace_back(new Region)->emplaceBlock();
3819
3820 // Declarations are scoped to the case.
3821 FIRModuleContext::ContextScope scope(moduleContext, caseBlock);
3822
3823 // After parsing the region, we can release any new entries in
3824 // unbundledValues since the symbol table entries that refer to them will be
3825 // gone.
3826 UnbundledValueRestorer x(moduleContext.unbundledValues);
3827
3828 // Parse the argument.
3829 if (consumeIf(FIRToken::l_paren)) {
3830 StringAttr identifier;
3831 if (parseId(identifier, "expected identifier for 'case' binding"))
3832 return failure();
3833
3834 // Add an argument to the block.
3835 auto dataType = enumType.getElementType(*tagIndex);
3836 caseBlock->addArgument(dataType, LocWithInfo(tagLoc, this).getLoc());
3837
3838 if (moduleContext.addSymbolEntry(identifier, caseBlock->getArgument(0),
3839 startTok.getLoc()))
3840 return failure();
3841
3842 if (parseToken(FIRToken::r_paren, "expected ')' in match statement case"))
3843 return failure();
3844
3845 } else {
3846 auto dataType = IntType::get(builder.getContext(), false, 0);
3847 caseBlock->addArgument(dataType, LocWithInfo(tagLoc, this).getLoc());
3848 }
3849
3850 if (parseToken(FIRToken::colon, "expected ':' in match statement case"))
3851 return failure();
3852
3853 // Parse a block of statements that are indented more than the case.
3854 auto subParser = std::make_unique<FIRStmtParser>(
3855 *caseBlock, moduleContext, innerSymFixups, circuitSymTbl, version,
3856 layerSym);
3857 if (subParser->parseSimpleStmtBlock(*caseIndent))
3858 return failure();
3859 }
3860
3861 MatchOp::create(builder, input, ArrayAttr::get(getContext(), tags), regions);
3862 return success();
3863}
3864
3865/// domain_exp ::= id
3866/// domain_exp ::= domain_exp '.' id
3867/// domain_exp ::= domain_exp '[' int ']'
3868ParseResult FIRStmtParser::parseDomainExp(Value &result) {
3869 auto loc = getToken().getLoc();
3870 SymbolValueEntry entry;
3871 StringRef id;
3872 if (parseId(id, "expected domain expression") ||
3873 moduleContext.lookupSymbolEntry(entry, id, loc))
3874 return failure();
3875
3876 if (moduleContext.resolveSymbolEntry(result, entry, loc, false)) {
3877 StringRef field;
3878 if (parseToken(FIRToken::period, "expected '.' in field reference") ||
3879 parseFieldId(field, "expected field name") ||
3880 moduleContext.resolveSymbolEntry(result, entry, field, loc))
3881 return failure();
3882 }
3883
3884 if (parseOptionalExpPostscript(result, /*allowDynamic=*/false))
3885 return failure();
3886
3887 auto type = result.getType();
3888 if (!type_isa<DomainType>(type))
3889 return emitError(loc) << "expected domain-type expression, got " << type;
3890
3891 return success();
3892}
3893
3894/// ref_expr ::= probe | rwprobe | static_reference
3895// NOLINTNEXTLINE(misc-no-recursion)
3896ParseResult FIRStmtParser::parseRefExp(Value &result, const Twine &message) {
3897 auto token = getToken().getKind();
3898 if (token == FIRToken::lp_probe)
3899 return parseProbe(result);
3900 if (token == FIRToken::lp_rwprobe)
3901 return parseRWProbe(result);
3902
3903 // Default to parsing as static reference expression.
3904 // Don't check token kind, we need to support literal_identifier and keywords,
3905 // let parseId handle this.
3906 return parseStaticRefExp(result, message);
3907}
3908
3909/// static_reference ::= id
3910/// ::= static_reference '.' id
3911/// ::= static_reference '[' int ']'
3912// NOLINTNEXTLINE(misc-no-recursion)
3913ParseResult FIRStmtParser::parseStaticRefExp(Value &result,
3914 const Twine &message) {
3915 auto parseIdOrInstance = [&]() -> ParseResult {
3916 StringRef id;
3917 auto loc = getToken().getLoc();
3918 SymbolValueEntry symtabEntry;
3919 if (parseId(id, message) ||
3920 moduleContext.lookupSymbolEntry(symtabEntry, id, loc))
3921 return failure();
3922
3923 // If we looked up a normal value, then we're done.
3924 if (!moduleContext.resolveSymbolEntry(result, symtabEntry, loc, false))
3925 return success();
3926
3927 assert(isa<UnbundledID>(symtabEntry) && "should be an instance");
3928
3929 // Handle the normal "instance.x" reference.
3930 StringRef fieldName;
3931 return failure(
3932 parseToken(FIRToken::period, "expected '.' in field reference") ||
3933 parseFieldId(fieldName, "expected field name") ||
3934 moduleContext.resolveSymbolEntry(result, symtabEntry, fieldName, loc));
3935 };
3936 return failure(parseIdOrInstance() ||
3937 parseOptionalExpPostscript(result, false));
3938}
3939/// static_reference ::= id
3940/// ::= static_reference '.' id
3941/// ::= static_reference '[' int ']'
3942/// Populate `refResult` with rwprobe "root" and parsed indexing.
3943/// Root is base-type target, and will be block argument or forceable.
3944/// Also set `Type`, so we can handle const-ness while visiting.
3945/// If root is an unbundled entry, replace with bounce wire and update
3946/// the unbundled entry to point to this for future users.
3947// NOLINTNEXTLINE(misc-no-recursion)
3948ParseResult FIRStmtParser::parseRWProbeStaticRefExp(FieldRef &refResult,
3949 Type &type,
3950 const Twine &message) {
3951 auto loc = getToken().getLoc();
3952
3953 StringRef id;
3954 SymbolValueEntry symtabEntry;
3955 if (parseId(id, message) ||
3956 moduleContext.lookupSymbolEntry(symtabEntry, id, loc))
3957 return failure();
3958
3959 // Three kinds of rwprobe targets:
3960 // 1. Instance result. Replace with a forceable wire, handle as (2).
3961 // 2. Forceable declaration.
3962 // 3. BlockArgument.
3963
3964 // We use inner symbols for all.
3965
3966 // Figure out what we have, and parse indexing.
3967 Value result;
3968 if (auto unbundledId = dyn_cast<UnbundledID>(symtabEntry)) {
3969 // This means we have an instance.
3970 auto &ubEntry = moduleContext.getUnbundledEntry(unbundledId - 1);
3971
3972 StringRef fieldName;
3973 auto loc = getToken().getLoc();
3974 if (parseToken(FIRToken::period, "expected '.' in field reference") ||
3975 parseFieldId(fieldName, "expected field name"))
3976 return failure();
3977
3978 // Find unbundled entry for the specified result/port.
3979 // Get a reference to it--as we may update it (!!).
3980 auto fieldAttr = StringAttr::get(getContext(), fieldName);
3981 for (auto &elt : ubEntry) {
3982 if (elt.first == fieldAttr) {
3983 // Grab the unbundled entry /by reference/ so we can update it with the
3984 // new forceable wire we insert (if not already done).
3985 auto &instResult = elt.second;
3986
3987 // If it's already forceable, use that.
3988 auto *defining = instResult.getDefiningOp();
3989 assert(defining);
3990 if (isa<WireOp>(defining)) {
3991 result = instResult;
3992 break;
3993 }
3994
3995 // Otherwise, replace with bounce wire.
3996 auto type = instResult.getType();
3997
3998 // Create bounce wire for the instance result.
3999 // This may be an open aggregate, or other non-base type.
4000 auto annotations = getConstants().emptyArrayAttr;
4001 StringAttr sym = {};
4002 SmallString<64> name;
4003 (id + "_" + fieldName + "_bounce").toVector(name);
4004 locationProcessor.setLoc(loc);
4005 OpBuilder::InsertionGuard guard(builder);
4006 builder.setInsertionPoint(defining);
4007 auto bounce =
4008 WireOp::create(builder, type, name, NameKindEnum::InterestingName,
4009 annotations, sym);
4010 auto bounceVal = bounce.getDataRaw();
4011
4012 // Replace instance result with reads from bounce wire.
4013 instResult.replaceAllUsesWith(bounceVal);
4014
4015 // Connect to/from the result per flow.
4016 builder.setInsertionPointAfter(defining);
4017 if (foldFlow(instResult) == Flow::Source)
4018 emitConnect(builder, bounceVal, instResult,
4019 getConstants().options.warnOnTruncation);
4020 else
4021 emitConnect(builder, instResult, bounceVal,
4022 getConstants().options.warnOnTruncation);
4023 // Set the parse result AND update `instResult` which is a reference to
4024 // the unbundled entry for the instance result, so that future uses also
4025 // find this new wire.
4026 result = instResult = bounce.getDataRaw();
4027 break;
4028 }
4029 }
4030
4031 if (!result) {
4032 emitError(loc, "use of invalid field name '")
4033 << fieldName << "' on bundle value";
4034 return failure();
4035 }
4036 } else {
4037 // This target can be a port or a regular value.
4038 result = cast<Value>(symtabEntry);
4039 }
4040
4041 assert(result);
4042 assert(isa<BlockArgument>(result) ||
4043 result.getDefiningOp<hw::InnerSymbolOpInterface>());
4044
4045 // We have our root value, we just need to parse the field id.
4046 // Build up the FieldRef as processing indexing expressions, and
4047 // compute the type so that we know the const-ness of the final expression.
4048 refResult = FieldRef(result, 0);
4049 type = result.getType();
4050 while (true) {
4051 if (consumeIf(FIRToken::period)) {
4052 SmallVector<StringRef, 3> fields;
4053 if (parseFieldIdSeq(fields, "expected field name"))
4054 return failure();
4055 for (auto fieldName : fields) {
4056 if (auto bundle = type_dyn_cast<BundleType>(type)) {
4057 if (auto index = bundle.getElementIndex(fieldName)) {
4058 refResult = refResult.getSubField(bundle.getFieldID(*index));
4059 type = bundle.getElementTypePreservingConst(*index);
4060 continue;
4061 }
4062 } else if (auto bundle = type_dyn_cast<OpenBundleType>(type)) {
4063 if (auto index = bundle.getElementIndex(fieldName)) {
4064 refResult = refResult.getSubField(bundle.getFieldID(*index));
4065 type = bundle.getElementTypePreservingConst(*index);
4066 continue;
4067 }
4068 } else {
4069 return emitError(loc, "subfield requires bundle operand")
4070 << "got " << type << "\n";
4071 }
4072 return emitError(loc,
4073 "unknown field '" + fieldName + "' in bundle type ")
4074 << type;
4075 }
4076 continue;
4077 }
4078 if (consumeIf(FIRToken::l_square)) {
4079 auto loc = getToken().getLoc();
4080 int32_t index;
4081 if (parseIntLit(index, "expected index") ||
4082 parseToken(FIRToken::r_square, "expected ']'"))
4083 return failure();
4084
4085 if (index < 0)
4086 return emitError(loc, "invalid index specifier");
4087
4088 if (auto vector = type_dyn_cast<FVectorType>(type)) {
4089 if ((unsigned)index < vector.getNumElements()) {
4090 refResult = refResult.getSubField(vector.getFieldID(index));
4091 type = vector.getElementTypePreservingConst();
4092 continue;
4093 }
4094 } else if (auto vector = type_dyn_cast<OpenVectorType>(type)) {
4095 if ((unsigned)index < vector.getNumElements()) {
4096 refResult = refResult.getSubField(vector.getFieldID(index));
4097 type = vector.getElementTypePreservingConst();
4098 continue;
4099 }
4100 } else {
4101 return emitError(loc, "subindex requires vector operand");
4102 }
4103 return emitError(loc, "out of range index '")
4104 << index << "' for vector type " << type;
4105 }
4106 return success();
4107 }
4108}
4109
4110/// intrinsic_expr ::= 'intrinsic(' Id (params)? ':' type exp* ')'
4111/// intrinsic_stmt ::= 'intrinsic(' Id (params)? (':' type )? exp* ')'
4112ParseResult FIRStmtParser::parseIntrinsic(Value &result, bool isStatement) {
4113 auto startTok = consumeToken(FIRToken::lp_intrinsic);
4114 StringRef intrinsic;
4115 ArrayAttr parameters;
4116 FIRRTLType type;
4117
4118 if (parseId(intrinsic, "expected intrinsic identifier") ||
4119 parseOptionalParams(parameters))
4120 return failure();
4121
4122 if (consumeIf(FIRToken::colon)) {
4123 if (parseType(type, "expected intrinsic return type"))
4124 return failure();
4125 } else if (!isStatement)
4126 return emitError("expected ':' in intrinsic expression");
4127
4128 SmallVector<Value> operands;
4129 auto loc = startTok.getLoc();
4130 if (consumeIf(FIRToken::comma)) {
4131 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
4132 Value operand;
4133 if (parseExp(operand, "expected operand in intrinsic"))
4134 return failure();
4135 operands.push_back(operand);
4136 locationProcessor.setLoc(loc);
4137 return success();
4138 }))
4139 return failure();
4140 } else {
4141 if (parseToken(FIRToken::r_paren, "expected ')' in intrinsic"))
4142 return failure();
4143 }
4144
4145 if (isStatement)
4146 if (parseOptionalInfo())
4147 return failure();
4148
4149 locationProcessor.setLoc(loc);
4150
4151 auto op = GenericIntrinsicOp::create(
4152 builder, type, builder.getStringAttr(intrinsic), operands, parameters);
4153 if (type)
4154 result = op.getResult();
4155 return success();
4156}
4157
4158/// params ::= '<' param','* '>'
4159ParseResult FIRStmtParser::parseOptionalParams(ArrayAttr &resultParameters) {
4160 if (!consumeIf(FIRToken::less))
4161 return success();
4162
4163 SmallVector<Attribute, 8> parameters;
4164 SmallPtrSet<StringAttr, 8> seen;
4165 if (parseListUntil(FIRToken::greater, [&]() -> ParseResult {
4166 StringAttr name;
4167 Attribute value;
4168 SMLoc loc;
4169 if (parseParameter(name, value, loc))
4170 return failure();
4171 auto typedValue = dyn_cast<TypedAttr>(value);
4172 if (!typedValue)
4173 return emitError(loc)
4174 << "invalid value for parameter '" << name.getValue() << "'";
4175 if (!seen.insert(name).second)
4176 return emitError(loc, "redefinition of parameter '" +
4177 name.getValue() + "'");
4178 parameters.push_back(ParamDeclAttr::get(name, typedValue));
4179 return success();
4180 }))
4181 return failure();
4182
4183 resultParameters = ArrayAttr::get(getContext(), parameters);
4184 return success();
4185}
4186
4187/// path ::= 'path(' StringLit ')'
4188// NOLINTNEXTLINE(misc-no-recursion)
4189ParseResult FIRStmtParser::parsePathExp(Value &result) {
4190 auto startTok = consumeToken(FIRToken::lp_path);
4191 locationProcessor.setLoc(startTok.getLoc());
4192 StringRef target;
4193 if (parseGetSpelling(target) ||
4194 parseToken(FIRToken::string,
4195 "expected target string in path expression") ||
4196 parseToken(FIRToken::r_paren, "expected ')' in path expression"))
4197 return failure();
4198 result = UnresolvedPathOp::create(
4199 builder, StringAttr::get(getContext(), FIRToken::getStringValue(target)));
4200 return success();
4201}
4202
4203/// domain_instantiation ::= 'domain' id 'of' id ('(' exp (',' exp)* ')')? info?
4204ParseResult FIRStmtParser::parseDomainInstantiation() {
4205 auto startTok = consumeToken(FIRToken::kw_domain);
4206 auto startLoc = startTok.getLoc();
4207
4208 // If this was actually the start of a connect or something else handle that.
4209 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
4210 return *isExpr;
4211
4212 locationProcessor.setLoc(startTok.getLoc());
4213
4214 StringAttr instanceName;
4215 StringAttr domainKind;
4216
4217 if (requireFeature(missingSpecFIRVersion, "domains", startLoc) ||
4218 parseId(instanceName, "expected domain instance name") ||
4219 parseToken(FIRToken::kw_of, "expected 'of' after domain instance name") ||
4220 parseId(domainKind, "expected domain type name"))
4221 return failure();
4222
4223 // Create the domain instance
4224 // Look up the domain to get its fields
4225 const auto &domainMap = getConstants().domainMap;
4226 auto lookup = domainMap.find(domainKind.getValue());
4227 if (lookup == domainMap.end())
4228 return emitError(startTok.getLoc())
4229 << "unknown domain '" << domainKind.getValue() << "'";
4230
4231 auto domainType = DomainType::getFromDomainOp(lookup->second);
4232
4233 // Parse optional field values
4234 SmallVector<Value> fieldValues;
4235 if (consumeIf(FIRToken::l_paren)) {
4236 // Parse comma-separated list of expressions
4237 if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
4238 Value value;
4239 if (parseExp(value, "expected field value expression"))
4240 return failure();
4241 fieldValues.push_back(value);
4242 return success();
4243 }))
4244 return failure();
4245 }
4246
4247 if (parseOptionalInfo())
4248 return failure();
4249
4250 locationProcessor.setLoc(startLoc);
4251 auto result =
4252 DomainCreateOp::create(builder, domainType, instanceName, fieldValues);
4253
4254 // Add to symbol table
4255 return moduleContext.addSymbolEntry(instanceName.getValue(), result,
4256 startTok.getLoc());
4257}
4258
4259/// domain_define ::= 'domain_define' domain_exp '=' domain_exp info?
4260ParseResult FIRStmtParser::parseDomainDefine() {
4261 auto startTok = consumeToken(FIRToken::kw_domain_define);
4262 auto startLoc = startTok.getLoc();
4263 locationProcessor.setLoc(startLoc);
4264
4265 Value dest, src;
4266 if (requireFeature(missingSpecFIRVersion, "domains", startLoc) ||
4267 parseDomainExp(dest) || parseToken(FIRToken::equal, "expected '='") ||
4268 parseDomainExp(src) || parseOptionalInfo())
4269 return failure();
4270
4271 emitConnect(builder, dest, src, getConstants().options.warnOnTruncation);
4272 return success();
4273}
4274
4275/// define ::= 'define' static_reference '=' ref_expr info?
4276ParseResult FIRStmtParser::parseRefDefine() {
4277 auto startTok = consumeToken(FIRToken::kw_define);
4278
4279 Value src, target;
4280 if (parseStaticRefExp(target,
4281 "expected static reference expression in 'define'") ||
4282 parseToken(FIRToken::equal,
4283 "expected '=' after define reference expression") ||
4284 parseRefExp(src, "expected reference expression in 'define'") ||
4285 parseOptionalInfo())
4286 return failure();
4287
4288 // Check reference expressions are of reference type.
4289 if (!type_isa<RefType>(target.getType()))
4290 return emitError(startTok.getLoc(), "expected reference-type expression in "
4291 "'define' target (LHS), got ")
4292 << target.getType();
4293 if (!type_isa<RefType>(src.getType()))
4294 return emitError(startTok.getLoc(), "expected reference-type expression in "
4295 "'define' source (RHS), got ")
4296 << src.getType();
4297
4298 // static_reference doesn't differentiate which can be ref.sub'd, so check
4299 // this explicitly:
4300 if (isa_and_nonnull<RefSubOp>(target.getDefiningOp()))
4301 return emitError(startTok.getLoc(),
4302 "cannot define into a sub-element of a reference");
4303
4304 locationProcessor.setLoc(startTok.getLoc());
4305
4306 if (!areTypesRefCastable(target.getType(), src.getType()))
4307 return emitError(startTok.getLoc(), "cannot define reference of type ")
4308 << target.getType() << " with incompatible reference of type "
4309 << src.getType();
4310
4311 emitConnect(builder, target, src, getConstants().options.warnOnTruncation);
4312
4313 return success();
4314}
4315
4316/// read ::= '(' ref_expr ')'
4317/// XXX: spec says static_reference, allow ref_expr anyway for read(probe(x)).
4318ParseResult FIRStmtParser::parseRefRead(Value &result) {
4319 auto startTok = consumeToken(FIRToken::lp_read);
4320
4321 Value ref;
4322 if (parseRefExp(ref, "expected reference expression in 'read'") ||
4323 parseToken(FIRToken::r_paren, "expected ')' in 'read'"))
4324 return failure();
4325
4326 locationProcessor.setLoc(startTok.getLoc());
4327
4328 // Check argument is a ref-type value.
4329 if (!type_isa<RefType>(ref.getType()))
4330 return emitError(startTok.getLoc(),
4331 "expected reference-type expression in 'read', got ")
4332 << ref.getType();
4333
4334 result = RefResolveOp::create(builder, ref);
4335
4336 return success();
4337}
4338
4339/// probe ::= 'probe' '(' static_ref ')'
4340ParseResult FIRStmtParser::parseProbe(Value &result) {
4341 auto startTok = consumeToken(FIRToken::lp_probe);
4342
4343 Value staticRef;
4344 if (parseStaticRefExp(staticRef,
4345 "expected static reference expression in 'probe'") ||
4346 parseToken(FIRToken::r_paren, "expected ')' in 'probe'"))
4347 return failure();
4348
4349 locationProcessor.setLoc(startTok.getLoc());
4350
4351 // Check probe expression is base-type.
4352 if (!type_isa<FIRRTLBaseType>(staticRef.getType()))
4353 return emitError(startTok.getLoc(),
4354 "expected base-type expression in 'probe', got ")
4355 << staticRef.getType();
4356
4357 // Check for other unsupported reference sources.
4358 // TODO: Add to ref.send verifier / inferReturnTypes.
4359 if (isa_and_nonnull<MemOp, CombMemOp, SeqMemOp, MemoryPortOp,
4360 MemoryDebugPortOp, MemoryPortAccessOp>(
4361 staticRef.getDefiningOp()))
4362 return emitError(startTok.getLoc(), "cannot probe memories or their ports");
4363
4364 result = RefSendOp::create(builder, staticRef);
4365
4366 return success();
4367}
4368
4369/// rwprobe ::= 'rwprobe' '(' static_ref ')'
4370ParseResult FIRStmtParser::parseRWProbe(Value &result) {
4371 auto startTok = consumeToken(FIRToken::lp_rwprobe);
4372
4373 FieldRef staticRef;
4374 Type parsedTargetType;
4375 if (parseRWProbeStaticRefExp(
4376 staticRef, parsedTargetType,
4377 "expected static reference expression in 'rwprobe'") ||
4378 parseToken(FIRToken::r_paren, "expected ')' in 'rwprobe'"))
4379 return failure();
4380
4381 locationProcessor.setLoc(startTok.getLoc());
4382
4383 // Checks:
4384 // Not public port (verifier)
4385
4386 // Check probe expression is base-type.
4387 auto targetType = type_dyn_cast<FIRRTLBaseType>(parsedTargetType);
4388 if (!targetType)
4389 return emitError(startTok.getLoc(),
4390 "expected base-type expression in 'rwprobe', got ")
4391 << parsedTargetType;
4392
4393 auto root = staticRef.getValue();
4394 auto *definingOp = root.getDefiningOp();
4395
4396 if (isa_and_nonnull<MemOp, CombMemOp, SeqMemOp, MemoryPortOp,
4397 MemoryDebugPortOp, MemoryPortAccessOp>(definingOp))
4398 return emitError(startTok.getLoc(), "cannot probe memories or their ports");
4399
4400 auto forceableType = firrtl::detail::getForceableResultType(true, targetType);
4401 if (!forceableType)
4402 return emitError(startTok.getLoc(), "cannot force target of type ")
4403 << targetType;
4404
4405 // Create the operation with a placeholder reference and add to fixup list.
4406 auto op = RWProbeOp::create(builder, forceableType,
4407 getConstants().placeholderInnerRef);
4408 innerSymFixups.add(op, getTargetFor(staticRef));
4409 result = op;
4410 return success();
4411}
4412
4413/// force ::= 'force(' exp exp ref_expr exp ')' info?
4414ParseResult FIRStmtParser::parseRefForce() {
4415 auto startTok = consumeToken(FIRToken::lp_force);
4416
4417 Value clock, pred, dest, src;
4418 if (parseExp(clock, "expected clock expression in force") ||
4419 parseToken(FIRToken::comma, "expected ','") ||
4420 parseExp(pred, "expected predicate expression in force") ||
4421 parseToken(FIRToken::comma, "expected ','") ||
4422 parseRefExp(dest, "expected destination reference expression in force") ||
4423 parseToken(FIRToken::comma, "expected ','") ||
4424 parseExp(src, "expected source expression in force") ||
4425 parseToken(FIRToken::r_paren, "expected ')' in force") ||
4426 parseOptionalInfo())
4427 return failure();
4428
4429 // Check reference expression is of reference type.
4430 auto ref = type_dyn_cast<RefType>(dest.getType());
4431 if (!ref || !ref.getForceable())
4432 return emitError(
4433 startTok.getLoc(),
4434 "expected rwprobe-type expression for force destination, got ")
4435 << dest.getType();
4436 auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(src.getType());
4437 if (!srcBaseType)
4438 return emitError(startTok.getLoc(),
4439 "expected base-type for force source, got ")
4440 << src.getType();
4441 if (!srcBaseType.isPassive())
4442 return emitError(startTok.getLoc(),
4443 "expected passive value for force source, got ")
4444 << srcBaseType;
4445
4446 locationProcessor.setLoc(startTok.getLoc());
4447
4448 // Cast ref to accommodate uninferred sources.
4449 auto noConstSrcType = srcBaseType.getAllConstDroppedType();
4450 if (noConstSrcType != ref.getType()) {
4451 // Try to cast destination to rwprobe of source type (dropping const).
4452 auto compatibleRWProbe = RefType::get(noConstSrcType, true, ref.getLayer());
4453 if (areTypesRefCastable(compatibleRWProbe, ref))
4454 dest = RefCastOp::create(builder, compatibleRWProbe, dest);
4455 else
4456 return emitError(startTok.getLoc(), "incompatible force source of type ")
4457 << src.getType() << " cannot target destination "
4458 << dest.getType();
4459 }
4460
4461 RefForceOp::create(builder, clock, pred, dest, src);
4462
4463 return success();
4464}
4465
4466/// force_initial ::= 'force_initial(' ref_expr exp ')' info?
4467ParseResult FIRStmtParser::parseRefForceInitial() {
4468 auto startTok = consumeToken(FIRToken::lp_force_initial);
4469
4470 Value dest, src;
4471 if (parseRefExp(
4472 dest, "expected destination reference expression in force_initial") ||
4473 parseToken(FIRToken::comma, "expected ','") ||
4474 parseExp(src, "expected source expression in force_initial") ||
4475 parseToken(FIRToken::r_paren, "expected ')' in force_initial") ||
4476 parseOptionalInfo())
4477 return failure();
4478
4479 // Check reference expression is of reference type.
4480 auto ref = type_dyn_cast<RefType>(dest.getType());
4481 if (!ref || !ref.getForceable())
4482 return emitError(startTok.getLoc(), "expected rwprobe-type expression for "
4483 "force_initial destination, got ")
4484 << dest.getType();
4485 auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(src.getType());
4486 if (!srcBaseType)
4487 return emitError(startTok.getLoc(),
4488 "expected base-type expression for force_initial "
4489 "source, got ")
4490 << src.getType();
4491 if (!srcBaseType.isPassive())
4492 return emitError(startTok.getLoc(),
4493 "expected passive value for force_initial source, got ")
4494 << srcBaseType;
4495
4496 locationProcessor.setLoc(startTok.getLoc());
4497
4498 // Cast ref to accommodate uninferred sources.
4499 auto noConstSrcType = srcBaseType.getAllConstDroppedType();
4500 if (noConstSrcType != ref.getType()) {
4501 // Try to cast destination to rwprobe of source type (dropping const).
4502 auto compatibleRWProbe = RefType::get(noConstSrcType, true, ref.getLayer());
4503 if (areTypesRefCastable(compatibleRWProbe, ref))
4504 dest = RefCastOp::create(builder, compatibleRWProbe, dest);
4505 else
4506 return emitError(startTok.getLoc(),
4507 "incompatible force_initial source of type ")
4508 << src.getType() << " cannot target destination "
4509 << dest.getType();
4510 }
4511
4512 auto value = APInt::getAllOnes(1);
4513 auto type = UIntType::get(builder.getContext(), 1);
4514 auto attr = builder.getIntegerAttr(IntegerType::get(type.getContext(),
4515 value.getBitWidth(),
4516 IntegerType::Unsigned),
4517 value);
4518 auto pred = moduleContext.getCachedConstant(builder, attr, type, attr);
4519 RefForceInitialOp::create(builder, pred, dest, src);
4520
4521 return success();
4522}
4523
4524/// release ::= 'release(' exp exp ref_expr ')' info?
4525ParseResult FIRStmtParser::parseRefRelease() {
4526 auto startTok = consumeToken(FIRToken::lp_release);
4527
4528 Value clock, pred, dest;
4529 if (parseExp(clock, "expected clock expression in release") ||
4530 parseToken(FIRToken::comma, "expected ','") ||
4531 parseExp(pred, "expected predicate expression in release") ||
4532 parseToken(FIRToken::comma, "expected ','") ||
4533 parseRefExp(dest,
4534 "expected destination reference expression in release") ||
4535 parseToken(FIRToken::r_paren, "expected ')' in release") ||
4536 parseOptionalInfo())
4537 return failure();
4538
4539 // Check reference expression is of reference type.
4540 if (auto ref = type_dyn_cast<RefType>(dest.getType());
4541 !ref || !ref.getForceable())
4542 return emitError(
4543 startTok.getLoc(),
4544 "expected rwprobe-type expression for release destination, got ")
4545 << dest.getType();
4546
4547 locationProcessor.setLoc(startTok.getLoc());
4548
4549 RefReleaseOp::create(builder, clock, pred, dest);
4550
4551 return success();
4552}
4553
4554/// release_initial ::= 'release_initial(' ref_expr ')' info?
4555ParseResult FIRStmtParser::parseRefReleaseInitial() {
4556 auto startTok = consumeToken(FIRToken::lp_release_initial);
4557
4558 Value dest;
4559 if (parseRefExp(
4560 dest,
4561 "expected destination reference expression in release_initial") ||
4562 parseToken(FIRToken::r_paren, "expected ')' in release_initial") ||
4563 parseOptionalInfo())
4564 return failure();
4565
4566 // Check reference expression is of reference type.
4567 if (auto ref = type_dyn_cast<RefType>(dest.getType());
4568 !ref || !ref.getForceable())
4569 return emitError(startTok.getLoc(), "expected rwprobe-type expression for "
4570 "release_initial destination, got ")
4571 << dest.getType();
4572
4573 locationProcessor.setLoc(startTok.getLoc());
4574
4575 auto value = APInt::getAllOnes(1);
4576 auto type = UIntType::get(builder.getContext(), 1);
4577 auto attr = builder.getIntegerAttr(IntegerType::get(type.getContext(),
4578 value.getBitWidth(),
4579 IntegerType::Unsigned),
4580 value);
4581 auto pred = moduleContext.getCachedConstant(builder, attr, type, attr);
4582 RefReleaseInitialOp::create(builder, pred, dest);
4583
4584 return success();
4585}
4586
4587/// connect ::= 'connect' expr expr
4588ParseResult FIRStmtParser::parseConnect() {
4589 auto startTok = consumeToken(FIRToken::kw_connect);
4590 auto loc = startTok.getLoc();
4591
4592 Value lhs, rhs;
4593 if (parseExp(lhs, "expected connect expression") ||
4594 parseToken(FIRToken::comma, "expected ','") ||
4595 parseExp(rhs, "expected connect expression") || parseOptionalInfo())
4596 return failure();
4597
4598 auto lhsType = type_dyn_cast<FIRRTLBaseType>(lhs.getType());
4599 auto rhsType = type_dyn_cast<FIRRTLBaseType>(rhs.getType());
4600 if (!lhsType || !rhsType)
4601 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4602 "cannot connect reference or property types");
4603 // TODO: Once support lands for agg-of-ref, add test for this check!
4604 if (lhsType.containsReference() || rhsType.containsReference())
4605 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4606 "cannot connect types containing references");
4607
4608 if (!areTypesEquivalent(lhsType, rhsType))
4609 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4610 "cannot connect non-equivalent type ")
4611 << rhsType << " to " << lhsType;
4612
4613 locationProcessor.setLoc(loc);
4615 builder, lhs, rhs, [&] { return locationProcessor.getLoc(*this, loc); },
4616 getConstants().options.warnOnTruncation);
4617 return success();
4618}
4619
4620/// FIRRTL 6.0.0 <= version < FIRRTL 8.0.0:
4621/// propassert ::= 'propassert' expr ',' string_literal
4622/// FIRRTL 7.0.0 <= version:
4623/// propassert ::= 'propassert' expr ',' expr
4624///
4625/// Before calling, it has already been verified that the FIRRTL version is
4626/// greater than 6.0.0.
4627ParseResult FIRStmtParser::parsePropAssert() {
4628 auto startTok = consumeToken(FIRToken::kw_propassert);
4629 auto loc = startTok.getLoc();
4630
4631 llvm::SMLoc conditionLoc = getToken().getLoc(), messageLoc;
4632 Value condition, message;
4633 if (parseExp(condition, "expected condition in 'propassert'") ||
4634 parseToken(FIRToken::comma, "expected ','"))
4635 return failure();
4636 // String message handling
4637 if (getToken().is(FIRToken::string)) {
4638 if (removedFeature({8, 0, 0}, "string messages in property asserts"))
4639 return failure();
4640 StringRef messageStr;
4641 messageLoc = getToken().getLoc();
4642 if (parseGetSpelling(messageStr) ||
4643 parseToken(FIRToken::string, "expected message string in 'propassert'"))
4644 return failure();
4645 locationProcessor.setLoc(messageLoc);
4646 auto attr = builder.getStringAttr(FIRToken::getStringValue(messageStr));
4647 message = moduleContext.getCachedConstant<StringConstantOp>(
4648 builder, attr, builder.getType<StringType>(), attr);
4649 } else {
4650 if (requireFeature(
4651 {7, 0, 0},
4652 "string property expression message in property asserts"))
4653 return failure();
4654 messageLoc = getToken().getLoc();
4655 if (parseExp(message, "expected message in 'propassert'"))
4656 return failure();
4657 }
4658
4659 if (!isa<BoolType>(condition.getType()))
4660 return emitError(conditionLoc,
4661 "propassert condition must be of boolean type");
4662
4663 // Note: This is a dead check for FIRRTL < 7.0.0.
4664 if (!type_isa<StringType>(message.getType()))
4665 return emitError(messageLoc, "propassert message must be a string type");
4666
4667 if (parseOptionalInfo())
4668 return failure();
4669
4670 locationProcessor.setLoc(loc);
4671 PropertyAssertOp::create(builder, condition, message);
4672 return success();
4673}
4674
4675/// propassign ::= 'propassign' expr expr
4676ParseResult FIRStmtParser::parsePropAssign() {
4677 auto startTok = consumeToken(FIRToken::kw_propassign);
4678 auto loc = startTok.getLoc();
4679
4680 Value lhs, rhs;
4681 if (parseExp(lhs, "expected propassign expression") ||
4682 parseToken(FIRToken::comma, "expected ','") ||
4683 parseExp(rhs, "expected propassign expression") || parseOptionalInfo())
4684 return failure();
4685
4686 auto lhsType = type_dyn_cast<PropertyType>(lhs.getType());
4687 auto rhsType = type_dyn_cast<PropertyType>(rhs.getType());
4688 if (!lhsType || !rhsType)
4689 return emitError(loc, "can only propassign property types");
4690 locationProcessor.setLoc(loc);
4691 if (lhsType != rhsType) {
4692 // If the lhs is anyref, and the rhs is a ClassType, insert a cast.
4693 if (isa<AnyRefType>(lhsType) && isa<ClassType>(rhsType))
4694 rhs = ObjectAnyRefCastOp::create(builder, rhs);
4695 else
4696 return emitError(loc, "cannot propassign non-equivalent type ")
4697 << rhsType << " to " << lhsType;
4698 }
4699 PropAssignOp::create(builder, lhs, rhs);
4700 return success();
4701}
4702
4703/// invalidate ::= 'invalidate' expr
4704ParseResult FIRStmtParser::parseInvalidate() {
4705 auto startTok = consumeToken(FIRToken::kw_invalidate);
4706
4707 Value lhs;
4708
4709 StringRef id;
4710 auto loc = getToken().getLoc();
4711 SymbolValueEntry symtabEntry;
4712 if (parseId(id, "expected static reference expression") ||
4713 moduleContext.lookupSymbolEntry(symtabEntry, id, loc))
4714 return failure();
4715
4716 // If we looked up a normal value (e.g., wire, register, or port), then we
4717 // just need to get any optional trailing expression. Invalidate this.
4718 if (!moduleContext.resolveSymbolEntry(lhs, symtabEntry, loc, false)) {
4719 if (parseOptionalExpPostscript(lhs, /*allowDynamic=*/false) ||
4720 parseOptionalInfo())
4721 return failure();
4722
4723 locationProcessor.setLoc(startTok.getLoc());
4724 emitInvalidate(lhs);
4725 return success();
4726 }
4727
4728 // We're dealing with an instance. This instance may or may not have a
4729 // trailing expression. Handle the special case of no trailing expression
4730 // first by invalidating all of its results.
4731 assert(isa<UnbundledID>(symtabEntry) && "should be an instance");
4732
4733 if (getToken().isNot(FIRToken::period)) {
4734 locationProcessor.setLoc(loc);
4735 // Invalidate all of the results of the bundled value.
4736 unsigned unbundledId = cast<UnbundledID>(symtabEntry) - 1;
4737 UnbundledValueEntry &ubEntry = moduleContext.getUnbundledEntry(unbundledId);
4738 for (auto elt : ubEntry)
4739 emitInvalidate(elt.second);
4740 return success();
4741 }
4742
4743 // Handle the case of an instance with a trailing expression. This must begin
4744 // with a '.' (until we add instance arrays).
4745 StringRef fieldName;
4746 if (parseToken(FIRToken::period, "expected '.' in field reference") ||
4747 parseFieldId(fieldName, "expected field name") ||
4748 moduleContext.resolveSymbolEntry(lhs, symtabEntry, fieldName, loc))
4749 return failure();
4750
4751 // Update with any trailing expression and invalidate it.
4752 if (parseOptionalExpPostscript(lhs, /*allowDynamic=*/false) ||
4753 parseOptionalInfo())
4754 return failure();
4755
4756 locationProcessor.setLoc(startTok.getLoc());
4757 emitInvalidate(lhs);
4758 return success();
4759}
4760
4761ParseResult FIRStmtParser::parseLayerBlockOrGroup(unsigned indent) {
4762
4763 auto startTok = consumeToken();
4764 assert(startTok.isAny(FIRToken::kw_layerblock, FIRToken::kw_group) &&
4765 "consumed an unexpected token");
4766 auto loc = startTok.getLoc();
4767
4768 StringRef id;
4769 if (parseId(id, "expected layer identifer") ||
4770 parseToken(FIRToken::colon, "expected ':' at end of layer block") ||
4771 parseOptionalInfo())
4772 return failure();
4773
4774 locationProcessor.setLoc(loc);
4775
4776 StringRef rootLayer;
4777 SmallVector<FlatSymbolRefAttr> nestedLayers;
4778 if (!layerSym) {
4779 rootLayer = id;
4780 } else {
4781 rootLayer = layerSym.getRootReference();
4782 auto nestedRefs = layerSym.getNestedReferences();
4783 nestedLayers.append(nestedRefs.begin(), nestedRefs.end());
4784 nestedLayers.push_back(FlatSymbolRefAttr::get(builder.getContext(), id));
4785 }
4786
4787 auto layerBlockOp = LayerBlockOp::create(
4788 builder,
4789 SymbolRefAttr::get(builder.getContext(), rootLayer, nestedLayers));
4790 layerBlockOp->getRegion(0).push_back(new Block());
4791
4792 if (getIndentation() > indent)
4793 if (parseSubBlock(layerBlockOp.getRegion().front(), indent,
4794 layerBlockOp.getLayerName()))
4795 return failure();
4796
4797 return success();
4798}
4799
4800/// leading-exp-stmt ::= exp '<=' exp info?
4801/// ::= exp 'is' 'invalid' info?
4802ParseResult FIRStmtParser::parseLeadingExpStmt(Value lhs) {
4803 auto loc = getToken().getLoc();
4804
4805 // If 'is' grammar is special.
4806 if (consumeIf(FIRToken::kw_is)) {
4807 if (parseToken(FIRToken::kw_invalid, "expected 'invalid'") ||
4808 parseOptionalInfo())
4809 return failure();
4810
4811 if (removedFeature({3, 0, 0}, "'is invalid' statements", loc))
4812 return failure();
4813
4814 locationProcessor.setLoc(loc);
4815 emitInvalidate(lhs);
4816 return success();
4817 }
4818
4819 if (parseToken(FIRToken::less_equal, "expected '<=' in statement"))
4820 return failure();
4821
4822 if (removedFeature({3, 0, 0}, "'<=' connections", loc))
4823 return failure();
4824
4825 Value rhs;
4826 if (parseExp(rhs, "unexpected token in statement") || parseOptionalInfo())
4827 return failure();
4828
4829 locationProcessor.setLoc(loc);
4830
4831 auto lhsType = type_dyn_cast<FIRRTLBaseType>(lhs.getType());
4832 auto rhsType = type_dyn_cast<FIRRTLBaseType>(rhs.getType());
4833 if (!lhsType || !rhsType)
4834 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4835 "cannot connect reference or property types");
4836 // TODO: Once support lands for agg-of-ref, add test for this check!
4837 if (lhsType.containsReference() || rhsType.containsReference())
4838 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4839 "cannot connect types containing references");
4840
4841 if (!areTypesEquivalent(lhsType, rhsType))
4842 return mlir::emitError(locationProcessor.getLoc(*this, loc),
4843 "cannot connect non-equivalent type ")
4844 << rhsType << " to " << lhsType;
4846 builder, lhs, rhs, [&] { return locationProcessor.getLoc(*this, loc); },
4847 getConstants().options.warnOnTruncation);
4848 return success();
4849}
4850
4851//===-------------------------------
4852// FIRStmtParser Declaration Parsing
4853
4854/// instance ::= 'inst' id 'of' id info?
4855ParseResult FIRStmtParser::parseInstance() {
4856 auto startTok = consumeToken(FIRToken::kw_inst);
4857
4858 // If this was actually the start of a connect or something else handle
4859 // that.
4860 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
4861 return *isExpr;
4862
4863 StringRef id;
4864 StringRef moduleName;
4865 if (parseId(id, "expected instance name") ||
4866 parseToken(FIRToken::kw_of, "expected 'of' in instance") ||
4867 parseId(moduleName, "expected module name") || parseOptionalInfo())
4868 return failure();
4869
4870 locationProcessor.setLoc(startTok.getLoc());
4871
4872 // Look up the module that is being referenced.
4873 auto referencedModule = getReferencedModule(startTok.getLoc(), moduleName);
4874 if (!referencedModule)
4875 return failure();
4876
4877 SmallVector<PortInfo> modulePorts = referencedModule.getPorts();
4878
4879 auto annotations = getConstants().emptyArrayAttr;
4880 SmallVector<Attribute, 4> portAnnotations(modulePorts.size(), annotations);
4881
4882 hw::InnerSymAttr sym = {};
4883 auto result = InstanceOp::create(
4884 builder, referencedModule, id, NameKindEnum::InterestingName,
4885 annotations.getValue(), portAnnotations, false, false, sym);
4886
4887 // Since we are implicitly unbundling the instance results, we need to keep
4888 // track of the mapping from bundle fields to results in the unbundledValues
4889 // data structure. Build our entry now.
4890 UnbundledValueEntry unbundledValueEntry;
4891 unbundledValueEntry.reserve(modulePorts.size());
4892 for (size_t i = 0, e = modulePorts.size(); i != e; ++i)
4893 unbundledValueEntry.push_back({modulePorts[i].name, result.getResult(i)});
4894
4895 // Add it to unbundledValues and add an entry to the symbol table to remember
4896 // it.
4897 moduleContext.unbundledValues.push_back(std::move(unbundledValueEntry));
4898 auto entryId = UnbundledID(moduleContext.unbundledValues.size());
4899 return moduleContext.addSymbolEntry(id, entryId, startTok.getLoc());
4900}
4901
4902/// instance_choice ::=
4903/// 'inst_choice' id 'of' id id info? newline indent ( id "=>" id )+ dedent
4904ParseResult FIRStmtParser::parseInstanceChoice() {
4905 auto startTok = consumeToken(FIRToken::kw_instchoice);
4906 SMLoc loc = startTok.getLoc();
4907
4908 // If this was actually the start of a connect or something else handle that.
4909 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
4910 return *isExpr;
4911
4912 if (requireFeature(missingSpecFIRVersion, "option groups/instance choices"))
4913 return failure();
4914
4915 StringRef id;
4916 StringRef defaultModuleName;
4917 StringRef optionGroupName;
4918 if (parseId(id, "expected instance name") ||
4919 parseToken(FIRToken::kw_of, "expected 'of' in instance") ||
4920 parseId(defaultModuleName, "expected module name") ||
4921 parseToken(FIRToken::comma, "expected ','") ||
4922 parseId(optionGroupName, "expected option group name") ||
4923 parseToken(FIRToken::colon, "expected ':' after instchoice") ||
4924 parseOptionalInfo())
4925 return failure();
4926
4927 locationProcessor.setLoc(startTok.getLoc());
4928
4929 // Look up the default module referenced by the instance choice.
4930 // The port lists of all the other referenced modules must match this one.
4931 auto defaultModule = getReferencedModule(loc, defaultModuleName);
4932 if (!defaultModule)
4933 return failure();
4934
4935 SmallVector<PortInfo> modulePorts = defaultModule.getPorts();
4936
4937 // Find the option group.
4938 auto optionGroup = circuitSymTbl.lookup<OptionOp>(optionGroupName);
4939 if (!optionGroup)
4940 return emitError(loc,
4941 "use of undefined option group '" + optionGroupName + "'");
4942
4943 auto baseIndent = getIndentation();
4944 SmallVector<std::pair<OptionCaseOp, FModuleLike>> caseModules;
4945 while (getIndentation() == baseIndent) {
4946 StringRef caseId;
4947 StringRef caseModuleName;
4948 if (parseId(caseId, "expected a case identifier") ||
4949 parseToken(FIRToken::equal_greater,
4950 "expected '=> in instance choice definition") ||
4951 parseId(caseModuleName, "expected module name"))
4952 return failure();
4953
4954 auto caseModule = getReferencedModule(loc, caseModuleName);
4955 if (!caseModule)
4956 return failure();
4957
4958 for (const auto &[defaultPort, casePort] :
4959 llvm::zip(modulePorts, caseModule.getPorts())) {
4960 if (defaultPort.name != casePort.name)
4961 return emitError(loc, "instance case module port '")
4962 << casePort.name.getValue()
4963 << "' does not match the default module port '"
4964 << defaultPort.name.getValue() << "'";
4965 if (defaultPort.type != casePort.type)
4966 return emitError(loc, "instance case port '")
4967 << casePort.name.getValue()
4968 << "' type does not match the default module port";
4969 }
4970
4971 auto optionCase =
4972 dyn_cast_or_null<OptionCaseOp>(optionGroup.lookupSymbol(caseId));
4973 if (!optionCase)
4974 return emitError(loc, "use of undefined option case '" + caseId + "'");
4975 caseModules.emplace_back(optionCase, caseModule);
4976 }
4977
4978 auto annotations = getConstants().emptyArrayAttr;
4979 SmallVector<Attribute, 4> portAnnotations(modulePorts.size(), annotations);
4980
4981 // Create an instance choice op.
4982 StringAttr sym;
4983 auto result = InstanceChoiceOp::create(
4984 builder, defaultModule, caseModules, id, NameKindEnum::InterestingName,
4985 annotations.getValue(), portAnnotations, sym);
4986
4987 // Un-bundle the ports, identically to the regular instance operation.
4988 UnbundledValueEntry unbundledValueEntry;
4989 unbundledValueEntry.reserve(modulePorts.size());
4990 for (size_t i = 0, e = modulePorts.size(); i != e; ++i)
4991 unbundledValueEntry.push_back({modulePorts[i].name, result.getResult(i)});
4992
4993 moduleContext.unbundledValues.push_back(std::move(unbundledValueEntry));
4994 auto entryId = UnbundledID(moduleContext.unbundledValues.size());
4995 return moduleContext.addSymbolEntry(id, entryId, startTok.getLoc());
4996}
4997
4998FModuleLike FIRStmtParser::getReferencedModule(SMLoc loc,
4999 StringRef moduleName) {
5000 auto referencedModule = circuitSymTbl.lookup<FModuleLike>(moduleName);
5001 if (!referencedModule) {
5002 emitError(loc,
5003 "use of undefined module name '" + moduleName + "' in instance");
5004 return {};
5005 }
5006 if (isa<ClassOp /* ClassLike */>(referencedModule)) {
5007 emitError(loc, "cannot create instance of class '" + moduleName +
5008 "', did you mean object?");
5009 return {};
5010 }
5011 return referencedModule;
5012}
5013
5014/// object ::= 'object' id 'of' id info?
5015ParseResult FIRStmtParser::parseObject() {
5016 auto startTok = consumeToken(FIRToken::kw_object);
5017
5018 // If this was actually the start of a connect or something else handle
5019 // that.
5020 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5021 return *isExpr;
5022
5023 if (requireFeature({6, 0, 0}, "object statements"))
5024 return failure();
5025
5026 StringRef id;
5027 StringRef className;
5028 if (parseId(id, "expected object name") ||
5029 parseToken(FIRToken::kw_of, "expected 'of' in object") ||
5030 parseId(className, "expected class name") || parseOptionalInfo())
5031 return failure();
5032
5033 locationProcessor.setLoc(startTok.getLoc());
5034
5035 // Look up the class that is being referenced.
5036 const auto &classMap = getConstants().classMap;
5037 auto lookup = classMap.find(className);
5038 if (lookup == classMap.end())
5039 return emitError(startTok.getLoc(), "use of undefined class name '" +
5040 className + "' in object");
5041 auto referencedClass = lookup->getSecond();
5042 auto result = ObjectOp::create(builder, referencedClass, id);
5043 return moduleContext.addSymbolEntry(id, result, startTok.getLoc());
5044}
5045
5046/// cmem ::= 'cmem' id ':' type info?
5047ParseResult FIRStmtParser::parseCombMem() {
5048 // TODO(firrtl spec) cmem is completely undocumented.
5049 auto startTok = consumeToken(FIRToken::kw_cmem);
5050
5051 // If this was actually the start of a connect or something else handle
5052 // that.
5053 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5054 return *isExpr;
5055
5056 StringRef id;
5057 FIRRTLType type;
5058 if (parseId(id, "expected cmem name") ||
5059 parseToken(FIRToken::colon, "expected ':' in cmem") ||
5060 parseType(type, "expected cmem type") || parseOptionalInfo())
5061 return failure();
5062
5063 locationProcessor.setLoc(startTok.getLoc());
5064
5065 // Transform the parsed vector type into a memory type.
5066 auto vectorType = type_dyn_cast<FVectorType>(type);
5067 if (!vectorType)
5068 return emitError("cmem requires vector type");
5069
5070 auto annotations = getConstants().emptyArrayAttr;
5071 StringAttr sym = {};
5072 auto result = CombMemOp::create(
5073 builder, vectorType.getElementType(), vectorType.getNumElements(), id,
5074 NameKindEnum::InterestingName, annotations, sym);
5075 return moduleContext.addSymbolEntry(id, result, startTok.getLoc());
5076}
5077
5078/// smem ::= 'smem' id ':' type ruw? info?
5079ParseResult FIRStmtParser::parseSeqMem() {
5080 // TODO(firrtl spec) smem is completely undocumented.
5081 auto startTok = consumeToken(FIRToken::kw_smem);
5082
5083 // If this was actually the start of a connect or something else handle
5084 // that.
5085 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5086 return *isExpr;
5087
5088 StringRef id;
5089 FIRRTLType type;
5090 RUWBehavior ruw = RUWBehavior::Undefined;
5091
5092 if (parseId(id, "expected smem name") ||
5093 parseToken(FIRToken::colon, "expected ':' in smem") ||
5094 parseType(type, "expected smem type"))
5095 return failure();
5096
5097 if (consumeIf(FIRToken::comma)) {
5098 if (parseRUW(ruw))
5099 return failure();
5100 }
5101
5102 if (parseOptionalInfo()) {
5103 return failure();
5104 }
5105
5106 locationProcessor.setLoc(startTok.getLoc());
5107
5108 // Transform the parsed vector type into a memory type.
5109 auto vectorType = type_dyn_cast<FVectorType>(type);
5110 if (!vectorType)
5111 return emitError("smem requires vector type");
5112
5113 auto annotations = getConstants().emptyArrayAttr;
5114 StringAttr sym = {};
5115 auto result = SeqMemOp::create(
5116 builder, vectorType.getElementType(), vectorType.getNumElements(), ruw,
5117 id, NameKindEnum::InterestingName, annotations, sym);
5118 return moduleContext.addSymbolEntry(id, result, startTok.getLoc());
5119}
5120
5121/// mem ::= 'mem' id ':' info? INDENT memField* DEDENT
5122/// memField ::= 'data-type' '=>' type NEWLINE
5123/// ::= 'depth' '=>' intLit NEWLINE
5124/// ::= 'read-latency' '=>' intLit NEWLINE
5125/// ::= 'write-latency' '=>' intLit NEWLINE
5126/// ::= 'read-under-write' '=>' ruw NEWLINE
5127/// ::= 'reader' '=>' id+ NEWLINE
5128/// ::= 'writer' '=>' id+ NEWLINE
5129/// ::= 'readwriter' '=>' id+ NEWLINE
5130ParseResult FIRStmtParser::parseMem(unsigned memIndent) {
5131 auto startTok = consumeToken(FIRToken::kw_mem);
5132
5133 // If this was actually the start of a connect or something else handle
5134 // that.
5135 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5136 return *isExpr;
5137
5138 StringRef id;
5139 if (parseId(id, "expected mem name") ||
5140 parseToken(FIRToken::colon, "expected ':' in mem") || parseOptionalInfo())
5141 return failure();
5142
5143 FIRRTLType type;
5144 int64_t depth = -1, readLatency = -1, writeLatency = -1;
5145 RUWBehavior ruw = RUWBehavior::Undefined;
5146
5147 SmallVector<std::pair<StringAttr, Type>, 4> ports;
5148
5149 // Parse all the memfield records, which are indented more than the mem.
5150 while (1) {
5151 auto nextIndent = getIndentation();
5152 if (!nextIndent || *nextIndent <= memIndent)
5153 break;
5154
5155 auto spelling = getTokenSpelling();
5156 if (parseToken(FIRToken::identifier, "unexpected token in 'mem'") ||
5157 parseToken(FIRToken::equal_greater, "expected '=>' in 'mem'"))
5158 return failure();
5159
5160 if (spelling == "data-type") {
5161 if (type)
5162 return emitError("'mem' type specified multiple times"), failure();
5163
5164 if (parseType(type, "expected type in data-type declaration"))
5165 return failure();
5166 continue;
5167 }
5168 if (spelling == "depth") {
5169 if (parseIntLit(depth, "expected integer in depth specification"))
5170 return failure();
5171 continue;
5172 }
5173 if (spelling == "read-latency") {
5174 if (parseIntLit(readLatency, "expected integer latency"))
5175 return failure();
5176 continue;
5177 }
5178 if (spelling == "write-latency") {
5179 if (parseIntLit(writeLatency, "expected integer latency"))
5180 return failure();
5181 continue;
5182 }
5183 if (spelling == "read-under-write") {
5184 if (getToken().isNot(FIRToken::kw_old, FIRToken::kw_new,
5185 FIRToken::kw_undefined))
5186 return emitError("expected specifier"), failure();
5187
5188 if (parseOptionalRUW(ruw))
5189 return failure();
5190 continue;
5191 }
5192
5193 MemOp::PortKind portKind;
5194 if (spelling == "reader")
5195 portKind = MemOp::PortKind::Read;
5196 else if (spelling == "writer")
5197 portKind = MemOp::PortKind::Write;
5198 else if (spelling == "readwriter")
5199 portKind = MemOp::PortKind::ReadWrite;
5200 else
5201 return emitError("unexpected field in 'mem' declaration"), failure();
5202
5203 StringRef portName;
5204 if (parseId(portName, "expected port name"))
5205 return failure();
5206 auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
5207 if (!baseType)
5208 return emitError("unexpected type, must be base type");
5209 ports.push_back({builder.getStringAttr(portName),
5210 MemOp::getTypeForPort(depth, baseType, portKind)});
5211
5212 while (!getIndentation().has_value()) {
5213 if (parseId(portName, "expected port name"))
5214 return failure();
5215 ports.push_back({builder.getStringAttr(portName),
5216 MemOp::getTypeForPort(depth, baseType, portKind)});
5217 }
5218 }
5219
5220 // The FIRRTL dialect requires mems to have at least one port. Since portless
5221 // mems can never be referenced, it is always safe to drop them.
5222 if (ports.empty())
5223 return success();
5224
5225 // Canonicalize the ports into alphabetical order.
5226 // TODO: Move this into MemOp construction/canonicalization.
5227 llvm::array_pod_sort(ports.begin(), ports.end(),
5228 [](const std::pair<StringAttr, Type> *lhs,
5229 const std::pair<StringAttr, Type> *rhs) -> int {
5230 return lhs->first.getValue().compare(
5231 rhs->first.getValue());
5232 });
5233
5234 auto annotations = getConstants().emptyArrayAttr;
5235 SmallVector<Attribute, 4> resultNames;
5236 SmallVector<Type, 4> resultTypes;
5237 SmallVector<Attribute, 4> resultAnnotations;
5238 for (auto p : ports) {
5239 resultNames.push_back(p.first);
5240 resultTypes.push_back(p.second);
5241 resultAnnotations.push_back(annotations);
5242 }
5243
5244 locationProcessor.setLoc(startTok.getLoc());
5245
5246 auto result = MemOp::create(
5247 builder, resultTypes, readLatency, writeLatency, depth, ruw,
5248 builder.getArrayAttr(resultNames), id, NameKindEnum::InterestingName,
5249 annotations, builder.getArrayAttr(resultAnnotations), hw::InnerSymAttr(),
5250 MemoryInitAttr(), StringAttr());
5251
5252 UnbundledValueEntry unbundledValueEntry;
5253 unbundledValueEntry.reserve(result.getNumResults());
5254 for (size_t i = 0, e = result.getNumResults(); i != e; ++i)
5255 unbundledValueEntry.push_back({resultNames[i], result.getResult(i)});
5256
5257 moduleContext.unbundledValues.push_back(std::move(unbundledValueEntry));
5258 auto entryID = UnbundledID(moduleContext.unbundledValues.size());
5259 return moduleContext.addSymbolEntry(id, entryID, startTok.getLoc());
5260}
5261
5262/// node ::= 'node' id '=' exp info?
5263ParseResult FIRStmtParser::parseNode() {
5264 auto startTok = consumeToken(FIRToken::kw_node);
5265
5266 // If this was actually the start of a connect or something else handle
5267 // that.
5268 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5269 return *isExpr;
5270
5271 StringRef id;
5272 Value initializer;
5273 if (parseId(id, "expected node name") ||
5274 parseToken(FIRToken::equal, "expected '=' in node") ||
5275 parseExp(initializer, "expected expression for node") ||
5276 parseOptionalInfo())
5277 return failure();
5278
5279 locationProcessor.setLoc(startTok.getLoc());
5280
5281 // Error out in the following conditions:
5282 //
5283 // 1. Node type is Analog (at the top level)
5284 // 2. Node type is not passive under an optional outer flip
5285 // (analog field is okay)
5286 //
5287 // Note: (1) is more restictive than normal NodeOp verification, but
5288 // this is added to align with the SFC. (2) is less restrictive than
5289 // the SFC to accomodate for situations where the node is something
5290 // weird like a module output or an instance input.
5291 auto initializerType = type_cast<FIRRTLType>(initializer.getType());
5292 auto initializerBaseType =
5293 type_dyn_cast<FIRRTLBaseType>(initializer.getType());
5294 if (type_isa<AnalogType>(initializerType) ||
5295 !(initializerBaseType && initializerBaseType.isPassive())) {
5296 emitError(startTok.getLoc())
5297 << "Node cannot be analog and must be passive or passive under a flip "
5298 << initializer.getType();
5299 return failure();
5300 }
5301
5302 auto annotations = getConstants().emptyArrayAttr;
5303 StringAttr sym = {};
5304
5305 auto result = NodeOp::create(builder, initializer, id,
5306 NameKindEnum::InterestingName, annotations, sym);
5307 return moduleContext.addSymbolEntry(id, result.getResult(),
5308 startTok.getLoc());
5309}
5310
5311/// wire ::= 'wire' id ':' type ('domains' '[' domain_list ']')? info?
5312ParseResult FIRStmtParser::parseWire() {
5313 auto startTok = consumeToken(FIRToken::kw_wire);
5314
5315 // If this was actually the start of a connect or something else handle
5316 // that.
5317 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5318 return *isExpr;
5319
5320 StringRef id;
5321 FIRRTLType type;
5322 if (parseId(id, "expected wire name") ||
5323 parseToken(FIRToken::colon, "expected ':' in wire") ||
5324 parseType(type, "expected wire type"))
5325 return failure();
5326
5327 // Parse optional domain associations
5328 SmallVector<Value> domains;
5329 if (consumeIf(FIRToken::kw_domains)) {
5330 if (requireFeature(missingSpecFIRVersion, "domains", startTok.getLoc()))
5331 return failure();
5332
5333 if (parseToken(FIRToken::l_square, "expected '[' after 'domains'"))
5334 return failure();
5335
5336 if (parseListUntil(FIRToken::r_square, [&]() -> ParseResult {
5337 StringRef domainName;
5338 auto domainLoc = getToken().getLoc();
5339 if (parseId(domainName, "expected domain name"))
5340 return failure();
5341
5342 // Look up the domain value in the module context
5343 SymbolValueEntry lookup;
5344 if (moduleContext.lookupSymbolEntry(lookup, domainName, domainLoc))
5345 return failure();
5346
5347 // Resolve the symbol table entry to a Value
5348 Value domainValue;
5349 if (moduleContext.resolveSymbolEntry(domainValue, lookup, domainLoc))
5350 return failure();
5351
5352 if (!isa<DomainType>(domainValue.getType()))
5353 return emitError(domainLoc)
5354 << "'" << domainName << "' is not a domain";
5355
5356 domains.push_back(domainValue);
5357 return success();
5358 }))
5359 return failure();
5360 }
5361
5362 if (parseOptionalInfo())
5363 return failure();
5364
5365 locationProcessor.setLoc(startTok.getLoc());
5366
5367 auto annotations = getConstants().emptyArrayAttr;
5368 StringAttr sym = {};
5369
5370 // Names of only-nonHW should be droppable.
5371 auto namekind = isa<PropertyType, RefType>(type)
5372 ? NameKindEnum::DroppableName
5373 : NameKindEnum::InterestingName;
5374
5375 auto result = WireOp::create(builder, type, id, namekind, annotations, sym,
5376 /*forceable=*/false, domains);
5377 return moduleContext.addSymbolEntry(id, result.getResult(),
5378 startTok.getLoc());
5379}
5380
5381/// register ::= 'reg' id ':' type exp ('with' ':' reset_block)? info?
5382///
5383/// reset_block ::= INDENT simple_reset info? NEWLINE DEDENT
5384/// ::= '(' simple_reset ')'
5385///
5386/// simple_reset ::= simple_reset0
5387/// ::= '(' simple_reset0 ')'
5388///
5389/// simple_reset0: 'reset' '=>' '(' exp exp ')'
5390///
5391ParseResult FIRStmtParser::parseRegister(unsigned regIndent) {
5392 auto startTok = consumeToken(FIRToken::kw_reg);
5393
5394 // If this was actually the start of a connect or something else handle
5395 // that.
5396 if (auto isExpr = parseExpWithLeadingKeyword(startTok))
5397 return *isExpr;
5398
5399 StringRef id;
5400 FIRRTLType type;
5401 Value clock;
5402
5403 // TODO(firrtl spec): info? should come after the clock expression before
5404 // the 'with'.
5405 if (parseId(id, "expected reg name") ||
5406 parseToken(FIRToken::colon, "expected ':' in reg") ||
5407 parseType(type, "expected reg type") ||
5408 parseToken(FIRToken::comma, "expected ','") ||
5409 parseExp(clock, "expected expression for register clock"))
5410 return failure();
5411
5412 if (!type_isa<FIRRTLBaseType>(type))
5413 return emitError(startTok.getLoc(), "register must have base type");
5414
5415 // Parse the 'with' specifier if present.
5416 Value resetSignal, resetValue;
5417 if (consumeIf(FIRToken::kw_with)) {
5418 if (removedFeature({3, 0, 0}, "'reg with' registers"))
5419 return failure();
5420
5421 if (parseToken(FIRToken::colon, "expected ':' in reg"))
5422 return failure();
5423
5424 // TODO(firrtl spec): Simplify the grammar for register reset logic.
5425 // Why allow multiple ambiguous parentheses? Why rely on indentation at
5426 // all?
5427
5428 // This implements what the examples have in practice.
5429 bool hasExtraLParen = consumeIf(FIRToken::l_paren);
5430
5431 auto indent = getIndentation();
5432 if (!indent || *indent <= regIndent)
5433 if (!hasExtraLParen)
5434 return emitError("expected indented reset specifier in reg"), failure();
5435
5436 if (parseToken(FIRToken::kw_reset, "expected 'reset' in reg") ||
5437 parseToken(FIRToken::equal_greater, "expected => in reset specifier") ||
5438 parseToken(FIRToken::l_paren, "expected '(' in reset specifier") ||
5439 parseExp(resetSignal, "expected expression for reset signal") ||
5440 parseToken(FIRToken::comma, "expected ','"))
5441 return failure();
5442
5443 // The Scala implementation of FIRRTL represents registers without resets
5444 // as a self referential register... and the pretty printer doesn't print
5445 // the right form. Recognize that this is happening and treat it as a
5446 // register without a reset for compatibility.
5447 // TODO(firrtl scala impl): pretty print registers without resets right.
5448 if (getTokenSpelling() == id) {
5449 consumeToken();
5450 if (parseToken(FIRToken::r_paren, "expected ')' in reset specifier"))
5451 return failure();
5452 resetSignal = Value();
5453 } else {
5454 if (parseExp(resetValue, "expected expression for reset value") ||
5455 parseToken(FIRToken::r_paren, "expected ')' in reset specifier"))
5456 return failure();
5457 }
5458
5459 if (hasExtraLParen &&
5460 parseToken(FIRToken::r_paren, "expected ')' in reset specifier"))
5461 return failure();
5462 }
5463
5464 // Finally, handle the last info if present, providing location info for the
5465 // clock expression.
5466 if (parseOptionalInfo())
5467 return failure();
5468
5469 locationProcessor.setLoc(startTok.getLoc());
5470
5471 ArrayAttr annotations = getConstants().emptyArrayAttr;
5472 Value result;
5473 StringAttr sym = {};
5474 if (resetSignal)
5475 result =
5476 RegResetOp::create(builder, type, clock, resetSignal, resetValue, id,
5477 NameKindEnum::InterestingName, annotations, sym)
5478 .getResult();
5479 else
5480 result = RegOp::create(builder, type, clock, id,
5481 NameKindEnum::InterestingName, annotations, sym)
5482 .getResult();
5483 return moduleContext.addSymbolEntry(id, result, startTok.getLoc());
5484}
5485
5486/// registerWithReset ::= 'regreset' id ':' type exp exp exp
5487///
5488/// This syntax is only supported in FIRRTL versions >= 3.0.0. Because this
5489/// syntax is only valid for >= 3.0.0, there is no need to check if the leading
5490/// "regreset" is part of an expression with a leading keyword.
5491ParseResult FIRStmtParser::parseRegisterWithReset() {
5492 auto startTok = consumeToken(FIRToken::kw_regreset);
5493
5494 StringRef id;
5495 FIRRTLType type;
5496 Value clock, resetSignal, resetValue;
5497
5498 if (parseId(id, "expected reg name") ||
5499 parseToken(FIRToken::colon, "expected ':' in reg") ||
5500 parseType(type, "expected reg type") ||
5501 parseToken(FIRToken::comma, "expected ','") ||
5502 parseExp(clock, "expected expression for register clock") ||
5503 parseToken(FIRToken::comma, "expected ','") ||
5504 parseExp(resetSignal, "expected expression for register reset") ||
5505 parseToken(FIRToken::comma, "expected ','") ||
5506 parseExp(resetValue, "expected expression for register reset value") ||
5507 parseOptionalInfo())
5508 return failure();
5509
5510 if (!type_isa<FIRRTLBaseType>(type))
5511 return emitError(startTok.getLoc(), "register must have base type");
5512
5513 locationProcessor.setLoc(startTok.getLoc());
5514
5515 auto result =
5516 RegResetOp::create(builder, type, clock, resetSignal, resetValue, id,
5517 NameKindEnum::InterestingName,
5518 getConstants().emptyArrayAttr, StringAttr{})
5519 .getResult();
5520
5521 return moduleContext.addSymbolEntry(id, result, startTok.getLoc());
5522}
5523
5524/// contract ::= 'contract' (id,+ '=' exp,+) ':' info? contract_body
5525/// contract_body ::= simple_stmt | INDENT simple_stmt+ DEDENT
5526ParseResult FIRStmtParser::parseContract(unsigned blockIndent) {
5527 if (requireFeature(missingSpecFIRVersion, "contracts"))
5528 return failure();
5529
5530 auto startTok = consumeToken(FIRToken::kw_contract);
5531
5532 // Parse the contract results and expressions.
5533 SmallVector<StringRef> ids;
5534 SmallVector<SMLoc> locs;
5535 SmallVector<Value> values;
5536 SmallVector<Type> types;
5537 if (!consumeIf(FIRToken::colon)) {
5538 auto parseContractId = [&] {
5539 StringRef id;
5540 locs.push_back(getToken().getLoc());
5541 if (parseId(id, "expected contract result name"))
5542 return failure();
5543 ids.push_back(id);
5544 return success();
5545 };
5546 auto parseContractValue = [&] {
5547 Value value;
5548 if (parseExp(value, "expected expression for contract result"))
5549 return failure();
5550 values.push_back(value);
5551 types.push_back(value.getType());
5552 return success();
5553 };
5554 if (parseListUntil(FIRToken::equal, parseContractId) ||
5555 parseListUntil(FIRToken::colon, parseContractValue))
5556 return failure();
5557 }
5558 if (parseOptionalInfo())
5559 return failure();
5560
5561 // Each result must have a corresponding expression assigned.
5562 if (ids.size() != values.size())
5563 return emitError(startTok.getLoc())
5564 << "contract requires same number of results and expressions; got "
5565 << ids.size() << " results and " << values.size()
5566 << " expressions instead";
5567
5568 locationProcessor.setLoc(startTok.getLoc());
5569
5570 // Add block arguments for each result and declare their names in a subscope
5571 // for the contract body.
5572 auto contract = ContractOp::create(builder, types, values);
5573 auto &block = contract.getBody().emplaceBlock();
5574
5575 // Parse the contract body.
5576 {
5577 FIRModuleContext::ContextScope scope(moduleContext, &block);
5578 for (auto [id, loc, type] : llvm::zip(ids, locs, types)) {
5579 auto arg = block.addArgument(type, LocWithInfo(loc, this).getLoc());
5580 if (failed(moduleContext.addSymbolEntry(id, arg, loc)))
5581 return failure();
5582 }
5583 if (getIndentation() > blockIndent)
5584 if (parseSubBlock(block, blockIndent, SymbolRefAttr{}))
5585 return failure();
5586 }
5587
5588 // Declare the results.
5589 for (auto [id, loc, value, result] :
5590 llvm::zip(ids, locs, values, contract.getResults())) {
5591 // Remove previous symbol to avoid duplicates
5592 moduleContext.removeSymbolEntry(id);
5593 if (failed(moduleContext.addSymbolEntry(id, result, loc)))
5594 return failure();
5595 }
5596 return success();
5597}
5598
5599//===----------------------------------------------------------------------===//
5600// FIRCircuitParser
5601//===----------------------------------------------------------------------===//
5602
5603namespace {
5604/// This class implements the outer level of the parser, including things
5605/// like circuit and module.
5606struct FIRCircuitParser : public FIRParser {
5607 explicit FIRCircuitParser(SharedParserConstants &state, FIRLexer &lexer,
5608 ModuleOp mlirModule, FIRVersion version)
5609 : FIRParser(state, lexer, version), mlirModule(mlirModule) {}
5610
5611 ParseResult
5612 parseCircuit(SmallVectorImpl<const llvm::MemoryBuffer *> &annotationsBuf,
5613 mlir::TimingScope &ts);
5614
5615private:
5616 /// Extract Annotations from a JSON-encoded Annotation array string and add
5617 /// them to a vector of attributes.
5618 ParseResult importAnnotationsRaw(SMLoc loc, StringRef annotationsStr,
5619 SmallVectorImpl<Attribute> &attrs);
5620
5621 ParseResult parseToplevelDefinition(CircuitOp circuit, unsigned indent);
5622
5623 ParseResult parseClass(CircuitOp circuit, unsigned indent);
5624 ParseResult parseDomain(CircuitOp circuit, unsigned indent);
5625 ParseResult parseExtClass(CircuitOp circuit, unsigned indent);
5626 ParseResult parseExtModule(CircuitOp circuit, unsigned indent);
5627 ParseResult parseIntModule(CircuitOp circuit, unsigned indent);
5628 ParseResult parseModule(CircuitOp circuit, bool isPublic, unsigned indent);
5629 ParseResult parseFormal(CircuitOp circuit, unsigned indent);
5630 ParseResult parseSimulation(CircuitOp circuit, unsigned indent);
5631 template <class Op>
5632 ParseResult parseFormalLike(CircuitOp circuit, unsigned indent);
5633
5634 ParseResult parseLayerName(SymbolRefAttr &result);
5635 ParseResult parseLayerList(SmallVectorImpl<Attribute> &result);
5636 ParseResult parseEnableLayerSpec(SmallVectorImpl<Attribute> &result);
5637 ParseResult parseKnownLayerSpec(SmallVectorImpl<Attribute> &result);
5638 ParseResult parseRequiresSpec(SmallVectorImpl<Attribute> &result);
5639 ParseResult parseModuleLayerSpec(ArrayAttr &enabledLayers);
5640 ParseResult parseExtModuleAttributesSpec(ArrayAttr &enabledLayers,
5641 ArrayAttr &knownLayers,
5642 ArrayAttr &externalRequirements);
5643
5644 ParseResult parsePortList(SmallVectorImpl<PortInfo> &resultPorts,
5645 SmallVectorImpl<SMLoc> &resultPortLocs,
5646 unsigned indent);
5647 ParseResult parseParameterList(ArrayAttr &resultParameters);
5648
5649 ParseResult skipToModuleEnd(unsigned indent);
5650
5651 ParseResult parseTypeDecl();
5652
5653 ParseResult parseOptionDecl(CircuitOp circuit);
5654
5655 ParseResult parseLayer(CircuitOp circuit);
5656
5657 ParseResult resolveDomains(
5658 const SmallVectorImpl<std::pair<Attribute, llvm::SMLoc>> &domainsByName,
5659 const DenseMap<Attribute, size_t> &nameToIndex,
5660 SmallVectorImpl<Attribute> &domainsByIndex);
5661
5662 ParseResult
5663 parseDomains(SmallVectorImpl<std::pair<Attribute, llvm::SMLoc>> &domains,
5664 const DenseMap<Attribute, size_t> &nameToIndex);
5665
5666 struct DeferredModuleToParse {
5667 FModuleLike moduleOp;
5668 SmallVector<SMLoc> portLocs;
5669 FIRLexerCursor lexerCursor;
5670 unsigned indent;
5671 };
5672
5673 ParseResult parseModuleBody(const SymbolTable &circuitSymTbl,
5674 DeferredModuleToParse &deferredModule,
5675 InnerSymFixups &fixups);
5676
5677 SmallVector<DeferredModuleToParse, 0> deferredModules;
5678
5679 SmallVector<InnerSymFixups, 0> moduleFixups;
5680
5681 hw::InnerSymbolNamespaceCollection innerSymbolNamespaces;
5682
5683 ModuleOp mlirModule;
5684};
5685
5686} // end anonymous namespace
5687ParseResult
5688FIRCircuitParser::importAnnotationsRaw(SMLoc loc, StringRef annotationsStr,
5689 SmallVectorImpl<Attribute> &attrs) {
5690
5691 auto annotations = json::parse(annotationsStr);
5692 if (auto err = annotations.takeError()) {
5693 handleAllErrors(std::move(err), [&](const json::ParseError &a) {
5694 auto diag = emitError(loc, "Failed to parse JSON Annotations");
5695 diag.attachNote() << a.message();
5696 });
5697 return failure();
5698 }
5699
5700 json::Path::Root root;
5701 llvm::StringMap<ArrayAttr> thisAnnotationMap;
5702 if (!importAnnotationsFromJSONRaw(annotations.get(), attrs, root,
5703 getContext())) {
5704 auto diag = emitError(loc, "Invalid/unsupported annotation format");
5705 std::string jsonErrorMessage =
5706 "See inline comments for problem area in JSON:\n";
5707 llvm::raw_string_ostream s(jsonErrorMessage);
5708 root.printErrorContext(annotations.get(), s);
5709 diag.attachNote() << jsonErrorMessage;
5710 return failure();
5711 }
5712
5713 return success();
5714}
5715
5716ParseResult FIRCircuitParser::parseLayerName(SymbolRefAttr &result) {
5717 auto *context = getContext();
5718 SmallVector<StringRef> strings;
5719 do {
5720 StringRef name;
5721 if (parseId(name, "expected layer name"))
5722 return failure();
5723 strings.push_back(name);
5724 } while (consumeIf(FIRToken::period));
5725
5726 SmallVector<FlatSymbolRefAttr> nested;
5727 nested.reserve(strings.size() - 1);
5728 for (unsigned i = 1, e = strings.size(); i < e; ++i)
5729 nested.push_back(FlatSymbolRefAttr::get(context, strings[i]));
5730
5731 result = SymbolRefAttr::get(context, strings[0], nested);
5732 return success();
5733}
5734
5735ParseResult FIRCircuitParser::parseModuleLayerSpec(ArrayAttr &enabledLayers) {
5736 SmallVector<Attribute> enabledLayersBuffer;
5737 while (true) {
5738 auto tokenKind = getToken().getKind();
5739 // Parse an enablelayer spec.
5740 if (tokenKind == FIRToken::kw_enablelayer) {
5741 if (parseEnableLayerSpec(enabledLayersBuffer))
5742 return failure();
5743 continue;
5744 }
5745 // Didn't parse a layer spec.
5746 break;
5747 }
5748
5749 if (enabledLayersBuffer.size() != 0)
5750 if (requireFeature({4, 0, 0}, "modules with layers enabled"))
5751 return failure();
5752
5753 enabledLayers = ArrayAttr::get(getContext(), enabledLayersBuffer);
5754 return success();
5755}
5756
5757ParseResult FIRCircuitParser::parseExtModuleAttributesSpec(
5758 ArrayAttr &enabledLayers, ArrayAttr &knownLayers,
5759 ArrayAttr &externalRequirements) {
5760 SmallVector<Attribute> enabledLayersBuffer;
5761 SmallVector<Attribute> knownLayersBuffer;
5762 SmallVector<Attribute> requirementsBuffer;
5763 while (true) {
5764 auto tokenKind = getToken().getKind();
5765 // Parse an enablelayer spec.
5766 if (tokenKind == FIRToken::kw_enablelayer) {
5767 if (parseEnableLayerSpec(enabledLayersBuffer))
5768 return failure();
5769 continue;
5770 }
5771 // Parse a knownlayer spec.
5772 if (tokenKind == FIRToken::kw_knownlayer) {
5773 if (parseKnownLayerSpec(knownLayersBuffer))
5774 return failure();
5775 continue;
5776 }
5777 // Parse a requires spec.
5778 if (tokenKind == FIRToken::kw_requires) {
5779 if (parseRequiresSpec(requirementsBuffer))
5780 return failure();
5781 continue;
5782 }
5783 // Didn't parse a layer spec or requires.
5784 break;
5785 }
5786
5787 if (enabledLayersBuffer.size() != 0)
5788 if (requireFeature({4, 0, 0}, "extmodules with layers enabled"))
5789 return failure();
5790
5791 if (knownLayersBuffer.size() != 0)
5792 if (requireFeature({6, 0, 0}, "extmodules with known layers"))
5793 return failure();
5794
5795 enabledLayers = ArrayAttr::get(getContext(), enabledLayersBuffer);
5796 knownLayers = ArrayAttr::get(getContext(), knownLayersBuffer);
5797 externalRequirements = ArrayAttr::get(getContext(), requirementsBuffer);
5798 return success();
5799}
5800
5801ParseResult
5802FIRCircuitParser::parseLayerList(SmallVectorImpl<Attribute> &result) {
5803 do {
5804 SymbolRefAttr layer;
5805 if (parseLayerName(layer))
5806 return failure();
5807 result.push_back(layer);
5808 } while (consumeIf(FIRToken::comma));
5809 return success();
5810}
5811
5812ParseResult
5813FIRCircuitParser::parseEnableLayerSpec(SmallVectorImpl<Attribute> &result) {
5814 consumeToken(FIRToken::kw_enablelayer);
5815 return parseLayerList(result);
5816}
5817
5818ParseResult
5819FIRCircuitParser::parseKnownLayerSpec(SmallVectorImpl<Attribute> &result) {
5820 consumeToken(FIRToken::kw_knownlayer);
5821 return parseLayerList(result);
5822}
5823
5824ParseResult
5825FIRCircuitParser::parseRequiresSpec(SmallVectorImpl<Attribute> &result) {
5826 consumeToken(FIRToken::kw_requires);
5827 do {
5828 StringRef requireStr;
5829 if (parseGetSpelling(requireStr) ||
5830 parseToken(FIRToken::string, "expected string after 'requires'"))
5831 return failure();
5832 // Remove the surrounding quotes from the string.
5833 result.push_back(
5834 StringAttr::get(getContext(), requireStr.drop_front().drop_back()));
5835 } while (consumeIf(FIRToken::comma));
5836 return success();
5837}
5838
5839/// portlist ::= port*
5840/// port ::= dir id ':' type info? NEWLINE
5841/// dir ::= 'input' | 'output'
5842ParseResult
5843FIRCircuitParser::parsePortList(SmallVectorImpl<PortInfo> &resultPorts,
5844 SmallVectorImpl<SMLoc> &resultPortLocs,
5845 unsigned indent) {
5846 // Stores of information about domains as they are parsed:
5847 // 1. Mapping of domain name to port index
5848 // 2. Mapping of port index to domain associations, using domain _names_.
5849 // The locations are recorded to generate good error messages.
5850 DenseMap<Attribute, size_t> nameToIndex;
5851 DenseMap<size_t, SmallVector<std::pair<Attribute, SMLoc>>> domainNames;
5852
5853 // Parse any ports. Populate domain information.
5854 while (getToken().isAny(FIRToken::kw_input, FIRToken::kw_output) &&
5855 // Must be nested under the module.
5856 getIndentation() > indent) {
5857
5858 // We need one token lookahead to resolve the ambiguity between:
5859 // output foo ; port
5860 // output <= input ; identifier expression
5861 // output.thing <= input ; identifier expression
5862 auto backtrackState = getLexer().getCursor();
5863
5864 bool isOutput = getToken().is(FIRToken::kw_output);
5865 consumeToken();
5866
5867 // If we have something that isn't a keyword then this must be an
5868 // identifier, not an input/output marker.
5869 if (!getToken().isAny(FIRToken::identifier, FIRToken::literal_identifier) &&
5870 !getToken().isKeyword()) {
5871 backtrackState.restore(getLexer());
5872 break;
5873 }
5874
5875 StringAttr name;
5876 FIRRTLType type;
5877 LocWithInfo info(getToken().getLoc(), this);
5878 if (parseId(name, "expected port name") ||
5879 parseToken(FIRToken::colon, "expected ':' in port definition") ||
5880 parseType(type, "expected a type in port declaration"))
5881 return failure();
5882 Attribute domainInfoElement = {};
5883 size_t portIdx = resultPorts.size();
5884 if (auto domainType = dyn_cast<DomainType>(type)) {
5885 // Domain information is now stored in the type itself.
5886 // Domain type ports have no associations.
5887 domainInfoElement = ArrayAttr::get(getContext(), {});
5888 } else {
5889 if (getToken().is(FIRToken::kw_domains))
5890 if (parseDomains(domainNames[portIdx], nameToIndex))
5891 return failure();
5892 }
5893
5894 if (info.parseOptionalInfo())
5895 return failure();
5896
5897 StringAttr innerSym = {};
5898 resultPorts.push_back(PortInfo{name,
5899 type,
5900 direction::get(isOutput),
5901 innerSym,
5902 info.getLoc(),
5903 {},
5904 domainInfoElement});
5905 resultPortLocs.push_back(info.getFIRLoc());
5906 nameToIndex.insert({name, portIdx});
5907 }
5908
5909 // Apply domain assocations to ports.
5910 for (size_t portIdx = 0, e = resultPorts.size(); portIdx != e; ++portIdx) {
5911 auto &port = resultPorts[portIdx];
5912 Attribute &attr = port.domains;
5913 if (attr)
5914 continue;
5915
5916 SmallVector<Attribute> domainInfo;
5917 if (failed(resolveDomains(domainNames[portIdx], nameToIndex, domainInfo)))
5918 return failure();
5919 attr = ArrayAttr::get(getContext(), domainInfo);
5920 }
5921
5922 // Check for port name collisions.
5924 for (auto portAndLoc : llvm::zip(resultPorts, resultPortLocs)) {
5925 PortInfo &port = std::get<0>(portAndLoc);
5926 auto &entry = portIds[port.name];
5927 if (!entry.isValid()) {
5928 entry = std::get<1>(portAndLoc);
5929 continue;
5930 }
5931
5932 emitError(std::get<1>(portAndLoc),
5933 "redefinition of name '" + port.getName() + "'")
5934 .attachNote(translateLocation(entry))
5935 << "previous definition here";
5936 return failure();
5937 }
5938
5939 return success();
5940}
5941
5942/// We're going to defer parsing this module, so just skip tokens until we
5943/// get to the next module or the end of the file.
5944ParseResult FIRCircuitParser::skipToModuleEnd(unsigned indent) {
5945 while (true) {
5946 switch (getToken().getKind()) {
5947
5948 // End of file or invalid token will be handled by outer level.
5949 case FIRToken::eof:
5950 case FIRToken::error:
5951 return success();
5952
5953 // If we got to the next top-level declaration, then we're done.
5954 case FIRToken::kw_class:
5955 case FIRToken::kw_domain:
5956 case FIRToken::kw_declgroup:
5957 case FIRToken::kw_extclass:
5958 case FIRToken::kw_extmodule:
5959 case FIRToken::kw_intmodule:
5960 case FIRToken::kw_formal:
5961 case FIRToken::kw_module:
5962 case FIRToken::kw_public:
5963 case FIRToken::kw_layer:
5964 case FIRToken::kw_option:
5965 case FIRToken::kw_simulation:
5966 case FIRToken::kw_type:
5967 // All module declarations should have the same indentation
5968 // level. Use this fact to differentiate between module
5969 // declarations and usages of "module" as identifiers.
5970 if (getIndentation() == indent)
5971 return success();
5972 [[fallthrough]];
5973 default:
5974 consumeToken();
5975 break;
5976 }
5977 }
5978}
5979
5980/// parameter-list ::= parameter*
5981/// parameter ::= 'parameter' param NEWLINE
5982ParseResult FIRCircuitParser::parseParameterList(ArrayAttr &resultParameters) {
5983 SmallVector<Attribute, 8> parameters;
5984 SmallPtrSet<StringAttr, 8> seen;
5985 while (consumeIf(FIRToken::kw_parameter)) {
5986 StringAttr name;
5987 Attribute value;
5988 SMLoc loc;
5989 if (parseParameter(name, value, loc))
5990 return failure();
5991 auto typedValue = dyn_cast<TypedAttr>(value);
5992 if (!typedValue)
5993 return emitError(loc)
5994 << "invalid value for parameter '" << name.getValue() << "'";
5995 if (!seen.insert(name).second)
5996 return emitError(loc,
5997 "redefinition of parameter '" + name.getValue() + "'");
5998 parameters.push_back(ParamDeclAttr::get(name, typedValue));
5999 }
6000 resultParameters = ArrayAttr::get(getContext(), parameters);
6001 return success();
6002}
6003
6004/// class ::= 'class' id ':' info? INDENT portlist simple_stmt_block DEDENT
6005ParseResult FIRCircuitParser::parseClass(CircuitOp circuit, unsigned indent) {
6006 StringAttr name;
6007 SmallVector<PortInfo, 8> portList;
6008 SmallVector<SMLoc> portLocs;
6009 LocWithInfo info(getToken().getLoc(), this);
6010
6011 if (requireFeature({6, 0, 0}, "classes"))
6012 return failure();
6013
6014 consumeToken(FIRToken::kw_class);
6015 if (parseId(name, "expected class name") ||
6016 parseToken(FIRToken::colon, "expected ':' in class definition") ||
6017 info.parseOptionalInfo() || parsePortList(portList, portLocs, indent))
6018 return failure();
6019
6020 if (name == circuit.getName())
6021 return mlir::emitError(info.getLoc(),
6022 "class cannot be the top of a circuit");
6023
6024 for (auto &portInfo : portList)
6025 if (!isa<PropertyType>(portInfo.type))
6026 return mlir::emitError(portInfo.loc,
6027 "ports on classes must be properties");
6028
6029 // build it
6030 auto builder = circuit.getBodyBuilder();
6031 auto classOp = ClassOp::create(builder, info.getLoc(), name, portList);
6032 classOp.setPrivate();
6033 deferredModules.emplace_back(
6034 DeferredModuleToParse{classOp, portLocs, getLexer().getCursor(), indent});
6035
6036 // Stash the class name -> op in the constants, so we can resolve Inst types.
6037 getConstants().classMap[name.getValue()] = classOp;
6038 return skipToModuleEnd(indent);
6039}
6040
6041/// domain ::= 'domain' id ':' info?
6042ParseResult FIRCircuitParser::parseDomain(CircuitOp circuit, unsigned indent) {
6043 consumeToken(FIRToken::kw_domain);
6044
6045 StringAttr name;
6046 LocWithInfo info(getToken().getLoc(), this);
6047 if (parseId(name, "domain name") ||
6048 parseToken(FIRToken::colon, "expected ':' after domain definition") ||
6049 info.parseOptionalInfo())
6050 return failure();
6051
6052 SmallVector<Attribute> fields;
6053 while (true) {
6054 auto nextIndent = getIndentation();
6055 if (!nextIndent || *nextIndent <= indent)
6056 break;
6057
6058 StringAttr fieldName;
6059 PropertyType type;
6060 if (parseId(fieldName, "field name") ||
6061 parseToken(FIRToken::colon, "expected ':' after field name") ||
6062 parsePropertyType(type, "field type") || info.parseOptionalInfo())
6063 return failure();
6064
6065 fields.push_back(
6066 DomainFieldAttr::get(circuit.getContext(), fieldName, type));
6067 }
6068
6069 auto builder = circuit.getBodyBuilder();
6070 auto domainOp =
6071 DomainOp::create(builder, info.getLoc(), name, /*sym_visibility=*/{},
6072 builder.getArrayAttr(fields));
6073
6074 // Stash the domain name -> op in the constants, so we can resolve Domain
6075 // types.
6076 getConstants().domainMap[name.getValue()] = domainOp;
6077
6078 return success();
6079}
6080
6081/// extclass ::= 'extclass' id ':' info? INDENT portlist DEDENT
6082ParseResult FIRCircuitParser::parseExtClass(CircuitOp circuit,
6083 unsigned indent) {
6084 StringAttr name;
6085 SmallVector<PortInfo, 8> portList;
6086 SmallVector<SMLoc> portLocs;
6087 LocWithInfo info(getToken().getLoc(), this);
6088
6089 if (requireFeature({6, 0, 0}, "classes"))
6090 return failure();
6091
6092 consumeToken(FIRToken::kw_extclass);
6093 if (parseId(name, "expected extclass name") ||
6094 parseToken(FIRToken::colon, "expected ':' in extclass definition") ||
6095 info.parseOptionalInfo() || parsePortList(portList, portLocs, indent))
6096 return failure();
6097
6098 if (name == circuit.getName())
6099 return mlir::emitError(info.getLoc(),
6100 "extclass cannot be the top of a circuit");
6101
6102 for (auto &portInfo : portList)
6103 if (!isa<PropertyType>(portInfo.type))
6104 return mlir::emitError(portInfo.loc,
6105 "ports on extclasses must be properties");
6106
6107 // Build it
6108 auto builder = circuit.getBodyBuilder();
6109 auto extClassOp = ExtClassOp::create(builder, info.getLoc(), name, portList);
6110
6111 // Stash the class name -> op in the constants, so we can resolve Inst types.
6112 getConstants().classMap[name.getValue()] = extClassOp;
6113 return skipToModuleEnd(indent);
6114}
6115
6116/// extmodule ::=
6117/// 'extmodule' id requires? ':' info?
6118/// INDENT portlist defname? parameter-list DEDENT
6119/// defname ::= 'defname' '=' id NEWLINE
6120/// requires ::= 'requires' string (',' string)*
6121ParseResult FIRCircuitParser::parseExtModule(CircuitOp circuit,
6122 unsigned indent) {
6123 StringAttr name;
6124 ArrayAttr enabledLayers;
6125 ArrayAttr knownLayers;
6126 ArrayAttr externalRequirements;
6127 SmallVector<PortInfo, 8> portList;
6128 SmallVector<SMLoc> portLocs;
6129 LocWithInfo info(getToken().getLoc(), this);
6130 consumeToken(FIRToken::kw_extmodule);
6131 if (parseId(name, "expected extmodule name") ||
6132 parseExtModuleAttributesSpec(enabledLayers, knownLayers,
6133 externalRequirements) ||
6134 parseToken(FIRToken::colon, "expected ':' in extmodule definition") ||
6135 info.parseOptionalInfo() || parsePortList(portList, portLocs, indent))
6136 return failure();
6137
6138 StringRef defName;
6139 if (consumeIf(FIRToken::kw_defname)) {
6140 if (parseToken(FIRToken::equal, "expected '=' in defname") ||
6141 parseId(defName, "expected defname name"))
6142 return failure();
6143 }
6144
6145 ArrayAttr parameters;
6146 if (parseParameterList(parameters))
6147 return failure();
6148
6149 if (version >= FIRVersion({4, 0, 0})) {
6150 for (auto [pi, loc] : llvm::zip_equal(portList, portLocs)) {
6151 if (auto ftype = type_dyn_cast<FIRRTLType>(pi.type)) {
6152 if (ftype.hasUninferredWidth())
6153 return emitError(loc, "extmodule port must have known width");
6154 }
6155 }
6156 }
6157
6158 auto builder = circuit.getBodyBuilder();
6159 auto isMainModule = (name == circuit.getName());
6160 auto convention =
6161 (isMainModule && getConstants().options.scalarizePublicModules) ||
6162 getConstants().options.scalarizeExtModules
6163 ? Convention::Scalarized
6164 : Convention::Internal;
6165 auto conventionAttr = ConventionAttr::get(getContext(), convention);
6166 auto annotations = ArrayAttr::get(getContext(), {});
6167 auto extModuleOp = FExtModuleOp::create(
6168 builder, info.getLoc(), name, conventionAttr, portList, knownLayers,
6169 defName, annotations, parameters, enabledLayers, externalRequirements);
6170 auto visibility = isMainModule ? SymbolTable::Visibility::Public
6171 : SymbolTable::Visibility::Private;
6172 SymbolTable::setSymbolVisibility(extModuleOp, visibility);
6173 return success();
6174}
6175
6176/// intmodule ::=
6177/// 'intmodule' id ':' info?
6178/// INDENT portlist intname parameter-list ref-list DEDENT
6179/// intname ::= 'intrinsic' '=' id NEWLINE
6180ParseResult FIRCircuitParser::parseIntModule(CircuitOp circuit,
6181 unsigned indent) {
6182 StringAttr name;
6183 StringRef intName;
6184 ArrayAttr enabledLayers;
6185 SmallVector<PortInfo, 8> portList;
6186 SmallVector<SMLoc> portLocs;
6187 LocWithInfo info(getToken().getLoc(), this);
6188 consumeToken(FIRToken::kw_intmodule);
6189 if (parseId(name, "expected intmodule name") ||
6190 parseModuleLayerSpec(enabledLayers) ||
6191 parseToken(FIRToken::colon, "expected ':' in intmodule definition") ||
6192 info.parseOptionalInfo() || parsePortList(portList, portLocs, indent) ||
6193 parseToken(FIRToken::kw_intrinsic, "expected 'intrinsic'") ||
6194 parseToken(FIRToken::equal, "expected '=' in intrinsic") ||
6195 parseId(intName, "expected intrinsic name"))
6196 return failure();
6197
6198 ArrayAttr parameters;
6199 if (parseParameterList(parameters))
6200 return failure();
6201
6202 ArrayAttr annotations = getConstants().emptyArrayAttr;
6203 auto builder = circuit.getBodyBuilder();
6204 FIntModuleOp::create(builder, info.getLoc(), name, portList, intName,
6205 annotations, parameters, enabledLayers)
6206 .setPrivate();
6207 return success();
6208}
6209
6210/// module ::= 'module' id ':' info? INDENT portlist simple_stmt_block DEDENT
6211ParseResult FIRCircuitParser::parseModule(CircuitOp circuit, bool isPublic,
6212 unsigned indent) {
6213 StringAttr name;
6214 SmallVector<PortInfo, 8> portList;
6215 SmallVector<SMLoc> portLocs;
6216 ArrayAttr enabledLayers;
6217 auto modLoc = getToken().getLoc();
6218 LocWithInfo info(modLoc, this);
6219 consumeToken(FIRToken::kw_module);
6220 if (parseId(name, "expected module name") ||
6221 parseModuleLayerSpec(enabledLayers) ||
6222 parseToken(FIRToken::colon, "expected ':' in module definition") ||
6223 info.parseOptionalInfo() || parsePortList(portList, portLocs, indent))
6224 return failure();
6225
6226 // The main module is implicitly public.
6227 if (name == circuit.getName()) {
6228 if (!isPublic && removedFeature({4, 0, 0}, "private main modules", modLoc))
6229 return failure();
6230 isPublic = true;
6231 }
6232
6233 if (isPublic && version >= FIRVersion({4, 0, 0})) {
6234 for (auto [pi, loc] : llvm::zip_equal(portList, portLocs)) {
6235 if (auto ftype = type_dyn_cast<FIRRTLType>(pi.type)) {
6236 if (ftype.hasUninferredWidth())
6237 return emitError(loc, "public module port must have known width");
6238 if (ftype.hasUninferredReset())
6239 return emitError(loc,
6240 "public module port must have concrete reset type");
6241 }
6242 }
6243 }
6244
6245 ArrayAttr annotations = getConstants().emptyArrayAttr;
6246 auto convention = Convention::Internal;
6247 if (isPublic && getConstants().options.scalarizePublicModules)
6248 convention = Convention::Scalarized;
6249 if (!isPublic && getConstants().options.scalarizeInternalModules)
6250 convention = Convention::Scalarized;
6251 auto conventionAttr = ConventionAttr::get(getContext(), convention);
6252 auto builder = circuit.getBodyBuilder();
6253 auto moduleOp =
6254 FModuleOp::create(builder, info.getLoc(), name, conventionAttr, portList,
6255 annotations, enabledLayers);
6256
6257 auto visibility = isPublic ? SymbolTable::Visibility::Public
6258 : SymbolTable::Visibility::Private;
6259 SymbolTable::setSymbolVisibility(moduleOp, visibility);
6260
6261 // Parse the body of this module after all prototypes have been parsed. This
6262 // allows us to handle forward references correctly.
6263 deferredModules.emplace_back(DeferredModuleToParse{
6264 moduleOp, portLocs, getLexer().getCursor(), indent});
6265
6266 if (skipToModuleEnd(indent))
6267 return failure();
6268 return success();
6269}
6270
6271/// formal ::= 'formal' formal-like
6272ParseResult FIRCircuitParser::parseFormal(CircuitOp circuit, unsigned indent) {
6273 consumeToken(FIRToken::kw_formal);
6274 return parseFormalLike<FormalOp>(circuit, indent);
6275}
6276
6277/// simulation ::= 'simulation' formal-like
6278ParseResult FIRCircuitParser::parseSimulation(CircuitOp circuit,
6279 unsigned indent) {
6280 consumeToken(FIRToken::kw_simulation);
6281 return parseFormalLike<SimulationOp>(circuit, indent);
6282}
6283
6284/// formal-like ::= formal-like-old | formal-like-new
6285/// formal-like-old ::= id 'of' id ',' 'bound' '=' int info?
6286/// formal-like-new ::= id 'of' id ':' info? INDENT (param NEWLINE)* DEDENT
6287template <class Op>
6288ParseResult FIRCircuitParser::parseFormalLike(CircuitOp circuit,
6289 unsigned indent) {
6290 StringRef id, moduleName;
6291 int64_t bound = 0;
6292 LocWithInfo info(getToken().getLoc(), this);
6293 auto builder = circuit.getBodyBuilder();
6294
6295 // Parse the name and target module of the test.
6296 if (parseId(id, "expected test name") ||
6297 parseToken(FIRToken::kw_of, "expected 'of' in test") ||
6298 parseId(moduleName, "expected module name"))
6299 return failure();
6300
6301 // TODO: Remove the old `, bound = N` variant in favor of the new parameters.
6302 NamedAttrList params;
6303 if (consumeIf(FIRToken::comma)) {
6304 // Parse the old style declaration with a `, bound = N` suffix.
6305 if (getToken().isNot(FIRToken::identifier) || getTokenSpelling() != "bound")
6306 return emitError("expected 'bound' after ','");
6307 consumeToken();
6308 if (parseToken(FIRToken::equal, "expected '=' after 'bound'") ||
6309 parseIntLit(bound, "expected integer bound after '='"))
6310 return failure();
6311 if (bound <= 0)
6312 return emitError("bound must be a positive integer");
6313 if (info.parseOptionalInfo())
6314 return failure();
6315 params.set("bound", builder.getIntegerAttr(builder.getI32Type(), bound));
6316 } else {
6317 // Parse the new style declaration with a `:` and parameter list.
6318 if (parseToken(FIRToken::colon, "expected ':' in test") ||
6319 info.parseOptionalInfo())
6320 return failure();
6321 while (getIndentation() > indent) {
6322 StringAttr paramName;
6323 Attribute paramValue;
6324 SMLoc paramLoc;
6325 if (parseParameter(paramName, paramValue, paramLoc,
6326 /*allowAggregates=*/true))
6327 return failure();
6328 if (params.set(paramName, paramValue))
6329 return emitError(paramLoc, "redefinition of parameter '" +
6330 paramName.getValue() + "'");
6331 }
6332 }
6333
6334 Op::create(builder, info.getLoc(), id, /*sym_visibility=*/{}, moduleName,
6335 params.getDictionary(getContext()));
6336 return success();
6337}
6338
6339ParseResult FIRCircuitParser::parseToplevelDefinition(CircuitOp circuit,
6340 unsigned indent) {
6341 switch (getToken().getKind()) {
6342 case FIRToken::kw_class:
6343 return parseClass(circuit, indent);
6344 case FIRToken::kw_declgroup:
6345 if (requireFeature({3, 2, 0}, "optional groups") ||
6346 removedFeature({3, 3, 0}, "optional groups"))
6347 return failure();
6348 return parseLayer(circuit);
6349 case FIRToken::kw_domain:
6350 if (requireFeature(missingSpecFIRVersion, "domains"))
6351 return failure();
6352 return parseDomain(circuit, indent);
6353 case FIRToken::kw_extclass:
6354 return parseExtClass(circuit, indent);
6355 case FIRToken::kw_extmodule:
6356 return parseExtModule(circuit, indent);
6357 case FIRToken::kw_formal:
6358 if (requireFeature({4, 0, 0}, "formal tests"))
6359 return failure();
6360 return parseFormal(circuit, indent);
6361 case FIRToken::kw_intmodule:
6362 if (requireFeature({1, 2, 0}, "intrinsic modules") ||
6363 removedFeature({4, 0, 0}, "intrinsic modules"))
6364 return failure();
6365 return parseIntModule(circuit, indent);
6366 case FIRToken::kw_layer:
6367 if (requireFeature({3, 3, 0}, "layers"))
6368 return failure();
6369 return parseLayer(circuit);
6370 case FIRToken::kw_module:
6371 return parseModule(circuit, /*isPublic=*/false, indent);
6372 case FIRToken::kw_public:
6373 if (requireFeature({3, 3, 0}, "public modules"))
6374 return failure();
6375 consumeToken();
6376 if (getToken().getKind() == FIRToken::kw_module)
6377 return parseModule(circuit, /*isPublic=*/true, indent);
6378 return emitError(getToken().getLoc(), "only modules may be public");
6379 case FIRToken::kw_simulation:
6380 if (requireFeature(nextFIRVersion, "simulation tests"))
6381 return failure();
6382 return parseSimulation(circuit, indent);
6383 case FIRToken::kw_type:
6384 return parseTypeDecl();
6385 case FIRToken::kw_option:
6386 if (requireFeature(missingSpecFIRVersion, "option groups/instance choices"))
6387 return failure();
6388 return parseOptionDecl(circuit);
6389 default:
6390 return emitError(getToken().getLoc(), "unknown toplevel definition");
6391 }
6392}
6393
6394// Parse a type declaration.
6395ParseResult FIRCircuitParser::parseTypeDecl() {
6396 StringRef id;
6397 FIRRTLType type;
6398 consumeToken();
6399 auto loc = getToken().getLoc();
6400
6401 if (getToken().isKeyword())
6402 return emitError(loc) << "cannot use keyword '" << getToken().getSpelling()
6403 << "' for type alias name";
6404
6405 if (parseId(id, "expected type name") ||
6406 parseToken(FIRToken::equal, "expected '=' in type decl") ||
6407 parseType(type, "expected a type"))
6408 return failure();
6409 auto name = StringAttr::get(type.getContext(), id);
6410 // Create type alias only for base types. Otherwise just pass through the
6411 // type.
6412 if (auto base = type_dyn_cast<FIRRTLBaseType>(type))
6413 type = BaseTypeAliasType::get(name, base);
6414 else
6415 emitWarning(loc)
6416 << "type alias for non-base type " << type
6417 << " is currently not supported. Type alias is stripped immediately";
6418
6419 if (!getConstants().aliasMap.insert({id, type}).second)
6420 return emitError(loc) << "type alias `" << name.getValue()
6421 << "` is already defined";
6422 return success();
6423}
6424
6425// Parse an option group declaration.
6426ParseResult FIRCircuitParser::parseOptionDecl(CircuitOp circuit) {
6427 StringRef id;
6428 consumeToken();
6429 auto loc = getToken().getLoc();
6430
6431 LocWithInfo info(getToken().getLoc(), this);
6432 if (parseId(id, "expected an option group name") ||
6433 parseToken(FIRToken::colon,
6434 "expected ':' after option group definition") ||
6435 info.parseOptionalInfo())
6436 return failure();
6437
6438 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
6439 auto optionOp =
6440 OptionOp::create(builder, info.getLoc(), id, /*sym_visibility=*/{});
6441 auto *block = new Block;
6442 optionOp.getBody().push_back(block);
6443 builder.setInsertionPointToEnd(block);
6444
6445 auto baseIndent = getIndentation();
6446 StringSet<> cases;
6447 while (getIndentation() == baseIndent) {
6448 StringRef id;
6449 LocWithInfo caseInfo(getToken().getLoc(), this);
6450 if (parseId(id, "expected an option case ID") ||
6451 caseInfo.parseOptionalInfo())
6452 return failure();
6453
6454 if (!cases.insert(id).second)
6455 return emitError(loc)
6456 << "duplicate option case definition '" << id << "'";
6457
6458 OptionCaseOp::create(builder, caseInfo.getLoc(), id);
6459 }
6460
6461 return success();
6462}
6463
6464// Parse a layer definition.
6465ParseResult FIRCircuitParser::parseLayer(CircuitOp circuit) {
6466 auto baseIndent = getIndentation();
6467
6468 // A stack of all layers that are possibly parents of the current layer.
6469 SmallVector<std::pair<std::optional<unsigned>, LayerOp>> layerStack;
6470
6471 // Parse a single layer and add it to the layerStack.
6472 auto parseOne = [&](Block *block) -> ParseResult {
6473 auto indent = getIndentation();
6474 StringRef id, convention;
6475 LocWithInfo info(getToken().getLoc(), this);
6476 consumeToken();
6477 if (parseId(id, "expected layer name") ||
6478 parseToken(FIRToken::comma, "expected ','") ||
6479 parseGetSpelling(convention))
6480 return failure();
6481
6482 auto layerConvention = symbolizeLayerConvention(convention);
6483 if (!layerConvention) {
6484 emitError() << "unknown convention '" << convention
6485 << "' (did you misspell it?)";
6486 return failure();
6487 }
6488 if (layerConvention == LayerConvention::Inline &&
6489 requireFeature({4, 1, 0}, "inline layers"))
6490 return failure();
6491 consumeToken();
6492
6493 hw::OutputFileAttr outputDir;
6494 if (consumeIf(FIRToken::comma)) {
6495 if (getToken().getKind() == FIRToken::string) {
6496 auto text = getToken().getStringValue();
6497 if (text.empty())
6498 return emitError() << "output directory must not be blank";
6499 outputDir = hw::OutputFileAttr::getAsDirectory(getContext(), text);
6500 consumeToken(FIRToken::string);
6501 }
6502 }
6503
6504 if (parseToken(FIRToken::colon, "expected ':' after layer definition") ||
6505 info.parseOptionalInfo())
6506 return failure();
6507 auto builder = OpBuilder::atBlockEnd(block);
6508 // Create the layer definition and give it an empty block.
6509 auto layerOp = LayerOp::create(builder, info.getLoc(), id,
6510 /*sym_visibility=*/{}, *layerConvention);
6511 layerOp->getRegion(0).push_back(new Block());
6512 if (outputDir)
6513 layerOp->setAttr("output_file", outputDir);
6514 layerStack.push_back({indent, layerOp});
6515 return success();
6516 };
6517
6518 if (parseOne(circuit.getBodyBlock()))
6519 return failure();
6520
6521 // Parse any nested layers.
6522 while (getIndentation() > baseIndent) {
6523 switch (getToken().getKind()) {
6524 case FIRToken::kw_declgroup:
6525 case FIRToken::kw_layer: {
6526 // Pop nested layers off the stack until we find out what layer to insert
6527 // this into.
6528 while (layerStack.back().first >= getIndentation())
6529 layerStack.pop_back();
6530 auto parentLayer = layerStack.back().second;
6531 if (parseOne(&parentLayer.getBody().front()))
6532 return failure();
6533 break;
6534 }
6535 default:
6536 return emitError("expected 'layer'"), failure();
6537 }
6538 }
6539
6540 return success();
6541}
6542
6543ParseResult FIRCircuitParser::resolveDomains(
6544 const SmallVectorImpl<std::pair<Attribute, llvm::SMLoc>> &domainsByName,
6545 const DenseMap<Attribute, size_t> &nameToIndex,
6546 SmallVectorImpl<Attribute> &domainsByIndex) {
6547
6548 for (auto [attr, loc] : domainsByName) {
6549 auto domain = cast<StringAttr>(attr);
6550 auto indexItr = nameToIndex.find(domain);
6551 if (indexItr == nameToIndex.end()) {
6552 emitError(loc) << "unknown domain name '" << domain.getValue() << "'";
6553 return failure();
6554 }
6555 domainsByIndex.push_back(IntegerAttr::get(
6556 IntegerType::get(getContext(), 32, IntegerType::Unsigned),
6557 indexItr->second));
6558 }
6559
6560 return success();
6561}
6562
6563ParseResult FIRCircuitParser::parseDomains(
6564 SmallVectorImpl<std::pair<Attribute, llvm::SMLoc>> &domains,
6565 const DenseMap<Attribute, size_t> &nameToIndex) {
6566 if (requireFeature(missingSpecFIRVersion, "domains"))
6567 return failure();
6568 if (parseToken(FIRToken::kw_domains, "expected 'domains'") ||
6569 parseToken(FIRToken::l_square, "expected '['"))
6570 return failure();
6571
6572 if (parseListUntil(FIRToken::r_square, [&]() -> ParseResult {
6573 StringAttr domain;
6574 auto domainLoc = getToken().getLoc();
6575 if (parseId(domain, "expected domain name"))
6576 return failure();
6577 domains.push_back({domain, domainLoc});
6578 return success();
6579 }))
6580 return failure();
6581
6582 return success();
6583}
6584
6585// Parse the body of this module.
6586ParseResult
6587FIRCircuitParser::parseModuleBody(const SymbolTable &circuitSymTbl,
6588 DeferredModuleToParse &deferredModule,
6589 InnerSymFixups &fixups) {
6590 FModuleLike moduleOp = deferredModule.moduleOp;
6591 auto &body = moduleOp->getRegion(0).front();
6592 auto &portLocs = deferredModule.portLocs;
6593
6594 // We parse the body of this module with its own lexer, enabling parallel
6595 // parsing with the rest of the other module bodies.
6596 FIRLexer moduleBodyLexer(getLexer().getSourceMgr(), getContext());
6597
6598 // Reset the parser/lexer state back to right after the port list.
6599 deferredModule.lexerCursor.restore(moduleBodyLexer);
6600
6601 FIRModuleContext moduleContext(&body, getConstants(), moduleBodyLexer,
6602 version);
6603
6604 // Install all of the ports into the symbol table, associated with their
6605 // block arguments.
6606 auto portList = moduleOp.getPorts();
6607 auto portArgs = body.getArguments();
6608 for (auto tuple : llvm::zip(portList, portLocs, portArgs)) {
6609 PortInfo &port = std::get<0>(tuple);
6610 llvm::SMLoc loc = std::get<1>(tuple);
6611 BlockArgument portArg = std::get<2>(tuple);
6612 assert(!port.sym);
6613 if (moduleContext.addSymbolEntry(port.getName(), portArg, loc))
6614 return failure();
6615 }
6616
6617 FIRStmtParser stmtParser(body, moduleContext, fixups, circuitSymTbl, version);
6618
6619 // Parse the moduleBlock.
6620 auto result = stmtParser.parseSimpleStmtBlock(deferredModule.indent);
6621 if (failed(result))
6622 return result;
6623
6624 return success();
6625}
6626
6627/// file ::= circuit
6628/// versionHeader ::= 'FIRRTL' 'version' versionLit NEWLINE
6629/// circuit ::= versionHeader? 'circuit' id ':' info? INDENT module* DEDENT EOF
6630///
6631/// If non-null, annotationsBuf is a memory buffer containing JSON annotations.
6632///
6633ParseResult FIRCircuitParser::parseCircuit(
6634 SmallVectorImpl<const llvm::MemoryBuffer *> &annotationsBufs,
6635 mlir::TimingScope &ts) {
6636
6637 auto indent = getIndentation();
6638 if (parseToken(FIRToken::kw_FIRRTL, "expected 'FIRRTL'"))
6639 return failure();
6640 if (!indent.has_value())
6641 return emitError("'FIRRTL' must be first token on its line");
6642 if (parseToken(FIRToken::kw_version, "expected version after 'FIRRTL'") ||
6643 parseVersionLit("expected version literal"))
6644 return failure();
6645 indent = getIndentation();
6646
6647 if (!indent.has_value())
6648 return emitError("'circuit' must be first token on its line");
6649 unsigned circuitIndent = *indent;
6650
6651 LocWithInfo info(getToken().getLoc(), this);
6652 StringAttr name;
6653 SMLoc inlineAnnotationsLoc;
6654 StringRef inlineAnnotations;
6655
6656 // A file must contain a top level `circuit` definition.
6657 if (parseToken(FIRToken::kw_circuit,
6658 "expected a top-level 'circuit' definition") ||
6659 parseId(name, "expected circuit name") ||
6660 parseToken(FIRToken::colon, "expected ':' in circuit definition") ||
6661 parseOptionalAnnotations(inlineAnnotationsLoc, inlineAnnotations) ||
6662 info.parseOptionalInfo())
6663 return failure();
6664
6665 // Create the top-level circuit op in the MLIR module.
6666 OpBuilder b(mlirModule.getBodyRegion());
6667 auto circuit = CircuitOp::create(b, info.getLoc(), name);
6668
6669 // A timer to get execution time of annotation parsing.
6670 auto parseAnnotationTimer = ts.nest("Parse annotations");
6671
6672 // Deal with any inline annotations, if they exist. These are processed
6673 // first to place any annotations from an annotation file *after* the inline
6674 // annotations. While arbitrary, this makes the annotation file have
6675 // "append" semantics.
6676 SmallVector<Attribute> annos;
6677 if (!inlineAnnotations.empty())
6678 if (importAnnotationsRaw(inlineAnnotationsLoc, inlineAnnotations, annos))
6679 return failure();
6680
6681 // Deal with the annotation file if one was specified
6682 for (auto *annotationsBuf : annotationsBufs)
6683 if (importAnnotationsRaw(info.getFIRLoc(), annotationsBuf->getBuffer(),
6684 annos))
6685 return failure();
6686
6687 parseAnnotationTimer.stop();
6688
6689 // Get annotations that are supposed to be specially handled by the
6690 // LowerAnnotations pass.
6691 if (!annos.empty())
6692 circuit->setAttr(rawAnnotations, b.getArrayAttr(annos));
6693
6694 // A timer to get execution time of module parsing.
6695 auto parseTimer = ts.nest("Parse modules");
6696 deferredModules.reserve(16);
6697
6698 // Parse any contained modules.
6699 while (true) {
6700 switch (getToken().getKind()) {
6701 // If we got to the end of the file, then we're done.
6702 case FIRToken::eof:
6703 goto DoneParsing;
6704
6705 // If we got an error token, then the lexer already emitted an error,
6706 // just stop. We could introduce error recovery if there was demand for
6707 // it.
6708 case FIRToken::error:
6709 return failure();
6710
6711 default:
6712 emitError("unexpected token in circuit");
6713 return failure();
6714
6715 case FIRToken::kw_class:
6716 case FIRToken::kw_declgroup:
6717 case FIRToken::kw_domain:
6718 case FIRToken::kw_extclass:
6719 case FIRToken::kw_extmodule:
6720 case FIRToken::kw_intmodule:
6721 case FIRToken::kw_layer:
6722 case FIRToken::kw_formal:
6723 case FIRToken::kw_module:
6724 case FIRToken::kw_option:
6725 case FIRToken::kw_public:
6726 case FIRToken::kw_simulation:
6727 case FIRToken::kw_type: {
6728 auto indent = getIndentation();
6729 if (!indent.has_value())
6730 return emitError("'module' must be first token on its line"), failure();
6731 unsigned definitionIndent = *indent;
6732
6733 if (definitionIndent <= circuitIndent)
6734 return emitError("module should be indented more"), failure();
6735
6736 if (parseToplevelDefinition(circuit, definitionIndent))
6737 return failure();
6738 break;
6739 }
6740 }
6741 }
6742
6743 // After the outline of the file has been parsed, we can go ahead and parse
6744 // all the bodies. This allows us to resolve forward-referenced modules and
6745 // makes it possible to parse their bodies in parallel.
6746DoneParsing:
6747 // Each of the modules may translate source locations, and doing so touches
6748 // the SourceMgr to build a line number cache. This isn't thread safe, so we
6749 // proactively touch it to make sure that it is always already created.
6750 (void)getLexer().translateLocation(info.getFIRLoc());
6751
6752 // Pre-verify symbol table, so we can construct it next. Ideally, we would do
6753 // this verification through the trait.
6754 { // Memory is tight in parsing.
6755 // Check that all symbols are uniquely named within child regions.
6756 DenseMap<Attribute, Location> nameToOrigLoc;
6757 for (auto &op : *circuit.getBodyBlock()) {
6758 // Check for a symbol name attribute.
6759 auto symbol = dyn_cast<mlir::SymbolOpInterface>(&op);
6760 if (!symbol)
6761 continue;
6762
6763 auto nameAttr = symbol.getNameAttr();
6764
6765 // Try to insert this symbol into the table.
6766 auto it = nameToOrigLoc.try_emplace(nameAttr, op.getLoc());
6767 if (!it.second) {
6768 op.emitError()
6769 .append("redefinition of symbol named '", nameAttr.getValue(), "'")
6770 .attachNote(it.first->second)
6771 .append("see existing symbol definition here");
6772 return failure();
6773 }
6774 }
6775 }
6776
6777 SymbolTable circuitSymTbl(circuit);
6778
6779 moduleFixups.resize(deferredModules.size());
6780
6781 // Stub out inner symbol namespace for each module,
6782 // none should be added so do this now to avoid walking later
6783 // to discover that this is the case.
6784 for (auto &d : deferredModules)
6785 innerSymbolNamespaces.get(d.moduleOp.getOperation());
6786
6787 // Next, parse all the module bodies.
6788 auto anyFailed = mlir::failableParallelForEachN(
6789 getContext(), 0, deferredModules.size(), [&](size_t index) {
6790 if (parseModuleBody(circuitSymTbl, deferredModules[index],
6791 moduleFixups[index]))
6792 return failure();
6793 return success();
6794 });
6795 if (failed(anyFailed))
6796 return failure();
6797
6798 // Walk operations created that have inner symbol references
6799 // that need replacing now that it's safe to create inner symbols everywhere.
6800 for (auto &fixups : moduleFixups) {
6801 if (failed(fixups.resolve(innerSymbolNamespaces)))
6802 return failure();
6803 }
6804
6805 // Helper to transform a layer name specification of the form `A::B::C` into
6806 // a SymbolRefAttr.
6807 auto parseLayerName = [&](StringRef name) -> Attribute {
6808 // Parse the layer name into a SymbolRefAttr.
6809 auto [head, rest] = name.split(".");
6810 SmallVector<FlatSymbolRefAttr> nestedRefs;
6811 while (!rest.empty()) {
6812 StringRef next;
6813 std::tie(next, rest) = rest.split(".");
6814 nestedRefs.push_back(FlatSymbolRefAttr::get(getContext(), next));
6815 }
6816 return SymbolRefAttr::get(getContext(), head, nestedRefs);
6817 };
6818
6819 auto getArrayAttr = [&](ArrayRef<std::string> strArray, auto getAttr) {
6820 SmallVector<Attribute> attrArray;
6821 auto *context = getContext();
6822 for (const auto &str : strArray)
6823 attrArray.push_back(getAttr(str));
6824 if (attrArray.empty())
6825 return ArrayAttr();
6826 return ArrayAttr::get(context, attrArray);
6827 };
6828
6829 if (auto enableLayers =
6830 getArrayAttr(getConstants().options.enableLayers, parseLayerName))
6831 circuit.setEnableLayersAttr(enableLayers);
6832 if (auto disableLayers =
6833 getArrayAttr(getConstants().options.disableLayers, parseLayerName))
6834 circuit.setDisableLayersAttr(disableLayers);
6835
6836 auto getStrAttr = [&](StringRef str) -> Attribute {
6837 return StringAttr::get(getContext(), str);
6838 };
6839
6840 if (auto selectInstChoice =
6841 getArrayAttr(getConstants().options.selectInstanceChoice, getStrAttr))
6842 circuit.setSelectInstChoiceAttr(selectInstChoice);
6843
6844 circuit.setDefaultLayerSpecialization(
6845 getConstants().options.defaultLayerSpecialization);
6846
6847 return success();
6848}
6849
6850//===----------------------------------------------------------------------===//
6851// Driver
6852//===----------------------------------------------------------------------===//
6853
6854// Parse the specified .fir file into the specified MLIR context.
6856circt::firrtl::importFIRFile(SourceMgr &sourceMgr, MLIRContext *context,
6857 mlir::TimingScope &ts, FIRParserOptions options) {
6858 auto sourceBuf = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
6859 SmallVector<const llvm::MemoryBuffer *> annotationsBufs;
6860 unsigned fileID = 1;
6861 for (unsigned e = options.numAnnotationFiles + 1; fileID < e; ++fileID)
6862 annotationsBufs.push_back(
6863 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID() + fileID));
6864
6865 context->loadDialect<CHIRRTLDialect>();
6866 context->loadDialect<FIRRTLDialect, hw::HWDialect>();
6867
6868 // This is the result module we are parsing into.
6869 mlir::OwningOpRef<mlir::ModuleOp> module(ModuleOp::create(
6870 FileLineColLoc::get(context, sourceBuf->getBufferIdentifier(),
6871 /*line=*/0,
6872 /*column=*/0)));
6873 SharedParserConstants state(context, options);
6874 FIRLexer lexer(sourceMgr, context);
6875 if (FIRCircuitParser(state, lexer, *module, minimumFIRVersion)
6876 .parseCircuit(annotationsBufs, ts))
6877 return nullptr;
6878
6879 // Make sure the parse module has no other structural problems detected by
6880 // the verifier.
6881 auto circuitVerificationTimer = ts.nest("Verify circuit");
6882 if (failed(verify(*module)))
6883 return {};
6884
6885 return module;
6886}
6887
6889 static mlir::TranslateToMLIRRegistration fromFIR(
6890 "import-firrtl", "import .fir",
6891 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
6892 mlir::TimingScope ts;
6893 return importFIRFile(sourceMgr, context, ts);
6894 });
6895}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > &parameters)
Parse an parameter list if present.
static std::unique_ptr< Context > context
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
std::vector< UnbundledValueEntry > UnbundledValuesList
SmallVector< std::pair< Attribute, Value > > UnbundledValueEntry
llvm::StringMap< std::pair< SMLoc, SymbolValueEntry >, llvm::BumpPtrAllocator > ModuleSymbolTable
llvm::PointerUnion< Value, UnbundledID > SymbolValueEntry
llvm::DenseMap< std::pair< Value, unsigned >, Value > SubaccessCache
ModuleSymbolTable::MapEntryTy ModuleSymbolTableEntry
llvm::PointerEmbeddedInt< unsigned, 31 > UnbundledID
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static ParseResult parsePortList(OpAsmParser &p, SmallVectorImpl< module_like_impl::PortParse > &result)
static Block * getBodyBlock(FModuleLike mod)
This helper class is used to handle Info records, which specify higher level symbolic source location...
std::optional< Location > infoLoc
This is the location specified by the @ marker if present.
LocWithInfo(SMLoc firLoc, FIRParser *parser)
void setDefaultLoc(Location loc)
If we didn't parse an info locator for the specified value, this sets a default, overriding a fall ba...
FIRParser *const parser
SMLoc firLoc
This is the designated location in the .fir file for use when there is no @ info marker.
ParseResult parseOptionalInfo()
Parse an @info marker if present and update our location.
SMLoc getFIRLoc() const
This class represents a reference to a specific field or element of an aggregate value.
Definition FieldRef.h:28
FieldRef getSubField(unsigned subFieldID) const
Get a reference to a subfield.
Definition FieldRef.h:64
Value getValue() const
Get the Value which created this location.
Definition FieldRef.h:39
Location getLoc() const
Get the location associated with the value of this field ref.
Definition FieldRef.h:69
This is the state captured for a lexer cursor.
Definition FIRLexer.h:154
This implements a lexer for .fir files.
Definition FIRLexer.h:101
std::optional< unsigned > getIndentation(const FIRToken &tok) const
Return the indentation level of the specified token or None if this token is preceded by another toke...
Definition FIRLexer.cpp:185
This represents a specific token for .fir files.
Definition FIRLexer.h:29
bool isNot(Kind k) const
Definition FIRLexer.h:61
StringRef getSpelling() const
Definition FIRLexer.h:45
bool is(Kind K) const
Definition FIRLexer.h:49
std::string getStringValue() const
Given a token containing a string literal, return its value, including removing the quote characters ...
Definition FIRLexer.cpp:58
llvm::SMLoc getLoc() const
Definition FIRLexer.cpp:33
static IntType get(MLIRContext *context, bool isSigned, int32_t widthOrSentinel=-1, bool isConst=false)
Return an SIntType or UIntType with the specified signedness, width, and constness.
The target of an inner symbol, the entity the symbol is a handle for.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:423
RefType getForceableResultType(bool forceable, Type type)
Return null or forceable reference result type.
static Direction get(bool isOutput)
Return an output direction if isOutput is true, otherwise return an input direction.
Definition FIRRTLEnums.h:36
Flow swapFlow(Flow flow)
Get a flow's reverse.
void registerFromFIRFileTranslation()
constexpr FIRVersion nextFIRVersion(7, 0, 0)
The next version of FIRRTL that is not yet released.
std::pair< bool, std::optional< mlir::LocationAttr > > maybeStringToLocation(llvm::StringRef spelling, bool skipParsing, mlir::StringAttr &locatorFilenameCache, FileLineColLoc &fileLineColLocCache, MLIRContext *context)
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
constexpr const char * rawAnnotations
bool areTypesRefCastable(Type dstType, Type srcType)
Return true if destination ref type can be cast from source ref type, per FIRRTL spec rules they must...
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs, bool warnOnTruncation=false)
Emit a connect between two values.
mlir::ParseResult parseFormatString(mlir::OpBuilder &builder, mlir::Location loc, llvm::StringRef formatString, llvm::ArrayRef< mlir::Value > specOperands, mlir::StringAttr &formatStringResult, llvm::SmallVectorImpl< mlir::Value > &operands)
mlir::OwningOpRef< mlir::ModuleOp > importFIRFile(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, mlir::TimingScope &ts, FIRParserOptions options={})
constexpr FIRVersion missingSpecFIRVersion
A marker for parser features that are currently missing from the spec.
Definition FIRParser.h:147
hw::InnerSymTarget getTargetFor(FieldRef ref)
Return the inner sym target for the specified value and fieldID.
constexpr FIRVersion minimumFIRVersion(2, 0, 0)
The current minimum version of FIRRTL that the parser supports.
bool importAnnotationsFromJSONRaw(llvm::json::Value &value, SmallVectorImpl< Attribute > &annotations, llvm::json::Path path, MLIRContext *context)
Deserialize a JSON value into FIRRTL Annotations.
void info(Twine message)
Definition LSPUtils.cpp:20
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
unsigned numAnnotationFiles
The number of annotation files that were specified on the command line.
Definition FIRParser.h:51
InfoLocHandling
Specify how @info locators should be handled.
Definition FIRParser.h:37
The FIRRTL specification version.
Definition FIRParser.h:89
static std::optional< FIRVersion > fromString(StringRef str)
Parse a version string of the form "major.minor.patch".
Definition FIRParser.h:115
This holds the name and type that describes the module's ports.