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