CIRCT 23.0.0git
Loading...
Searching...
No Matches
FIRRTLTypes.cpp
Go to the documentation of this file.
1//===- FIRRTLTypes.cpp - Implement the FIRRTL dialect type system ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implement the FIRRTL dialect type system.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/IR/DialectImplementation.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringSwitch.h"
20#include "llvm/ADT/TypeSwitch.h"
21
22using namespace circt;
23using namespace firrtl;
24
25using mlir::OptionalParseResult;
26using mlir::TypeStorageAllocator;
27
28//===----------------------------------------------------------------------===//
29// TableGen generated logic.
30//===----------------------------------------------------------------------===//
31
32// Provide the autogenerated implementation for types.
33#define GET_TYPEDEF_CLASSES
34#include "circt/Dialect/FIRRTL/FIRRTLTypes.cpp.inc"
35
36//===----------------------------------------------------------------------===//
37// Type Printing
38//===----------------------------------------------------------------------===//
39
40// NOLINTBEGIN(misc-no-recursion)
41/// Print a type with a custom printer implementation.
42///
43/// This only prints a subset of all types in the dialect. Use `printNestedType`
44/// instead, which will call this function in turn, as appropriate.
45static LogicalResult customTypePrinter(Type type, AsmPrinter &os) {
46 if (isConst(type))
47 os << "const.";
48
49 auto printWidthQualifier = [&](std::optional<int32_t> width) {
50 if (width)
51 os << '<' << *width << '>';
52 };
53 bool anyFailed = false;
54 TypeSwitch<Type>(type)
55 .Case<ClockType>([&](auto) { os << "clock"; })
56 .Case<ResetType>([&](auto) { os << "reset"; })
57 .Case<AsyncResetType>([&](auto) { os << "asyncreset"; })
58 .Case<SIntType>([&](auto sIntType) {
59 os << "sint";
60 printWidthQualifier(sIntType.getWidth());
61 })
62 .Case<UIntType>([&](auto uIntType) {
63 os << "uint";
64 printWidthQualifier(uIntType.getWidth());
65 })
66 .Case<AnalogType>([&](auto analogType) {
67 os << "analog";
68 printWidthQualifier(analogType.getWidth());
69 })
70 .Case<BundleType, OpenBundleType>([&](auto bundleType) {
71 if (firrtl::type_isa<OpenBundleType>(bundleType))
72 os << "open";
73 os << "bundle<";
74 llvm::interleaveComma(bundleType, os, [&](auto element) {
75 StringRef fieldName = element.name.getValue();
76 bool isLiteralIdentifier =
77 !fieldName.empty() && llvm::isDigit(fieldName.front());
78 if (isLiteralIdentifier)
79 os << "\"";
80 os << element.name.getValue();
81 if (isLiteralIdentifier)
82 os << "\"";
83 if (element.isFlip)
84 os << " flip";
85 os << ": ";
86 printNestedType(element.type, os);
87 });
88 os << '>';
89 })
90 .Case<FEnumType>([&](auto fenumType) {
91 os << "enum<";
92 std::optional<APInt> previous;
93 llvm::interleaveComma(
94 fenumType, os, [&](FEnumType::EnumElement element) {
95 // Print the variant name.
96 os << element.name.getValue();
97
98 // Print the variant value.
99 auto value = element.value.getValue();
100 if (previous) {
101 // This APInt should have enough space to
102 // safely add 1 without overflowing.
103 *previous += 1;
104 if (value != previous) {
105 os << " = ";
106 os.printAttributeWithoutType(element.value);
107 }
108 } else if (!element.value.getValue().isZero()) {
109 os << " = ";
110 os.printAttributeWithoutType(element.value);
111 }
112 previous = value;
113
114 // Print the data type.
115 bool skipType = false;
116 if (auto type = dyn_cast<UIntType>(element.type))
117 if (type.getWidth() == 0)
118 skipType = true;
119 if (!skipType) {
120 os << ": ";
121 printNestedType(element.type, os);
122 }
123 });
124 os << '>';
125 })
126 .Case<FVectorType, OpenVectorType>([&](auto vectorType) {
127 if (firrtl::type_isa<OpenVectorType>(vectorType))
128 os << "open";
129 os << "vector<";
130 printNestedType(vectorType.getElementType(), os);
131 os << ", " << vectorType.getNumElements() << '>';
132 })
133 .Case<RefType>([&](RefType refType) {
134 if (refType.getForceable())
135 os << "rw";
136 os << "probe<";
137 printNestedType(refType.getType(), os);
138 if (auto layer = refType.getLayer())
139 os << ", " << layer;
140 os << '>';
141 })
142 .Case<LHSType>([&](LHSType lhstype) {
143 os << "lhs<";
144 printNestedType(lhstype.getType(), os);
145 os << ">";
146 })
147 .Case<StringType>([&](auto stringType) { os << "string"; })
148 .Case<FIntegerType>([&](auto integerType) { os << "integer"; })
149 .Case<BoolType>([&](auto boolType) { os << "bool"; })
150 .Case<DoubleType>([&](auto doubleType) { os << "double"; })
151 .Case<ListType>([&](auto listType) {
152 os << "list<";
153 printNestedType(listType.getElementType(), os);
154 os << '>';
155 })
156 .Case<PathType>([&](auto pathType) { os << "path"; })
157 .Case<BaseTypeAliasType>([&](BaseTypeAliasType alias) {
158 os << "alias<" << alias.getName().getValue() << ", ";
159 printNestedType(alias.getInnerType(), os);
160 os << '>';
161 })
162 .Case<ClassType>([&](ClassType type) {
163 os << "class<";
164 type.printInterface(os);
165 os << ">";
166 })
167 .Case<AnyRefType>([&](AnyRefType type) { os << "anyref"; })
168 .Case<FStringType>([&](auto) { os << "fstring"; })
169 .Case<DomainType>([&](DomainType type) {
170 os << "domain<";
171 os.printSymbolName(type.getName().getValue());
172 os << "(";
173 llvm::interleaveComma(type.getFields(), os, [&](Attribute attr) {
174 auto field = cast<DomainFieldAttr>(attr);
175 os << field.getName().getValue() << ": ";
176 os.printType(field.getType());
177 });
178 os << ")";
179 os << ">";
180 })
181 .Default([&](auto) { anyFailed = true; });
182 return failure(anyFailed);
183}
184// NOLINTEND(misc-no-recursion)
185
186/// Print a type defined by this dialect.
187void circt::firrtl::printNestedType(Type type, AsmPrinter &os) {
188 // Try the custom type printer.
189 if (succeeded(customTypePrinter(type, os)))
190 return;
191
192 // None of the above recognized the type, so we bail.
193 assert(false && "type to print unknown to FIRRTL dialect");
194}
195
196//===----------------------------------------------------------------------===//
197// Type Parsing
198//===----------------------------------------------------------------------===//
199
200/// Parse a type with a custom parser implementation.
201///
202/// This only accepts a subset of all types in the dialect. Use `parseType`
203/// instead, which will call this function in turn, as appropriate.
204///
205/// Returns `std::nullopt` if the type `name` is not covered by the custom
206/// parsers. Otherwise returns success or failure as appropriate. On success,
207/// `result` is set to the resulting type.
208///
209/// ```plain
210/// firrtl-type
211/// ::= clock
212/// ::= reset
213/// ::= asyncreset
214/// ::= sint ('<' int '>')?
215/// ::= uint ('<' int '>')?
216/// ::= analog ('<' int '>')?
217/// ::= bundle '<' (bundle-elt (',' bundle-elt)*)? '>'
218/// ::= enum '<' (enum-elt (',' enum-elt)*)? '>'
219/// ::= vector '<' type ',' int '>'
220/// ::= const '.' type
221/// ::= 'property.' firrtl-phased-type
222/// bundle-elt ::= identifier flip? ':' type
223/// enum-elt ::= identifier ':' type
224/// ```
225static OptionalParseResult customTypeParser(AsmParser &parser, StringRef name,
226 Type &result) {
227 bool isConst = false;
228 const char constPrefix[] = "const.";
229 if (name.starts_with(constPrefix)) {
230 isConst = true;
231 name = name.drop_front(std::size(constPrefix) - 1);
232 }
233
234 auto *context = parser.getContext();
235 if (name == "clock")
236 return result = ClockType::get(context, isConst), success();
237 if (name == "reset")
238 return result = ResetType::get(context, isConst), success();
239 if (name == "asyncreset")
240 return result = AsyncResetType::get(context, isConst), success();
241
242 if (name == "sint" || name == "uint" || name == "analog") {
243 // Parse the width specifier if it exists.
244 int32_t width = -1;
245 if (!parser.parseOptionalLess()) {
246 if (parser.parseInteger(width) || parser.parseGreater())
247 return failure();
248
249 if (width < 0)
250 return parser.emitError(parser.getNameLoc(), "unknown width"),
251 failure();
252 }
253
254 if (name == "sint")
255 result = SIntType::get(context, width, isConst);
256 else if (name == "uint")
257 result = UIntType::get(context, width, isConst);
258 else {
259 assert(name == "analog");
260 result = AnalogType::get(context, width, isConst);
261 }
262 return success();
263 }
264
265 if (name == "bundle") {
266 SmallVector<BundleType::BundleElement, 4> elements;
267
268 auto parseBundleElement = [&]() -> ParseResult {
269 std::string nameStr;
270 StringRef name;
271 FIRRTLBaseType type;
272
273 if (failed(parser.parseKeywordOrString(&nameStr)))
274 return failure();
275 name = nameStr;
276
277 bool isFlip = succeeded(parser.parseOptionalKeyword("flip"));
278 if (parser.parseColon() || parseNestedBaseType(type, parser))
279 return failure();
280
281 elements.push_back({StringAttr::get(context, name), isFlip, type});
282 return success();
283 };
284
285 if (parser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::LessGreater,
286 parseBundleElement))
287 return failure();
288
289 result = parser.getChecked<BundleType>(context, elements, isConst);
290 return failure(!result);
291 }
292 if (name == "openbundle") {
293 SmallVector<OpenBundleType::BundleElement, 4> elements;
294
295 auto parseBundleElement = [&]() -> ParseResult {
296 std::string nameStr;
297 StringRef name;
298 FIRRTLType type;
299
300 if (failed(parser.parseKeywordOrString(&nameStr)))
301 return failure();
302 name = nameStr;
303
304 bool isFlip = succeeded(parser.parseOptionalKeyword("flip"));
305 if (parser.parseColon() || parseNestedType(type, parser))
306 return failure();
307
308 elements.push_back({StringAttr::get(context, name), isFlip, type});
309 return success();
310 };
311
312 if (parser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::LessGreater,
313 parseBundleElement))
314 return failure();
315
316 result = parser.getChecked<OpenBundleType>(context, elements, isConst);
317 return failure(!result);
318 }
319
320 if (name == "enum") {
321 SmallVector<StringAttr> names;
322 SmallVector<APInt> values;
323 SmallVector<FIRRTLBaseType> types;
324 auto parseEnumElement = [&]() -> ParseResult {
325 // Parse the variant tag.
326 std::string nameStr;
327 if (failed(parser.parseKeywordOrString(&nameStr)))
328 return failure();
329 names.push_back(StringAttr::get(context, nameStr));
330
331 // Parse the integer value if it exists. If its the first element of the
332 // enum, it implicitly has a value of 0, otherwise it defaults to the
333 // previous value + 1.
334 APInt value;
335 if (succeeded(parser.parseOptionalEqual())) {
336 if (parser.parseInteger(value))
337 return failure();
338 } else if (values.empty()) {
339 // This is the first enum variant, so it defaults to 0.
340 value = APInt(1, 0);
341 } else {
342 // This value is not specified, so it defaults to the previous value
343 // + 1.
344 auto &prev = values.back();
345 if (prev.isMaxValue())
346 value = prev.zext(prev.getBitWidth() + 1);
347 else
348 value = prev;
349 ++value;
350 }
351 values.push_back(std::move(value));
352
353 // Parse the type of the variant data.
354 FIRRTLBaseType type;
355 if (succeeded(parser.parseOptionalColon())) {
356 if (parseNestedBaseType(type, parser))
357 return failure();
358 } else {
359 type = UIntType::get(parser.getContext(), 0);
360 }
361 types.push_back(type);
362
363 return success();
364 };
365
366 if (parser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::LessGreater,
367 parseEnumElement))
368 return failure();
369
370 // Find the bitwidth of the enum.
371 unsigned bitwidth = 0;
372 for (auto &value : values)
373 bitwidth = std::max(bitwidth, value.getActiveBits());
374 auto tagType = IntegerType::get(context, bitwidth, IntegerType::Unsigned);
375
376 SmallVector<FEnumType::EnumElement, 4> elements;
377 for (auto [name, value, type] : llvm::zip(names, values, types)) {
378 auto tagValue = value.zextOrTrunc(bitwidth);
379 elements.push_back({name, IntegerAttr::get(tagType, tagValue), type});
380 }
381
382 if (failed(FEnumType::verify(
383 [&]() { return parser.emitError(parser.getNameLoc()); }, elements,
384 isConst)))
385 return failure();
386
387 result = parser.getChecked<FEnumType>(context, elements, isConst);
388 return failure(!result);
389 }
390
391 if (name == "vector") {
393 uint64_t width = 0;
394
395 if (parser.parseLess() || parseNestedBaseType(elementType, parser) ||
396 parser.parseComma() || parser.parseInteger(width) ||
397 parser.parseGreater())
398 return failure();
399
400 return result = FVectorType::get(elementType, width, isConst), success();
401 }
402 if (name == "openvector") {
404 uint64_t width = 0;
405
406 if (parser.parseLess() || parseNestedType(elementType, parser) ||
407 parser.parseComma() || parser.parseInteger(width) ||
408 parser.parseGreater())
409 return failure();
410
411 result =
412 parser.getChecked<OpenVectorType>(context, elementType, width, isConst);
413 return failure(!result);
414 }
415
416 // For now, support both firrtl.ref and firrtl.probe.
417 if (name == "ref" || name == "probe") {
418 FIRRTLBaseType type;
419 SymbolRefAttr layer;
420 // Don't pass `isConst` to `parseNestedBaseType since `ref` can point to
421 // either `const` or non-`const` types
422 if (parser.parseLess() || parseNestedBaseType(type, parser))
423 return failure();
424 if (parser.parseOptionalComma().succeeded())
425 if (parser.parseOptionalAttribute(layer).value())
426 return parser.emitError(parser.getNameLoc(),
427 "expected symbol reference");
428 if (parser.parseGreater())
429 return failure();
430
431 if (failed(RefType::verify(
432 [&]() { return parser.emitError(parser.getNameLoc()); }, type,
433 false, layer)))
434 return failure();
435
436 return result = RefType::get(type, false, layer), success();
437 }
438 if (name == "lhs") {
439 FIRRTLType type;
440 if (parser.parseLess() || parseNestedType(type, parser) ||
441 parser.parseGreater())
442 return failure();
443 if (!isa<FIRRTLBaseType>(type))
444 return parser.emitError(parser.getNameLoc(), "expected base type");
445 result = parser.getChecked<LHSType>(context, cast<FIRRTLBaseType>(type));
446 return failure(!result);
447 }
448 if (name == "rwprobe") {
449 FIRRTLBaseType type;
450 SymbolRefAttr layer;
451 if (parser.parseLess() || parseNestedBaseType(type, parser))
452 return failure();
453 if (parser.parseOptionalComma().succeeded())
454 if (parser.parseOptionalAttribute(layer).value())
455 return parser.emitError(parser.getNameLoc(),
456 "expected symbol reference");
457 if (parser.parseGreater())
458 return failure();
459
460 if (failed(RefType::verify(
461 [&]() { return parser.emitError(parser.getNameLoc()); }, type, true,
462 layer)))
463 return failure();
464
465 return result = RefType::get(type, true, layer), success();
466 }
467 if (name == "class") {
468 if (isConst)
469 return parser.emitError(parser.getNameLoc(), "classes cannot be const");
470 ClassType classType;
471 if (parser.parseLess() || ClassType::parseInterface(parser, classType) ||
472 parser.parseGreater())
473 return failure();
474 result = classType;
475 return success();
476 }
477 if (name == "anyref") {
478 if (isConst)
479 return parser.emitError(parser.getNameLoc(), "any refs cannot be const");
480
481 result = AnyRefType::get(parser.getContext());
482 return success();
483 }
484 if (name == "string") {
485 if (isConst) {
486 parser.emitError(parser.getNameLoc(), "strings cannot be const");
487 return failure();
488 }
489 result = StringType::get(parser.getContext());
490 return success();
491 }
492 if (name == "integer") {
493 if (isConst) {
494 parser.emitError(parser.getNameLoc(), "bigints cannot be const");
495 return failure();
496 }
497 result = FIntegerType::get(parser.getContext());
498 return success();
499 }
500 if (name == "bool") {
501 if (isConst) {
502 parser.emitError(parser.getNameLoc(), "bools cannot be const");
503 return failure();
504 }
505 result = BoolType::get(parser.getContext());
506 return success();
507 }
508 if (name == "double") {
509 if (isConst) {
510 parser.emitError(parser.getNameLoc(), "doubles cannot be const");
511 return failure();
512 }
513 result = DoubleType::get(parser.getContext());
514 return success();
515 }
516 if (name == "list") {
517 if (isConst) {
518 parser.emitError(parser.getNameLoc(), "lists cannot be const");
519 return failure();
520 }
522 if (parser.parseLess() || parseNestedPropertyType(elementType, parser) ||
523 parser.parseGreater())
524 return failure();
525 result = parser.getChecked<ListType>(context, elementType);
526 if (!result)
527 return failure();
528 return success();
529 }
530 if (name == "path") {
531 if (isConst) {
532 parser.emitError(parser.getNameLoc(), "path cannot be const");
533 return failure();
534 }
535 result = PathType::get(parser.getContext());
536 return success();
537 }
538 if (name == "alias") {
539 FIRRTLBaseType type;
540 StringRef name;
541 if (parser.parseLess() || parser.parseKeyword(&name) ||
542 parser.parseComma() || parseNestedBaseType(type, parser) ||
543 parser.parseGreater())
544 return failure();
545
546 return result =
547 BaseTypeAliasType::get(StringAttr::get(context, name), type),
548 success();
549 }
550 if (name == "fstring") {
551 return result = FStringType::get(context), success();
552 }
553 if (name == "domain") {
554 // Parse: !firrtl.domain<@SymbolName> or
555 // !firrtl.domain<@SymbolName(name: type, ...)>
556 DomainType domainType;
557 if (parser.parseLess() || DomainType::parseInterface(parser, domainType) ||
558 parser.parseGreater())
559 return failure();
560
561 result = domainType;
562 return success();
563 }
564
565 return {};
566}
567
568/// Parse a type defined by this dialect.
569///
570/// This will first try the generated type parsers and then resort to the custom
571/// parser implementation. Emits an error and returns failure if `name` does not
572/// refer to a type defined in this dialect.
573static ParseResult parseType(Type &result, StringRef name, AsmParser &parser) {
574 // Try the custom type parser.
575 OptionalParseResult parseResult = customTypeParser(parser, name, result);
576 if (parseResult.has_value())
577 return parseResult.value();
578
579 // None of the above recognized the type, so we bail.
580 parser.emitError(parser.getNameLoc(), "unknown FIRRTL dialect type: \"")
581 << name << "\"";
582 return failure();
583}
584
585/// Parse a `FIRRTLType` with a `name` that has already been parsed.
586///
587/// Note that only a subset of types defined in the FIRRTL dialect inherit from
588/// `FIRRTLType`. Use `parseType` to parse *any* of the defined types.
589static ParseResult parseFIRRTLType(FIRRTLType &result, StringRef name,
590 AsmParser &parser) {
591 Type type;
592 if (failed(parseType(type, name, parser)))
593 return failure();
594 result = type_dyn_cast<FIRRTLType>(type);
595 if (result)
596 return success();
597 parser.emitError(parser.getNameLoc(), "unknown FIRRTL type: \"")
598 << name << "\"";
599 return failure();
600}
601
602static ParseResult parseFIRRTLBaseType(FIRRTLBaseType &result, StringRef name,
603 AsmParser &parser) {
604 FIRRTLType type;
605 if (failed(parseFIRRTLType(type, name, parser)))
606 return failure();
607 if (auto base = type_dyn_cast<FIRRTLBaseType>(type)) {
608 result = base;
609 return success();
610 }
611 parser.emitError(parser.getNameLoc(), "expected base type, found ") << type;
612 return failure();
613}
614
615static ParseResult parseFIRRTLPropertyType(PropertyType &result, StringRef name,
616 AsmParser &parser) {
617 FIRRTLType type;
618 if (failed(parseFIRRTLType(type, name, parser)))
619 return failure();
620 if (auto prop = type_dyn_cast<PropertyType>(type)) {
621 result = prop;
622 return success();
623 }
624 parser.emitError(parser.getNameLoc(), "expected property type, found ")
625 << type;
626 return failure();
627}
628
629// NOLINTBEGIN(misc-no-recursion)
630/// Parse a `FIRRTLType`.
631///
632/// Note that only a subset of types defined in the FIRRTL dialect inherit from
633/// `FIRRTLType`. Use `parseType` to parse *any* of the defined types.
635 AsmParser &parser) {
636 StringRef name;
637 if (parser.parseKeyword(&name))
638 return failure();
639 return parseFIRRTLType(result, name, parser);
640}
641// NOLINTEND(misc-no-recursion)
642
643// NOLINTBEGIN(misc-no-recursion)
645 AsmParser &parser) {
646 StringRef name;
647 if (parser.parseKeyword(&name))
648 return failure();
649 return parseFIRRTLBaseType(result, name, parser);
650}
651// NOLINTEND(misc-no-recursion)
652
653// NOLINTBEGIN(misc-no-recursion)
655 AsmParser &parser) {
656 StringRef name;
657 if (parser.parseKeyword(&name))
658 return failure();
659 return parseFIRRTLPropertyType(result, name, parser);
660}
661// NOLINTEND(misc-no-recursion)
662
663//===---------------------------------------------------------------------===//
664// Dialect Type Parsing and Printing
665//===----------------------------------------------------------------------===//
666
667/// Print a type registered to this dialect.
668void FIRRTLDialect::printType(Type type, DialectAsmPrinter &os) const {
669 printNestedType(type, os);
670}
671
672/// Parse a type registered to this dialect.
673Type FIRRTLDialect::parseType(DialectAsmParser &parser) const {
674 StringRef name;
675 Type result;
676 if (parser.parseKeyword(&name) || ::parseType(result, name, parser))
677 return Type();
678 return result;
679}
680
681//===----------------------------------------------------------------------===//
682// Recursive Type Properties
683//===----------------------------------------------------------------------===//
684
685enum {
686 /// Bit set if the type only contains passive elements.
688 /// Bit set if the type contains an analog type.
690 /// Bit set fi the type has any uninferred bit widths.
692};
693
694//===----------------------------------------------------------------------===//
695// FIRRTLBaseType Implementation
696//===----------------------------------------------------------------------===//
697
699 // Use `char` instead of `bool` since llvm already provides a
700 // DenseMapInfo<char> specialization
701 using KeyTy = char;
702
703 FIRRTLBaseTypeStorage(bool isConst) : isConst(static_cast<char>(isConst)) {}
704
705 bool operator==(const KeyTy &key) const { return key == isConst; }
706
707 KeyTy getAsKey() const { return isConst; }
708
709 static FIRRTLBaseTypeStorage *construct(TypeStorageAllocator &allocator,
710 KeyTy key) {
711 return new (allocator.allocate<FIRRTLBaseTypeStorage>())
713 }
714
716};
717
718/// Return true if this is a 'ground' type, aka a non-aggregate type.
719bool FIRRTLType::isGround() {
720 return TypeSwitch<FIRRTLType, bool>(*this)
721 .Case<ClockType, ResetType, AsyncResetType, SIntType, UIntType,
722 AnalogType>([](Type) { return true; })
723 .Case<BundleType, FVectorType, FEnumType, OpenBundleType, OpenVectorType>(
724 [](Type) { return false; })
725 .Case<BaseTypeAliasType>([](BaseTypeAliasType alias) {
726 return alias.getAnonymousType().isGround();
727 })
728 // Not ground per spec, but leaf of aggregate.
729 .Case<PropertyType, RefType>([](Type) { return false; })
730 .Default([](Type) {
731 llvm_unreachable("unknown FIRRTL type");
732 return false;
733 });
734}
735
736bool FIRRTLType::isConst() const {
737 return TypeSwitch<FIRRTLType, bool>(*this)
738 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>(
739 [](auto type) { return type.isConst(); })
740 .Default(false);
741}
742
743bool FIRRTLBaseType::isConst() const { return getImpl()->isConst; }
744
746 return TypeSwitch<FIRRTLType, RecursiveTypeProperties>(*this)
747 .Case<ClockType, ResetType, AsyncResetType>([](FIRRTLBaseType type) {
748 return RecursiveTypeProperties{true,
749 false,
750 false,
751 type.isConst(),
752 false,
753 false,
754 firrtl::type_isa<ResetType>(type)};
755 })
756 .Case<SIntType, UIntType>([](auto type) {
758 true, false, false, type.isConst(), false, !type.hasWidth(), false};
759 })
760 .Case<AnalogType>([](auto type) {
762 true, false, true, type.isConst(), false, !type.hasWidth(), false};
763 })
764 .Case<BundleType, FVectorType, FEnumType, OpenBundleType, OpenVectorType,
765 RefType, BaseTypeAliasType>(
766 [](auto type) { return type.getRecursiveTypeProperties(); })
767 .Case<PropertyType>([](auto type) {
768 return RecursiveTypeProperties{true, false, false, false,
769 false, false, false};
770 })
771 .Case<LHSType>(
772 [](auto type) { return type.getType().getRecursiveTypeProperties(); })
773 .Case<FStringType>([](auto type) {
774 return RecursiveTypeProperties{true, false, false, false,
775 false, false, false};
776 })
777 .Case<DomainType>([](auto type) {
778 return RecursiveTypeProperties{true, false, false, false,
779 false, false, false};
780 })
781 .Default([](Type) {
782 llvm_unreachable("unknown FIRRTL type");
784 });
785}
786
787/// Return this type with any type aliases recursively removed from itself.
789 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
790 .Case<ClockType, ResetType, AsyncResetType, SIntType, UIntType,
791 AnalogType>([&](Type) { return *this; })
792 .Case<BundleType, FVectorType, FEnumType, BaseTypeAliasType>(
793 [](auto type) { return type.getAnonymousType(); })
794 .Default([](Type) {
795 llvm_unreachable("unknown FIRRTL type");
796 return FIRRTLBaseType();
797 });
798}
799
800/// Return this type with any flip types recursively removed from itself.
802 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
803 .Case<ClockType, ResetType, AsyncResetType, SIntType, UIntType,
804 AnalogType, FEnumType>([&](Type) { return *this; })
805 .Case<BundleType, FVectorType, FEnumType, BaseTypeAliasType>(
806 [](auto type) { return type.getPassiveType(); })
807 .Default([](Type) {
808 llvm_unreachable("unknown FIRRTL type");
809 return FIRRTLBaseType();
810 });
811}
812
813/// Return a 'const' or non-'const' version of this type.
815 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
816 .Case<ClockType, ResetType, AsyncResetType, AnalogType, SIntType,
817 UIntType, BundleType, FVectorType, FEnumType, BaseTypeAliasType>(
818 [&](auto type) { return type.getConstType(isConst); })
819 .Default([](Type) {
820 llvm_unreachable("unknown FIRRTL type");
821 return FIRRTLBaseType();
822 });
823}
824
825/// Return this type with a 'const' modifiers dropped
827 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
828 .Case<ClockType, ResetType, AsyncResetType, AnalogType, SIntType,
829 UIntType>([&](auto type) { return type.getConstType(false); })
830 .Case<BundleType, FVectorType, FEnumType, BaseTypeAliasType>(
831 [&](auto type) { return type.getAllConstDroppedType(); })
832 .Default([](Type) {
833 llvm_unreachable("unknown FIRRTL type");
834 return FIRRTLBaseType();
835 });
836}
837
838/// Return this type with all ground types replaced with UInt<1>. This is
839/// used for `mem` operations.
841 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
842 .Case<ClockType, ResetType, AsyncResetType, SIntType, UIntType,
843 AnalogType, FEnumType>([&](Type) {
844 return UIntType::get(this->getContext(), 1, this->isConst());
845 })
846 .Case<BundleType>([&](BundleType bundleType) {
847 SmallVector<BundleType::BundleElement, 4> newElements;
848 newElements.reserve(bundleType.getElements().size());
849 for (auto elt : bundleType)
850 newElements.push_back(
851 {elt.name, false /* FIXME */, elt.type.getMaskType()});
852 return BundleType::get(this->getContext(), newElements,
853 bundleType.isConst());
854 })
855 .Case<FVectorType>([](FVectorType vectorType) {
856 return FVectorType::get(vectorType.getElementType().getMaskType(),
857 vectorType.getNumElements(),
858 vectorType.isConst());
859 })
860 .Case<BaseTypeAliasType>([](BaseTypeAliasType base) {
861 return base.getModifiedType(base.getInnerType().getMaskType());
862 })
863 .Default([](Type) {
864 llvm_unreachable("unknown FIRRTL type");
865 return FIRRTLBaseType();
866 });
867}
868
869/// Remove the widths from this type. All widths are replaced with an
870/// unknown width.
872 return TypeSwitch<FIRRTLBaseType, FIRRTLBaseType>(*this)
873 .Case<ClockType, ResetType, AsyncResetType>([](auto a) { return a; })
874 .Case<UIntType, SIntType, AnalogType>(
875 [&](auto a) { return a.get(this->getContext(), -1, a.isConst()); })
876 .Case<BundleType>([&](auto a) {
877 SmallVector<BundleType::BundleElement, 4> newElements;
878 newElements.reserve(a.getElements().size());
879 for (auto elt : a)
880 newElements.push_back(
881 {elt.name, elt.isFlip, elt.type.getWidthlessType()});
882 return BundleType::get(this->getContext(), newElements, a.isConst());
883 })
884 .Case<FVectorType>([](auto a) {
885 return FVectorType::get(a.getElementType().getWidthlessType(),
886 a.getNumElements(), a.isConst());
887 })
888 .Case<FEnumType>([&](FEnumType a) {
889 SmallVector<FEnumType::EnumElement, 4> newElements;
890 newElements.reserve(a.getNumElements());
891 for (auto elt : a)
892 newElements.push_back(
893 {elt.name, elt.value, elt.type.getWidthlessType()});
894 return FEnumType::get(this->getContext(), newElements, a.isConst());
895 })
896 .Case<BaseTypeAliasType>([](BaseTypeAliasType type) {
897 return type.getModifiedType(type.getInnerType().getWidthlessType());
898 })
899 .Default([](auto) {
900 llvm_unreachable("unknown FIRRTL type");
901 return FIRRTLBaseType();
902 });
903}
904
905/// If this is an IntType, AnalogType, or sugar type for a single bit (Clock,
906/// Reset, etc) then return the bitwidth. Return -1 if the is one of these
907/// types but without a specified bitwidth. Return -2 if this isn't a simple
908/// type.
910 return TypeSwitch<FIRRTLBaseType, int32_t>(*this)
911 .Case<ClockType, ResetType, AsyncResetType>([](Type) { return 1; })
912 .Case<SIntType, UIntType>(
913 [&](IntType intType) { return intType.getWidthOrSentinel(); })
914 .Case<AnalogType>(
915 [](AnalogType analogType) { return analogType.getWidthOrSentinel(); })
916 .Case<FEnumType>([&](FEnumType fenum) { return fenum.getBitWidth(); })
917 .Case<BundleType, FVectorType>([](Type) { return -2; })
918 .Case<BaseTypeAliasType>([](BaseTypeAliasType type) {
919 // It's faster to use its anonymous type.
920 return type.getAnonymousType().getBitWidthOrSentinel();
921 })
922 .Default([](Type) {
923 llvm_unreachable("unknown FIRRTL type");
924 return -2;
925 });
926}
927
928/// Return true if this is a type usable as a reset. This must be
929/// either an abstract reset, a concrete 1-bit UInt, an
930/// asynchronous reset, or an uninfered width UInt.
932 return TypeSwitch<FIRRTLType, bool>(*this)
933 .Case<ResetType, AsyncResetType>([](Type) { return true; })
934 .Case<UIntType>(
935 [](UIntType a) { return !a.hasWidth() || a.getWidth() == 1; })
936 .Case<BaseTypeAliasType>(
937 [](auto type) { return type.getInnerType().isResetType(); })
938 .Default([](Type) { return false; });
939}
940
941bool firrtl::isConst(Type type) {
942 return TypeSwitch<Type, bool>(type)
943 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>(
944 [](auto base) { return base.isConst(); })
945 .Default(false);
946}
947
948bool firrtl::containsConst(Type type) {
949 return TypeSwitch<Type, bool>(type)
950 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>(
951 [](auto base) { return base.containsConst(); })
952 .Default(false);
953}
954
955// NOLINTBEGIN(misc-no-recursion)
958 .Case<BundleType>([&](auto bundle) {
959 for (size_t i = 0, e = bundle.getNumElements(); i < e; ++i) {
960 auto elt = bundle.getElement(i);
961 if (hasZeroBitWidth(elt.type))
962 return true;
963 }
964 return bundle.getNumElements() == 0;
965 })
966 .Case<FVectorType>([&](auto vector) {
967 if (vector.getNumElements() == 0)
968 return true;
969 return hasZeroBitWidth(vector.getElementType());
970 })
971 .Case<FIRRTLBaseType>([](auto groundType) {
972 return firrtl::getBitWidth(groundType).value_or(0) == 0;
973 })
974 .Case<RefType>([](auto ref) { return hasZeroBitWidth(ref.getType()); })
975 .Default([](auto) { return false; });
976}
977// NOLINTEND(misc-no-recursion)
978
979/// Helper to implement the equivalence logic for a pair of bundle elements.
980/// Note that the FIRRTL spec requires bundle elements to have the same
981/// orientation, but this only compares their passive types. The FIRRTL dialect
982/// differs from the spec in how it uses flip types for module output ports and
983/// canonicalizes flips in bundles, so only passive types can be compared here.
984static bool areBundleElementsEquivalent(BundleType::BundleElement destElement,
985 BundleType::BundleElement srcElement,
986 bool destOuterTypeIsConst,
987 bool srcOuterTypeIsConst,
988 bool requiresSameWidth) {
989 if (destElement.name != srcElement.name)
990 return false;
991 if (destElement.isFlip != srcElement.isFlip)
992 return false;
993
994 if (destElement.isFlip) {
995 std::swap(destElement, srcElement);
996 std::swap(destOuterTypeIsConst, srcOuterTypeIsConst);
997 }
998
999 return areTypesEquivalent(destElement.type, srcElement.type,
1000 destOuterTypeIsConst, srcOuterTypeIsConst,
1001 requiresSameWidth);
1002}
1003
1004/// Returns whether the two types are equivalent. This implements the exact
1005/// definition of type equivalence in the FIRRTL spec. If the types being
1006/// compared have any outer flips that encode FIRRTL module directions (input or
1007/// output), these should be stripped before using this method.
1009 bool destOuterTypeIsConst,
1010 bool srcOuterTypeIsConst,
1011 bool requireSameWidths) {
1012 auto destType = type_dyn_cast<FIRRTLBaseType>(destFType);
1013 auto srcType = type_dyn_cast<FIRRTLBaseType>(srcFType);
1014
1015 // For non-base types, only equivalent if identical.
1016 if (!destType || !srcType)
1017 return destFType == srcFType;
1018
1019 bool srcIsConst = srcOuterTypeIsConst || srcFType.isConst();
1020 bool destIsConst = destOuterTypeIsConst || destFType.isConst();
1021
1022 // Vector types can be connected if they have the same size and element type.
1023 auto destVectorType = type_dyn_cast<FVectorType>(destType);
1024 auto srcVectorType = type_dyn_cast<FVectorType>(srcType);
1025 if (destVectorType && srcVectorType)
1026 return destVectorType.getNumElements() == srcVectorType.getNumElements() &&
1027 areTypesEquivalent(destVectorType.getElementType(),
1028 srcVectorType.getElementType(), destIsConst,
1029 srcIsConst, requireSameWidths);
1030
1031 // Bundle types can be connected if they have the same size, element names,
1032 // and element types.
1033 auto destBundleType = type_dyn_cast<BundleType>(destType);
1034 auto srcBundleType = type_dyn_cast<BundleType>(srcType);
1035 if (destBundleType && srcBundleType) {
1036 auto destElements = destBundleType.getElements();
1037 auto srcElements = srcBundleType.getElements();
1038 size_t numDestElements = destElements.size();
1039 if (numDestElements != srcElements.size())
1040 return false;
1041
1042 for (size_t i = 0; i < numDestElements; ++i) {
1043 auto destElement = destElements[i];
1044 auto srcElement = srcElements[i];
1045 if (!areBundleElementsEquivalent(destElement, srcElement, destIsConst,
1046 srcIsConst, requireSameWidths))
1047 return false;
1048 }
1049 return true;
1050 }
1051
1052 // Enum types can be connected if they have the same size, element names, and
1053 // element types.
1054 auto dstEnumType = type_dyn_cast<FEnumType>(destType);
1055 auto srcEnumType = type_dyn_cast<FEnumType>(srcType);
1056
1057 if (dstEnumType && srcEnumType) {
1058 if (dstEnumType.getNumElements() != srcEnumType.getNumElements())
1059 return false;
1060 // Enums requires the types to match exactly.
1061 for (const auto &[dst, src] : llvm::zip(dstEnumType, srcEnumType)) {
1062 // The variant names must match.
1063 if (dst.name != src.name)
1064 return false;
1065 // Enumeration types can only be connected if the inner types have the
1066 // same width.
1067 if (!areTypesEquivalent(dst.type, src.type, destIsConst, srcIsConst,
1068 true))
1069 return false;
1070 }
1071 return true;
1072 }
1073
1074 // Ground type connections must be const compatible.
1075 if (destIsConst && !srcIsConst)
1076 return false;
1077
1078 // Reset types can be driven by UInt<1>, AsyncReset, or Reset types.
1079 if (firrtl::type_isa<ResetType>(destType))
1080 return srcType.isResetType();
1081
1082 // Reset types can drive UInt<1>, AsyncReset, or Reset types.
1083 if (firrtl::type_isa<ResetType>(srcType))
1084 return destType.isResetType();
1085
1086 // If we can implicitly truncate or extend the bitwidth, or either width is
1087 // currently uninferred, then compare the widthless version of these types.
1088 if (!requireSameWidths || destType.getBitWidthOrSentinel() == -1)
1089 srcType = srcType.getWidthlessType();
1090 if (!requireSameWidths || srcType.getBitWidthOrSentinel() == -1)
1091 destType = destType.getWidthlessType();
1092
1093 // Ground types can be connected if their constless types are the same
1094 return destType.getConstType(false) == srcType.getConstType(false);
1095}
1096
1097/// Returns whether the srcType can be const-casted to the destType.
1099 bool srcOuterTypeIsConst) {
1100 // Identical types are always castable
1101 if (destFType == srcFType)
1102 return true;
1103
1104 auto destType = type_dyn_cast<FIRRTLBaseType>(destFType);
1105 auto srcType = type_dyn_cast<FIRRTLBaseType>(srcFType);
1106
1107 // For non-base types, only castable if identical.
1108 if (!destType || !srcType)
1109 return false;
1110
1111 // Types must be passive
1112 if (!destType.isPassive() || !srcType.isPassive())
1113 return false;
1114
1115 bool srcIsConst = srcType.isConst() || srcOuterTypeIsConst;
1116
1117 // Cannot cast non-'const' src to 'const' dest
1118 if (destType.isConst() && !srcIsConst)
1119 return false;
1120
1121 // Vector types can be casted if they have the same size and castable element
1122 // type.
1123 auto destVectorType = type_dyn_cast<FVectorType>(destType);
1124 auto srcVectorType = type_dyn_cast<FVectorType>(srcType);
1125 if (destVectorType && srcVectorType)
1126 return destVectorType.getNumElements() == srcVectorType.getNumElements() &&
1127 areTypesConstCastable(destVectorType.getElementType(),
1128 srcVectorType.getElementType(), srcIsConst);
1129 if (destVectorType != srcVectorType)
1130 return false;
1131
1132 // Bundle types can be casted if they have the same size, element names,
1133 // and castable element types.
1134 auto destBundleType = type_dyn_cast<BundleType>(destType);
1135 auto srcBundleType = type_dyn_cast<BundleType>(srcType);
1136 if (destBundleType && srcBundleType) {
1137 auto destElements = destBundleType.getElements();
1138 auto srcElements = srcBundleType.getElements();
1139 size_t numDestElements = destElements.size();
1140 if (numDestElements != srcElements.size())
1141 return false;
1142
1143 return llvm::all_of_zip(
1144 destElements, srcElements,
1145 [&](const auto &destElement, const auto &srcElement) {
1146 return destElement.name == srcElement.name &&
1147 areTypesConstCastable(destElement.type, srcElement.type,
1148 srcIsConst);
1149 });
1150 }
1151 if (destBundleType != srcBundleType)
1152 return false;
1153
1154 // Ground types can be casted if the source type is a const
1155 // version of the destination type
1156 return destType == srcType.getConstType(destType.isConst());
1157}
1158
1159bool firrtl::areTypesRefCastable(Type dstType, Type srcType) {
1160 auto dstRefType = type_dyn_cast<RefType>(dstType);
1161 auto srcRefType = type_dyn_cast<RefType>(srcType);
1162 if (!dstRefType || !srcRefType)
1163 return false;
1164 if (dstRefType == srcRefType)
1165 return true;
1166 if (dstRefType.getForceable() && !srcRefType.getForceable())
1167 return false;
1168
1169 // Okay walk the types recursively. They must be identical "structurally"
1170 // with exception leaf (ground) types of destination can be uninferred
1171 // versions of the corresponding source type. (can lose width information or
1172 // become a more general reset type)
1173 // In addition, while not explicitly in spec its useful to allow probes
1174 // to have const cast away, especially for probes of literals and expressions
1175 // derived from them. Check const as with const cast.
1176 // NOLINTBEGIN(misc-no-recursion)
1177 auto recurse = [&](auto &&f, FIRRTLBaseType dest, FIRRTLBaseType src,
1178 bool srcOuterTypeIsConst) -> bool {
1179 // Fast-path for identical types.
1180 if (dest == src)
1181 return true;
1182
1183 // Always passive inside probes, but for sanity assert this.
1184 assert(dest.isPassive() && src.isPassive());
1185
1186 bool srcIsConst = src.isConst() || srcOuterTypeIsConst;
1187
1188 // Cannot cast non-'const' src to 'const' dest
1189 if (dest.isConst() && !srcIsConst)
1190 return false;
1191
1192 // Recurse through aggregates to get the leaves, checking
1193 // structural equivalence re:element count + names.
1194
1195 if (auto destVectorType = type_dyn_cast<FVectorType>(dest)) {
1196 auto srcVectorType = type_dyn_cast<FVectorType>(src);
1197 return srcVectorType &&
1198 destVectorType.getNumElements() ==
1199 srcVectorType.getNumElements() &&
1200 f(f, destVectorType.getElementType(),
1201 srcVectorType.getElementType(), srcIsConst);
1202 }
1203
1204 if (auto destBundleType = type_dyn_cast<BundleType>(dest)) {
1205 auto srcBundleType = type_dyn_cast<BundleType>(src);
1206 if (!srcBundleType)
1207 return false;
1208 // (no need to check orientation, these are always passive)
1209 auto destElements = destBundleType.getElements();
1210 auto srcElements = srcBundleType.getElements();
1211
1212 return destElements.size() == srcElements.size() &&
1213 llvm::all_of_zip(
1214 destElements, srcElements,
1215 [&](const auto &destElement, const auto &srcElement) {
1216 return destElement.name == srcElement.name &&
1217 f(f, destElement.type, srcElement.type, srcIsConst);
1218 });
1219 }
1220
1221 if (auto destEnumType = type_dyn_cast<FEnumType>(dest)) {
1222 auto srcEnumType = type_dyn_cast<FEnumType>(src);
1223 if (!srcEnumType)
1224 return false;
1225 auto destElements = destEnumType.getElements();
1226 auto srcElements = srcEnumType.getElements();
1227
1228 return destElements.size() == srcElements.size() &&
1229 llvm::all_of_zip(
1230 destElements, srcElements,
1231 [&](const auto &destElement, const auto &srcElement) {
1232 return destElement.name == srcElement.name &&
1233 f(f, destElement.type, srcElement.type, srcIsConst);
1234 });
1235 }
1236
1237 // Reset types can be driven by UInt<1>, AsyncReset, or Reset types.
1238 if (type_isa<ResetType>(dest))
1239 return src.isResetType();
1240 // (but don't allow the other direction, can only become more general)
1241
1242 // Compare against const src if dest is const.
1243 src = src.getConstType(dest.isConst());
1244
1245 // Compare against widthless src if dest is widthless.
1246 if (dest.getBitWidthOrSentinel() == -1)
1247 src = src.getWidthlessType();
1248
1249 return dest == src;
1250 };
1251
1252 return recurse(recurse, dstRefType.getType(), srcRefType.getType(), false);
1253 // NOLINTEND(misc-no-recursion)
1254}
1255
1256// NOLINTBEGIN(misc-no-recursion)
1257/// Returns true if the destination is at least as wide as an equivalent source.
1259 return TypeSwitch<FIRRTLBaseType, bool>(dstType)
1260 .Case<BundleType>([&](auto dstBundle) {
1261 auto srcBundle = type_cast<BundleType>(srcType);
1262 for (size_t i = 0, n = dstBundle.getNumElements(); i < n; ++i) {
1263 auto srcElem = srcBundle.getElement(i);
1264 auto dstElem = dstBundle.getElement(i);
1265 if (dstElem.isFlip) {
1266 if (!isTypeLarger(srcElem.type, dstElem.type))
1267 return false;
1268 } else {
1269 if (!isTypeLarger(dstElem.type, srcElem.type))
1270 return false;
1271 }
1272 }
1273 return true;
1274 })
1275 .Case<FVectorType>([&](auto vector) {
1276 return isTypeLarger(vector.getElementType(),
1277 type_cast<FVectorType>(srcType).getElementType());
1278 })
1279 .Default([&](auto dstGround) {
1280 int32_t destWidth = dstType.getPassiveType().getBitWidthOrSentinel();
1281 int32_t srcWidth = srcType.getPassiveType().getBitWidthOrSentinel();
1282 return destWidth <= -1 || srcWidth <= -1 || destWidth >= srcWidth;
1283 });
1284}
1285// NOLINTEND(misc-no-recursion)
1286
1291
1292bool firrtl::areAnonymousTypesEquivalent(mlir::Type lhs, mlir::Type rhs) {
1293 if (auto destBaseType = type_dyn_cast<FIRRTLBaseType>(lhs))
1294 if (auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(rhs))
1295 return areAnonymousTypesEquivalent(destBaseType, srcBaseType);
1296
1297 if (auto destRefType = type_dyn_cast<RefType>(lhs))
1298 if (auto srcRefType = type_dyn_cast<RefType>(rhs))
1299 return areAnonymousTypesEquivalent(destRefType.getType(),
1300 srcRefType.getType());
1301
1302 return lhs == rhs;
1303}
1304
1305/// Return the passive version of a firrtl type
1306/// top level for ODS constraint usage
1307Type firrtl::getPassiveType(Type anyBaseFIRRTLType) {
1308 return type_cast<FIRRTLBaseType>(anyBaseFIRRTLType).getPassiveType();
1309}
1310
1311bool firrtl::isTypeInOut(Type type) {
1312 return llvm::TypeSwitch<Type, bool>(type)
1313 .Case<FIRRTLBaseType>([](auto type) {
1314 return !type.containsReference() &&
1315 (!type.isPassive() || type.containsAnalog());
1316 })
1317 .Default(false);
1318}
1319
1320//===----------------------------------------------------------------------===//
1321// IntType
1322//===----------------------------------------------------------------------===//
1323
1324/// Return a SIntType or UIntType with the specified signedness, width, and
1325/// constness
1326IntType IntType::get(MLIRContext *context, bool isSigned,
1327 int32_t widthOrSentinel, bool isConst) {
1328 if (isSigned)
1329 return SIntType::get(context, widthOrSentinel, isConst);
1330 return UIntType::get(context, widthOrSentinel, isConst);
1331}
1332
1334 if (auto sintType = type_dyn_cast<SIntType>(*this))
1335 return sintType.getWidthOrSentinel();
1336 if (auto uintType = type_dyn_cast<UIntType>(*this))
1337 return uintType.getWidthOrSentinel();
1338 return -1;
1339}
1340
1341//===----------------------------------------------------------------------===//
1342// WidthTypeStorage
1343//===----------------------------------------------------------------------===//
1344
1348 using KeyTy = std::tuple<int32_t, char>;
1349
1350 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1351
1352 KeyTy getAsKey() const { return KeyTy(width, isConst); }
1353
1354 static WidthTypeStorage *construct(TypeStorageAllocator &allocator,
1355 const KeyTy &key) {
1356 return new (allocator.allocate<WidthTypeStorage>())
1357 WidthTypeStorage(std::get<0>(key), std::get<1>(key));
1358 }
1359
1360 int32_t width;
1361};
1362
1364
1365 if (auto sIntType = type_dyn_cast<SIntType>(*this))
1366 return sIntType.getConstType(isConst);
1367 return type_cast<UIntType>(*this).getConstType(isConst);
1368}
1369
1370//===----------------------------------------------------------------------===//
1371// SIntType
1372//===----------------------------------------------------------------------===//
1373
1374SIntType SIntType::get(MLIRContext *context) { return get(context, -1, false); }
1375
1376SIntType SIntType::get(MLIRContext *context, std::optional<int32_t> width,
1377 bool isConst) {
1378 return get(context, width ? *width : -1, isConst);
1379}
1380
1381LogicalResult SIntType::verify(function_ref<InFlightDiagnostic()> emitError,
1382 int32_t widthOrSentinel, bool isConst) {
1383 if (widthOrSentinel < -1)
1384 return emitError() << "invalid width";
1385 return success();
1386}
1387
1388int32_t SIntType::getWidthOrSentinel() const { return getImpl()->width; }
1389
1390SIntType SIntType::getConstType(bool isConst) const {
1391 if (isConst == this->isConst())
1392 return *this;
1393 return get(getContext(), getWidthOrSentinel(), isConst);
1394}
1395
1396//===----------------------------------------------------------------------===//
1397// UIntType
1398//===----------------------------------------------------------------------===//
1399
1400UIntType UIntType::get(MLIRContext *context) { return get(context, -1, false); }
1401
1402UIntType UIntType::get(MLIRContext *context, std::optional<int32_t> width,
1403 bool isConst) {
1404 return get(context, width ? *width : -1, isConst);
1405}
1406
1407LogicalResult UIntType::verify(function_ref<InFlightDiagnostic()> emitError,
1408 int32_t widthOrSentinel, bool isConst) {
1409 if (widthOrSentinel < -1)
1410 return emitError() << "invalid width";
1411 return success();
1412}
1413
1414int32_t UIntType::getWidthOrSentinel() const { return getImpl()->width; }
1415
1416UIntType UIntType::getConstType(bool isConst) const {
1417 if (isConst == this->isConst())
1418 return *this;
1419 return get(getContext(), getWidthOrSentinel(), isConst);
1420}
1421
1422//===----------------------------------------------------------------------===//
1423// Bundle Type
1424//===----------------------------------------------------------------------===//
1425
1428 using KeyTy = std::tuple<ArrayRef<BundleType::BundleElement>, char>;
1429
1430 BundleTypeStorage(ArrayRef<BundleType::BundleElement> elements, bool isConst)
1432 elements(elements.begin(), elements.end()),
1433 props{true, false, false, isConst, false, false, false} {
1434 uint64_t fieldID = 0;
1435 fieldIDs.reserve(elements.size());
1436 for (auto &element : elements) {
1437 auto type = element.type;
1438 auto eltInfo = type.getRecursiveTypeProperties();
1439 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1440 props.containsAnalog |= eltInfo.containsAnalog;
1441 props.containsReference |= eltInfo.containsReference;
1442 props.containsConst |= eltInfo.containsConst;
1443 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1444 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1445 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1446 fieldID += 1;
1447 fieldIDs.push_back(fieldID);
1448 // Increment the field ID for the next field by the number of subfields.
1449 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1450 }
1451 maxFieldID = fieldID;
1452 }
1453
1454 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1455
1456 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1457
1458 static llvm::hash_code hashKey(const KeyTy &key) {
1459 return llvm::hash_value(key);
1460 }
1461
1462 static BundleTypeStorage *construct(TypeStorageAllocator &allocator,
1463 KeyTy key) {
1464 return new (allocator.allocate<BundleTypeStorage>()) BundleTypeStorage(
1465 std::get<0>(key), static_cast<bool>(std::get<1>(key)));
1466 }
1467
1468 SmallVector<BundleType::BundleElement, 4> elements;
1469 SmallVector<uint64_t, 4> fieldIDs;
1470 uint64_t maxFieldID;
1471
1472 /// This holds the bits for the type's recursive properties, and can hold a
1473 /// pointer to a passive version of the type.
1475 BundleType passiveType;
1476 BundleType anonymousType;
1477};
1478
1479BundleType BundleType::get(MLIRContext *context,
1480 ArrayRef<BundleElement> elements, bool isConst) {
1481 return Base::get(context, elements, isConst);
1482}
1483
1484auto BundleType::getElements() const -> ArrayRef<BundleElement> {
1485 return getImpl()->elements;
1486}
1487
1488/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1489RecursiveTypeProperties BundleType::getRecursiveTypeProperties() const {
1490 return getImpl()->props;
1491}
1492
1493/// Return this type with any flip types recursively removed from itself.
1494FIRRTLBaseType BundleType::getPassiveType() {
1495 auto *impl = getImpl();
1496
1497 // If we've already determined and cached the passive type, use it.
1498 if (impl->passiveType)
1499 return impl->passiveType;
1500
1501 // If this type is already passive, use it and remember for next time.
1502 if (impl->props.isPassive) {
1503 impl->passiveType = *this;
1504 return *this;
1505 }
1506
1507 // Otherwise at least one element is non-passive, rebuild a passive version.
1508 SmallVector<BundleType::BundleElement, 16> newElements;
1509 newElements.reserve(impl->elements.size());
1510 for (auto &elt : impl->elements) {
1511 newElements.push_back({elt.name, false, elt.type.getPassiveType()});
1512 }
1513
1514 auto passiveType = BundleType::get(getContext(), newElements, isConst());
1515 impl->passiveType = passiveType;
1516 return passiveType;
1517}
1518
1519BundleType BundleType::getConstType(bool isConst) const {
1520 if (isConst == this->isConst())
1521 return *this;
1522 return get(getContext(), getElements(), isConst);
1523}
1524
1525BundleType BundleType::getAllConstDroppedType() {
1526 if (!containsConst())
1527 return *this;
1528
1529 SmallVector<BundleElement> constDroppedElements(
1530 llvm::map_range(getElements(), [](BundleElement element) {
1531 element.type = element.type.getAllConstDroppedType();
1532 return element;
1533 }));
1534 return get(getContext(), constDroppedElements, false);
1535}
1536
1537std::optional<unsigned> BundleType::getElementIndex(StringAttr name) {
1538 for (const auto &it : llvm::enumerate(getElements())) {
1539 auto element = it.value();
1540 if (element.name == name) {
1541 return unsigned(it.index());
1542 }
1543 }
1544 return std::nullopt;
1545}
1546
1547std::optional<unsigned> BundleType::getElementIndex(StringRef name) {
1548 for (const auto &it : llvm::enumerate(getElements())) {
1549 auto element = it.value();
1550 if (element.name.getValue() == name) {
1551 return unsigned(it.index());
1552 }
1553 }
1554 return std::nullopt;
1555}
1556
1557StringAttr BundleType::getElementNameAttr(size_t index) {
1558 assert(index < getNumElements() &&
1559 "index must be less than number of fields in bundle");
1560 return getElements()[index].name;
1561}
1562
1563StringRef BundleType::getElementName(size_t index) {
1564 return getElementNameAttr(index).getValue();
1565}
1566
1567std::optional<BundleType::BundleElement>
1568BundleType::getElement(StringAttr name) {
1569 if (auto maybeIndex = getElementIndex(name))
1570 return getElements()[*maybeIndex];
1571 return std::nullopt;
1572}
1573
1574std::optional<BundleType::BundleElement>
1575BundleType::getElement(StringRef name) {
1576 if (auto maybeIndex = getElementIndex(name))
1577 return getElements()[*maybeIndex];
1578 return std::nullopt;
1579}
1580
1581/// Look up an element by index.
1582BundleType::BundleElement BundleType::getElement(size_t index) {
1583 assert(index < getNumElements() &&
1584 "index must be less than number of fields in bundle");
1585 return getElements()[index];
1586}
1587
1588FIRRTLBaseType BundleType::getElementType(StringAttr name) {
1589 auto element = getElement(name);
1590 return element ? element->type : FIRRTLBaseType();
1591}
1592
1593FIRRTLBaseType BundleType::getElementType(StringRef name) {
1594 auto element = getElement(name);
1595 return element ? element->type : FIRRTLBaseType();
1596}
1597
1598FIRRTLBaseType BundleType::getElementType(size_t index) const {
1599 assert(index < getNumElements() &&
1600 "index must be less than number of fields in bundle");
1601 return getElements()[index].type;
1602}
1603
1604uint64_t BundleType::getFieldID(uint64_t index) const {
1605 return getImpl()->fieldIDs[index];
1606}
1607
1608uint64_t BundleType::getIndexForFieldID(uint64_t fieldID) const {
1609 assert(!getElements().empty() && "Bundle must have >0 fields");
1610 auto fieldIDs = getImpl()->fieldIDs;
1611 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1612 return std::distance(fieldIDs.begin(), it);
1613}
1614
1615std::pair<uint64_t, uint64_t>
1616BundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1617 auto index = getIndexForFieldID(fieldID);
1618 auto elementFieldID = getFieldID(index);
1619 return {index, fieldID - elementFieldID};
1620}
1621
1622std::pair<Type, uint64_t>
1623BundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1624 if (fieldID == 0)
1625 return {*this, 0};
1626 auto subfieldIndex = getIndexForFieldID(fieldID);
1627 auto subfieldType = getElementType(subfieldIndex);
1628 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1629 return {subfieldType, subfieldID};
1630}
1631
1632uint64_t BundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1633
1634std::pair<uint64_t, bool>
1635BundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1636 auto childRoot = getFieldID(index);
1637 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1638 : (getFieldID(index + 1) - 1);
1639 return std::make_pair(fieldID - childRoot,
1640 fieldID >= childRoot && fieldID <= rangeEnd);
1641}
1642
1643bool BundleType::isConst() const { return getImpl()->isConst; }
1644
1645BundleType::ElementType
1646BundleType::getElementTypePreservingConst(size_t index) {
1647 auto type = getElementType(index);
1648 return type.getConstType(type.isConst() || isConst());
1649}
1650
1651/// Return this type with any type aliases recursively removed from itself.
1652FIRRTLBaseType BundleType::getAnonymousType() {
1653 auto *impl = getImpl();
1654
1655 // If we've already determined and cached the anonymous type, use it.
1656 if (impl->anonymousType)
1657 return impl->anonymousType;
1658
1659 // If this type is already anonymous, use it and remember for next time.
1660 if (!impl->props.containsTypeAlias) {
1661 impl->anonymousType = *this;
1662 return *this;
1663 }
1664
1665 // Otherwise at least one element has an alias type, rebuild an anonymous
1666 // version.
1667 SmallVector<BundleType::BundleElement, 16> newElements;
1668 newElements.reserve(impl->elements.size());
1669 for (auto &elt : impl->elements)
1670 newElements.push_back({elt.name, elt.isFlip, elt.type.getAnonymousType()});
1671
1672 auto anonymousType = BundleType::get(getContext(), newElements, isConst());
1673 impl->anonymousType = anonymousType;
1674 return anonymousType;
1675}
1676
1677LogicalResult BundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1678 ArrayRef<BundleElement> elements,
1679 bool isConst) {
1680 SmallPtrSet<StringAttr, 4> nameSet;
1681 for (auto &element : elements) {
1682 if (!nameSet.insert(element.name).second)
1683 return emitErrorFn() << "duplicate field name " << element.name
1684 << " in bundle";
1685 }
1686
1687 return success();
1688}
1689
1690//===----------------------------------------------------------------------===//
1691// OpenBundle Type
1692//===----------------------------------------------------------------------===//
1693
1695 using KeyTy = std::tuple<ArrayRef<OpenBundleType::BundleElement>, char>;
1696
1697 OpenBundleTypeStorage(ArrayRef<OpenBundleType::BundleElement> elements,
1698 bool isConst)
1699 : elements(elements.begin(), elements.end()),
1700 props{true, false, false, isConst, false, false, false},
1701 isConst(static_cast<char>(isConst)) {
1702 uint64_t fieldID = 0;
1703 fieldIDs.reserve(elements.size());
1704 for (auto &element : elements) {
1705 auto type = element.type;
1706 auto eltInfo = type.getRecursiveTypeProperties();
1707 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1708 props.containsAnalog |= eltInfo.containsAnalog;
1709 props.containsReference |= eltInfo.containsReference;
1710 props.containsConst |= eltInfo.containsConst;
1711 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1712 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1713 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1714 fieldID += 1;
1715 fieldIDs.push_back(fieldID);
1716 // Increment the field ID for the next field by the number of subfields.
1717 // TODO: Maybe just have elementType be FieldIDTypeInterface ?
1718 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1719 }
1720 maxFieldID = fieldID;
1721 }
1722
1723 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1724
1725 static llvm::hash_code hashKey(const KeyTy &key) {
1726 return llvm::hash_value(key);
1727 }
1728
1729 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1730
1731 static OpenBundleTypeStorage *construct(TypeStorageAllocator &allocator,
1732 KeyTy key) {
1733 return new (allocator.allocate<OpenBundleTypeStorage>())
1734 OpenBundleTypeStorage(std::get<0>(key),
1735 static_cast<bool>(std::get<1>(key)));
1736 }
1737
1738 SmallVector<OpenBundleType::BundleElement, 4> elements;
1739 SmallVector<uint64_t, 4> fieldIDs;
1740 uint64_t maxFieldID;
1741
1742 /// This holds the bits for the type's recursive properties, and can hold a
1743 /// pointer to a passive version of the type.
1745
1746 // Whether this is 'const'.
1748};
1749
1750OpenBundleType OpenBundleType::get(MLIRContext *context,
1751 ArrayRef<BundleElement> elements,
1752 bool isConst) {
1753 return Base::get(context, elements, isConst);
1754}
1755
1756auto OpenBundleType::getElements() const -> ArrayRef<BundleElement> {
1757 return getImpl()->elements;
1758}
1759
1760/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1761RecursiveTypeProperties OpenBundleType::getRecursiveTypeProperties() const {
1762 return getImpl()->props;
1763}
1764
1765OpenBundleType OpenBundleType::getConstType(bool isConst) const {
1766 if (isConst == this->isConst())
1767 return *this;
1768 return get(getContext(), getElements(), isConst);
1769}
1770
1771std::optional<unsigned> OpenBundleType::getElementIndex(StringAttr name) {
1772 for (const auto &it : llvm::enumerate(getElements())) {
1773 auto element = it.value();
1774 if (element.name == name) {
1775 return unsigned(it.index());
1776 }
1777 }
1778 return std::nullopt;
1779}
1780
1781std::optional<unsigned> OpenBundleType::getElementIndex(StringRef name) {
1782 for (const auto &it : llvm::enumerate(getElements())) {
1783 auto element = it.value();
1784 if (element.name.getValue() == name) {
1785 return unsigned(it.index());
1786 }
1787 }
1788 return std::nullopt;
1789}
1790
1791StringAttr OpenBundleType::getElementNameAttr(size_t index) {
1792 assert(index < getNumElements() &&
1793 "index must be less than number of fields in bundle");
1794 return getElements()[index].name;
1795}
1796
1797StringRef OpenBundleType::getElementName(size_t index) {
1798 return getElementNameAttr(index).getValue();
1799}
1800
1801std::optional<OpenBundleType::BundleElement>
1802OpenBundleType::getElement(StringAttr name) {
1803 if (auto maybeIndex = getElementIndex(name))
1804 return getElements()[*maybeIndex];
1805 return std::nullopt;
1806}
1807
1808std::optional<OpenBundleType::BundleElement>
1809OpenBundleType::getElement(StringRef name) {
1810 if (auto maybeIndex = getElementIndex(name))
1811 return getElements()[*maybeIndex];
1812 return std::nullopt;
1813}
1814
1815/// Look up an element by index.
1816OpenBundleType::BundleElement OpenBundleType::getElement(size_t index) {
1817 assert(index < getNumElements() &&
1818 "index must be less than number of fields in bundle");
1819 return getElements()[index];
1820}
1821
1822OpenBundleType::ElementType OpenBundleType::getElementType(StringAttr name) {
1823 auto element = getElement(name);
1824 return element ? element->type : FIRRTLBaseType();
1825}
1826
1827OpenBundleType::ElementType OpenBundleType::getElementType(StringRef name) {
1828 auto element = getElement(name);
1829 return element ? element->type : FIRRTLBaseType();
1830}
1831
1832OpenBundleType::ElementType OpenBundleType::getElementType(size_t index) const {
1833 assert(index < getNumElements() &&
1834 "index must be less than number of fields in bundle");
1835 return getElements()[index].type;
1836}
1837
1838uint64_t OpenBundleType::getFieldID(uint64_t index) const {
1839 return getImpl()->fieldIDs[index];
1840}
1841
1842uint64_t OpenBundleType::getIndexForFieldID(uint64_t fieldID) const {
1843 assert(!getElements().empty() && "Bundle must have >0 fields");
1844 auto fieldIDs = getImpl()->fieldIDs;
1845 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1846 return std::distance(fieldIDs.begin(), it);
1847}
1848
1849std::pair<uint64_t, uint64_t>
1850OpenBundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1851 auto index = getIndexForFieldID(fieldID);
1852 auto elementFieldID = getFieldID(index);
1853 return {index, fieldID - elementFieldID};
1854}
1855
1856std::pair<Type, uint64_t>
1857OpenBundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1858 if (fieldID == 0)
1859 return {*this, 0};
1860 auto subfieldIndex = getIndexForFieldID(fieldID);
1861 auto subfieldType = getElementType(subfieldIndex);
1862 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1863 return {subfieldType, subfieldID};
1864}
1865
1866uint64_t OpenBundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1867
1868std::pair<uint64_t, bool>
1869OpenBundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1870 auto childRoot = getFieldID(index);
1871 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1872 : (getFieldID(index + 1) - 1);
1873 return std::make_pair(fieldID - childRoot,
1874 fieldID >= childRoot && fieldID <= rangeEnd);
1875}
1876
1877bool OpenBundleType::isConst() const { return getImpl()->isConst; }
1878
1879OpenBundleType::ElementType
1880OpenBundleType::getElementTypePreservingConst(size_t index) {
1881 auto type = getElementType(index);
1882 // TODO: ConstTypeInterface / Trait ?
1883 return TypeSwitch<FIRRTLType, ElementType>(type)
1884 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
1885 return type.getConstType(type.isConst() || isConst());
1886 })
1887 .Default(type);
1888}
1889
1890LogicalResult
1891OpenBundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1892 ArrayRef<BundleElement> elements, bool isConst) {
1893 SmallPtrSet<StringAttr, 4> nameSet;
1894 for (auto &element : elements) {
1895 if (!nameSet.insert(element.name).second)
1896 return emitErrorFn() << "duplicate field name " << element.name
1897 << " in openbundle";
1898 if (FIRRTLType(element.type).containsReference() && isConst)
1899 return emitErrorFn()
1900 << "'const' bundle cannot have references, but element "
1901 << element.name << " has type " << element.type;
1902 if (type_isa<LHSType>(element.type))
1903 return emitErrorFn() << "bundle element " << element.name
1904 << " cannot have a left-hand side type";
1905 }
1906
1907 return success();
1908}
1909
1910//===----------------------------------------------------------------------===//
1911// FVectorType
1912//===----------------------------------------------------------------------===//
1913
1916 using KeyTy = std::tuple<FIRRTLBaseType, size_t, char>;
1917
1925
1926 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1927
1929
1930 static FVectorTypeStorage *construct(TypeStorageAllocator &allocator,
1931 KeyTy key) {
1932 return new (allocator.allocate<FVectorTypeStorage>())
1933 FVectorTypeStorage(std::get<0>(key), std::get<1>(key),
1934 static_cast<bool>(std::get<2>(key)));
1935 }
1936
1939
1940 /// This holds the bits for the type's recursive properties, and can hold a
1941 /// pointer to a passive version of the type.
1945};
1946
1947FVectorType FVectorType::get(FIRRTLBaseType elementType, size_t numElements,
1948 bool isConst) {
1949 return Base::get(elementType.getContext(), elementType, numElements, isConst);
1950}
1951
1952FIRRTLBaseType FVectorType::getElementType() const {
1953 return getImpl()->elementType;
1954}
1955
1956size_t FVectorType::getNumElements() const { return getImpl()->numElements; }
1957
1958/// Return the recursive properties of the type.
1959RecursiveTypeProperties FVectorType::getRecursiveTypeProperties() const {
1960 return getImpl()->props;
1961}
1962
1963/// Return this type with any flip types recursively removed from itself.
1964FIRRTLBaseType FVectorType::getPassiveType() {
1965 auto *impl = getImpl();
1966
1967 // If we've already determined and cached the passive type, use it.
1968 if (impl->passiveType)
1969 return impl->passiveType;
1970
1971 // If this type is already passive, return it and remember for next time.
1972 if (impl->elementType.getRecursiveTypeProperties().isPassive)
1973 return impl->passiveType = *this;
1974
1975 // Otherwise, rebuild a passive version.
1976 auto passiveType = FVectorType::get(getElementType().getPassiveType(),
1977 getNumElements(), isConst());
1978 impl->passiveType = passiveType;
1979 return passiveType;
1980}
1981
1982FVectorType FVectorType::getConstType(bool isConst) const {
1983 if (isConst == this->isConst())
1984 return *this;
1985 return get(getElementType(), getNumElements(), isConst);
1986}
1987
1988FVectorType FVectorType::getAllConstDroppedType() {
1989 if (!containsConst())
1990 return *this;
1991 return get(getElementType().getAllConstDroppedType(), getNumElements(),
1992 false);
1993}
1994
1995/// Return this type with any type aliases recursively removed from itself.
1996FIRRTLBaseType FVectorType::getAnonymousType() {
1997 auto *impl = getImpl();
1998
1999 if (impl->anonymousType)
2000 return impl->anonymousType;
2001
2002 // If this type is already anonymous, return it and remember for next time.
2003 if (!impl->props.containsTypeAlias)
2004 return impl->anonymousType = *this;
2005
2006 // Otherwise, rebuild an anonymous version.
2007 auto anonymousType = FVectorType::get(getElementType().getAnonymousType(),
2008 getNumElements(), isConst());
2009 impl->anonymousType = anonymousType;
2010 return anonymousType;
2011}
2012
2013uint64_t FVectorType::getFieldID(uint64_t index) const {
2014 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2015}
2016
2017uint64_t FVectorType::getIndexForFieldID(uint64_t fieldID) const {
2018 assert(fieldID && "fieldID must be at least 1");
2019 // Divide the field ID by the number of fieldID's per element.
2020 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2021}
2022
2023std::pair<uint64_t, uint64_t>
2024FVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2025 auto index = getIndexForFieldID(fieldID);
2026 auto elementFieldID = getFieldID(index);
2027 return {index, fieldID - elementFieldID};
2028}
2029
2030std::pair<Type, uint64_t>
2031FVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2032 if (fieldID == 0)
2033 return {*this, 0};
2034 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2035}
2036
2037uint64_t FVectorType::getMaxFieldID() const {
2038 return getNumElements() *
2039 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2040}
2041
2042std::pair<uint64_t, bool>
2043FVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2044 auto childRoot = getFieldID(index);
2045 auto rangeEnd =
2046 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2047 return std::make_pair(fieldID - childRoot,
2048 fieldID >= childRoot && fieldID <= rangeEnd);
2049}
2050
2051bool FVectorType::isConst() const { return getImpl()->isConst; }
2052
2053FVectorType::ElementType FVectorType::getElementTypePreservingConst() {
2054 auto type = getElementType();
2055 return type.getConstType(type.isConst() || isConst());
2056}
2057
2058//===----------------------------------------------------------------------===//
2059// OpenVectorType
2060//===----------------------------------------------------------------------===//
2061
2063 using KeyTy = std::tuple<FIRRTLType, size_t, char>;
2064
2072
2073 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2074
2076
2077 static OpenVectorTypeStorage *construct(TypeStorageAllocator &allocator,
2078 KeyTy key) {
2079 return new (allocator.allocate<OpenVectorTypeStorage>())
2080 OpenVectorTypeStorage(std::get<0>(key), std::get<1>(key),
2081 static_cast<bool>(std::get<2>(key)));
2082 }
2083
2086
2089};
2090
2091OpenVectorType OpenVectorType::get(FIRRTLType elementType, size_t numElements,
2092 bool isConst) {
2093 return Base::get(elementType.getContext(), elementType, numElements, isConst);
2094}
2095
2096FIRRTLType OpenVectorType::getElementType() const {
2097 return getImpl()->elementType;
2098}
2099
2100size_t OpenVectorType::getNumElements() const { return getImpl()->numElements; }
2101
2102/// Return the recursive properties of the type.
2103RecursiveTypeProperties OpenVectorType::getRecursiveTypeProperties() const {
2104 return getImpl()->props;
2105}
2106
2107OpenVectorType OpenVectorType::getConstType(bool isConst) const {
2108 if (isConst == this->isConst())
2109 return *this;
2110 return get(getElementType(), getNumElements(), isConst);
2111}
2112
2113uint64_t OpenVectorType::getFieldID(uint64_t index) const {
2114 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2115}
2116
2117uint64_t OpenVectorType::getIndexForFieldID(uint64_t fieldID) const {
2118 assert(fieldID && "fieldID must be at least 1");
2119 // Divide the field ID by the number of fieldID's per element.
2120 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2121}
2122
2123std::pair<uint64_t, uint64_t>
2124OpenVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2125 auto index = getIndexForFieldID(fieldID);
2126 auto elementFieldID = getFieldID(index);
2127 return {index, fieldID - elementFieldID};
2128}
2129
2130std::pair<Type, uint64_t>
2131OpenVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2132 if (fieldID == 0)
2133 return {*this, 0};
2134 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2135}
2136
2137uint64_t OpenVectorType::getMaxFieldID() const {
2138 // If this is requirement, make ODS constraint or actual elementType.
2139 return getNumElements() *
2140 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2141}
2142
2143std::pair<uint64_t, bool>
2144OpenVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2145 auto childRoot = getFieldID(index);
2146 auto rangeEnd =
2147 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2148 return std::make_pair(fieldID - childRoot,
2149 fieldID >= childRoot && fieldID <= rangeEnd);
2150}
2151
2152bool OpenVectorType::isConst() const { return getImpl()->isConst; }
2153
2154OpenVectorType::ElementType OpenVectorType::getElementTypePreservingConst() {
2155 auto type = getElementType();
2156 // TODO: ConstTypeInterface / Trait ?
2157 return TypeSwitch<FIRRTLType, ElementType>(type)
2158 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
2159 return type.getConstType(type.isConst() || isConst());
2160 })
2161 .Default(type);
2162}
2163
2164LogicalResult
2165OpenVectorType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2167 bool isConst) {
2168 if (elementType.containsReference() && isConst)
2169 return emitErrorFn() << "vector cannot be const with references";
2170 if (type_isa<LHSType>(elementType))
2171 return emitErrorFn() << "vector cannot have a left-hand side type";
2172 return success();
2173}
2174
2175//===----------------------------------------------------------------------===//
2176// FEnum Type
2177//===----------------------------------------------------------------------===//
2178
2180 using KeyTy = std::tuple<ArrayRef<FEnumType::EnumElement>, char>;
2181
2182 FEnumTypeStorage(ArrayRef<FEnumType::EnumElement> elements, bool isConst)
2184 elements(elements.begin(), elements.end()) {
2185 RecursiveTypeProperties props{true, false, false, isConst,
2186 false, false, false};
2187 dataSize = 0;
2188 for (auto &element : elements) {
2189 auto type = element.type;
2190 auto eltInfo = type.getRecursiveTypeProperties();
2191 props.containsConst |= eltInfo.containsConst;
2192 props.containsTypeAlias |= eltInfo.containsTypeAlias;
2193
2194 dataSize = std::max((size_t)type.getBitWidthOrSentinel(), dataSize);
2195 }
2196 recProps = props;
2197 }
2198
2199 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2200
2201 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
2202
2203 static llvm::hash_code hashKey(const KeyTy &key) {
2204 return llvm::hash_value(key);
2205 }
2206
2207 static FEnumTypeStorage *construct(TypeStorageAllocator &allocator,
2208 KeyTy key) {
2209 return new (allocator.allocate<FEnumTypeStorage>())
2210 FEnumTypeStorage(std::get<0>(key), static_cast<bool>(std::get<1>(key)));
2211 }
2212
2213 SmallVector<FEnumType::EnumElement, 4> elements;
2215 size_t dataSize;
2217};
2218
2219FEnumType FEnumType::get(::mlir::MLIRContext *context,
2220 ArrayRef<EnumElement> elements, bool isConst) {
2221 return Base::get(context, elements, isConst);
2222}
2223
2224ArrayRef<FEnumType::EnumElement> FEnumType::getElements() const {
2225 return getImpl()->elements;
2226}
2227
2228FEnumType FEnumType::getConstType(bool isConst) const {
2229 return get(getContext(), getElements(), isConst);
2230}
2231
2232FEnumType FEnumType::getAllConstDroppedType() {
2233 if (!containsConst())
2234 return *this;
2235
2236 SmallVector<EnumElement> constDroppedElements(
2237 llvm::map_range(getElements(), [](EnumElement element) {
2238 element.type = element.type.getAllConstDroppedType();
2239 return element;
2240 }));
2241 return get(getContext(), constDroppedElements, false);
2242}
2243
2244/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
2245RecursiveTypeProperties FEnumType::getRecursiveTypeProperties() const {
2246 return getImpl()->recProps;
2247}
2248
2249std::optional<unsigned> FEnumType::getElementIndex(StringAttr name) {
2250 for (const auto &it : llvm::enumerate(getElements())) {
2251 auto element = it.value();
2252 if (element.name == name) {
2253 return unsigned(it.index());
2254 }
2255 }
2256 return std::nullopt;
2257}
2258
2259size_t FEnumType::getBitWidth() { return getDataWidth() + getTagWidth(); }
2260
2261size_t FEnumType::getDataWidth() { return getImpl()->dataSize; }
2262
2263size_t FEnumType::getTagWidth() {
2264 if (getElements().size() == 0)
2265 return 0;
2266 // Each tag has the same type.
2267 return cast<IntegerType>(getElements()[0].value.getType()).getWidth();
2268}
2269
2270std::optional<unsigned> FEnumType::getElementIndex(StringRef name) {
2271 for (const auto &it : llvm::enumerate(getElements())) {
2272 auto element = it.value();
2273 if (element.name.getValue() == name) {
2274 return unsigned(it.index());
2275 }
2276 }
2277 return std::nullopt;
2278}
2279
2280StringAttr FEnumType::getElementNameAttr(size_t index) {
2281 assert(index < getNumElements() &&
2282 "index must be less than number of fields in enum");
2283 return getElements()[index].name;
2284}
2285
2286StringRef FEnumType::getElementName(size_t index) {
2287 return getElementNameAttr(index).getValue();
2288}
2289
2290IntegerAttr FEnumType::getElementValueAttr(size_t index) {
2291 return getElements()[index].value;
2292}
2293
2294APInt FEnumType::getElementValue(size_t index) {
2295 return getElementValueAttr(index).getValue();
2296}
2297
2298FIRRTLBaseType FEnumType::getElementType(size_t index) {
2299 return getElements()[index].type;
2300}
2301
2302std::optional<FEnumType::EnumElement> FEnumType::getElement(StringAttr name) {
2303 if (auto maybeIndex = getElementIndex(name))
2304 return getElements()[*maybeIndex];
2305 return std::nullopt;
2306}
2307
2308std::optional<FEnumType::EnumElement> FEnumType::getElement(StringRef name) {
2309 if (auto maybeIndex = getElementIndex(name))
2310 return getElements()[*maybeIndex];
2311 return std::nullopt;
2312}
2313
2314/// Look up an element by index.
2315FEnumType::EnumElement FEnumType::getElement(size_t index) {
2316 assert(index < getNumElements() &&
2317 "index must be less than number of fields in enum");
2318 return getElements()[index];
2319}
2320
2321FIRRTLBaseType FEnumType::getElementType(StringAttr name) {
2322 auto element = getElement(name);
2323 return element ? element->type : FIRRTLBaseType();
2324}
2325
2326FIRRTLBaseType FEnumType::getElementType(StringRef name) {
2327 auto element = getElement(name);
2328 return element ? element->type : FIRRTLBaseType();
2329}
2330
2331FIRRTLBaseType FEnumType::getElementType(size_t index) const {
2332 assert(index < getNumElements() &&
2333 "index must be less than number of fields in enum");
2334 return getElements()[index].type;
2335}
2336
2337FIRRTLBaseType FEnumType::getElementTypePreservingConst(size_t index) {
2338 auto type = getElementType(index);
2339 return type.getConstType(type.isConst() || isConst());
2340}
2341
2342LogicalResult FEnumType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2343 ArrayRef<EnumElement> elements, bool isConst) {
2344 bool first = true;
2345 IntegerAttr previous;
2346 SmallPtrSet<Attribute, 4> nameSet;
2347
2348 for (auto &elt : elements) {
2349 auto r = elt.type.getRecursiveTypeProperties();
2350 if (!r.isPassive)
2351 return emitErrorFn() << "enum field " << elt.name << " not passive";
2352 if (r.containsAnalog)
2353 return emitErrorFn() << "enum field " << elt.name << " contains analog";
2354 if (r.hasUninferredWidth)
2355 return emitErrorFn() << "enum field " << elt.name
2356 << " has uninferred width";
2357 if (r.hasUninferredReset)
2358 return emitErrorFn() << "enum field " << elt.name
2359 << " has uninferred reset";
2360 if (r.containsConst && !isConst)
2361 return emitErrorFn() << "enum with 'const' elements must be 'const'";
2362 // Ensure that each tag has a unique name.
2363 if (!nameSet.insert(elt.name).second)
2364 return emitErrorFn() << "duplicate variant name " << elt.name
2365 << " in enum";
2366 // Ensure that each tag is increasing and unique.
2367 if (first) {
2368 previous = elt.value;
2369 first = false;
2370 } else {
2371 auto current = elt.value;
2372 if (previous.getType() != current.getType())
2373 return emitErrorFn() << "enum variant " << elt.name << " has type"
2374 << current.getType()
2375 << " which is different than previous variant "
2376 << previous.getType();
2377
2378 if (previous.getValue().getBitWidth() != current.getValue().getBitWidth())
2379 return emitErrorFn() << "enum variant " << elt.name << " has bitwidth"
2380 << current.getValue().getBitWidth()
2381 << " which is different than previous variant "
2382 << previous.getValue().getBitWidth();
2383 if (previous.getValue().uge(current.getValue()))
2384 return emitErrorFn()
2385 << "enum variant " << elt.name << " has value " << current
2386 << " which is not greater than previous variant " << previous;
2387 }
2388 // TODO: exclude reference containing
2389 }
2390 return success();
2391}
2392
2393/// Return this type with any type aliases recursively removed from itself.
2394FIRRTLBaseType FEnumType::getAnonymousType() {
2395 auto *impl = getImpl();
2396
2397 if (impl->anonymousType)
2398 return impl->anonymousType;
2399
2400 if (!impl->recProps.containsTypeAlias)
2401 return impl->anonymousType = *this;
2402
2403 SmallVector<FEnumType::EnumElement, 4> elements;
2404
2405 for (auto element : getElements())
2406 elements.push_back(
2407 {element.name, element.value, element.type.getAnonymousType()});
2408 return impl->anonymousType = FEnumType::get(getContext(), elements);
2409}
2410
2411//===----------------------------------------------------------------------===//
2412// BaseTypeAliasType
2413//===----------------------------------------------------------------------===//
2414
2417 using KeyTy = std::tuple<StringAttr, FIRRTLBaseType>;
2418
2422
2423 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2424
2425 KeyTy getAsKey() const { return KeyTy(name, innerType); }
2426
2427 static llvm::hash_code hashKey(const KeyTy &key) {
2428 return llvm::hash_value(key);
2429 }
2430
2431 static BaseTypeAliasStorage *construct(TypeStorageAllocator &allocator,
2432 KeyTy key) {
2433 return new (allocator.allocate<BaseTypeAliasStorage>())
2434 BaseTypeAliasStorage(std::get<0>(key), std::get<1>(key));
2435 }
2436 StringAttr name;
2439};
2440
2441auto BaseTypeAliasType::get(StringAttr name, FIRRTLBaseType innerType)
2442 -> BaseTypeAliasType {
2443 return Base::get(name.getContext(), name, innerType);
2444}
2445
2446auto BaseTypeAliasType::getName() const -> StringAttr {
2447 return getImpl()->name;
2448}
2449
2450auto BaseTypeAliasType::getInnerType() const -> FIRRTLBaseType {
2451 return getImpl()->innerType;
2452}
2453
2454FIRRTLBaseType BaseTypeAliasType::getAnonymousType() {
2455 auto *impl = getImpl();
2456 if (impl->anonymousType)
2457 return impl->anonymousType;
2458 return impl->anonymousType = getInnerType().getAnonymousType();
2459}
2460
2461FIRRTLBaseType BaseTypeAliasType::getPassiveType() {
2462 return getModifiedType(getInnerType().getPassiveType());
2463}
2464
2465RecursiveTypeProperties BaseTypeAliasType::getRecursiveTypeProperties() const {
2466 auto rtp = getInnerType().getRecursiveTypeProperties();
2467 rtp.containsTypeAlias = true;
2468 return rtp;
2469}
2470
2471// If a given `newInnerType` is identical to innerType, return `*this`
2472// because we can reuse the type alias. Otherwise return `newInnerType`.
2474BaseTypeAliasType::getModifiedType(FIRRTLBaseType newInnerType) const {
2475 if (newInnerType == getInnerType())
2476 return *this;
2477 return newInnerType;
2478}
2479
2480// FieldIDTypeInterface implementation.
2481FIRRTLBaseType BaseTypeAliasType::getAllConstDroppedType() {
2482 return getModifiedType(getInnerType().getAllConstDroppedType());
2483}
2484
2485FIRRTLBaseType BaseTypeAliasType::getConstType(bool isConst) const {
2486 return getModifiedType(getInnerType().getConstType(isConst));
2487}
2488
2489std::pair<Type, uint64_t>
2490BaseTypeAliasType::getSubTypeByFieldID(uint64_t fieldID) const {
2491 return hw::FieldIdImpl::getSubTypeByFieldID(getInnerType(), fieldID);
2492}
2493
2494uint64_t BaseTypeAliasType::getMaxFieldID() const {
2495 return hw::FieldIdImpl::getMaxFieldID(getInnerType());
2496}
2497
2498std::pair<uint64_t, bool>
2499BaseTypeAliasType::projectToChildFieldID(uint64_t fieldID,
2500 uint64_t index) const {
2501 return hw::FieldIdImpl::projectToChildFieldID(getInnerType(), fieldID, index);
2502}
2503
2504uint64_t BaseTypeAliasType::getIndexForFieldID(uint64_t fieldID) const {
2505 return hw::FieldIdImpl::getIndexForFieldID(getInnerType(), fieldID);
2506}
2507
2508uint64_t BaseTypeAliasType::getFieldID(uint64_t index) const {
2509 return hw::FieldIdImpl::getFieldID(getInnerType(), index);
2510}
2511
2512std::pair<uint64_t, uint64_t>
2513BaseTypeAliasType::getIndexAndSubfieldID(uint64_t fieldID) const {
2514 return hw::FieldIdImpl::getIndexAndSubfieldID(getInnerType(), fieldID);
2515}
2516
2517//===----------------------------------------------------------------------===//
2518// LHSType
2519//===----------------------------------------------------------------------===//
2520
2521LHSType LHSType::get(FIRRTLBaseType type) {
2522 return LHSType::get(type.getContext(), type);
2523}
2524
2525LogicalResult LHSType::verify(function_ref<InFlightDiagnostic()> emitError,
2526 FIRRTLBaseType type) {
2527 if (type.containsAnalog())
2528 return emitError() << "lhs type cannot contain an AnalogType";
2529 if (!type.isPassive())
2530 return emitError() << "lhs type cannot contain a non-passive type";
2531 if (type.containsReference())
2532 return emitError() << "lhs type cannot contain a reference";
2533 if (type_isa<LHSType>(type))
2534 return emitError() << "lhs type cannot contain a lhs type";
2535
2536 return success();
2537}
2538
2539//===----------------------------------------------------------------------===//
2540// RefType
2541//===----------------------------------------------------------------------===//
2542
2543auto RefType::get(FIRRTLBaseType type, bool forceable, SymbolRefAttr layer)
2544 -> RefType {
2545 return Base::get(type.getContext(), type, forceable, layer);
2546}
2547
2548auto RefType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2549 FIRRTLBaseType base, bool forceable, SymbolRefAttr layer)
2550 -> LogicalResult {
2551 if (!base.isPassive())
2552 return emitErrorFn() << "reference base type must be passive";
2553 if (forceable && base.containsConst())
2554 return emitErrorFn()
2555 << "forceable reference base type cannot contain const";
2556 return success();
2557}
2558
2559RecursiveTypeProperties RefType::getRecursiveTypeProperties() const {
2560 auto rtp = getType().getRecursiveTypeProperties();
2561 rtp.containsReference = true;
2562 // References are not "passive", per FIRRTL spec.
2563 rtp.isPassive = false;
2564 return rtp;
2565}
2566
2567//===----------------------------------------------------------------------===//
2568// AnalogType
2569//===----------------------------------------------------------------------===//
2570
2571AnalogType AnalogType::get(mlir::MLIRContext *context) {
2572 return AnalogType::get(context, -1, false);
2573}
2574
2575AnalogType AnalogType::get(mlir::MLIRContext *context,
2576 std::optional<int32_t> width, bool isConst) {
2577 return AnalogType::get(context, width ? *width : -1, isConst);
2578}
2579
2580LogicalResult AnalogType::verify(function_ref<InFlightDiagnostic()> emitError,
2581 int32_t widthOrSentinel, bool isConst) {
2582 if (widthOrSentinel < -1)
2583 return emitError() << "invalid width";
2584 return success();
2585}
2586
2587int32_t AnalogType::getWidthOrSentinel() const { return getImpl()->width; }
2588
2589AnalogType AnalogType::getConstType(bool isConst) const {
2590 if (isConst == this->isConst())
2591 return *this;
2592 return get(getContext(), getWidthOrSentinel(), isConst);
2593}
2594
2595//===----------------------------------------------------------------------===//
2596// ClockType
2597//===----------------------------------------------------------------------===//
2598
2599ClockType ClockType::getConstType(bool isConst) const {
2600 if (isConst == this->isConst())
2601 return *this;
2602 return get(getContext(), isConst);
2603}
2604
2605//===----------------------------------------------------------------------===//
2606// ResetType
2607//===----------------------------------------------------------------------===//
2608
2609ResetType ResetType::getConstType(bool isConst) const {
2610 if (isConst == this->isConst())
2611 return *this;
2612 return get(getContext(), isConst);
2613}
2614
2615//===----------------------------------------------------------------------===//
2616// AsyncResetType
2617//===----------------------------------------------------------------------===//
2618
2619AsyncResetType AsyncResetType::getConstType(bool isConst) const {
2620 if (isConst == this->isConst())
2621 return *this;
2622 return get(getContext(), isConst);
2623}
2624
2625//===----------------------------------------------------------------------===//
2626// ClassType
2627//===----------------------------------------------------------------------===//
2628
2630 using KeyTy = std::tuple<FlatSymbolRefAttr, ArrayRef<ClassElement>>;
2631
2632 static ClassTypeStorage *construct(TypeStorageAllocator &allocator,
2633 KeyTy key) {
2634 auto name = std::get<0>(key);
2635 auto elements = allocator.copyInto(std::get<1>(key));
2636
2637 // build the field ID table
2638 SmallVector<uint64_t, 4> ids;
2639 uint64_t id = 0;
2640 ids.reserve(elements.size());
2641 for (auto &element : elements) {
2642 id += 1;
2643 ids.push_back(id);
2644 id += hw::FieldIdImpl::getMaxFieldID(element.type);
2645 }
2646
2647 auto fieldIDs = allocator.copyInto(ArrayRef(ids));
2648 auto maxFieldID = id;
2649
2650 return new (allocator.allocate<ClassTypeStorage>())
2652 }
2653
2654 ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef<ClassElement> elements,
2655 ArrayRef<uint64_t> fieldIDs, uint64_t maxFieldID)
2658
2659 bool operator==(const KeyTy &key) const { return getAsKey() == key; }
2660
2661 KeyTy getAsKey() const { return KeyTy(name, elements); }
2662
2663 FlatSymbolRefAttr name;
2664 ArrayRef<ClassElement> elements;
2665 ArrayRef<uint64_t> fieldIDs;
2666 uint64_t maxFieldID;
2667};
2668
2669ClassType ClassType::get(FlatSymbolRefAttr name,
2670 ArrayRef<ClassElement> elements) {
2671 return get(name.getContext(), name, elements);
2672}
2673
2674StringRef ClassType::getName() const {
2675 return getNameAttr().getAttr().getValue();
2676}
2677
2678FlatSymbolRefAttr ClassType::getNameAttr() const { return getImpl()->name; }
2679
2680ArrayRef<ClassElement> ClassType::getElements() const {
2681 return getImpl()->elements;
2682}
2683
2684const ClassElement &ClassType::getElement(IntegerAttr index) const {
2685 return getElement(index.getValue().getZExtValue());
2686}
2687
2688const ClassElement &ClassType::getElement(size_t index) const {
2689 return getElements()[index];
2690}
2691
2692std::optional<uint64_t> ClassType::getElementIndex(StringRef fieldName) const {
2693 for (const auto [i, e] : llvm::enumerate(getElements()))
2694 if (fieldName == e.name)
2695 return i;
2696 return {};
2697}
2698
2699void ClassType::printInterface(AsmPrinter &p) const {
2700 p.printSymbolName(getName());
2701 p << "(";
2702 bool first = true;
2703 for (const auto &element : getElements()) {
2704 if (!first)
2705 p << ", ";
2706 p << element.direction << " ";
2707 p.printKeywordOrString(element.name);
2708 p << ": " << element.type;
2709 first = false;
2710 }
2711 p << ")";
2712}
2713
2714uint64_t ClassType::getFieldID(uint64_t index) const {
2715 return getImpl()->fieldIDs[index];
2716}
2717
2718uint64_t ClassType::getIndexForFieldID(uint64_t fieldID) const {
2719 assert(!getElements().empty() && "Class must have >0 fields");
2720 auto fieldIDs = getImpl()->fieldIDs;
2721 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
2722 return std::distance(fieldIDs.begin(), it);
2723}
2724
2725std::pair<uint64_t, uint64_t>
2726ClassType::getIndexAndSubfieldID(uint64_t fieldID) const {
2727 auto index = getIndexForFieldID(fieldID);
2728 auto elementFieldID = getFieldID(index);
2729 return {index, fieldID - elementFieldID};
2730}
2731
2732std::pair<Type, uint64_t>
2733ClassType::getSubTypeByFieldID(uint64_t fieldID) const {
2734 if (fieldID == 0)
2735 return {*this, 0};
2736 auto subfieldIndex = getIndexForFieldID(fieldID);
2737 auto subfieldType = getElement(subfieldIndex).type;
2738 auto subfieldID = fieldID - getFieldID(subfieldIndex);
2739 return {subfieldType, subfieldID};
2740}
2741
2742uint64_t ClassType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2743
2744std::pair<uint64_t, bool>
2745ClassType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2746 auto childRoot = getFieldID(index);
2747 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
2748 : (getFieldID(index + 1) - 1);
2749 return std::make_pair(fieldID - childRoot,
2750 fieldID >= childRoot && fieldID <= rangeEnd);
2751}
2752
2753namespace {
2754/// Helper to parse interface-like types (ClassType, DomainType).
2755/// This encapsulates the common pattern of parsing @SymbolName(field, field,
2756/// ...)
2757struct InterfaceParser {
2758 AsmParser &parser;
2759
2760 InterfaceParser(AsmParser &parser) : parser(parser) {}
2761
2762 /// Parse the common structure: @SymbolName(field, field, ...)
2763 template <typename FieldContainer>
2764 ParseResult parse(
2765 function_ref<ParseResult(FieldContainer &, AsmParser &parser)> parseField,
2766 StringAttr &symbolName, FieldContainer &fields) {
2767
2768 if (parser.parseSymbolName(symbolName))
2769 return failure();
2770
2771 // Parse required parentheses
2772 if (parser.parseLParen())
2773 return failure();
2774
2775 // Check for empty list (immediate closing paren)
2776 if (failed(parser.parseOptionalRParen())) {
2777 auto parseElement = [&]() -> ParseResult {
2778 return parseField(fields, parser);
2779 };
2780
2781 if (parser.parseCommaSeparatedList(parseElement) || parser.parseRParen())
2782 return failure();
2783 }
2784
2785 return success();
2786 }
2787};
2788} // namespace
2789
2790ParseResult ClassType::parseInterface(AsmParser &parser, ClassType &result) {
2791 InterfaceParser helper(parser);
2792
2793 auto parseField = [](SmallVector<ClassElement> &elements,
2794 AsmParser &parser) -> ParseResult {
2795 // Parse port direction.
2796 Direction direction;
2797 if (succeeded(parser.parseOptionalKeyword("out")))
2798 direction = Direction::Out;
2799 else if (succeeded(parser.parseKeyword("in", "or 'out'")))
2800 direction = Direction::In;
2801 else
2802 return failure();
2803
2804 // Parse port name.
2805 std::string keyword;
2806 if (parser.parseKeywordOrString(&keyword))
2807 return failure();
2808 StringAttr name = StringAttr::get(parser.getContext(), keyword);
2809
2810 // Parse port type.
2811 Type type;
2812 if (parser.parseColonType(type))
2813 return failure();
2814
2815 elements.emplace_back(name, type, direction);
2816 return success();
2817 };
2818
2819 StringAttr symbolName;
2820 SmallVector<ClassElement> elements;
2821 if (helper.parse<SmallVector<ClassElement>>(parseField, symbolName, elements))
2822 return failure();
2823
2824 result = ClassType::get(FlatSymbolRefAttr::get(symbolName), elements);
2825 return success();
2826}
2827
2828//===----------------------------------------------------------------------===//
2829// DomainType
2830//===----------------------------------------------------------------------===//
2831
2832ParseResult DomainType::parseInterface(AsmParser &parser, DomainType &result) {
2833 InterfaceParser helper(parser);
2834
2835 auto parseField = [](SmallVector<Attribute> &fields,
2836 AsmParser &parser) -> ParseResult {
2837 std::string fieldNameStr;
2838 Type fieldTypeRaw;
2839 if (parser.parseKeywordOrString(&fieldNameStr) || parser.parseColon() ||
2840 parser.parseType(fieldTypeRaw))
2841 return failure();
2842
2843 auto fieldType = dyn_cast<PropertyType>(fieldTypeRaw);
2844 if (!fieldType)
2845 return parser.emitError(parser.getCurrentLocation(),
2846 "expected property type");
2847
2848 auto fieldName = StringAttr::get(parser.getContext(), fieldNameStr);
2849 fields.push_back(
2850 DomainFieldAttr::get(parser.getContext(), fieldName, fieldType));
2851 return success();
2852 };
2853
2854 StringAttr symbolName;
2855 SmallVector<Attribute> fields;
2856 if (helper.parse<SmallVector<Attribute>>(parseField, symbolName, fields))
2857 return failure();
2858
2859 result = DomainType::get(FlatSymbolRefAttr::get(symbolName),
2860 ArrayAttr::get(parser.getContext(), fields));
2861 return success();
2862}
2863
2864DomainType DomainType::get(FlatSymbolRefAttr name, ArrayAttr fields) {
2865 return Base::get(name.getContext(), name, fields);
2866}
2867
2868DomainType DomainType::getFromDomainOp(DomainOp domainOp) {
2869 auto name = FlatSymbolRefAttr::get(domainOp.getNameAttr());
2870 return DomainType::get(name, domainOp.getFieldsAttr());
2871}
2872
2873DomainFieldAttr DomainType::getField(size_t index) const {
2874 assert(index < getNumFields() && "index out of bounds");
2875 return cast<DomainFieldAttr>(getFields()[index]);
2876}
2877
2878std::optional<uint64_t> DomainType::getFieldIndex(StringRef fieldName) const {
2879 for (const auto [i, attr] : llvm::enumerate(getFields())) {
2880 auto field = cast<DomainFieldAttr>(attr);
2881 if (fieldName == field.getName())
2882 return i;
2883 }
2884 return {};
2885}
2886
2887std::pair<Type, uint64_t>
2888DomainType::getSubTypeByFieldID(uint64_t fieldID) const {
2889 if (fieldID == 0)
2890 return {*this, 0};
2891
2892 // Domain fields don't have sub-fieldIDs, so fieldID directly maps to field
2893 // index
2894 if (fieldID > getNumFields())
2895 return {Type(), fieldID};
2896
2897 return {getField(fieldID - 1).getType(), 0};
2898}
2899
2900uint64_t DomainType::getMaxFieldID() const {
2901 // Each field gets one fieldID
2902 return getNumFields();
2903}
2904
2905std::pair<uint64_t, bool>
2906DomainType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2907 // Domain fields are flat, so projection is simple
2908 if (index >= getNumFields())
2909 return {0, false};
2910
2911 uint64_t childFieldID = index + 1;
2912 return {0, fieldID == childFieldID};
2913}
2914
2915uint64_t DomainType::getFieldID(uint64_t index) const {
2916 // Domain fields are flat, so fieldID is just index + 1
2917 assert(index < getNumFields() && "index out of bounds");
2918 return index + 1;
2919}
2920
2921uint64_t DomainType::getIndexForFieldID(uint64_t fieldID) const {
2922 // Domain fields are flat, so index is just fieldID - 1
2923 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2924 return fieldID - 1;
2925}
2926
2927std::pair<uint64_t, uint64_t>
2928DomainType::getIndexAndSubfieldID(uint64_t fieldID) const {
2929 // Domain fields are flat (no sub-fields), so subfieldID is always 0
2930 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2931 return {fieldID - 1, 0};
2932}
2933
2934LogicalResult
2935DomainType::verifySymbolUses(Operation *op,
2936 SymbolTableCollection &symbolTable) const {
2937 // Find the circuit op to look up the domain definition
2938 auto circuitOp = op->getParentOfType<CircuitOp>();
2939 if (!circuitOp)
2940 return op->emitError() << "domain type used outside of a circuit";
2941
2942 // Check that the symbol exists
2943 auto *symbol = symbolTable.lookupSymbolIn(circuitOp, getName());
2944 if (!symbol)
2945 return op->emitError() << "domain type references undefined symbol '"
2946 << getName().getValue() << "'";
2947
2948 // Check that the symbol is a domain
2949 auto domainOp = dyn_cast<DomainOp>(symbol);
2950 if (!domainOp)
2951 return op->emitError() << "domain type references symbol '"
2952 << getName().getValue() << "' which is not a domain";
2953
2954 // Verify that the domain type fields match the domain definition
2955 auto expectedFields = domainOp.getFieldsAttr();
2956 auto actualFields = getFields();
2957
2958 // Check field count
2959 if (actualFields.size() != expectedFields.size())
2960 return op->emitError() << "domain type has " << actualFields.size()
2961 << " fields but domain definition has "
2962 << expectedFields.size() << " fields";
2963
2964 // Check each field
2965 for (size_t i = 0; i < actualFields.size(); ++i) {
2966 auto actualField = cast<DomainFieldAttr>(actualFields[i]);
2967 auto expectedField = cast<DomainFieldAttr>(expectedFields[i]);
2968
2969 // Check field name
2970 if (actualField.getName() != expectedField.getName())
2971 return op->emitError() << "domain type field " << i << " has name '"
2972 << actualField.getName().getValue()
2973 << "' but domain definition expects '"
2974 << expectedField.getName().getValue() << "'";
2975
2976 // Check field type
2977 if (actualField.getType() != expectedField.getType())
2978 return op->emitError()
2979 << "domain type field '" << actualField.getName().getValue()
2980 << "' has type " << actualField.getType()
2981 << " but domain definition expects " << expectedField.getType();
2982 }
2983
2984 return success();
2985}
2986
2987//===----------------------------------------------------------------------===//
2988// FIRRTLDialect
2989//===----------------------------------------------------------------------===//
2990
2991void FIRRTLDialect::registerTypes() {
2992 addTypes<
2993#define GET_TYPEDEF_LIST
2994#include "circt/Dialect/FIRRTL/FIRRTLTypes.cpp.inc"
2995 >();
2996}
2997
2998// Get the bit width for this type, return None if unknown. Unlike
2999// getBitWidthOrSentinel(), this can recursively compute the bitwidth of
3000// aggregate types. For bundle and vectors, recursively get the width of each
3001// field element and return the total bit width of the aggregate type. This
3002// returns None, if any of the bundle fields is a flip type, or ground type with
3003// unknown bit width.
3004std::optional<int64_t> firrtl::getBitWidth(FIRRTLBaseType type,
3005 bool ignoreFlip) {
3006 std::function<std::optional<int64_t>(FIRRTLBaseType)> getWidth =
3007 [&](FIRRTLBaseType type) -> std::optional<int64_t> {
3008 return TypeSwitch<FIRRTLBaseType, std::optional<int64_t>>(type)
3009 .Case<BundleType>([&](BundleType bundle) -> std::optional<int64_t> {
3010 int64_t width = 0;
3011 for (auto &elt : bundle) {
3012 if (elt.isFlip && !ignoreFlip)
3013 return std::nullopt;
3014 auto w = getBitWidth(elt.type);
3015 if (!w.has_value())
3016 return std::nullopt;
3017 width += *w;
3018 }
3019 return width;
3020 })
3021 .Case<FEnumType>([&](FEnumType fenum) -> std::optional<int64_t> {
3022 int64_t width = 0;
3023 for (auto &elt : fenum) {
3024 auto w = getBitWidth(elt.type);
3025 if (!w.has_value())
3026 return std::nullopt;
3027 width = std::max(width, *w);
3028 }
3029 return width + fenum.getTagWidth();
3030 })
3031 .Case<FVectorType>([&](auto vector) -> std::optional<int64_t> {
3032 auto w = getBitWidth(vector.getElementType());
3033 if (!w.has_value())
3034 return std::nullopt;
3035 return *w * vector.getNumElements();
3036 })
3037 .Case<IntType>([&](IntType iType) { return iType.getWidth(); })
3038 .Case<ClockType, ResetType, AsyncResetType>([](Type) { return 1; })
3039 .Default([&](auto t) { return std::nullopt; });
3040 };
3041 return getWidth(type);
3042}
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition CHIRRTL.cpp:30
MlirType elementType
Definition CHIRRTL.cpp:29
static std::unique_ptr< Context > context
static ParseResult parseFIRRTLBaseType(FIRRTLBaseType &result, StringRef name, AsmParser &parser)
static ParseResult parseFIRRTLPropertyType(PropertyType &result, StringRef name, AsmParser &parser)
static LogicalResult customTypePrinter(Type type, AsmPrinter &os)
Print a type with a custom printer implementation.
static OptionalParseResult customTypeParser(AsmParser &parser, StringRef name, Type &result)
Parse a type with a custom parser implementation.
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static bool areBundleElementsEquivalent(BundleType::BundleElement destElement, BundleType::BundleElement srcElement, bool destOuterTypeIsConst, bool srcOuterTypeIsConst, bool requiresSameWidth)
Helper to implement the equivalence logic for a pair of bundle elements.
@ ContainsAnalogBitMask
Bit set if the type contains an analog type.
@ HasUninferredWidthBitMask
Bit set fi the type has any uninferred bit widths.
@ IsPassiveBitMask
Bit set if the type only contains passive elements.
static ParseResult parseFIRRTLType(FIRRTLType &result, StringRef name, AsmParser &parser)
Parse a FIRRTLType with a name that has already been parsed.
static unsigned getFieldID(BundleType type, unsigned index)
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static InstancePath empty
FIRRTLBaseType getConstType(bool isConst) const
Return a 'const' or non-'const' version of this type.
FIRRTLBaseType getAnonymousType()
Return this type with any type alias types recursively removed from itself.
bool isResetType()
Return true if this is a valid "reset" type.
FIRRTLBaseType getMaskType()
Return this type with all ground types replaced with UInt<1>.
FIRRTLBaseType getPassiveType()
Return this type with any flip types recursively removed from itself.
int32_t getBitWidthOrSentinel()
If this is an IntType, AnalogType, or sugar type for a single bit (Clock, Reset, etc) then return the...
FIRRTLBaseType getAllConstDroppedType()
Return this type with a 'const' modifiers dropped.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
FIRRTLBaseType getWidthlessType()
Return this type with widths of all ground types removed.
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
bool containsReference()
Return true if this is or contains a Reference type.
RecursiveTypeProperties getRecursiveTypeProperties() const
Return the recursive properties of the type, containing the isPassive, containsAnalog,...
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
This is the common base class between SIntType and UIntType.
IntType getConstType(bool isConst) const
Return a 'const' or non-'const' version of this type.
int32_t getWidthOrSentinel() const
Return the width of this type, or -1 if it has none specified.
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.
std::optional< int32_t > getWidth() const
Return an optional containing the width, if the width is known (or empty if width is unknown).
Represents a limited word-length unsigned integer in SystemC as described in IEEE 1666-2011 ยง7....
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
Direction
This represents the direction of a single port.
Definition FIRRTLEnums.h:27
ParseResult parseNestedType(FIRRTLType &result, AsmParser &parser)
Parse a FIRRTLType.
bool areAnonymousTypesEquivalent(FIRRTLBaseType lhs, FIRRTLBaseType rhs)
Return true if anonymous types of given arguments are equivalent by pointer comparison.
ParseResult parseNestedBaseType(FIRRTLBaseType &result, AsmParser &parser)
bool isTypeInOut(mlir::Type type)
Returns true if the given type has some flipped (aka unaligned) dataflow.
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.
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
bool isTypeLarger(FIRRTLBaseType dstType, FIRRTLBaseType srcType)
Returns true if the destination is at least as wide as a source.
bool containsConst(Type type)
Returns true if the type is or contains a 'const' type whose value is guaranteed to be unchanging at ...
bool hasZeroBitWidth(FIRRTLType type)
Return true if the type has zero bit width.
void printNestedType(Type type, AsmPrinter &os)
Print a type defined by this dialect.
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool areTypesConstCastable(FIRRTLType destType, FIRRTLType srcType, bool srcOuterTypeIsConst=false)
Returns whether the srcType can be const-casted to the destType.
ParseResult parseNestedPropertyType(PropertyType &result, AsmParser &parser)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
std::pair< uint64_t, uint64_t > getIndexAndSubfieldID(Type type, uint64_t fieldID)
uint64_t getFieldID(Type type, uint64_t index)
std::pair<::mlir::Type, uint64_t > getSubTypeByFieldID(Type, uint64_t fieldID)
std::pair< uint64_t, bool > projectToChildFieldID(Type, uint64_t fieldID, uint64_t index)
uint64_t getIndexForFieldID(Type type, uint64_t fieldID)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::hash_code hash_value(const DenseSet< T > &set)
A collection of bits indicating the recursive properties of a type.
Definition FIRRTLTypes.h:72
bool containsReference
Whether the type contains a reference type.
Definition FIRRTLTypes.h:76
bool isPassive
Whether the type only contains passive elements.
Definition FIRRTLTypes.h:74
bool containsAnalog
Whether the type contains an analog type.
Definition FIRRTLTypes.h:78
bool hasUninferredReset
Whether the type has any uninferred reset.
Definition FIRRTLTypes.h:86
bool containsTypeAlias
Whether the type contains a type alias.
Definition FIRRTLTypes.h:82
bool containsConst
Whether the type contains a const type.
Definition FIRRTLTypes.h:80
bool hasUninferredWidth
Whether the type has any uninferred bit widths.
Definition FIRRTLTypes.h:84
bool operator==(const KeyTy &key) const
static BaseTypeAliasStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
BaseTypeAliasStorage(StringAttr name, FIRRTLBaseType innerType)
std::tuple< StringAttr, FIRRTLBaseType > KeyTy
static llvm::hash_code hashKey(const KeyTy &key)
static BundleTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
std::tuple< ArrayRef< BundleType::BundleElement >, char > KeyTy
SmallVector< BundleType::BundleElement, 4 > elements
static llvm::hash_code hashKey(const KeyTy &key)
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
BundleTypeStorage(ArrayRef< BundleType::BundleElement > elements, bool isConst)
bool operator==(const KeyTy &key) const
bool operator==(const KeyTy &key) const
static ClassTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
std::tuple< FlatSymbolRefAttr, ArrayRef< ClassElement > > KeyTy
ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef< ClassElement > elements, ArrayRef< uint64_t > fieldIDs, uint64_t maxFieldID)
SmallVector< FEnumType::EnumElement, 4 > elements
static llvm::hash_code hashKey(const KeyTy &key)
bool operator==(const KeyTy &key) const
static FEnumTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
FEnumTypeStorage(ArrayRef< FEnumType::EnumElement > elements, bool isConst)
std::tuple< ArrayRef< FEnumType::EnumElement >, char > KeyTy
bool operator==(const KeyTy &key) const
static FIRRTLBaseTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
bool operator==(const KeyTy &key) const
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
static FVectorTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
std::tuple< FIRRTLBaseType, size_t, char > KeyTy
FVectorTypeStorage(FIRRTLBaseType elementType, size_t numElements, bool isConst)
SmallVector< OpenBundleType::BundleElement, 4 > elements
static OpenBundleTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
static llvm::hash_code hashKey(const KeyTy &key)
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
OpenBundleTypeStorage(ArrayRef< OpenBundleType::BundleElement > elements, bool isConst)
std::tuple< ArrayRef< OpenBundleType::BundleElement >, char > KeyTy
std::tuple< FIRRTLType, size_t, char > KeyTy
static OpenVectorTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
OpenVectorTypeStorage(FIRRTLType elementType, size_t numElements, bool isConst)
WidthTypeStorage(int32_t width, bool isConst)
std::tuple< int32_t, char > KeyTy
bool operator==(const KeyTy &key) const
static WidthTypeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)