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) {
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.
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,
774 })
775 .Case<SIntType, UIntType>([](auto type) {
777 true, false, false, type.isConst(), false, !type.hasWidth(), false};
778 })
779 .Case<AnalogType>([](auto type) {
780 return RecursiveTypeProperties{
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");
802 return RecursiveTypeProperties{};
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
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);
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
1790OpenBundleType
1791OpenBundleType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
1792 MLIRContext *context,
1793 ArrayRef<BundleElement> elements, bool isConst) {
1794 if (failed(verify(emitErrorFn, elements, isConst)))
1795 return {};
1796 return get(context, elements, isConst);
1797}
1798
1799auto OpenBundleType::getElements() const -> ArrayRef<BundleElement> {
1800 return getImpl()->elements;
1801}
1802
1803/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1804RecursiveTypeProperties OpenBundleType::getRecursiveTypeProperties() const {
1805 return getImpl()->props;
1806}
1807
1808OpenBundleType OpenBundleType::getConstType(bool isConst) const {
1809 if (isConst == this->isConst())
1810 return *this;
1811 return get(getContext(), getElements(), isConst);
1812}
1813
1814std::optional<unsigned> OpenBundleType::getElementIndex(StringAttr name) {
1815 for (const auto &it : llvm::enumerate(getElements())) {
1816 auto element = it.value();
1817 if (element.name == name) {
1818 return unsigned(it.index());
1819 }
1820 }
1821 return std::nullopt;
1822}
1823
1824std::optional<unsigned> OpenBundleType::getElementIndex(StringRef name) {
1825 for (const auto &it : llvm::enumerate(getElements())) {
1826 auto element = it.value();
1827 if (element.name.getValue() == name) {
1828 return unsigned(it.index());
1829 }
1830 }
1831 return std::nullopt;
1832}
1833
1834StringAttr OpenBundleType::getElementNameAttr(size_t index) {
1835 assert(index < getNumElements() &&
1836 "index must be less than number of fields in bundle");
1837 return getElements()[index].name;
1838}
1839
1840StringRef OpenBundleType::getElementName(size_t index) {
1841 return getElementNameAttr(index).getValue();
1842}
1843
1844std::optional<OpenBundleType::BundleElement>
1845OpenBundleType::getElement(StringAttr name) {
1846 if (auto maybeIndex = getElementIndex(name))
1847 return getElements()[*maybeIndex];
1848 return std::nullopt;
1849}
1850
1851std::optional<OpenBundleType::BundleElement>
1852OpenBundleType::getElement(StringRef name) {
1853 if (auto maybeIndex = getElementIndex(name))
1854 return getElements()[*maybeIndex];
1855 return std::nullopt;
1856}
1857
1858/// Look up an element by index.
1859OpenBundleType::BundleElement OpenBundleType::getElement(size_t index) {
1860 assert(index < getNumElements() &&
1861 "index must be less than number of fields in bundle");
1862 return getElements()[index];
1863}
1864
1865OpenBundleType::ElementType OpenBundleType::getElementType(StringAttr name) {
1866 auto element = getElement(name);
1867 return element ? element->type : FIRRTLBaseType();
1868}
1869
1870OpenBundleType::ElementType OpenBundleType::getElementType(StringRef name) {
1871 auto element = getElement(name);
1872 return element ? element->type : FIRRTLBaseType();
1873}
1874
1875OpenBundleType::ElementType OpenBundleType::getElementType(size_t index) const {
1876 assert(index < getNumElements() &&
1877 "index must be less than number of fields in bundle");
1878 return getElements()[index].type;
1879}
1880
1881uint64_t OpenBundleType::getFieldID(uint64_t index) const {
1882 return getImpl()->fieldIDs[index];
1883}
1884
1885uint64_t OpenBundleType::getIndexForFieldID(uint64_t fieldID) const {
1886 assert(!getElements().empty() && "Bundle must have >0 fields");
1887 auto fieldIDs = getImpl()->fieldIDs;
1888 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1889 return std::distance(fieldIDs.begin(), it);
1890}
1891
1892std::pair<uint64_t, uint64_t>
1893OpenBundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1894 auto index = getIndexForFieldID(fieldID);
1895 auto elementFieldID = getFieldID(index);
1896 return {index, fieldID - elementFieldID};
1897}
1898
1899std::pair<Type, uint64_t>
1900OpenBundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1901 if (fieldID == 0)
1902 return {*this, 0};
1903 auto subfieldIndex = getIndexForFieldID(fieldID);
1904 auto subfieldType = getElementType(subfieldIndex);
1905 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1906 return {subfieldType, subfieldID};
1907}
1908
1909uint64_t OpenBundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1910
1911std::pair<uint64_t, bool>
1912OpenBundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1913 auto childRoot = getFieldID(index);
1914 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1915 : (getFieldID(index + 1) - 1);
1916 return std::make_pair(fieldID - childRoot,
1917 fieldID >= childRoot && fieldID <= rangeEnd);
1918}
1919
1920bool OpenBundleType::isConst() const { return getImpl()->isConst; }
1921
1922OpenBundleType::ElementType
1923OpenBundleType::getElementTypePreservingConst(size_t index) {
1924 auto type = getElementType(index);
1925 // TODO: ConstTypeInterface / Trait ?
1926 return TypeSwitch<FIRRTLType, ElementType>(type)
1927 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
1928 return type.getConstType(type.isConst() || isConst());
1929 })
1930 .Default(type);
1931}
1932
1933LogicalResult
1934OpenBundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1935 ArrayRef<BundleElement> elements, bool isConst) {
1936 SmallPtrSet<StringAttr, 4> nameSet;
1937 for (auto &element : elements) {
1938 if (!nameSet.insert(element.name).second)
1939 return emitErrorFn() << "duplicate field name " << element.name
1940 << " in openbundle";
1941 if (FIRRTLType(element.type).containsReference() && isConst)
1942 return emitErrorFn()
1943 << "'const' bundle cannot have references, but element "
1944 << element.name << " has type " << element.type;
1945 if (type_isa<LHSType>(element.type))
1946 return emitErrorFn() << "bundle element " << element.name
1947 << " cannot have a left-hand side type";
1948 if (type_isa<DomainType>(element.type))
1949 return emitErrorFn() << "bundle element " << element.name
1950 << " cannot have a domain type";
1951 }
1952
1953 return success();
1954}
1955
1956//===----------------------------------------------------------------------===//
1957// FVectorType
1958//===----------------------------------------------------------------------===//
1959
1962 using KeyTy = std::tuple<FIRRTLBaseType, size_t, char>;
1963
1965 bool isConst)
1968 props(elementType.getRecursiveTypeProperties()) {
1969 props.containsConst |= isConst;
1970 }
1971
1972 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1973
1975
1976 static FVectorTypeStorage *construct(TypeStorageAllocator &allocator,
1977 KeyTy key) {
1978 return new (allocator.allocate<FVectorTypeStorage>())
1979 FVectorTypeStorage(std::get<0>(key), std::get<1>(key),
1980 static_cast<bool>(std::get<2>(key)));
1981 }
1982
1985
1986 /// This holds the bits for the type's recursive properties, and can hold a
1987 /// pointer to a passive version of the type.
1991};
1992
1993FVectorType FVectorType::get(FIRRTLBaseType elementType, size_t numElements,
1994 bool isConst) {
1995 return Base::get(elementType.getContext(), elementType, numElements, isConst);
1996}
1997
1998FIRRTLBaseType FVectorType::getElementType() const {
1999 return getImpl()->elementType;
2000}
2001
2002size_t FVectorType::getNumElements() const { return getImpl()->numElements; }
2003
2004/// Return the recursive properties of the type.
2005RecursiveTypeProperties FVectorType::getRecursiveTypeProperties() const {
2006 return getImpl()->props;
2007}
2008
2009/// Return this type with any flip types recursively removed from itself.
2010FIRRTLBaseType FVectorType::getPassiveType() {
2011 auto *impl = getImpl();
2012
2013 // If we've already determined and cached the passive type, use it.
2014 if (impl->passiveType)
2015 return impl->passiveType;
2016
2017 // If this type is already passive, return it and remember for next time.
2018 if (impl->elementType.getRecursiveTypeProperties().isPassive)
2019 return impl->passiveType = *this;
2020
2021 // Otherwise, rebuild a passive version.
2022 auto passiveType = FVectorType::get(getElementType().getPassiveType(),
2023 getNumElements(), isConst());
2024 impl->passiveType = passiveType;
2025 return passiveType;
2026}
2027
2028FVectorType FVectorType::getConstType(bool isConst) const {
2029 if (isConst == this->isConst())
2030 return *this;
2031 return get(getElementType(), getNumElements(), isConst);
2032}
2033
2034FVectorType FVectorType::getAllConstDroppedType() {
2035 if (!containsConst())
2036 return *this;
2037 return get(getElementType().getAllConstDroppedType(), getNumElements(),
2038 false);
2039}
2040
2041/// Return this type with any type aliases recursively removed from itself.
2042FIRRTLBaseType FVectorType::getAnonymousType() {
2043 auto *impl = getImpl();
2044
2045 if (impl->anonymousType)
2046 return impl->anonymousType;
2047
2048 // If this type is already anonymous, return it and remember for next time.
2049 if (!impl->props.containsTypeAlias)
2050 return impl->anonymousType = *this;
2051
2052 // Otherwise, rebuild an anonymous version.
2053 auto anonymousType = FVectorType::get(getElementType().getAnonymousType(),
2054 getNumElements(), isConst());
2055 impl->anonymousType = anonymousType;
2056 return anonymousType;
2057}
2058
2059uint64_t FVectorType::getFieldID(uint64_t index) const {
2060 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2061}
2062
2063uint64_t FVectorType::getIndexForFieldID(uint64_t fieldID) const {
2064 assert(fieldID && "fieldID must be at least 1");
2065 // Divide the field ID by the number of fieldID's per element.
2066 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2067}
2068
2069std::pair<uint64_t, uint64_t>
2070FVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2071 auto index = getIndexForFieldID(fieldID);
2072 auto elementFieldID = getFieldID(index);
2073 return {index, fieldID - elementFieldID};
2074}
2075
2076std::pair<Type, uint64_t>
2077FVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2078 if (fieldID == 0)
2079 return {*this, 0};
2080 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2081}
2082
2083uint64_t FVectorType::getMaxFieldID() const {
2084 return getNumElements() *
2085 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2086}
2087
2088std::pair<uint64_t, bool>
2089FVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2090 auto childRoot = getFieldID(index);
2091 auto rangeEnd =
2092 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2093 return std::make_pair(fieldID - childRoot,
2094 fieldID >= childRoot && fieldID <= rangeEnd);
2095}
2096
2097bool FVectorType::isConst() const { return getImpl()->isConst; }
2098
2099FVectorType::ElementType FVectorType::getElementTypePreservingConst() {
2100 auto type = getElementType();
2101 return type.getConstType(type.isConst() || isConst());
2102}
2103
2104//===----------------------------------------------------------------------===//
2105// OpenVectorType
2106//===----------------------------------------------------------------------===//
2107
2109 using KeyTy = std::tuple<FIRRTLType, size_t, char>;
2110
2112 bool isConst)
2114 isConst(static_cast<char>(isConst)) {
2115 props = elementType.getRecursiveTypeProperties();
2116 props.containsConst |= isConst;
2117 }
2118
2119 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2120
2122
2123 static OpenVectorTypeStorage *construct(TypeStorageAllocator &allocator,
2124 KeyTy key) {
2125 return new (allocator.allocate<OpenVectorTypeStorage>())
2126 OpenVectorTypeStorage(std::get<0>(key), std::get<1>(key),
2127 static_cast<bool>(std::get<2>(key)));
2128 }
2129
2132
2135};
2136
2137OpenVectorType OpenVectorType::get(FIRRTLType elementType, size_t numElements,
2138 bool isConst) {
2139 return Base::get(elementType.getContext(), elementType, numElements, isConst);
2140}
2141
2142OpenVectorType
2143OpenVectorType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
2145 bool isConst) {
2146 if (failed(verify(emitErrorFn, elementType, numElements, isConst)))
2147 return {};
2148 return get(elementType, numElements, isConst);
2149}
2150
2151FIRRTLType OpenVectorType::getElementType() const {
2152 return getImpl()->elementType;
2153}
2154
2155size_t OpenVectorType::getNumElements() const { return getImpl()->numElements; }
2156
2157/// Return the recursive properties of the type.
2158RecursiveTypeProperties OpenVectorType::getRecursiveTypeProperties() const {
2159 return getImpl()->props;
2160}
2161
2162OpenVectorType OpenVectorType::getConstType(bool isConst) const {
2163 if (isConst == this->isConst())
2164 return *this;
2165 return get(getElementType(), getNumElements(), isConst);
2166}
2167
2168uint64_t OpenVectorType::getFieldID(uint64_t index) const {
2169 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2170}
2171
2172uint64_t OpenVectorType::getIndexForFieldID(uint64_t fieldID) const {
2173 assert(fieldID && "fieldID must be at least 1");
2174 // Divide the field ID by the number of fieldID's per element.
2175 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2176}
2177
2178std::pair<uint64_t, uint64_t>
2179OpenVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2180 auto index = getIndexForFieldID(fieldID);
2181 auto elementFieldID = getFieldID(index);
2182 return {index, fieldID - elementFieldID};
2183}
2184
2185std::pair<Type, uint64_t>
2186OpenVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2187 if (fieldID == 0)
2188 return {*this, 0};
2189 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2190}
2191
2192uint64_t OpenVectorType::getMaxFieldID() const {
2193 // If this is requirement, make ODS constraint or actual elementType.
2194 return getNumElements() *
2195 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2196}
2197
2198std::pair<uint64_t, bool>
2199OpenVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2200 auto childRoot = getFieldID(index);
2201 auto rangeEnd =
2202 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2203 return std::make_pair(fieldID - childRoot,
2204 fieldID >= childRoot && fieldID <= rangeEnd);
2205}
2206
2207bool OpenVectorType::isConst() const { return getImpl()->isConst; }
2208
2209OpenVectorType::ElementType OpenVectorType::getElementTypePreservingConst() {
2210 auto type = getElementType();
2211 // TODO: ConstTypeInterface / Trait ?
2212 return TypeSwitch<FIRRTLType, ElementType>(type)
2213 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
2214 return type.getConstType(type.isConst() || isConst());
2215 })
2216 .Default(type);
2217}
2218
2219LogicalResult
2220OpenVectorType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2222 bool isConst) {
2223 if (elementType.containsReference() && isConst)
2224 return emitErrorFn() << "vector cannot be const with references";
2226 return emitErrorFn() << "vector cannot have a left-hand side type";
2228 return emitErrorFn() << "vector cannot have a domain type";
2229 return success();
2230}
2231
2232//===----------------------------------------------------------------------===//
2233// FEnum Type
2234//===----------------------------------------------------------------------===//
2235
2237 using KeyTy = std::tuple<ArrayRef<FEnumType::EnumElement>, char>;
2238
2239 FEnumTypeStorage(ArrayRef<FEnumType::EnumElement> elements, bool isConst)
2241 elements(elements.begin(), elements.end()) {
2242 RecursiveTypeProperties props{true, false, false, isConst,
2243 false, false, false};
2244 dataSize = 0;
2245 for (auto &element : elements) {
2246 auto type = element.type;
2247 auto eltInfo = type.getRecursiveTypeProperties();
2248 props.containsConst |= eltInfo.containsConst;
2249 props.containsTypeAlias |= eltInfo.containsTypeAlias;
2250
2251 dataSize = std::max((size_t)type.getBitWidthOrSentinel(), dataSize);
2252 }
2253 recProps = props;
2254 }
2255
2256 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2257
2258 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
2259
2260 static llvm::hash_code hashKey(const KeyTy &key) {
2261 return llvm::hash_value(key);
2262 }
2263
2264 static FEnumTypeStorage *construct(TypeStorageAllocator &allocator,
2265 KeyTy key) {
2266 return new (allocator.allocate<FEnumTypeStorage>())
2267 FEnumTypeStorage(std::get<0>(key), static_cast<bool>(std::get<1>(key)));
2268 }
2269
2270 SmallVector<FEnumType::EnumElement, 4> elements;
2272 size_t dataSize;
2274};
2275
2276FEnumType FEnumType::get(::mlir::MLIRContext *context,
2277 ArrayRef<EnumElement> elements, bool isConst) {
2278 return Base::get(context, elements, isConst);
2279}
2280
2281ArrayRef<FEnumType::EnumElement> FEnumType::getElements() const {
2282 return getImpl()->elements;
2283}
2284
2285FEnumType FEnumType::getConstType(bool isConst) const {
2286 return get(getContext(), getElements(), isConst);
2287}
2288
2289FEnumType FEnumType::getAllConstDroppedType() {
2290 if (!containsConst())
2291 return *this;
2292
2293 SmallVector<EnumElement> constDroppedElements(
2294 llvm::map_range(getElements(), [](EnumElement element) {
2295 element.type = element.type.getAllConstDroppedType();
2296 return element;
2297 }));
2298 return get(getContext(), constDroppedElements, false);
2299}
2300
2301/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
2302RecursiveTypeProperties FEnumType::getRecursiveTypeProperties() const {
2303 return getImpl()->recProps;
2304}
2305
2306std::optional<unsigned> FEnumType::getElementIndex(StringAttr name) {
2307 for (const auto &it : llvm::enumerate(getElements())) {
2308 auto element = it.value();
2309 if (element.name == name) {
2310 return unsigned(it.index());
2311 }
2312 }
2313 return std::nullopt;
2314}
2315
2316size_t FEnumType::getBitWidth() { return getDataWidth() + getTagWidth(); }
2317
2318size_t FEnumType::getDataWidth() { return getImpl()->dataSize; }
2319
2320size_t FEnumType::getTagWidth() {
2321 if (getElements().size() == 0)
2322 return 0;
2323 // Each tag has the same type.
2324 return cast<IntegerType>(getElements()[0].value.getType()).getWidth();
2325}
2326
2327std::optional<unsigned> FEnumType::getElementIndex(StringRef name) {
2328 for (const auto &it : llvm::enumerate(getElements())) {
2329 auto element = it.value();
2330 if (element.name.getValue() == name) {
2331 return unsigned(it.index());
2332 }
2333 }
2334 return std::nullopt;
2335}
2336
2337StringAttr FEnumType::getElementNameAttr(size_t index) {
2338 assert(index < getNumElements() &&
2339 "index must be less than number of fields in enum");
2340 return getElements()[index].name;
2341}
2342
2343StringRef FEnumType::getElementName(size_t index) {
2344 return getElementNameAttr(index).getValue();
2345}
2346
2347IntegerAttr FEnumType::getElementValueAttr(size_t index) {
2348 return getElements()[index].value;
2349}
2350
2351APInt FEnumType::getElementValue(size_t index) {
2352 return getElementValueAttr(index).getValue();
2353}
2354
2355FIRRTLBaseType FEnumType::getElementType(size_t index) {
2356 return getElements()[index].type;
2357}
2358
2359std::optional<FEnumType::EnumElement> FEnumType::getElement(StringAttr name) {
2360 if (auto maybeIndex = getElementIndex(name))
2361 return getElements()[*maybeIndex];
2362 return std::nullopt;
2363}
2364
2365std::optional<FEnumType::EnumElement> FEnumType::getElement(StringRef name) {
2366 if (auto maybeIndex = getElementIndex(name))
2367 return getElements()[*maybeIndex];
2368 return std::nullopt;
2369}
2370
2371/// Look up an element by index.
2372FEnumType::EnumElement FEnumType::getElement(size_t index) {
2373 assert(index < getNumElements() &&
2374 "index must be less than number of fields in enum");
2375 return getElements()[index];
2376}
2377
2378FIRRTLBaseType FEnumType::getElementType(StringAttr name) {
2379 auto element = getElement(name);
2380 return element ? element->type : FIRRTLBaseType();
2381}
2382
2383FIRRTLBaseType FEnumType::getElementType(StringRef name) {
2384 auto element = getElement(name);
2385 return element ? element->type : FIRRTLBaseType();
2386}
2387
2388FIRRTLBaseType FEnumType::getElementType(size_t index) const {
2389 assert(index < getNumElements() &&
2390 "index must be less than number of fields in enum");
2391 return getElements()[index].type;
2392}
2393
2394FIRRTLBaseType FEnumType::getElementTypePreservingConst(size_t index) {
2395 auto type = getElementType(index);
2396 return type.getConstType(type.isConst() || isConst());
2397}
2398
2399LogicalResult FEnumType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2400 ArrayRef<EnumElement> elements, bool isConst) {
2401 bool first = true;
2402 IntegerAttr previous;
2403 SmallPtrSet<Attribute, 4> nameSet;
2404
2405 for (auto &elt : elements) {
2406 auto r = elt.type.getRecursiveTypeProperties();
2407 if (!r.isPassive)
2408 return emitErrorFn() << "enum field " << elt.name << " not passive";
2409 if (r.containsAnalog)
2410 return emitErrorFn() << "enum field " << elt.name << " contains analog";
2411 if (r.hasUninferredWidth)
2412 return emitErrorFn() << "enum field " << elt.name
2413 << " has uninferred width";
2414 if (r.hasUninferredReset)
2415 return emitErrorFn() << "enum field " << elt.name
2416 << " has uninferred reset";
2417 if (r.containsConst && !isConst)
2418 return emitErrorFn() << "enum with 'const' elements must be 'const'";
2419 // Ensure that each tag has a unique name.
2420 if (!nameSet.insert(elt.name).second)
2421 return emitErrorFn() << "duplicate variant name " << elt.name
2422 << " in enum";
2423 // Ensure that each tag is increasing and unique.
2424 if (first) {
2425 previous = elt.value;
2426 first = false;
2427 } else {
2428 auto current = elt.value;
2429 if (previous.getType() != current.getType())
2430 return emitErrorFn() << "enum variant " << elt.name << " has type"
2431 << current.getType()
2432 << " which is different than previous variant "
2433 << previous.getType();
2434
2435 if (previous.getValue().getBitWidth() != current.getValue().getBitWidth())
2436 return emitErrorFn() << "enum variant " << elt.name << " has bitwidth"
2437 << current.getValue().getBitWidth()
2438 << " which is different than previous variant "
2439 << previous.getValue().getBitWidth();
2440 if (previous.getValue().uge(current.getValue()))
2441 return emitErrorFn()
2442 << "enum variant " << elt.name << " has value " << current
2443 << " which is not greater than previous variant " << previous;
2444 }
2445 // TODO: exclude reference containing
2446 }
2447 return success();
2448}
2449
2450/// Return this type with any type aliases recursively removed from itself.
2451FIRRTLBaseType FEnumType::getAnonymousType() {
2452 auto *impl = getImpl();
2453
2454 if (impl->anonymousType)
2455 return impl->anonymousType;
2456
2457 if (!impl->recProps.containsTypeAlias)
2458 return impl->anonymousType = *this;
2459
2460 SmallVector<FEnumType::EnumElement, 4> elements;
2461
2462 for (auto element : getElements())
2463 elements.push_back(
2464 {element.name, element.value, element.type.getAnonymousType()});
2465 return impl->anonymousType = FEnumType::get(getContext(), elements);
2466}
2467
2468//===----------------------------------------------------------------------===//
2469// BaseTypeAliasType
2470//===----------------------------------------------------------------------===//
2471
2474 using KeyTy = std::tuple<StringAttr, FIRRTLBaseType>;
2475
2479
2480 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2481
2482 KeyTy getAsKey() const { return KeyTy(name, innerType); }
2483
2484 static llvm::hash_code hashKey(const KeyTy &key) {
2485 return llvm::hash_value(key);
2486 }
2487
2488 static BaseTypeAliasStorage *construct(TypeStorageAllocator &allocator,
2489 KeyTy key) {
2490 return new (allocator.allocate<BaseTypeAliasStorage>())
2491 BaseTypeAliasStorage(std::get<0>(key), std::get<1>(key));
2492 }
2493 StringAttr name;
2496};
2497
2498auto BaseTypeAliasType::get(StringAttr name, FIRRTLBaseType innerType)
2499 -> BaseTypeAliasType {
2500 return Base::get(name.getContext(), name, innerType);
2501}
2502
2503auto BaseTypeAliasType::getName() const -> StringAttr {
2504 return getImpl()->name;
2505}
2506
2507auto BaseTypeAliasType::getInnerType() const -> FIRRTLBaseType {
2508 return getImpl()->innerType;
2509}
2510
2511FIRRTLBaseType BaseTypeAliasType::getAnonymousType() {
2512 auto *impl = getImpl();
2513 if (impl->anonymousType)
2514 return impl->anonymousType;
2515 return impl->anonymousType = getInnerType().getAnonymousType();
2516}
2517
2518FIRRTLBaseType BaseTypeAliasType::getPassiveType() {
2519 return getModifiedType(getInnerType().getPassiveType());
2520}
2521
2522RecursiveTypeProperties BaseTypeAliasType::getRecursiveTypeProperties() const {
2523 auto rtp = getInnerType().getRecursiveTypeProperties();
2524 rtp.containsTypeAlias = true;
2525 return rtp;
2526}
2527
2528// If a given `newInnerType` is identical to innerType, return `*this`
2529// because we can reuse the type alias. Otherwise return `newInnerType`.
2531BaseTypeAliasType::getModifiedType(FIRRTLBaseType newInnerType) const {
2532 if (newInnerType == getInnerType())
2533 return *this;
2534 return newInnerType;
2535}
2536
2537// FieldIDTypeInterface implementation.
2538FIRRTLBaseType BaseTypeAliasType::getAllConstDroppedType() {
2539 return getModifiedType(getInnerType().getAllConstDroppedType());
2540}
2541
2542FIRRTLBaseType BaseTypeAliasType::getConstType(bool isConst) const {
2543 return getModifiedType(getInnerType().getConstType(isConst));
2544}
2545
2546std::pair<Type, uint64_t>
2547BaseTypeAliasType::getSubTypeByFieldID(uint64_t fieldID) const {
2548 return hw::FieldIdImpl::getSubTypeByFieldID(getInnerType(), fieldID);
2549}
2550
2551uint64_t BaseTypeAliasType::getMaxFieldID() const {
2552 return hw::FieldIdImpl::getMaxFieldID(getInnerType());
2553}
2554
2555std::pair<uint64_t, bool>
2556BaseTypeAliasType::projectToChildFieldID(uint64_t fieldID,
2557 uint64_t index) const {
2558 return hw::FieldIdImpl::projectToChildFieldID(getInnerType(), fieldID, index);
2559}
2560
2561uint64_t BaseTypeAliasType::getIndexForFieldID(uint64_t fieldID) const {
2562 return hw::FieldIdImpl::getIndexForFieldID(getInnerType(), fieldID);
2563}
2564
2565uint64_t BaseTypeAliasType::getFieldID(uint64_t index) const {
2566 return hw::FieldIdImpl::getFieldID(getInnerType(), index);
2567}
2568
2569std::pair<uint64_t, uint64_t>
2570BaseTypeAliasType::getIndexAndSubfieldID(uint64_t fieldID) const {
2571 return hw::FieldIdImpl::getIndexAndSubfieldID(getInnerType(), fieldID);
2572}
2573
2574//===----------------------------------------------------------------------===//
2575// LHSType
2576//===----------------------------------------------------------------------===//
2577
2578LHSType LHSType::get(FIRRTLBaseType type) {
2579 return LHSType::get(type.getContext(), type);
2580}
2581
2582LogicalResult LHSType::verify(function_ref<InFlightDiagnostic()> emitError,
2583 FIRRTLBaseType type) {
2584 if (type.containsAnalog())
2585 return emitError() << "lhs type cannot contain an AnalogType";
2586 if (!type.isPassive())
2587 return emitError() << "lhs type cannot contain a non-passive type";
2588 if (type.containsReference())
2589 return emitError() << "lhs type cannot contain a reference";
2590 if (type_isa<LHSType>(type))
2591 return emitError() << "lhs type cannot contain a lhs type";
2592
2593 return success();
2594}
2595
2596//===----------------------------------------------------------------------===//
2597// RefType
2598//===----------------------------------------------------------------------===//
2599
2600auto RefType::get(FIRRTLBaseType type, bool forceable, SymbolRefAttr layer)
2601 -> RefType {
2602 return Base::get(type.getContext(), type, forceable, layer);
2603}
2604
2605auto RefType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2606 FIRRTLBaseType base, bool forceable, SymbolRefAttr layer)
2607 -> LogicalResult {
2608 if (!base.isPassive())
2609 return emitErrorFn() << "reference base type must be passive";
2610 if (forceable && base.containsConst())
2611 return emitErrorFn()
2612 << "forceable reference base type cannot contain const";
2613 return success();
2614}
2615
2616RecursiveTypeProperties RefType::getRecursiveTypeProperties() const {
2617 auto rtp = getType().getRecursiveTypeProperties();
2618 rtp.containsReference = true;
2619 // References are not "passive", per FIRRTL spec.
2620 rtp.isPassive = false;
2621 return rtp;
2622}
2623
2624//===----------------------------------------------------------------------===//
2625// AnalogType
2626//===----------------------------------------------------------------------===//
2627
2628AnalogType AnalogType::get(mlir::MLIRContext *context) {
2629 return AnalogType::get(context, -1, false);
2630}
2631
2632AnalogType AnalogType::get(mlir::MLIRContext *context,
2633 std::optional<int32_t> width, bool isConst) {
2634 return AnalogType::get(context, width ? *width : -1, isConst);
2635}
2636
2637LogicalResult AnalogType::verify(function_ref<InFlightDiagnostic()> emitError,
2638 int32_t widthOrSentinel, bool isConst) {
2639 if (widthOrSentinel < -1)
2640 return emitError() << "invalid width";
2641 return success();
2642}
2643
2644int32_t AnalogType::getWidthOrSentinel() const { return getImpl()->width; }
2645
2646AnalogType AnalogType::getConstType(bool isConst) const {
2647 if (isConst == this->isConst())
2648 return *this;
2649 return get(getContext(), getWidthOrSentinel(), isConst);
2650}
2651
2652//===----------------------------------------------------------------------===//
2653// ClockType
2654//===----------------------------------------------------------------------===//
2655
2656ClockType ClockType::getConstType(bool isConst) const {
2657 if (isConst == this->isConst())
2658 return *this;
2659 return get(getContext(), isConst);
2660}
2661
2662//===----------------------------------------------------------------------===//
2663// ResetType
2664//===----------------------------------------------------------------------===//
2665
2666ResetType ResetType::getConstType(bool isConst) const {
2667 if (isConst == this->isConst())
2668 return *this;
2669 return get(getContext(), isConst);
2670}
2671
2672//===----------------------------------------------------------------------===//
2673// AsyncResetType
2674//===----------------------------------------------------------------------===//
2675
2676AsyncResetType AsyncResetType::getConstType(bool isConst) const {
2677 if (isConst == this->isConst())
2678 return *this;
2679 return get(getContext(), isConst);
2680}
2681
2682//===----------------------------------------------------------------------===//
2683// ClassType
2684//===----------------------------------------------------------------------===//
2685
2687 using KeyTy = std::tuple<FlatSymbolRefAttr, ArrayRef<ClassElement>>;
2688
2689 static ClassTypeStorage *construct(TypeStorageAllocator &allocator,
2690 KeyTy key) {
2691 auto name = std::get<0>(key);
2692 auto elements = allocator.copyInto(std::get<1>(key));
2693
2694 // build the field ID table
2695 SmallVector<uint64_t, 4> ids;
2696 uint64_t id = 0;
2697 ids.reserve(elements.size());
2698 for (auto &element : elements) {
2699 id += 1;
2700 ids.push_back(id);
2701 id += hw::FieldIdImpl::getMaxFieldID(element.type);
2702 }
2703
2704 auto fieldIDs = allocator.copyInto(ArrayRef(ids));
2705 auto maxFieldID = id;
2706
2707 return new (allocator.allocate<ClassTypeStorage>())
2709 }
2710
2711 ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef<ClassElement> elements,
2712 ArrayRef<uint64_t> fieldIDs, uint64_t maxFieldID)
2715
2716 bool operator==(const KeyTy &key) const { return getAsKey() == key; }
2717
2718 KeyTy getAsKey() const { return KeyTy(name, elements); }
2719
2720 FlatSymbolRefAttr name;
2721 ArrayRef<ClassElement> elements;
2722 ArrayRef<uint64_t> fieldIDs;
2723 uint64_t maxFieldID;
2724};
2725
2726ClassType ClassType::get(FlatSymbolRefAttr name,
2727 ArrayRef<ClassElement> elements) {
2728 return get(name.getContext(), name, elements);
2729}
2730
2731StringRef ClassType::getName() const {
2732 return getNameAttr().getAttr().getValue();
2733}
2734
2735FlatSymbolRefAttr ClassType::getNameAttr() const { return getImpl()->name; }
2736
2737ArrayRef<ClassElement> ClassType::getElements() const {
2738 return getImpl()->elements;
2739}
2740
2741const ClassElement &ClassType::getElement(IntegerAttr index) const {
2742 return getElement(index.getValue().getZExtValue());
2743}
2744
2745const ClassElement &ClassType::getElement(size_t index) const {
2746 return getElements()[index];
2747}
2748
2749std::optional<uint64_t> ClassType::getElementIndex(StringRef fieldName) const {
2750 for (const auto [i, e] : llvm::enumerate(getElements()))
2751 if (fieldName == e.name)
2752 return i;
2753 return {};
2754}
2755
2756void ClassType::printInterface(AsmPrinter &p) const {
2757 p.printSymbolName(getName());
2758 p << "(";
2759 bool first = true;
2760 for (const auto &element : getElements()) {
2761 if (!first)
2762 p << ", ";
2763 p << element.direction << " ";
2764 p.printKeywordOrString(element.name);
2765 p << ": " << element.type;
2766 first = false;
2767 }
2768 p << ")";
2769}
2770
2771uint64_t ClassType::getFieldID(uint64_t index) const {
2772 return getImpl()->fieldIDs[index];
2773}
2774
2775uint64_t ClassType::getIndexForFieldID(uint64_t fieldID) const {
2776 assert(!getElements().empty() && "Class must have >0 fields");
2777 auto fieldIDs = getImpl()->fieldIDs;
2778 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
2779 return std::distance(fieldIDs.begin(), it);
2780}
2781
2782std::pair<uint64_t, uint64_t>
2783ClassType::getIndexAndSubfieldID(uint64_t fieldID) const {
2784 auto index = getIndexForFieldID(fieldID);
2785 auto elementFieldID = getFieldID(index);
2786 return {index, fieldID - elementFieldID};
2787}
2788
2789std::pair<Type, uint64_t>
2790ClassType::getSubTypeByFieldID(uint64_t fieldID) const {
2791 if (fieldID == 0)
2792 return {*this, 0};
2793 auto subfieldIndex = getIndexForFieldID(fieldID);
2794 auto subfieldType = getElement(subfieldIndex).type;
2795 auto subfieldID = fieldID - getFieldID(subfieldIndex);
2796 return {subfieldType, subfieldID};
2797}
2798
2799uint64_t ClassType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2800
2801std::pair<uint64_t, bool>
2802ClassType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2803 auto childRoot = getFieldID(index);
2804 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
2805 : (getFieldID(index + 1) - 1);
2806 return std::make_pair(fieldID - childRoot,
2807 fieldID >= childRoot && fieldID <= rangeEnd);
2808}
2809
2810namespace {
2811/// Helper to parse interface-like types (ClassType, DomainType).
2812/// This encapsulates the common pattern of parsing @SymbolName(field, field,
2813/// ...)
2814struct InterfaceParser {
2815 AsmParser &parser;
2816
2817 InterfaceParser(AsmParser &parser) : parser(parser) {}
2818
2819 /// Parse the common structure: @SymbolName(field, field, ...)
2820 template <typename FieldContainer>
2821 ParseResult parse(
2822 function_ref<ParseResult(FieldContainer &, AsmParser &parser)> parseField,
2823 StringAttr &symbolName, FieldContainer &fields) {
2824
2825 if (parser.parseSymbolName(symbolName))
2826 return failure();
2827
2828 // Parse required parentheses
2829 if (parser.parseLParen())
2830 return failure();
2831
2832 // Check for empty list (immediate closing paren)
2833 if (failed(parser.parseOptionalRParen())) {
2834 auto parseElement = [&]() -> ParseResult {
2835 return parseField(fields, parser);
2836 };
2837
2838 if (parser.parseCommaSeparatedList(parseElement) || parser.parseRParen())
2839 return failure();
2840 }
2841
2842 return success();
2843 }
2844};
2845} // namespace
2846
2847ParseResult ClassType::parseInterface(AsmParser &parser, ClassType &result) {
2848 InterfaceParser helper(parser);
2849
2850 auto parseField = [](SmallVector<ClassElement> &elements,
2851 AsmParser &parser) -> ParseResult {
2852 // Parse port direction.
2854 if (succeeded(parser.parseOptionalKeyword("out")))
2856 else if (succeeded(parser.parseKeyword("in", "or 'out'")))
2858 else
2859 return failure();
2860
2861 // Parse port name.
2862 std::string keyword;
2863 if (parser.parseKeywordOrString(&keyword))
2864 return failure();
2865 StringAttr name = StringAttr::get(parser.getContext(), keyword);
2866
2867 // Parse port type.
2868 Type type;
2869 if (parser.parseColonType(type))
2870 return failure();
2871
2872 elements.emplace_back(name, type, direction);
2873 return success();
2874 };
2875
2876 StringAttr symbolName;
2877 SmallVector<ClassElement> elements;
2878 if (helper.parse<SmallVector<ClassElement>>(parseField, symbolName, elements))
2879 return failure();
2880
2881 result = ClassType::get(FlatSymbolRefAttr::get(symbolName), elements);
2882 return success();
2883}
2884
2885//===----------------------------------------------------------------------===//
2886// DomainType
2887//===----------------------------------------------------------------------===//
2888
2889ParseResult DomainType::parseInterface(AsmParser &parser, DomainType &result) {
2890 InterfaceParser helper(parser);
2891
2892 auto parseField = [](SmallVector<Attribute> &fields,
2893 AsmParser &parser) -> ParseResult {
2894 std::string fieldNameStr;
2895 Type fieldTypeRaw;
2896 if (parser.parseKeywordOrString(&fieldNameStr) || parser.parseColon() ||
2897 parser.parseType(fieldTypeRaw))
2898 return failure();
2899
2900 auto fieldType = dyn_cast<PropertyType>(fieldTypeRaw);
2901 if (!fieldType)
2902 return parser.emitError(parser.getCurrentLocation(),
2903 "expected property type");
2904
2905 auto fieldName = StringAttr::get(parser.getContext(), fieldNameStr);
2906 fields.push_back(
2907 DomainFieldAttr::get(parser.getContext(), fieldName, fieldType));
2908 return success();
2909 };
2910
2911 StringAttr symbolName;
2912 SmallVector<Attribute> fields;
2913 if (helper.parse<SmallVector<Attribute>>(parseField, symbolName, fields))
2914 return failure();
2915
2916 result = DomainType::get(FlatSymbolRefAttr::get(symbolName),
2917 ArrayAttr::get(parser.getContext(), fields));
2918 return success();
2919}
2920
2921DomainType DomainType::get(FlatSymbolRefAttr name, ArrayAttr fields) {
2922 return Base::get(name.getContext(), name, fields);
2923}
2924
2925DomainType DomainType::getFromDomainOp(DomainOp domainOp) {
2926 auto name = FlatSymbolRefAttr::get(domainOp.getNameAttr());
2927 return DomainType::get(name, domainOp.getFieldsAttr());
2928}
2929
2930DomainFieldAttr DomainType::getField(size_t index) const {
2931 assert(index < getNumFields() && "index out of bounds");
2932 return cast<DomainFieldAttr>(getFields()[index]);
2933}
2934
2935std::optional<uint64_t> DomainType::getFieldIndex(StringRef fieldName) const {
2936 for (const auto [i, attr] : llvm::enumerate(getFields())) {
2937 auto field = cast<DomainFieldAttr>(attr);
2938 if (fieldName == field.getName())
2939 return i;
2940 }
2941 return {};
2942}
2943
2944std::pair<Type, uint64_t>
2945DomainType::getSubTypeByFieldID(uint64_t fieldID) const {
2946 if (fieldID == 0)
2947 return {*this, 0};
2948
2949 // Domain fields don't have sub-fieldIDs, so fieldID directly maps to field
2950 // index
2951 if (fieldID > getNumFields())
2952 return {Type(), fieldID};
2953
2954 return {getField(fieldID - 1).getType(), 0};
2955}
2956
2957uint64_t DomainType::getMaxFieldID() const {
2958 // Each field gets one fieldID
2959 return getNumFields();
2960}
2961
2962std::pair<uint64_t, bool>
2963DomainType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2964 // Domain fields are flat, so projection is simple
2965 if (index >= getNumFields())
2966 return {0, false};
2967
2968 uint64_t childFieldID = index + 1;
2969 return {0, fieldID == childFieldID};
2970}
2971
2972uint64_t DomainType::getFieldID(uint64_t index) const {
2973 // Domain fields are flat, so fieldID is just index + 1
2974 assert(index < getNumFields() && "index out of bounds");
2975 return index + 1;
2976}
2977
2978uint64_t DomainType::getIndexForFieldID(uint64_t fieldID) const {
2979 // Domain fields are flat, so index is just fieldID - 1
2980 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2981 return fieldID - 1;
2982}
2983
2984std::pair<uint64_t, uint64_t>
2985DomainType::getIndexAndSubfieldID(uint64_t fieldID) const {
2986 // Domain fields are flat (no sub-fields), so subfieldID is always 0
2987 assert(fieldID > 0 && fieldID <= getNumFields() && "fieldID out of bounds");
2988 return {fieldID - 1, 0};
2989}
2990
2991LogicalResult
2992DomainType::verifySymbolUses(Operation *op,
2993 SymbolTableCollection &symbolTable) const {
2994 // Find the circuit op to look up the domain definition
2995 auto circuitOp = op->getParentOfType<CircuitOp>();
2996 if (!circuitOp)
2997 return op->emitError() << "domain type used outside of a circuit";
2998
2999 // Check that the symbol exists
3000 auto *symbol = symbolTable.lookupSymbolIn(circuitOp, getName());
3001 if (!symbol)
3002 return op->emitError() << "domain type references undefined symbol '"
3003 << getName().getValue() << "'";
3004
3005 // Check that the symbol is a domain
3006 auto domainOp = dyn_cast<DomainOp>(symbol);
3007 if (!domainOp)
3008 return op->emitError() << "domain type references symbol '"
3009 << getName().getValue() << "' which is not a domain";
3010
3011 // Verify that the domain type fields match the domain definition
3012 auto expectedFields = domainOp.getFieldsAttr();
3013 auto actualFields = getFields();
3014
3015 // Check field count
3016 if (actualFields.size() != expectedFields.size())
3017 return op->emitError() << "domain type has " << actualFields.size()
3018 << " fields but domain definition has "
3019 << expectedFields.size() << " fields";
3020
3021 // Check each field
3022 for (size_t i = 0; i < actualFields.size(); ++i) {
3023 auto actualField = cast<DomainFieldAttr>(actualFields[i]);
3024 auto expectedField = cast<DomainFieldAttr>(expectedFields[i]);
3025
3026 // Check field name
3027 if (actualField.getName() != expectedField.getName())
3028 return op->emitError() << "domain type field " << i << " has name '"
3029 << actualField.getName().getValue()
3030 << "' but domain definition expects '"
3031 << expectedField.getName().getValue() << "'";
3032
3033 // Check field type
3034 if (actualField.getType() != expectedField.getType())
3035 return op->emitError()
3036 << "domain type field '" << actualField.getName().getValue()
3037 << "' has type " << actualField.getType()
3038 << " but domain definition expects " << expectedField.getType();
3039 }
3040
3041 return success();
3042}
3043
3044//===----------------------------------------------------------------------===//
3045// FIRRTLDialect
3046//===----------------------------------------------------------------------===//
3047
3048void FIRRTLDialect::registerTypes() {
3049 addTypes<
3050#define GET_TYPEDEF_LIST
3051#include "circt/Dialect/FIRRTL/FIRRTLTypes.cpp.inc"
3052 >();
3053}
3054
3055// Get the bit width for this type, return None if unknown. Unlike
3056// getBitWidthOrSentinel(), this can recursively compute the bitwidth of
3057// aggregate types. For bundle and vectors, recursively get the width of each
3058// field element and return the total bit width of the aggregate type. This
3059// returns None, if any of the bundle fields is a flip type, or ground type with
3060// unknown bit width.
3061std::optional<int64_t> firrtl::getBitWidth(FIRRTLBaseType type,
3062 bool ignoreFlip) {
3063 std::function<std::optional<int64_t>(FIRRTLBaseType)> getWidth =
3064 [&](FIRRTLBaseType type) -> std::optional<int64_t> {
3065 return TypeSwitch<FIRRTLBaseType, std::optional<int64_t>>(type)
3066 .Case<BundleType>([&](BundleType bundle) -> std::optional<int64_t> {
3067 int64_t width = 0;
3068 for (auto &elt : bundle) {
3069 if (elt.isFlip && !ignoreFlip)
3070 return std::nullopt;
3071 auto w = getBitWidth(elt.type);
3072 if (!w.has_value())
3073 return std::nullopt;
3074 width += *w;
3075 }
3076 return width;
3077 })
3078 .Case<FEnumType>([&](FEnumType fenum) -> std::optional<int64_t> {
3079 int64_t width = 0;
3080 for (auto &elt : fenum) {
3081 auto w = getBitWidth(elt.type);
3082 if (!w.has_value())
3083 return std::nullopt;
3084 width = std::max(width, *w);
3085 }
3086 return width + fenum.getTagWidth();
3087 })
3088 .Case<FVectorType>([&](auto vector) -> std::optional<int64_t> {
3089 auto w = getBitWidth(vector.getElementType());
3090 if (!w.has_value())
3091 return std::nullopt;
3092 return *w * vector.getNumElements();
3093 })
3094 .Case<IntType>([&](IntType iType) { return iType.getWidth(); })
3095 .Case<ClockType, ResetType, AsyncResetType>([](Type) { return 1; })
3096 .Default([&](auto t) { return std::nullopt; });
3097 };
3098 return getWidth(type);
3099}
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
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.
bool isGround()
Return true if this is a 'ground' type, aka a non-aggregate 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....
static UIntType get(MLIRContext *context, unsigned width)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
BaseTy type_cast(Type type)
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.
BaseTy type_dyn_cast(Type type)
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.
bool type_isa(Type type)
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)
uint64_t getMaxFieldID(Type)
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:74
bool containsReference
Whether the type contains a reference type.
Definition FIRRTLTypes.h:78
bool containsTypeAlias
Whether the type contains a type alias.
Definition FIRRTLTypes.h:84
bool containsConst
Whether the type contains a const type.
Definition FIRRTLTypes.h:82
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
std::tuple< FlatSymbolRefAttr, ArrayRef< ClassElement > > KeyTy
bool operator==(const KeyTy &key) const
static ClassTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
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)