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