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// NOLINTBEGIN(misc-no-recursion)
1322 // Is a hardware base type
1323 if (isa<FIRRTLBaseType>(type))
1324 return true;
1325 // Check each element of the aggregate
1326 if (auto bundle = dyn_cast<OpenBundleType>(type))
1327 return llvm::any_of(bundle,
1328 [](auto elt) { return hasHardwareElements(elt.type); });
1329 if (auto vector = dyn_cast<OpenVectorType>(type))
1330 return hasHardwareElements(vector.getElementType());
1331 return false;
1332}
1333// NOLINTEND(misc-no-recursion)
1334
1335//===----------------------------------------------------------------------===//
1336// IntType
1337//===----------------------------------------------------------------------===//
1338
1339/// Return a SIntType or UIntType with the specified signedness, width, and
1340/// constness
1341IntType IntType::get(MLIRContext *context, bool isSigned,
1342 int32_t widthOrSentinel, bool isConst) {
1343 if (isSigned)
1344 return SIntType::get(context, widthOrSentinel, isConst);
1345 return UIntType::get(context, widthOrSentinel, isConst);
1346}
1347
1349 if (auto sintType = type_dyn_cast<SIntType>(*this))
1350 return sintType.getWidthOrSentinel();
1351 if (auto uintType = type_dyn_cast<UIntType>(*this))
1352 return uintType.getWidthOrSentinel();
1353 return -1;
1354}
1355
1356//===----------------------------------------------------------------------===//
1357// WidthTypeStorage
1358//===----------------------------------------------------------------------===//
1359
1363 using KeyTy = std::tuple<int32_t, char>;
1364
1365 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1366
1367 KeyTy getAsKey() const { return KeyTy(width, isConst); }
1368
1369 static WidthTypeStorage *construct(TypeStorageAllocator &allocator,
1370 const KeyTy &key) {
1371 return new (allocator.allocate<WidthTypeStorage>())
1372 WidthTypeStorage(std::get<0>(key), std::get<1>(key));
1373 }
1374
1375 int32_t width;
1376};
1377
1379
1380 if (auto sIntType = type_dyn_cast<SIntType>(*this))
1381 return sIntType.getConstType(isConst);
1382 return type_cast<UIntType>(*this).getConstType(isConst);
1383}
1384
1385//===----------------------------------------------------------------------===//
1386// SIntType
1387//===----------------------------------------------------------------------===//
1388
1389SIntType SIntType::get(MLIRContext *context) { return get(context, -1, false); }
1390
1391SIntType SIntType::get(MLIRContext *context, std::optional<int32_t> width,
1392 bool isConst) {
1393 return get(context, width ? *width : -1, isConst);
1394}
1395
1396LogicalResult SIntType::verify(function_ref<InFlightDiagnostic()> emitError,
1397 int32_t widthOrSentinel, bool isConst) {
1398 if (widthOrSentinel < -1)
1399 return emitError() << "invalid width";
1400 return success();
1401}
1402
1403int32_t SIntType::getWidthOrSentinel() const { return getImpl()->width; }
1404
1405SIntType SIntType::getConstType(bool isConst) const {
1406 if (isConst == this->isConst())
1407 return *this;
1408 return get(getContext(), getWidthOrSentinel(), isConst);
1409}
1410
1411//===----------------------------------------------------------------------===//
1412// UIntType
1413//===----------------------------------------------------------------------===//
1414
1415UIntType UIntType::get(MLIRContext *context) { return get(context, -1, false); }
1416
1417UIntType UIntType::get(MLIRContext *context, std::optional<int32_t> width,
1418 bool isConst) {
1419 return get(context, width ? *width : -1, isConst);
1420}
1421
1422LogicalResult UIntType::verify(function_ref<InFlightDiagnostic()> emitError,
1423 int32_t widthOrSentinel, bool isConst) {
1424 if (widthOrSentinel < -1)
1425 return emitError() << "invalid width";
1426 return success();
1427}
1428
1429int32_t UIntType::getWidthOrSentinel() const { return getImpl()->width; }
1430
1431UIntType UIntType::getConstType(bool isConst) const {
1432 if (isConst == this->isConst())
1433 return *this;
1434 return get(getContext(), getWidthOrSentinel(), isConst);
1435}
1436
1437//===----------------------------------------------------------------------===//
1438// Bundle Type
1439//===----------------------------------------------------------------------===//
1440
1443 using KeyTy = std::tuple<ArrayRef<BundleType::BundleElement>, char>;
1444
1445 BundleTypeStorage(ArrayRef<BundleType::BundleElement> elements, bool isConst)
1447 elements(elements.begin(), elements.end()),
1448 props{true, false, false, isConst, false, false, false} {
1449 uint64_t fieldID = 0;
1450 fieldIDs.reserve(elements.size());
1451 for (auto &element : elements) {
1452 auto type = element.type;
1453 auto eltInfo = type.getRecursiveTypeProperties();
1454 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1455 props.containsAnalog |= eltInfo.containsAnalog;
1456 props.containsReference |= eltInfo.containsReference;
1457 props.containsConst |= eltInfo.containsConst;
1458 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1459 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1460 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1461 fieldID += 1;
1462 fieldIDs.push_back(fieldID);
1463 // Increment the field ID for the next field by the number of subfields.
1464 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1465 }
1466 maxFieldID = fieldID;
1467 }
1468
1469 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1470
1471 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1472
1473 static llvm::hash_code hashKey(const KeyTy &key) {
1474 return llvm::hash_value(key);
1475 }
1476
1477 static BundleTypeStorage *construct(TypeStorageAllocator &allocator,
1478 KeyTy key) {
1479 return new (allocator.allocate<BundleTypeStorage>()) BundleTypeStorage(
1480 std::get<0>(key), static_cast<bool>(std::get<1>(key)));
1481 }
1482
1483 SmallVector<BundleType::BundleElement, 4> elements;
1484 SmallVector<uint64_t, 4> fieldIDs;
1485 uint64_t maxFieldID;
1486
1487 /// This holds the bits for the type's recursive properties, and can hold a
1488 /// pointer to a passive version of the type.
1490 BundleType passiveType;
1491 BundleType anonymousType;
1492};
1493
1494BundleType BundleType::get(MLIRContext *context,
1495 ArrayRef<BundleElement> elements, bool isConst) {
1496 return Base::get(context, elements, isConst);
1497}
1498
1499auto BundleType::getElements() const -> ArrayRef<BundleElement> {
1500 return getImpl()->elements;
1501}
1502
1503/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1504RecursiveTypeProperties BundleType::getRecursiveTypeProperties() const {
1505 return getImpl()->props;
1506}
1507
1508/// Return this type with any flip types recursively removed from itself.
1509FIRRTLBaseType BundleType::getPassiveType() {
1510 auto *impl = getImpl();
1511
1512 // If we've already determined and cached the passive type, use it.
1513 if (impl->passiveType)
1514 return impl->passiveType;
1515
1516 // If this type is already passive, use it and remember for next time.
1517 if (impl->props.isPassive) {
1518 impl->passiveType = *this;
1519 return *this;
1520 }
1521
1522 // Otherwise at least one element is non-passive, rebuild a passive version.
1523 SmallVector<BundleType::BundleElement, 16> newElements;
1524 newElements.reserve(impl->elements.size());
1525 for (auto &elt : impl->elements) {
1526 newElements.push_back({elt.name, false, elt.type.getPassiveType()});
1527 }
1528
1529 auto passiveType = BundleType::get(getContext(), newElements, isConst());
1530 impl->passiveType = passiveType;
1531 return passiveType;
1532}
1533
1534BundleType BundleType::getConstType(bool isConst) const {
1535 if (isConst == this->isConst())
1536 return *this;
1537 return get(getContext(), getElements(), isConst);
1538}
1539
1540BundleType BundleType::getAllConstDroppedType() {
1541 if (!containsConst())
1542 return *this;
1543
1544 SmallVector<BundleElement> constDroppedElements(
1545 llvm::map_range(getElements(), [](BundleElement element) {
1546 element.type = element.type.getAllConstDroppedType();
1547 return element;
1548 }));
1549 return get(getContext(), constDroppedElements, false);
1550}
1551
1552std::optional<unsigned> BundleType::getElementIndex(StringAttr name) {
1553 for (const auto &it : llvm::enumerate(getElements())) {
1554 auto element = it.value();
1555 if (element.name == name) {
1556 return unsigned(it.index());
1557 }
1558 }
1559 return std::nullopt;
1560}
1561
1562std::optional<unsigned> BundleType::getElementIndex(StringRef name) {
1563 for (const auto &it : llvm::enumerate(getElements())) {
1564 auto element = it.value();
1565 if (element.name.getValue() == name) {
1566 return unsigned(it.index());
1567 }
1568 }
1569 return std::nullopt;
1570}
1571
1572StringAttr BundleType::getElementNameAttr(size_t index) {
1573 assert(index < getNumElements() &&
1574 "index must be less than number of fields in bundle");
1575 return getElements()[index].name;
1576}
1577
1578StringRef BundleType::getElementName(size_t index) {
1579 return getElementNameAttr(index).getValue();
1580}
1581
1582std::optional<BundleType::BundleElement>
1583BundleType::getElement(StringAttr name) {
1584 if (auto maybeIndex = getElementIndex(name))
1585 return getElements()[*maybeIndex];
1586 return std::nullopt;
1587}
1588
1589std::optional<BundleType::BundleElement>
1590BundleType::getElement(StringRef name) {
1591 if (auto maybeIndex = getElementIndex(name))
1592 return getElements()[*maybeIndex];
1593 return std::nullopt;
1594}
1595
1596/// Look up an element by index.
1597BundleType::BundleElement BundleType::getElement(size_t index) {
1598 assert(index < getNumElements() &&
1599 "index must be less than number of fields in bundle");
1600 return getElements()[index];
1601}
1602
1603FIRRTLBaseType BundleType::getElementType(StringAttr name) {
1604 auto element = getElement(name);
1605 return element ? element->type : FIRRTLBaseType();
1606}
1607
1608FIRRTLBaseType BundleType::getElementType(StringRef name) {
1609 auto element = getElement(name);
1610 return element ? element->type : FIRRTLBaseType();
1611}
1612
1613FIRRTLBaseType BundleType::getElementType(size_t index) const {
1614 assert(index < getNumElements() &&
1615 "index must be less than number of fields in bundle");
1616 return getElements()[index].type;
1617}
1618
1619uint64_t BundleType::getFieldID(uint64_t index) const {
1620 return getImpl()->fieldIDs[index];
1621}
1622
1623uint64_t BundleType::getIndexForFieldID(uint64_t fieldID) const {
1624 assert(!getElements().empty() && "Bundle must have >0 fields");
1625 auto fieldIDs = getImpl()->fieldIDs;
1626 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1627 return std::distance(fieldIDs.begin(), it);
1628}
1629
1630std::pair<uint64_t, uint64_t>
1631BundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1632 auto index = getIndexForFieldID(fieldID);
1633 auto elementFieldID = getFieldID(index);
1634 return {index, fieldID - elementFieldID};
1635}
1636
1637std::pair<Type, uint64_t>
1638BundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1639 if (fieldID == 0)
1640 return {*this, 0};
1641 auto subfieldIndex = getIndexForFieldID(fieldID);
1642 auto subfieldType = getElementType(subfieldIndex);
1643 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1644 return {subfieldType, subfieldID};
1645}
1646
1647uint64_t BundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1648
1649std::pair<uint64_t, bool>
1650BundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1651 auto childRoot = getFieldID(index);
1652 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1653 : (getFieldID(index + 1) - 1);
1654 return std::make_pair(fieldID - childRoot,
1655 fieldID >= childRoot && fieldID <= rangeEnd);
1656}
1657
1658bool BundleType::isConst() const { return getImpl()->isConst; }
1659
1660BundleType::ElementType
1661BundleType::getElementTypePreservingConst(size_t index) {
1662 auto type = getElementType(index);
1663 return type.getConstType(type.isConst() || isConst());
1664}
1665
1666/// Return this type with any type aliases recursively removed from itself.
1667FIRRTLBaseType BundleType::getAnonymousType() {
1668 auto *impl = getImpl();
1669
1670 // If we've already determined and cached the anonymous type, use it.
1671 if (impl->anonymousType)
1672 return impl->anonymousType;
1673
1674 // If this type is already anonymous, use it and remember for next time.
1675 if (!impl->props.containsTypeAlias) {
1676 impl->anonymousType = *this;
1677 return *this;
1678 }
1679
1680 // Otherwise at least one element has an alias type, rebuild an anonymous
1681 // version.
1682 SmallVector<BundleType::BundleElement, 16> newElements;
1683 newElements.reserve(impl->elements.size());
1684 for (auto &elt : impl->elements)
1685 newElements.push_back({elt.name, elt.isFlip, elt.type.getAnonymousType()});
1686
1687 auto anonymousType = BundleType::get(getContext(), newElements, isConst());
1688 impl->anonymousType = anonymousType;
1689 return anonymousType;
1690}
1691
1692LogicalResult BundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1693 ArrayRef<BundleElement> elements,
1694 bool isConst) {
1695 SmallPtrSet<StringAttr, 4> nameSet;
1696 for (auto &element : elements) {
1697 if (!nameSet.insert(element.name).second)
1698 return emitErrorFn() << "duplicate field name " << element.name
1699 << " in bundle";
1700 }
1701
1702 return success();
1703}
1704
1705//===----------------------------------------------------------------------===//
1706// OpenBundle Type
1707//===----------------------------------------------------------------------===//
1708
1710 using KeyTy = std::tuple<ArrayRef<OpenBundleType::BundleElement>, char>;
1711
1712 OpenBundleTypeStorage(ArrayRef<OpenBundleType::BundleElement> elements,
1713 bool isConst)
1714 : elements(elements.begin(), elements.end()),
1715 props{true, false, false, isConst, false, false, false},
1716 isConst(static_cast<char>(isConst)) {
1717 uint64_t fieldID = 0;
1718 fieldIDs.reserve(elements.size());
1719 for (auto &element : elements) {
1720 auto type = element.type;
1721 auto eltInfo = type.getRecursiveTypeProperties();
1722 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1723 props.containsAnalog |= eltInfo.containsAnalog;
1724 props.containsReference |= eltInfo.containsReference;
1725 props.containsConst |= eltInfo.containsConst;
1726 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1727 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1728 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1729 fieldID += 1;
1730 fieldIDs.push_back(fieldID);
1731 // Increment the field ID for the next field by the number of subfields.
1732 // TODO: Maybe just have elementType be FieldIDTypeInterface ?
1733 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1734 }
1735 maxFieldID = fieldID;
1736 }
1737
1738 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1739
1740 static llvm::hash_code hashKey(const KeyTy &key) {
1741 return llvm::hash_value(key);
1742 }
1743
1744 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1745
1746 static OpenBundleTypeStorage *construct(TypeStorageAllocator &allocator,
1747 KeyTy key) {
1748 return new (allocator.allocate<OpenBundleTypeStorage>())
1749 OpenBundleTypeStorage(std::get<0>(key),
1750 static_cast<bool>(std::get<1>(key)));
1751 }
1752
1753 SmallVector<OpenBundleType::BundleElement, 4> elements;
1754 SmallVector<uint64_t, 4> fieldIDs;
1755 uint64_t maxFieldID;
1756
1757 /// This holds the bits for the type's recursive properties, and can hold a
1758 /// pointer to a passive version of the type.
1760
1761 // Whether this is 'const'.
1763};
1764
1765OpenBundleType OpenBundleType::get(MLIRContext *context,
1766 ArrayRef<BundleElement> elements,
1767 bool isConst) {
1768 return Base::get(context, elements, isConst);
1769}
1770
1771auto OpenBundleType::getElements() const -> ArrayRef<BundleElement> {
1772 return getImpl()->elements;
1773}
1774
1775/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1776RecursiveTypeProperties OpenBundleType::getRecursiveTypeProperties() const {
1777 return getImpl()->props;
1778}
1779
1780OpenBundleType OpenBundleType::getConstType(bool isConst) const {
1781 if (isConst == this->isConst())
1782 return *this;
1783 return get(getContext(), getElements(), isConst);
1784}
1785
1786std::optional<unsigned> OpenBundleType::getElementIndex(StringAttr name) {
1787 for (const auto &it : llvm::enumerate(getElements())) {
1788 auto element = it.value();
1789 if (element.name == name) {
1790 return unsigned(it.index());
1791 }
1792 }
1793 return std::nullopt;
1794}
1795
1796std::optional<unsigned> OpenBundleType::getElementIndex(StringRef name) {
1797 for (const auto &it : llvm::enumerate(getElements())) {
1798 auto element = it.value();
1799 if (element.name.getValue() == name) {
1800 return unsigned(it.index());
1801 }
1802 }
1803 return std::nullopt;
1804}
1805
1806StringAttr OpenBundleType::getElementNameAttr(size_t index) {
1807 assert(index < getNumElements() &&
1808 "index must be less than number of fields in bundle");
1809 return getElements()[index].name;
1810}
1811
1812StringRef OpenBundleType::getElementName(size_t index) {
1813 return getElementNameAttr(index).getValue();
1814}
1815
1816std::optional<OpenBundleType::BundleElement>
1817OpenBundleType::getElement(StringAttr name) {
1818 if (auto maybeIndex = getElementIndex(name))
1819 return getElements()[*maybeIndex];
1820 return std::nullopt;
1821}
1822
1823std::optional<OpenBundleType::BundleElement>
1824OpenBundleType::getElement(StringRef name) {
1825 if (auto maybeIndex = getElementIndex(name))
1826 return getElements()[*maybeIndex];
1827 return std::nullopt;
1828}
1829
1830/// Look up an element by index.
1831OpenBundleType::BundleElement OpenBundleType::getElement(size_t index) {
1832 assert(index < getNumElements() &&
1833 "index must be less than number of fields in bundle");
1834 return getElements()[index];
1835}
1836
1837OpenBundleType::ElementType OpenBundleType::getElementType(StringAttr name) {
1838 auto element = getElement(name);
1839 return element ? element->type : FIRRTLBaseType();
1840}
1841
1842OpenBundleType::ElementType OpenBundleType::getElementType(StringRef name) {
1843 auto element = getElement(name);
1844 return element ? element->type : FIRRTLBaseType();
1845}
1846
1847OpenBundleType::ElementType OpenBundleType::getElementType(size_t index) const {
1848 assert(index < getNumElements() &&
1849 "index must be less than number of fields in bundle");
1850 return getElements()[index].type;
1851}
1852
1853uint64_t OpenBundleType::getFieldID(uint64_t index) const {
1854 return getImpl()->fieldIDs[index];
1855}
1856
1857uint64_t OpenBundleType::getIndexForFieldID(uint64_t fieldID) const {
1858 assert(!getElements().empty() && "Bundle must have >0 fields");
1859 auto fieldIDs = getImpl()->fieldIDs;
1860 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1861 return std::distance(fieldIDs.begin(), it);
1862}
1863
1864std::pair<uint64_t, uint64_t>
1865OpenBundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1866 auto index = getIndexForFieldID(fieldID);
1867 auto elementFieldID = getFieldID(index);
1868 return {index, fieldID - elementFieldID};
1869}
1870
1871std::pair<Type, uint64_t>
1872OpenBundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1873 if (fieldID == 0)
1874 return {*this, 0};
1875 auto subfieldIndex = getIndexForFieldID(fieldID);
1876 auto subfieldType = getElementType(subfieldIndex);
1877 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1878 return {subfieldType, subfieldID};
1879}
1880
1881uint64_t OpenBundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1882
1883std::pair<uint64_t, bool>
1884OpenBundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1885 auto childRoot = getFieldID(index);
1886 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1887 : (getFieldID(index + 1) - 1);
1888 return std::make_pair(fieldID - childRoot,
1889 fieldID >= childRoot && fieldID <= rangeEnd);
1890}
1891
1892bool OpenBundleType::isConst() const { return getImpl()->isConst; }
1893
1894OpenBundleType::ElementType
1895OpenBundleType::getElementTypePreservingConst(size_t index) {
1896 auto type = getElementType(index);
1897 // TODO: ConstTypeInterface / Trait ?
1898 return TypeSwitch<FIRRTLType, ElementType>(type)
1899 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
1900 return type.getConstType(type.isConst() || isConst());
1901 })
1902 .Default(type);
1903}
1904
1905LogicalResult
1906OpenBundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1907 ArrayRef<BundleElement> elements, bool isConst) {
1908 SmallPtrSet<StringAttr, 4> nameSet;
1909 for (auto &element : elements) {
1910 if (!nameSet.insert(element.name).second)
1911 return emitErrorFn() << "duplicate field name " << element.name
1912 << " in openbundle";
1913 if (FIRRTLType(element.type).containsReference() && isConst)
1914 return emitErrorFn()
1915 << "'const' bundle cannot have references, but element "
1916 << element.name << " has type " << element.type;
1917 if (type_isa<LHSType>(element.type))
1918 return emitErrorFn() << "bundle element " << element.name
1919 << " cannot have a left-hand side type";
1920 }
1921
1922 return success();
1923}
1924
1925//===----------------------------------------------------------------------===//
1926// FVectorType
1927//===----------------------------------------------------------------------===//
1928
1931 using KeyTy = std::tuple<FIRRTLBaseType, size_t, char>;
1932
1940
1941 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1942
1944
1945 static FVectorTypeStorage *construct(TypeStorageAllocator &allocator,
1946 KeyTy key) {
1947 return new (allocator.allocate<FVectorTypeStorage>())
1948 FVectorTypeStorage(std::get<0>(key), std::get<1>(key),
1949 static_cast<bool>(std::get<2>(key)));
1950 }
1951
1954
1955 /// This holds the bits for the type's recursive properties, and can hold a
1956 /// pointer to a passive version of the type.
1960};
1961
1962FVectorType FVectorType::get(FIRRTLBaseType elementType, size_t numElements,
1963 bool isConst) {
1964 return Base::get(elementType.getContext(), elementType, numElements, isConst);
1965}
1966
1967FIRRTLBaseType FVectorType::getElementType() const {
1968 return getImpl()->elementType;
1969}
1970
1971size_t FVectorType::getNumElements() const { return getImpl()->numElements; }
1972
1973/// Return the recursive properties of the type.
1974RecursiveTypeProperties FVectorType::getRecursiveTypeProperties() const {
1975 return getImpl()->props;
1976}
1977
1978/// Return this type with any flip types recursively removed from itself.
1979FIRRTLBaseType FVectorType::getPassiveType() {
1980 auto *impl = getImpl();
1981
1982 // If we've already determined and cached the passive type, use it.
1983 if (impl->passiveType)
1984 return impl->passiveType;
1985
1986 // If this type is already passive, return it and remember for next time.
1987 if (impl->elementType.getRecursiveTypeProperties().isPassive)
1988 return impl->passiveType = *this;
1989
1990 // Otherwise, rebuild a passive version.
1991 auto passiveType = FVectorType::get(getElementType().getPassiveType(),
1992 getNumElements(), isConst());
1993 impl->passiveType = passiveType;
1994 return passiveType;
1995}
1996
1997FVectorType FVectorType::getConstType(bool isConst) const {
1998 if (isConst == this->isConst())
1999 return *this;
2000 return get(getElementType(), getNumElements(), isConst);
2001}
2002
2003FVectorType FVectorType::getAllConstDroppedType() {
2004 if (!containsConst())
2005 return *this;
2006 return get(getElementType().getAllConstDroppedType(), getNumElements(),
2007 false);
2008}
2009
2010/// Return this type with any type aliases recursively removed from itself.
2011FIRRTLBaseType FVectorType::getAnonymousType() {
2012 auto *impl = getImpl();
2013
2014 if (impl->anonymousType)
2015 return impl->anonymousType;
2016
2017 // If this type is already anonymous, return it and remember for next time.
2018 if (!impl->props.containsTypeAlias)
2019 return impl->anonymousType = *this;
2020
2021 // Otherwise, rebuild an anonymous version.
2022 auto anonymousType = FVectorType::get(getElementType().getAnonymousType(),
2023 getNumElements(), isConst());
2024 impl->anonymousType = anonymousType;
2025 return anonymousType;
2026}
2027
2028uint64_t FVectorType::getFieldID(uint64_t index) const {
2029 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2030}
2031
2032uint64_t FVectorType::getIndexForFieldID(uint64_t fieldID) const {
2033 assert(fieldID && "fieldID must be at least 1");
2034 // Divide the field ID by the number of fieldID's per element.
2035 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2036}
2037
2038std::pair<uint64_t, uint64_t>
2039FVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2040 auto index = getIndexForFieldID(fieldID);
2041 auto elementFieldID = getFieldID(index);
2042 return {index, fieldID - elementFieldID};
2043}
2044
2045std::pair<Type, uint64_t>
2046FVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2047 if (fieldID == 0)
2048 return {*this, 0};
2049 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2050}
2051
2052uint64_t FVectorType::getMaxFieldID() const {
2053 return getNumElements() *
2054 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2055}
2056
2057std::pair<uint64_t, bool>
2058FVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2059 auto childRoot = getFieldID(index);
2060 auto rangeEnd =
2061 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2062 return std::make_pair(fieldID - childRoot,
2063 fieldID >= childRoot && fieldID <= rangeEnd);
2064}
2065
2066bool FVectorType::isConst() const { return getImpl()->isConst; }
2067
2068FVectorType::ElementType FVectorType::getElementTypePreservingConst() {
2069 auto type = getElementType();
2070 return type.getConstType(type.isConst() || isConst());
2071}
2072
2073//===----------------------------------------------------------------------===//
2074// OpenVectorType
2075//===----------------------------------------------------------------------===//
2076
2078 using KeyTy = std::tuple<FIRRTLType, size_t, char>;
2079
2087
2088 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2089
2091
2092 static OpenVectorTypeStorage *construct(TypeStorageAllocator &allocator,
2093 KeyTy key) {
2094 return new (allocator.allocate<OpenVectorTypeStorage>())
2095 OpenVectorTypeStorage(std::get<0>(key), std::get<1>(key),
2096 static_cast<bool>(std::get<2>(key)));
2097 }
2098
2101
2104};
2105
2106OpenVectorType OpenVectorType::get(FIRRTLType elementType, size_t numElements,
2107 bool isConst) {
2108 return Base::get(elementType.getContext(), elementType, numElements, isConst);
2109}
2110
2111FIRRTLType OpenVectorType::getElementType() const {
2112 return getImpl()->elementType;
2113}
2114
2115size_t OpenVectorType::getNumElements() const { return getImpl()->numElements; }
2116
2117/// Return the recursive properties of the type.
2118RecursiveTypeProperties OpenVectorType::getRecursiveTypeProperties() const {
2119 return getImpl()->props;
2120}
2121
2122OpenVectorType OpenVectorType::getConstType(bool isConst) const {
2123 if (isConst == this->isConst())
2124 return *this;
2125 return get(getElementType(), getNumElements(), isConst);
2126}
2127
2128uint64_t OpenVectorType::getFieldID(uint64_t index) const {
2129 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2130}
2131
2132uint64_t OpenVectorType::getIndexForFieldID(uint64_t fieldID) const {
2133 assert(fieldID && "fieldID must be at least 1");
2134 // Divide the field ID by the number of fieldID's per element.
2135 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2136}
2137
2138std::pair<uint64_t, uint64_t>
2139OpenVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2140 auto index = getIndexForFieldID(fieldID);
2141 auto elementFieldID = getFieldID(index);
2142 return {index, fieldID - elementFieldID};
2143}
2144
2145std::pair<Type, uint64_t>
2146OpenVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2147 if (fieldID == 0)
2148 return {*this, 0};
2149 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2150}
2151
2152uint64_t OpenVectorType::getMaxFieldID() const {
2153 // If this is requirement, make ODS constraint or actual elementType.
2154 return getNumElements() *
2155 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2156}
2157
2158std::pair<uint64_t, bool>
2159OpenVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2160 auto childRoot = getFieldID(index);
2161 auto rangeEnd =
2162 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2163 return std::make_pair(fieldID - childRoot,
2164 fieldID >= childRoot && fieldID <= rangeEnd);
2165}
2166
2167bool OpenVectorType::isConst() const { return getImpl()->isConst; }
2168
2169OpenVectorType::ElementType OpenVectorType::getElementTypePreservingConst() {
2170 auto type = getElementType();
2171 // TODO: ConstTypeInterface / Trait ?
2172 return TypeSwitch<FIRRTLType, ElementType>(type)
2173 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
2174 return type.getConstType(type.isConst() || isConst());
2175 })
2176 .Default(type);
2177}
2178
2179LogicalResult
2180OpenVectorType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2182 bool isConst) {
2183 if (elementType.containsReference() && isConst)
2184 return emitErrorFn() << "vector cannot be const with references";
2185 if (type_isa<LHSType>(elementType))
2186 return emitErrorFn() << "vector cannot have a left-hand side type";
2187 return success();
2188}
2189
2190//===----------------------------------------------------------------------===//
2191// FEnum Type
2192//===----------------------------------------------------------------------===//
2193
2195 using KeyTy = std::tuple<ArrayRef<FEnumType::EnumElement>, char>;
2196
2197 FEnumTypeStorage(ArrayRef<FEnumType::EnumElement> elements, bool isConst)
2199 elements(elements.begin(), elements.end()) {
2200 RecursiveTypeProperties props{true, false, false, isConst,
2201 false, false, false};
2202 dataSize = 0;
2203 for (auto &element : elements) {
2204 auto type = element.type;
2205 auto eltInfo = type.getRecursiveTypeProperties();
2206 props.containsConst |= eltInfo.containsConst;
2207 props.containsTypeAlias |= eltInfo.containsTypeAlias;
2208
2209 dataSize = std::max((size_t)type.getBitWidthOrSentinel(), dataSize);
2210 }
2211 recProps = props;
2212 }
2213
2214 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2215
2216 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
2217
2218 static llvm::hash_code hashKey(const KeyTy &key) {
2219 return llvm::hash_value(key);
2220 }
2221
2222 static FEnumTypeStorage *construct(TypeStorageAllocator &allocator,
2223 KeyTy key) {
2224 return new (allocator.allocate<FEnumTypeStorage>())
2225 FEnumTypeStorage(std::get<0>(key), static_cast<bool>(std::get<1>(key)));
2226 }
2227
2228 SmallVector<FEnumType::EnumElement, 4> elements;
2230 size_t dataSize;
2232};
2233
2234FEnumType FEnumType::get(::mlir::MLIRContext *context,
2235 ArrayRef<EnumElement> elements, bool isConst) {
2236 return Base::get(context, elements, isConst);
2237}
2238
2239ArrayRef<FEnumType::EnumElement> FEnumType::getElements() const {
2240 return getImpl()->elements;
2241}
2242
2243FEnumType FEnumType::getConstType(bool isConst) const {
2244 return get(getContext(), getElements(), isConst);
2245}
2246
2247FEnumType FEnumType::getAllConstDroppedType() {
2248 if (!containsConst())
2249 return *this;
2250
2251 SmallVector<EnumElement> constDroppedElements(
2252 llvm::map_range(getElements(), [](EnumElement element) {
2253 element.type = element.type.getAllConstDroppedType();
2254 return element;
2255 }));
2256 return get(getContext(), constDroppedElements, false);
2257}
2258
2259/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
2260RecursiveTypeProperties FEnumType::getRecursiveTypeProperties() const {
2261 return getImpl()->recProps;
2262}
2263
2264std::optional<unsigned> FEnumType::getElementIndex(StringAttr name) {
2265 for (const auto &it : llvm::enumerate(getElements())) {
2266 auto element = it.value();
2267 if (element.name == name) {
2268 return unsigned(it.index());
2269 }
2270 }
2271 return std::nullopt;
2272}
2273
2274size_t FEnumType::getBitWidth() { return getDataWidth() + getTagWidth(); }
2275
2276size_t FEnumType::getDataWidth() { return getImpl()->dataSize; }
2277
2278size_t FEnumType::getTagWidth() {
2279 if (getElements().size() == 0)
2280 return 0;
2281 // Each tag has the same type.
2282 return cast<IntegerType>(getElements()[0].value.getType()).getWidth();
2283}
2284
2285std::optional<unsigned> FEnumType::getElementIndex(StringRef name) {
2286 for (const auto &it : llvm::enumerate(getElements())) {
2287 auto element = it.value();
2288 if (element.name.getValue() == name) {
2289 return unsigned(it.index());
2290 }
2291 }
2292 return std::nullopt;
2293}
2294
2295StringAttr FEnumType::getElementNameAttr(size_t index) {
2296 assert(index < getNumElements() &&
2297 "index must be less than number of fields in enum");
2298 return getElements()[index].name;
2299}
2300
2301StringRef FEnumType::getElementName(size_t index) {
2302 return getElementNameAttr(index).getValue();
2303}
2304
2305IntegerAttr FEnumType::getElementValueAttr(size_t index) {
2306 return getElements()[index].value;
2307}
2308
2309APInt FEnumType::getElementValue(size_t index) {
2310 return getElementValueAttr(index).getValue();
2311}
2312
2313FIRRTLBaseType FEnumType::getElementType(size_t index) {
2314 return getElements()[index].type;
2315}
2316
2317std::optional<FEnumType::EnumElement> FEnumType::getElement(StringAttr name) {
2318 if (auto maybeIndex = getElementIndex(name))
2319 return getElements()[*maybeIndex];
2320 return std::nullopt;
2321}
2322
2323std::optional<FEnumType::EnumElement> FEnumType::getElement(StringRef name) {
2324 if (auto maybeIndex = getElementIndex(name))
2325 return getElements()[*maybeIndex];
2326 return std::nullopt;
2327}
2328
2329/// Look up an element by index.
2330FEnumType::EnumElement FEnumType::getElement(size_t index) {
2331 assert(index < getNumElements() &&
2332 "index must be less than number of fields in enum");
2333 return getElements()[index];
2334}
2335
2336FIRRTLBaseType FEnumType::getElementType(StringAttr name) {
2337 auto element = getElement(name);
2338 return element ? element->type : FIRRTLBaseType();
2339}
2340
2341FIRRTLBaseType FEnumType::getElementType(StringRef name) {
2342 auto element = getElement(name);
2343 return element ? element->type : FIRRTLBaseType();
2344}
2345
2346FIRRTLBaseType FEnumType::getElementType(size_t index) const {
2347 assert(index < getNumElements() &&
2348 "index must be less than number of fields in enum");
2349 return getElements()[index].type;
2350}
2351
2352FIRRTLBaseType FEnumType::getElementTypePreservingConst(size_t index) {
2353 auto type = getElementType(index);
2354 return type.getConstType(type.isConst() || isConst());
2355}
2356
2357LogicalResult FEnumType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2358 ArrayRef<EnumElement> elements, bool isConst) {
2359 bool first = true;
2360 IntegerAttr previous;
2361 SmallPtrSet<Attribute, 4> nameSet;
2362
2363 for (auto &elt : elements) {
2364 auto r = elt.type.getRecursiveTypeProperties();
2365 if (!r.isPassive)
2366 return emitErrorFn() << "enum field " << elt.name << " not passive";
2367 if (r.containsAnalog)
2368 return emitErrorFn() << "enum field " << elt.name << " contains analog";
2369 if (r.hasUninferredWidth)
2370 return emitErrorFn() << "enum field " << elt.name
2371 << " has uninferred width";
2372 if (r.hasUninferredReset)
2373 return emitErrorFn() << "enum field " << elt.name
2374 << " has uninferred reset";
2375 if (r.containsConst && !isConst)
2376 return emitErrorFn() << "enum with 'const' elements must be 'const'";
2377 // Ensure that each tag has a unique name.
2378 if (!nameSet.insert(elt.name).second)
2379 return emitErrorFn() << "duplicate variant name " << elt.name
2380 << " in enum";
2381 // Ensure that each tag is increasing and unique.
2382 if (first) {
2383 previous = elt.value;
2384 first = false;
2385 } else {
2386 auto current = elt.value;
2387 if (previous.getType() != current.getType())
2388 return emitErrorFn() << "enum variant " << elt.name << " has type"
2389 << current.getType()
2390 << " which is different than previous variant "
2391 << previous.getType();
2392
2393 if (previous.getValue().getBitWidth() != current.getValue().getBitWidth())
2394 return emitErrorFn() << "enum variant " << elt.name << " has bitwidth"
2395 << current.getValue().getBitWidth()
2396 << " which is different than previous variant "
2397 << previous.getValue().getBitWidth();
2398 if (previous.getValue().uge(current.getValue()))
2399 return emitErrorFn()
2400 << "enum variant " << elt.name << " has value " << current
2401 << " which is not greater than previous variant " << previous;
2402 }
2403 // TODO: exclude reference containing
2404 }
2405 return success();
2406}
2407
2408/// Return this type with any type aliases recursively removed from itself.
2409FIRRTLBaseType FEnumType::getAnonymousType() {
2410 auto *impl = getImpl();
2411
2412 if (impl->anonymousType)
2413 return impl->anonymousType;
2414
2415 if (!impl->recProps.containsTypeAlias)
2416 return impl->anonymousType = *this;
2417
2418 SmallVector<FEnumType::EnumElement, 4> elements;
2419
2420 for (auto element : getElements())
2421 elements.push_back(
2422 {element.name, element.value, element.type.getAnonymousType()});
2423 return impl->anonymousType = FEnumType::get(getContext(), elements);
2424}
2425
2426//===----------------------------------------------------------------------===//
2427// BaseTypeAliasType
2428//===----------------------------------------------------------------------===//
2429
2432 using KeyTy = std::tuple<StringAttr, FIRRTLBaseType>;
2433
2437
2438 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2439
2440 KeyTy getAsKey() const { return KeyTy(name, innerType); }
2441
2442 static llvm::hash_code hashKey(const KeyTy &key) {
2443 return llvm::hash_value(key);
2444 }
2445
2446 static BaseTypeAliasStorage *construct(TypeStorageAllocator &allocator,
2447 KeyTy key) {
2448 return new (allocator.allocate<BaseTypeAliasStorage>())
2449 BaseTypeAliasStorage(std::get<0>(key), std::get<1>(key));
2450 }
2451 StringAttr name;
2454};
2455
2456auto BaseTypeAliasType::get(StringAttr name, FIRRTLBaseType innerType)
2457 -> BaseTypeAliasType {
2458 return Base::get(name.getContext(), name, innerType);
2459}
2460
2461auto BaseTypeAliasType::getName() const -> StringAttr {
2462 return getImpl()->name;
2463}
2464
2465auto BaseTypeAliasType::getInnerType() const -> FIRRTLBaseType {
2466 return getImpl()->innerType;
2467}
2468
2469FIRRTLBaseType BaseTypeAliasType::getAnonymousType() {
2470 auto *impl = getImpl();
2471 if (impl->anonymousType)
2472 return impl->anonymousType;
2473 return impl->anonymousType = getInnerType().getAnonymousType();
2474}
2475
2476FIRRTLBaseType BaseTypeAliasType::getPassiveType() {
2477 return getModifiedType(getInnerType().getPassiveType());
2478}
2479
2480RecursiveTypeProperties BaseTypeAliasType::getRecursiveTypeProperties() const {
2481 auto rtp = getInnerType().getRecursiveTypeProperties();
2482 rtp.containsTypeAlias = true;
2483 return rtp;
2484}
2485
2486// If a given `newInnerType` is identical to innerType, return `*this`
2487// because we can reuse the type alias. Otherwise return `newInnerType`.
2489BaseTypeAliasType::getModifiedType(FIRRTLBaseType newInnerType) const {
2490 if (newInnerType == getInnerType())
2491 return *this;
2492 return newInnerType;
2493}
2494
2495// FieldIDTypeInterface implementation.
2496FIRRTLBaseType BaseTypeAliasType::getAllConstDroppedType() {
2497 return getModifiedType(getInnerType().getAllConstDroppedType());
2498}
2499
2500FIRRTLBaseType BaseTypeAliasType::getConstType(bool isConst) const {
2501 return getModifiedType(getInnerType().getConstType(isConst));
2502}
2503
2504std::pair<Type, uint64_t>
2505BaseTypeAliasType::getSubTypeByFieldID(uint64_t fieldID) const {
2506 return hw::FieldIdImpl::getSubTypeByFieldID(getInnerType(), fieldID);
2507}
2508
2509uint64_t BaseTypeAliasType::getMaxFieldID() const {
2510 return hw::FieldIdImpl::getMaxFieldID(getInnerType());
2511}
2512
2513std::pair<uint64_t, bool>
2514BaseTypeAliasType::projectToChildFieldID(uint64_t fieldID,
2515 uint64_t index) const {
2516 return hw::FieldIdImpl::projectToChildFieldID(getInnerType(), fieldID, index);
2517}
2518
2519uint64_t BaseTypeAliasType::getIndexForFieldID(uint64_t fieldID) const {
2520 return hw::FieldIdImpl::getIndexForFieldID(getInnerType(), fieldID);
2521}
2522
2523uint64_t BaseTypeAliasType::getFieldID(uint64_t index) const {
2524 return hw::FieldIdImpl::getFieldID(getInnerType(), index);
2525}
2526
2527std::pair<uint64_t, uint64_t>
2528BaseTypeAliasType::getIndexAndSubfieldID(uint64_t fieldID) const {
2529 return hw::FieldIdImpl::getIndexAndSubfieldID(getInnerType(), fieldID);
2530}
2531
2532//===----------------------------------------------------------------------===//
2533// LHSType
2534//===----------------------------------------------------------------------===//
2535
2536LHSType LHSType::get(FIRRTLBaseType type) {
2537 return LHSType::get(type.getContext(), type);
2538}
2539
2540LogicalResult LHSType::verify(function_ref<InFlightDiagnostic()> emitError,
2541 FIRRTLBaseType type) {
2542 if (type.containsAnalog())
2543 return emitError() << "lhs type cannot contain an AnalogType";
2544 if (!type.isPassive())
2545 return emitError() << "lhs type cannot contain a non-passive type";
2546 if (type.containsReference())
2547 return emitError() << "lhs type cannot contain a reference";
2548 if (type_isa<LHSType>(type))
2549 return emitError() << "lhs type cannot contain a lhs type";
2550
2551 return success();
2552}
2553
2554//===----------------------------------------------------------------------===//
2555// RefType
2556//===----------------------------------------------------------------------===//
2557
2558auto RefType::get(FIRRTLBaseType type, bool forceable, SymbolRefAttr layer)
2559 -> RefType {
2560 return Base::get(type.getContext(), type, forceable, layer);
2561}
2562
2563auto RefType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2564 FIRRTLBaseType base, bool forceable, SymbolRefAttr layer)
2565 -> LogicalResult {
2566 if (!base.isPassive())
2567 return emitErrorFn() << "reference base type must be passive";
2568 if (forceable && base.containsConst())
2569 return emitErrorFn()
2570 << "forceable reference base type cannot contain const";
2571 return success();
2572}
2573
2574RecursiveTypeProperties RefType::getRecursiveTypeProperties() const {
2575 auto rtp = getType().getRecursiveTypeProperties();
2576 rtp.containsReference = true;
2577 // References are not "passive", per FIRRTL spec.
2578 rtp.isPassive = false;
2579 return rtp;
2580}
2581
2582//===----------------------------------------------------------------------===//
2583// AnalogType
2584//===----------------------------------------------------------------------===//
2585
2586AnalogType AnalogType::get(mlir::MLIRContext *context) {
2587 return AnalogType::get(context, -1, false);
2588}
2589
2590AnalogType AnalogType::get(mlir::MLIRContext *context,
2591 std::optional<int32_t> width, bool isConst) {
2592 return AnalogType::get(context, width ? *width : -1, isConst);
2593}
2594
2595LogicalResult AnalogType::verify(function_ref<InFlightDiagnostic()> emitError,
2596 int32_t widthOrSentinel, bool isConst) {
2597 if (widthOrSentinel < -1)
2598 return emitError() << "invalid width";
2599 return success();
2600}
2601
2602int32_t AnalogType::getWidthOrSentinel() const { return getImpl()->width; }
2603
2604AnalogType AnalogType::getConstType(bool isConst) const {
2605 if (isConst == this->isConst())
2606 return *this;
2607 return get(getContext(), getWidthOrSentinel(), isConst);
2608}
2609
2610//===----------------------------------------------------------------------===//
2611// ClockType
2612//===----------------------------------------------------------------------===//
2613
2614ClockType ClockType::getConstType(bool isConst) const {
2615 if (isConst == this->isConst())
2616 return *this;
2617 return get(getContext(), isConst);
2618}
2619
2620//===----------------------------------------------------------------------===//
2621// ResetType
2622//===----------------------------------------------------------------------===//
2623
2624ResetType ResetType::getConstType(bool isConst) const {
2625 if (isConst == this->isConst())
2626 return *this;
2627 return get(getContext(), isConst);
2628}
2629
2630//===----------------------------------------------------------------------===//
2631// AsyncResetType
2632//===----------------------------------------------------------------------===//
2633
2634AsyncResetType AsyncResetType::getConstType(bool isConst) const {
2635 if (isConst == this->isConst())
2636 return *this;
2637 return get(getContext(), isConst);
2638}
2639
2640//===----------------------------------------------------------------------===//
2641// ClassType
2642//===----------------------------------------------------------------------===//
2643
2645 using KeyTy = std::tuple<FlatSymbolRefAttr, ArrayRef<ClassElement>>;
2646
2647 static ClassTypeStorage *construct(TypeStorageAllocator &allocator,
2648 KeyTy key) {
2649 auto name = std::get<0>(key);
2650 auto elements = allocator.copyInto(std::get<1>(key));
2651
2652 // build the field ID table
2653 SmallVector<uint64_t, 4> ids;
2654 uint64_t id = 0;
2655 ids.reserve(elements.size());
2656 for (auto &element : elements) {
2657 id += 1;
2658 ids.push_back(id);
2659 id += hw::FieldIdImpl::getMaxFieldID(element.type);
2660 }
2661
2662 auto fieldIDs = allocator.copyInto(ArrayRef(ids));
2663 auto maxFieldID = id;
2664
2665 return new (allocator.allocate<ClassTypeStorage>())
2667 }
2668
2669 ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef<ClassElement> elements,
2670 ArrayRef<uint64_t> fieldIDs, uint64_t maxFieldID)
2673
2674 bool operator==(const KeyTy &key) const { return getAsKey() == key; }
2675
2676 KeyTy getAsKey() const { return KeyTy(name, elements); }
2677
2678 FlatSymbolRefAttr name;
2679 ArrayRef<ClassElement> elements;
2680 ArrayRef<uint64_t> fieldIDs;
2681 uint64_t maxFieldID;
2682};
2683
2684ClassType ClassType::get(FlatSymbolRefAttr name,
2685 ArrayRef<ClassElement> elements) {
2686 return get(name.getContext(), name, elements);
2687}
2688
2689StringRef ClassType::getName() const {
2690 return getNameAttr().getAttr().getValue();
2691}
2692
2693FlatSymbolRefAttr ClassType::getNameAttr() const { return getImpl()->name; }
2694
2695ArrayRef<ClassElement> ClassType::getElements() const {
2696 return getImpl()->elements;
2697}
2698
2699const ClassElement &ClassType::getElement(IntegerAttr index) const {
2700 return getElement(index.getValue().getZExtValue());
2701}
2702
2703const ClassElement &ClassType::getElement(size_t index) const {
2704 return getElements()[index];
2705}
2706
2707std::optional<uint64_t> ClassType::getElementIndex(StringRef fieldName) const {
2708 for (const auto [i, e] : llvm::enumerate(getElements()))
2709 if (fieldName == e.name)
2710 return i;
2711 return {};
2712}
2713
2714void ClassType::printInterface(AsmPrinter &p) const {
2715 p.printSymbolName(getName());
2716 p << "(";
2717 bool first = true;
2718 for (const auto &element : getElements()) {
2719 if (!first)
2720 p << ", ";
2721 p << element.direction << " ";
2722 p.printKeywordOrString(element.name);
2723 p << ": " << element.type;
2724 first = false;
2725 }
2726 p << ")";
2727}
2728
2729uint64_t ClassType::getFieldID(uint64_t index) const {
2730 return getImpl()->fieldIDs[index];
2731}
2732
2733uint64_t ClassType::getIndexForFieldID(uint64_t fieldID) const {
2734 assert(!getElements().empty() && "Class must have >0 fields");
2735 auto fieldIDs = getImpl()->fieldIDs;
2736 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
2737 return std::distance(fieldIDs.begin(), it);
2738}
2739
2740std::pair<uint64_t, uint64_t>
2741ClassType::getIndexAndSubfieldID(uint64_t fieldID) const {
2742 auto index = getIndexForFieldID(fieldID);
2743 auto elementFieldID = getFieldID(index);
2744 return {index, fieldID - elementFieldID};
2745}
2746
2747std::pair<Type, uint64_t>
2748ClassType::getSubTypeByFieldID(uint64_t fieldID) const {
2749 if (fieldID == 0)
2750 return {*this, 0};
2751 auto subfieldIndex = getIndexForFieldID(fieldID);
2752 auto subfieldType = getElement(subfieldIndex).type;
2753 auto subfieldID = fieldID - getFieldID(subfieldIndex);
2754 return {subfieldType, subfieldID};
2755}
2756
2757uint64_t ClassType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2758
2759std::pair<uint64_t, bool>
2760ClassType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2761 auto childRoot = getFieldID(index);
2762 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
2763 : (getFieldID(index + 1) - 1);
2764 return std::make_pair(fieldID - childRoot,
2765 fieldID >= childRoot && fieldID <= rangeEnd);
2766}
2767
2768namespace {
2769/// Helper to parse interface-like types (ClassType, DomainType).
2770/// This encapsulates the common pattern of parsing @SymbolName(field, field,
2771/// ...)
2772struct InterfaceParser {
2773 AsmParser &parser;
2774
2775 InterfaceParser(AsmParser &parser) : parser(parser) {}
2776
2777 /// Parse the common structure: @SymbolName(field, field, ...)
2778 template <typename FieldContainer>
2779 ParseResult parse(
2780 function_ref<ParseResult(FieldContainer &, AsmParser &parser)> parseField,
2781 StringAttr &symbolName, FieldContainer &fields) {
2782
2783 if (parser.parseSymbolName(symbolName))
2784 return failure();
2785
2786 // Parse required parentheses
2787 if (parser.parseLParen())
2788 return failure();
2789
2790 // Check for empty list (immediate closing paren)
2791 if (failed(parser.parseOptionalRParen())) {
2792 auto parseElement = [&]() -> ParseResult {
2793 return parseField(fields, parser);
2794 };
2795
2796 if (parser.parseCommaSeparatedList(parseElement) || parser.parseRParen())
2797 return failure();
2798 }
2799
2800 return success();
2801 }
2802};
2803} // namespace
2804
2805ParseResult ClassType::parseInterface(AsmParser &parser, ClassType &result) {
2806 InterfaceParser helper(parser);
2807
2808 auto parseField = [](SmallVector<ClassElement> &elements,
2809 AsmParser &parser) -> ParseResult {
2810 // Parse port direction.
2811 Direction direction;
2812 if (succeeded(parser.parseOptionalKeyword("out")))
2813 direction = Direction::Out;
2814 else if (succeeded(parser.parseKeyword("in", "or 'out'")))
2815 direction = Direction::In;
2816 else
2817 return failure();
2818
2819 // Parse port name.
2820 std::string keyword;
2821 if (parser.parseKeywordOrString(&keyword))
2822 return failure();
2823 StringAttr name = StringAttr::get(parser.getContext(), keyword);
2824
2825 // Parse port type.
2826 Type type;
2827 if (parser.parseColonType(type))
2828 return failure();
2829
2830 elements.emplace_back(name, type, direction);
2831 return success();
2832 };
2833
2834 StringAttr symbolName;
2835 SmallVector<ClassElement> elements;
2836 if (helper.parse<SmallVector<ClassElement>>(parseField, symbolName, elements))
2837 return failure();
2838
2839 result = ClassType::get(FlatSymbolRefAttr::get(symbolName), elements);
2840 return success();
2841}
2842
2843//===----------------------------------------------------------------------===//
2844// DomainType
2845//===----------------------------------------------------------------------===//
2846
2847ParseResult DomainType::parseInterface(AsmParser &parser, DomainType &result) {
2848 InterfaceParser helper(parser);
2849
2850 auto parseField = [](SmallVector<Attribute> &fields,
2851 AsmParser &parser) -> ParseResult {
2852 std::string fieldNameStr;
2853 Type fieldTypeRaw;
2854 if (parser.parseKeywordOrString(&fieldNameStr) || parser.parseColon() ||
2855 parser.parseType(fieldTypeRaw))
2856 return failure();
2857
2858 auto fieldType = dyn_cast<PropertyType>(fieldTypeRaw);
2859 if (!fieldType)
2860 return parser.emitError(parser.getCurrentLocation(),
2861 "expected property type");
2862
2863 auto fieldName = StringAttr::get(parser.getContext(), fieldNameStr);
2864 fields.push_back(
2865 DomainFieldAttr::get(parser.getContext(), fieldName, fieldType));
2866 return success();
2867 };
2868
2869 StringAttr symbolName;
2870 SmallVector<Attribute> fields;
2871 if (helper.parse<SmallVector<Attribute>>(parseField, symbolName, fields))
2872 return failure();
2873
2874 result = DomainType::get(FlatSymbolRefAttr::get(symbolName),
2875 ArrayAttr::get(parser.getContext(), fields));
2876 return success();
2877}
2878
2879DomainType DomainType::get(FlatSymbolRefAttr name, ArrayAttr fields) {
2880 return Base::get(name.getContext(), name, fields);
2881}
2882
2883DomainType DomainType::getFromDomainOp(DomainOp domainOp) {
2884 auto name = FlatSymbolRefAttr::get(domainOp.getNameAttr());
2885 return DomainType::get(name, domainOp.getFieldsAttr());
2886}
2887
2888DomainFieldAttr DomainType::getField(size_t index) const {
2889 assert(index < getNumFields() && "index out of bounds");
2890 return cast<DomainFieldAttr>(getFields()[index]);
2891}
2892
2893std::optional<uint64_t> DomainType::getFieldIndex(StringRef fieldName) const {
2894 for (const auto [i, attr] : llvm::enumerate(getFields())) {
2895 auto field = cast<DomainFieldAttr>(attr);
2896 if (fieldName == field.getName())
2897 return i;
2898 }
2899 return {};
2900}
2901
2902std::pair<Type, uint64_t>
2903DomainType::getSubTypeByFieldID(uint64_t fieldID) const {
2904 if (fieldID == 0)
2905 return {*this, 0};
2906
2907 // Domain fields don't have sub-fieldIDs, so fieldID directly maps to field
2908 // index
2909 if (fieldID > getNumFields())
2910 return {Type(), fieldID};
2911
2912 return {getField(fieldID - 1).getType(), 0};
2913}
2914
2915uint64_t DomainType::getMaxFieldID() const {
2916 // Each field gets one fieldID
2917 return getNumFields();
2918}
2919
2920std::pair<uint64_t, bool>
2921DomainType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2922 // Domain fields are flat, so projection is simple
2923 if (index >= getNumFields())
2924 return {0, false};
2925
2926 uint64_t childFieldID = index + 1;
2927 return {0, fieldID == childFieldID};
2928}
2929
2930uint64_t DomainType::getFieldID(uint64_t index) const {
2931 // Domain fields are flat, so fieldID is just index + 1
2932 assert(index < getNumFields() && "index out of bounds");
2933 return index + 1;
2934}
2935
2936uint64_t DomainType::getIndexForFieldID(uint64_t fieldID) const {
2937 // Domain fields are flat, so index is just fieldID - 1
2938 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2939 return fieldID - 1;
2940}
2941
2942std::pair<uint64_t, uint64_t>
2943DomainType::getIndexAndSubfieldID(uint64_t fieldID) const {
2944 // Domain fields are flat (no sub-fields), so subfieldID is always 0
2945 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2946 return {fieldID - 1, 0};
2947}
2948
2949LogicalResult
2950DomainType::verifySymbolUses(Operation *op,
2951 SymbolTableCollection &symbolTable) const {
2952 // Find the circuit op to look up the domain definition
2953 auto circuitOp = op->getParentOfType<CircuitOp>();
2954 if (!circuitOp)
2955 return op->emitError() << "domain type used outside of a circuit";
2956
2957 // Check that the symbol exists
2958 auto *symbol = symbolTable.lookupSymbolIn(circuitOp, getName());
2959 if (!symbol)
2960 return op->emitError() << "domain type references undefined symbol '"
2961 << getName().getValue() << "'";
2962
2963 // Check that the symbol is a domain
2964 auto domainOp = dyn_cast<DomainOp>(symbol);
2965 if (!domainOp)
2966 return op->emitError() << "domain type references symbol '"
2967 << getName().getValue() << "' which is not a domain";
2968
2969 // Verify that the domain type fields match the domain definition
2970 auto expectedFields = domainOp.getFieldsAttr();
2971 auto actualFields = getFields();
2972
2973 // Check field count
2974 if (actualFields.size() != expectedFields.size())
2975 return op->emitError() << "domain type has " << actualFields.size()
2976 << " fields but domain definition has "
2977 << expectedFields.size() << " fields";
2978
2979 // Check each field
2980 for (size_t i = 0; i < actualFields.size(); ++i) {
2981 auto actualField = cast<DomainFieldAttr>(actualFields[i]);
2982 auto expectedField = cast<DomainFieldAttr>(expectedFields[i]);
2983
2984 // Check field name
2985 if (actualField.getName() != expectedField.getName())
2986 return op->emitError() << "domain type field " << i << " has name '"
2987 << actualField.getName().getValue()
2988 << "' but domain definition expects '"
2989 << expectedField.getName().getValue() << "'";
2990
2991 // Check field type
2992 if (actualField.getType() != expectedField.getType())
2993 return op->emitError()
2994 << "domain type field '" << actualField.getName().getValue()
2995 << "' has type " << actualField.getType()
2996 << " but domain definition expects " << expectedField.getType();
2997 }
2998
2999 return success();
3000}
3001
3002//===----------------------------------------------------------------------===//
3003// FIRRTLDialect
3004//===----------------------------------------------------------------------===//
3005
3006void FIRRTLDialect::registerTypes() {
3007 addTypes<
3008#define GET_TYPEDEF_LIST
3009#include "circt/Dialect/FIRRTL/FIRRTLTypes.cpp.inc"
3010 >();
3011}
3012
3013// Get the bit width for this type, return None if unknown. Unlike
3014// getBitWidthOrSentinel(), this can recursively compute the bitwidth of
3015// aggregate types. For bundle and vectors, recursively get the width of each
3016// field element and return the total bit width of the aggregate type. This
3017// returns None, if any of the bundle fields is a flip type, or ground type with
3018// unknown bit width.
3019std::optional<int64_t> firrtl::getBitWidth(FIRRTLBaseType type,
3020 bool ignoreFlip) {
3021 std::function<std::optional<int64_t>(FIRRTLBaseType)> getWidth =
3022 [&](FIRRTLBaseType type) -> std::optional<int64_t> {
3023 return TypeSwitch<FIRRTLBaseType, std::optional<int64_t>>(type)
3024 .Case<BundleType>([&](BundleType bundle) -> std::optional<int64_t> {
3025 int64_t width = 0;
3026 for (auto &elt : bundle) {
3027 if (elt.isFlip && !ignoreFlip)
3028 return std::nullopt;
3029 auto w = getBitWidth(elt.type);
3030 if (!w.has_value())
3031 return std::nullopt;
3032 width += *w;
3033 }
3034 return width;
3035 })
3036 .Case<FEnumType>([&](FEnumType fenum) -> std::optional<int64_t> {
3037 int64_t width = 0;
3038 for (auto &elt : fenum) {
3039 auto w = getBitWidth(elt.type);
3040 if (!w.has_value())
3041 return std::nullopt;
3042 width = std::max(width, *w);
3043 }
3044 return width + fenum.getTagWidth();
3045 })
3046 .Case<FVectorType>([&](auto vector) -> std::optional<int64_t> {
3047 auto w = getBitWidth(vector.getElementType());
3048 if (!w.has_value())
3049 return std::nullopt;
3050 return *w * vector.getNumElements();
3051 })
3052 .Case<IntType>([&](IntType iType) { return iType.getWidth(); })
3053 .Case<ClockType, ResetType, AsyncResetType>([](Type) { return 1; })
3054 .Default([&](auto t) { return std::nullopt; });
3055 };
3056 return getWidth(type);
3057}
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:56
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 hasHardwareElements(FIRRTLType type)
Return true if the given type contains any elements of hardware types.
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)