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::tuple<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(std::get<0>(key), std::get<1>(key));
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::tuple<ArrayRef<BundleType::BundleElement>, char>;
1410
1411 BundleTypeStorage(ArrayRef<BundleType::BundleElement> elements, bool isConst)
1413 elements(elements.begin(), elements.end()),
1414 props{true, false, false, isConst, false, false, false} {
1415 uint64_t fieldID = 0;
1416 fieldIDs.reserve(elements.size());
1417 for (auto &element : elements) {
1418 auto type = element.type;
1419 auto eltInfo = type.getRecursiveTypeProperties();
1420 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1421 props.containsAnalog |= eltInfo.containsAnalog;
1422 props.containsReference |= eltInfo.containsReference;
1423 props.containsConst |= eltInfo.containsConst;
1424 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1425 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1426 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1427 fieldID += 1;
1428 fieldIDs.push_back(fieldID);
1429 // Increment the field ID for the next field by the number of subfields.
1430 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1431 }
1432 maxFieldID = fieldID;
1433 }
1434
1435 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1436
1437 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1438
1439 static llvm::hash_code hashKey(const KeyTy &key) {
1440 return llvm::hash_value(key);
1441 }
1442
1443 static BundleTypeStorage *construct(TypeStorageAllocator &allocator,
1444 KeyTy key) {
1445 return new (allocator.allocate<BundleTypeStorage>()) BundleTypeStorage(
1446 std::get<0>(key), static_cast<bool>(std::get<1>(key)));
1447 }
1448
1449 SmallVector<BundleType::BundleElement, 4> elements;
1450 SmallVector<uint64_t, 4> fieldIDs;
1451 uint64_t maxFieldID;
1452
1453 /// This holds the bits for the type's recursive properties, and can hold a
1454 /// pointer to a passive version of the type.
1456 BundleType passiveType;
1457 BundleType anonymousType;
1458};
1459
1460BundleType BundleType::get(MLIRContext *context,
1461 ArrayRef<BundleElement> elements, bool isConst) {
1462 return Base::get(context, elements, isConst);
1463}
1464
1465auto BundleType::getElements() const -> ArrayRef<BundleElement> {
1466 return getImpl()->elements;
1467}
1468
1469/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1470RecursiveTypeProperties BundleType::getRecursiveTypeProperties() const {
1471 return getImpl()->props;
1472}
1473
1474/// Return this type with any flip types recursively removed from itself.
1475FIRRTLBaseType BundleType::getPassiveType() {
1476 auto *impl = getImpl();
1477
1478 // If we've already determined and cached the passive type, use it.
1479 if (impl->passiveType)
1480 return impl->passiveType;
1481
1482 // If this type is already passive, use it and remember for next time.
1483 if (impl->props.isPassive) {
1484 impl->passiveType = *this;
1485 return *this;
1486 }
1487
1488 // Otherwise at least one element is non-passive, rebuild a passive version.
1489 SmallVector<BundleType::BundleElement, 16> newElements;
1490 newElements.reserve(impl->elements.size());
1491 for (auto &elt : impl->elements) {
1492 newElements.push_back({elt.name, false, elt.type.getPassiveType()});
1493 }
1494
1495 auto passiveType = BundleType::get(getContext(), newElements, isConst());
1496 impl->passiveType = passiveType;
1497 return passiveType;
1498}
1499
1500BundleType BundleType::getConstType(bool isConst) const {
1501 if (isConst == this->isConst())
1502 return *this;
1503 return get(getContext(), getElements(), isConst);
1504}
1505
1506BundleType BundleType::getAllConstDroppedType() {
1507 if (!containsConst())
1508 return *this;
1509
1510 SmallVector<BundleElement> constDroppedElements(
1511 llvm::map_range(getElements(), [](BundleElement element) {
1512 element.type = element.type.getAllConstDroppedType();
1513 return element;
1514 }));
1515 return get(getContext(), constDroppedElements, false);
1516}
1517
1518std::optional<unsigned> BundleType::getElementIndex(StringAttr name) {
1519 for (const auto &it : llvm::enumerate(getElements())) {
1520 auto element = it.value();
1521 if (element.name == name) {
1522 return unsigned(it.index());
1523 }
1524 }
1525 return std::nullopt;
1526}
1527
1528std::optional<unsigned> BundleType::getElementIndex(StringRef name) {
1529 for (const auto &it : llvm::enumerate(getElements())) {
1530 auto element = it.value();
1531 if (element.name.getValue() == name) {
1532 return unsigned(it.index());
1533 }
1534 }
1535 return std::nullopt;
1536}
1537
1538StringAttr BundleType::getElementNameAttr(size_t index) {
1539 assert(index < getNumElements() &&
1540 "index must be less than number of fields in bundle");
1541 return getElements()[index].name;
1542}
1543
1544StringRef BundleType::getElementName(size_t index) {
1545 return getElementNameAttr(index).getValue();
1546}
1547
1548std::optional<BundleType::BundleElement>
1549BundleType::getElement(StringAttr name) {
1550 if (auto maybeIndex = getElementIndex(name))
1551 return getElements()[*maybeIndex];
1552 return std::nullopt;
1553}
1554
1555std::optional<BundleType::BundleElement>
1556BundleType::getElement(StringRef name) {
1557 if (auto maybeIndex = getElementIndex(name))
1558 return getElements()[*maybeIndex];
1559 return std::nullopt;
1560}
1561
1562/// Look up an element by index.
1563BundleType::BundleElement BundleType::getElement(size_t index) {
1564 assert(index < getNumElements() &&
1565 "index must be less than number of fields in bundle");
1566 return getElements()[index];
1567}
1568
1569FIRRTLBaseType BundleType::getElementType(StringAttr name) {
1570 auto element = getElement(name);
1571 return element ? element->type : FIRRTLBaseType();
1572}
1573
1574FIRRTLBaseType BundleType::getElementType(StringRef name) {
1575 auto element = getElement(name);
1576 return element ? element->type : FIRRTLBaseType();
1577}
1578
1579FIRRTLBaseType BundleType::getElementType(size_t index) const {
1580 assert(index < getNumElements() &&
1581 "index must be less than number of fields in bundle");
1582 return getElements()[index].type;
1583}
1584
1585uint64_t BundleType::getFieldID(uint64_t index) const {
1586 return getImpl()->fieldIDs[index];
1587}
1588
1589uint64_t BundleType::getIndexForFieldID(uint64_t fieldID) const {
1590 assert(!getElements().empty() && "Bundle must have >0 fields");
1591 auto fieldIDs = getImpl()->fieldIDs;
1592 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1593 return std::distance(fieldIDs.begin(), it);
1594}
1595
1596std::pair<uint64_t, uint64_t>
1597BundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1598 auto index = getIndexForFieldID(fieldID);
1599 auto elementFieldID = getFieldID(index);
1600 return {index, fieldID - elementFieldID};
1601}
1602
1603std::pair<Type, uint64_t>
1604BundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1605 if (fieldID == 0)
1606 return {*this, 0};
1607 auto subfieldIndex = getIndexForFieldID(fieldID);
1608 auto subfieldType = getElementType(subfieldIndex);
1609 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1610 return {subfieldType, subfieldID};
1611}
1612
1613uint64_t BundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1614
1615std::pair<uint64_t, bool>
1616BundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1617 auto childRoot = getFieldID(index);
1618 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1619 : (getFieldID(index + 1) - 1);
1620 return std::make_pair(fieldID - childRoot,
1621 fieldID >= childRoot && fieldID <= rangeEnd);
1622}
1623
1624bool BundleType::isConst() const { return getImpl()->isConst; }
1625
1626BundleType::ElementType
1627BundleType::getElementTypePreservingConst(size_t index) {
1628 auto type = getElementType(index);
1629 return type.getConstType(type.isConst() || isConst());
1630}
1631
1632/// Return this type with any type aliases recursively removed from itself.
1633FIRRTLBaseType BundleType::getAnonymousType() {
1634 auto *impl = getImpl();
1635
1636 // If we've already determined and cached the anonymous type, use it.
1637 if (impl->anonymousType)
1638 return impl->anonymousType;
1639
1640 // If this type is already anonymous, use it and remember for next time.
1641 if (!impl->props.containsTypeAlias) {
1642 impl->anonymousType = *this;
1643 return *this;
1644 }
1645
1646 // Otherwise at least one element has an alias type, rebuild an anonymous
1647 // version.
1648 SmallVector<BundleType::BundleElement, 16> newElements;
1649 newElements.reserve(impl->elements.size());
1650 for (auto &elt : impl->elements)
1651 newElements.push_back({elt.name, elt.isFlip, elt.type.getAnonymousType()});
1652
1653 auto anonymousType = BundleType::get(getContext(), newElements, isConst());
1654 impl->anonymousType = anonymousType;
1655 return anonymousType;
1656}
1657
1658LogicalResult BundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1659 ArrayRef<BundleElement> elements,
1660 bool isConst) {
1661 SmallPtrSet<StringAttr, 4> nameSet;
1662 for (auto &element : elements) {
1663 if (!nameSet.insert(element.name).second)
1664 return emitErrorFn() << "duplicate field name " << element.name
1665 << " in bundle";
1666 }
1667
1668 return success();
1669}
1670
1671//===----------------------------------------------------------------------===//
1672// OpenBundle Type
1673//===----------------------------------------------------------------------===//
1674
1676 using KeyTy = std::tuple<ArrayRef<OpenBundleType::BundleElement>, char>;
1677
1678 OpenBundleTypeStorage(ArrayRef<OpenBundleType::BundleElement> elements,
1679 bool isConst)
1680 : elements(elements.begin(), elements.end()),
1681 props{true, false, false, isConst, false, false, false},
1682 isConst(static_cast<char>(isConst)) {
1683 uint64_t fieldID = 0;
1684 fieldIDs.reserve(elements.size());
1685 for (auto &element : elements) {
1686 auto type = element.type;
1687 auto eltInfo = type.getRecursiveTypeProperties();
1688 props.isPassive &= eltInfo.isPassive & !element.isFlip;
1689 props.containsAnalog |= eltInfo.containsAnalog;
1690 props.containsReference |= eltInfo.containsReference;
1691 props.containsConst |= eltInfo.containsConst;
1692 props.containsTypeAlias |= eltInfo.containsTypeAlias;
1693 props.hasUninferredWidth |= eltInfo.hasUninferredWidth;
1694 props.hasUninferredReset |= eltInfo.hasUninferredReset;
1695 fieldID += 1;
1696 fieldIDs.push_back(fieldID);
1697 // Increment the field ID for the next field by the number of subfields.
1698 // TODO: Maybe just have elementType be FieldIDTypeInterface ?
1699 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
1700 }
1701 maxFieldID = fieldID;
1702 }
1703
1704 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1705
1706 static llvm::hash_code hashKey(const KeyTy &key) {
1707 return llvm::hash_value(key);
1708 }
1709
1710 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
1711
1712 static OpenBundleTypeStorage *construct(TypeStorageAllocator &allocator,
1713 KeyTy key) {
1714 return new (allocator.allocate<OpenBundleTypeStorage>())
1715 OpenBundleTypeStorage(std::get<0>(key),
1716 static_cast<bool>(std::get<1>(key)));
1717 }
1718
1719 SmallVector<OpenBundleType::BundleElement, 4> elements;
1720 SmallVector<uint64_t, 4> fieldIDs;
1721 uint64_t maxFieldID;
1722
1723 /// This holds the bits for the type's recursive properties, and can hold a
1724 /// pointer to a passive version of the type.
1726
1727 // Whether this is 'const'.
1729};
1730
1731OpenBundleType OpenBundleType::get(MLIRContext *context,
1732 ArrayRef<BundleElement> elements,
1733 bool isConst) {
1734 return Base::get(context, elements, isConst);
1735}
1736
1737auto OpenBundleType::getElements() const -> ArrayRef<BundleElement> {
1738 return getImpl()->elements;
1739}
1740
1741/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1742RecursiveTypeProperties OpenBundleType::getRecursiveTypeProperties() const {
1743 return getImpl()->props;
1744}
1745
1746OpenBundleType OpenBundleType::getConstType(bool isConst) const {
1747 if (isConst == this->isConst())
1748 return *this;
1749 return get(getContext(), getElements(), isConst);
1750}
1751
1752std::optional<unsigned> OpenBundleType::getElementIndex(StringAttr name) {
1753 for (const auto &it : llvm::enumerate(getElements())) {
1754 auto element = it.value();
1755 if (element.name == name) {
1756 return unsigned(it.index());
1757 }
1758 }
1759 return std::nullopt;
1760}
1761
1762std::optional<unsigned> OpenBundleType::getElementIndex(StringRef name) {
1763 for (const auto &it : llvm::enumerate(getElements())) {
1764 auto element = it.value();
1765 if (element.name.getValue() == name) {
1766 return unsigned(it.index());
1767 }
1768 }
1769 return std::nullopt;
1770}
1771
1772StringAttr OpenBundleType::getElementNameAttr(size_t index) {
1773 assert(index < getNumElements() &&
1774 "index must be less than number of fields in bundle");
1775 return getElements()[index].name;
1776}
1777
1778StringRef OpenBundleType::getElementName(size_t index) {
1779 return getElementNameAttr(index).getValue();
1780}
1781
1782std::optional<OpenBundleType::BundleElement>
1783OpenBundleType::getElement(StringAttr name) {
1784 if (auto maybeIndex = getElementIndex(name))
1785 return getElements()[*maybeIndex];
1786 return std::nullopt;
1787}
1788
1789std::optional<OpenBundleType::BundleElement>
1790OpenBundleType::getElement(StringRef name) {
1791 if (auto maybeIndex = getElementIndex(name))
1792 return getElements()[*maybeIndex];
1793 return std::nullopt;
1794}
1795
1796/// Look up an element by index.
1797OpenBundleType::BundleElement OpenBundleType::getElement(size_t index) {
1798 assert(index < getNumElements() &&
1799 "index must be less than number of fields in bundle");
1800 return getElements()[index];
1801}
1802
1803OpenBundleType::ElementType OpenBundleType::getElementType(StringAttr name) {
1804 auto element = getElement(name);
1805 return element ? element->type : FIRRTLBaseType();
1806}
1807
1808OpenBundleType::ElementType OpenBundleType::getElementType(StringRef name) {
1809 auto element = getElement(name);
1810 return element ? element->type : FIRRTLBaseType();
1811}
1812
1813OpenBundleType::ElementType OpenBundleType::getElementType(size_t index) const {
1814 assert(index < getNumElements() &&
1815 "index must be less than number of fields in bundle");
1816 return getElements()[index].type;
1817}
1818
1819uint64_t OpenBundleType::getFieldID(uint64_t index) const {
1820 return getImpl()->fieldIDs[index];
1821}
1822
1823uint64_t OpenBundleType::getIndexForFieldID(uint64_t fieldID) const {
1824 assert(!getElements().empty() && "Bundle must have >0 fields");
1825 auto fieldIDs = getImpl()->fieldIDs;
1826 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
1827 return std::distance(fieldIDs.begin(), it);
1828}
1829
1830std::pair<uint64_t, uint64_t>
1831OpenBundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1832 auto index = getIndexForFieldID(fieldID);
1833 auto elementFieldID = getFieldID(index);
1834 return {index, fieldID - elementFieldID};
1835}
1836
1837std::pair<Type, uint64_t>
1838OpenBundleType::getSubTypeByFieldID(uint64_t fieldID) const {
1839 if (fieldID == 0)
1840 return {*this, 0};
1841 auto subfieldIndex = getIndexForFieldID(fieldID);
1842 auto subfieldType = getElementType(subfieldIndex);
1843 auto subfieldID = fieldID - getFieldID(subfieldIndex);
1844 return {subfieldType, subfieldID};
1845}
1846
1847uint64_t OpenBundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1848
1849std::pair<uint64_t, bool>
1850OpenBundleType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
1851 auto childRoot = getFieldID(index);
1852 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
1853 : (getFieldID(index + 1) - 1);
1854 return std::make_pair(fieldID - childRoot,
1855 fieldID >= childRoot && fieldID <= rangeEnd);
1856}
1857
1858bool OpenBundleType::isConst() const { return getImpl()->isConst; }
1859
1860OpenBundleType::ElementType
1861OpenBundleType::getElementTypePreservingConst(size_t index) {
1862 auto type = getElementType(index);
1863 // TODO: ConstTypeInterface / Trait ?
1864 return TypeSwitch<FIRRTLType, ElementType>(type)
1865 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
1866 return type.getConstType(type.isConst() || isConst());
1867 })
1868 .Default(type);
1869}
1870
1871LogicalResult
1872OpenBundleType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
1873 ArrayRef<BundleElement> elements, bool isConst) {
1874 SmallPtrSet<StringAttr, 4> nameSet;
1875 for (auto &element : elements) {
1876 if (!nameSet.insert(element.name).second)
1877 return emitErrorFn() << "duplicate field name " << element.name
1878 << " in openbundle";
1879 if (FIRRTLType(element.type).containsReference() && isConst)
1880 return emitErrorFn()
1881 << "'const' bundle cannot have references, but element "
1882 << element.name << " has type " << element.type;
1883 if (type_isa<LHSType>(element.type))
1884 return emitErrorFn() << "bundle element " << element.name
1885 << " cannot have a left-hand side type";
1886 }
1887
1888 return success();
1889}
1890
1891//===----------------------------------------------------------------------===//
1892// FVectorType
1893//===----------------------------------------------------------------------===//
1894
1897 using KeyTy = std::tuple<FIRRTLBaseType, size_t, char>;
1898
1906
1907 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
1908
1910
1911 static FVectorTypeStorage *construct(TypeStorageAllocator &allocator,
1912 KeyTy key) {
1913 return new (allocator.allocate<FVectorTypeStorage>())
1914 FVectorTypeStorage(std::get<0>(key), std::get<1>(key),
1915 static_cast<bool>(std::get<2>(key)));
1916 }
1917
1920
1921 /// This holds the bits for the type's recursive properties, and can hold a
1922 /// pointer to a passive version of the type.
1926};
1927
1928FVectorType FVectorType::get(FIRRTLBaseType elementType, size_t numElements,
1929 bool isConst) {
1930 return Base::get(elementType.getContext(), elementType, numElements, isConst);
1931}
1932
1933FIRRTLBaseType FVectorType::getElementType() const {
1934 return getImpl()->elementType;
1935}
1936
1937size_t FVectorType::getNumElements() const { return getImpl()->numElements; }
1938
1939/// Return the recursive properties of the type.
1940RecursiveTypeProperties FVectorType::getRecursiveTypeProperties() const {
1941 return getImpl()->props;
1942}
1943
1944/// Return this type with any flip types recursively removed from itself.
1945FIRRTLBaseType FVectorType::getPassiveType() {
1946 auto *impl = getImpl();
1947
1948 // If we've already determined and cached the passive type, use it.
1949 if (impl->passiveType)
1950 return impl->passiveType;
1951
1952 // If this type is already passive, return it and remember for next time.
1953 if (impl->elementType.getRecursiveTypeProperties().isPassive)
1954 return impl->passiveType = *this;
1955
1956 // Otherwise, rebuild a passive version.
1957 auto passiveType = FVectorType::get(getElementType().getPassiveType(),
1958 getNumElements(), isConst());
1959 impl->passiveType = passiveType;
1960 return passiveType;
1961}
1962
1963FVectorType FVectorType::getConstType(bool isConst) const {
1964 if (isConst == this->isConst())
1965 return *this;
1966 return get(getElementType(), getNumElements(), isConst);
1967}
1968
1969FVectorType FVectorType::getAllConstDroppedType() {
1970 if (!containsConst())
1971 return *this;
1972 return get(getElementType().getAllConstDroppedType(), getNumElements(),
1973 false);
1974}
1975
1976/// Return this type with any type aliases recursively removed from itself.
1977FIRRTLBaseType FVectorType::getAnonymousType() {
1978 auto *impl = getImpl();
1979
1980 if (impl->anonymousType)
1981 return impl->anonymousType;
1982
1983 // If this type is already anonymous, return it and remember for next time.
1984 if (!impl->props.containsTypeAlias)
1985 return impl->anonymousType = *this;
1986
1987 // Otherwise, rebuild an anonymous version.
1988 auto anonymousType = FVectorType::get(getElementType().getAnonymousType(),
1989 getNumElements(), isConst());
1990 impl->anonymousType = anonymousType;
1991 return anonymousType;
1992}
1993
1994uint64_t FVectorType::getFieldID(uint64_t index) const {
1995 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
1996}
1997
1998uint64_t FVectorType::getIndexForFieldID(uint64_t fieldID) const {
1999 assert(fieldID && "fieldID must be at least 1");
2000 // Divide the field ID by the number of fieldID's per element.
2001 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2002}
2003
2004std::pair<uint64_t, uint64_t>
2005FVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2006 auto index = getIndexForFieldID(fieldID);
2007 auto elementFieldID = getFieldID(index);
2008 return {index, fieldID - elementFieldID};
2009}
2010
2011std::pair<Type, uint64_t>
2012FVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2013 if (fieldID == 0)
2014 return {*this, 0};
2015 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2016}
2017
2018uint64_t FVectorType::getMaxFieldID() const {
2019 return getNumElements() *
2020 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2021}
2022
2023std::pair<uint64_t, bool>
2024FVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2025 auto childRoot = getFieldID(index);
2026 auto rangeEnd =
2027 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2028 return std::make_pair(fieldID - childRoot,
2029 fieldID >= childRoot && fieldID <= rangeEnd);
2030}
2031
2032bool FVectorType::isConst() const { return getImpl()->isConst; }
2033
2034FVectorType::ElementType FVectorType::getElementTypePreservingConst() {
2035 auto type = getElementType();
2036 return type.getConstType(type.isConst() || isConst());
2037}
2038
2039//===----------------------------------------------------------------------===//
2040// OpenVectorType
2041//===----------------------------------------------------------------------===//
2042
2044 using KeyTy = std::tuple<FIRRTLType, size_t, char>;
2045
2053
2054 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2055
2057
2058 static OpenVectorTypeStorage *construct(TypeStorageAllocator &allocator,
2059 KeyTy key) {
2060 return new (allocator.allocate<OpenVectorTypeStorage>())
2061 OpenVectorTypeStorage(std::get<0>(key), std::get<1>(key),
2062 static_cast<bool>(std::get<2>(key)));
2063 }
2064
2067
2070};
2071
2072OpenVectorType OpenVectorType::get(FIRRTLType elementType, size_t numElements,
2073 bool isConst) {
2074 return Base::get(elementType.getContext(), elementType, numElements, isConst);
2075}
2076
2077FIRRTLType OpenVectorType::getElementType() const {
2078 return getImpl()->elementType;
2079}
2080
2081size_t OpenVectorType::getNumElements() const { return getImpl()->numElements; }
2082
2083/// Return the recursive properties of the type.
2084RecursiveTypeProperties OpenVectorType::getRecursiveTypeProperties() const {
2085 return getImpl()->props;
2086}
2087
2088OpenVectorType OpenVectorType::getConstType(bool isConst) const {
2089 if (isConst == this->isConst())
2090 return *this;
2091 return get(getElementType(), getNumElements(), isConst);
2092}
2093
2094uint64_t OpenVectorType::getFieldID(uint64_t index) const {
2095 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2096}
2097
2098uint64_t OpenVectorType::getIndexForFieldID(uint64_t fieldID) const {
2099 assert(fieldID && "fieldID must be at least 1");
2100 // Divide the field ID by the number of fieldID's per element.
2101 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2102}
2103
2104std::pair<uint64_t, uint64_t>
2105OpenVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2106 auto index = getIndexForFieldID(fieldID);
2107 auto elementFieldID = getFieldID(index);
2108 return {index, fieldID - elementFieldID};
2109}
2110
2111std::pair<Type, uint64_t>
2112OpenVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2113 if (fieldID == 0)
2114 return {*this, 0};
2115 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2116}
2117
2118uint64_t OpenVectorType::getMaxFieldID() const {
2119 // If this is requirement, make ODS constraint or actual elementType.
2120 return getNumElements() *
2121 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
2122}
2123
2124std::pair<uint64_t, bool>
2125OpenVectorType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2126 auto childRoot = getFieldID(index);
2127 auto rangeEnd =
2128 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
2129 return std::make_pair(fieldID - childRoot,
2130 fieldID >= childRoot && fieldID <= rangeEnd);
2131}
2132
2133bool OpenVectorType::isConst() const { return getImpl()->isConst; }
2134
2135OpenVectorType::ElementType OpenVectorType::getElementTypePreservingConst() {
2136 auto type = getElementType();
2137 // TODO: ConstTypeInterface / Trait ?
2138 return TypeSwitch<FIRRTLType, ElementType>(type)
2139 .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>([&](auto type) {
2140 return type.getConstType(type.isConst() || isConst());
2141 })
2142 .Default(type);
2143}
2144
2145LogicalResult
2146OpenVectorType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2148 bool isConst) {
2149 if (elementType.containsReference() && isConst)
2150 return emitErrorFn() << "vector cannot be const with references";
2151 if (type_isa<LHSType>(elementType))
2152 return emitErrorFn() << "vector cannot have a left-hand side type";
2153 return success();
2154}
2155
2156//===----------------------------------------------------------------------===//
2157// FEnum Type
2158//===----------------------------------------------------------------------===//
2159
2161 using KeyTy = std::tuple<ArrayRef<FEnumType::EnumElement>, char>;
2162
2163 FEnumTypeStorage(ArrayRef<FEnumType::EnumElement> elements, bool isConst)
2165 elements(elements.begin(), elements.end()) {
2166 RecursiveTypeProperties props{true, false, false, isConst,
2167 false, false, false};
2168 dataSize = 0;
2169 for (auto &element : elements) {
2170 auto type = element.type;
2171 auto eltInfo = type.getRecursiveTypeProperties();
2172 props.containsConst |= eltInfo.containsConst;
2173 props.containsTypeAlias |= eltInfo.containsTypeAlias;
2174
2175 dataSize = std::max((size_t)type.getBitWidthOrSentinel(), dataSize);
2176 }
2177 recProps = props;
2178 }
2179
2180 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2181
2182 KeyTy getAsKey() const { return KeyTy(elements, isConst); }
2183
2184 static llvm::hash_code hashKey(const KeyTy &key) {
2185 return llvm::hash_value(key);
2186 }
2187
2188 static FEnumTypeStorage *construct(TypeStorageAllocator &allocator,
2189 KeyTy key) {
2190 return new (allocator.allocate<FEnumTypeStorage>())
2191 FEnumTypeStorage(std::get<0>(key), static_cast<bool>(std::get<1>(key)));
2192 }
2193
2194 SmallVector<FEnumType::EnumElement, 4> elements;
2196 size_t dataSize;
2198};
2199
2200FEnumType FEnumType::get(::mlir::MLIRContext *context,
2201 ArrayRef<EnumElement> elements, bool isConst) {
2202 return Base::get(context, elements, isConst);
2203}
2204
2205ArrayRef<FEnumType::EnumElement> FEnumType::getElements() const {
2206 return getImpl()->elements;
2207}
2208
2209FEnumType FEnumType::getConstType(bool isConst) const {
2210 return get(getContext(), getElements(), isConst);
2211}
2212
2213FEnumType FEnumType::getAllConstDroppedType() {
2214 if (!containsConst())
2215 return *this;
2216
2217 SmallVector<EnumElement> constDroppedElements(
2218 llvm::map_range(getElements(), [](EnumElement element) {
2219 element.type = element.type.getAllConstDroppedType();
2220 return element;
2221 }));
2222 return get(getContext(), constDroppedElements, false);
2223}
2224
2225/// Return a pair with the 'isPassive' and 'containsAnalog' bits.
2226RecursiveTypeProperties FEnumType::getRecursiveTypeProperties() const {
2227 return getImpl()->recProps;
2228}
2229
2230std::optional<unsigned> FEnumType::getElementIndex(StringAttr name) {
2231 for (const auto &it : llvm::enumerate(getElements())) {
2232 auto element = it.value();
2233 if (element.name == name) {
2234 return unsigned(it.index());
2235 }
2236 }
2237 return std::nullopt;
2238}
2239
2240size_t FEnumType::getBitWidth() { return getDataWidth() + getTagWidth(); }
2241
2242size_t FEnumType::getDataWidth() { return getImpl()->dataSize; }
2243
2244size_t FEnumType::getTagWidth() {
2245 if (getElements().size() == 0)
2246 return 0;
2247 // Each tag has the same type.
2248 return cast<IntegerType>(getElements()[0].value.getType()).getWidth();
2249}
2250
2251std::optional<unsigned> FEnumType::getElementIndex(StringRef name) {
2252 for (const auto &it : llvm::enumerate(getElements())) {
2253 auto element = it.value();
2254 if (element.name.getValue() == name) {
2255 return unsigned(it.index());
2256 }
2257 }
2258 return std::nullopt;
2259}
2260
2261StringAttr FEnumType::getElementNameAttr(size_t index) {
2262 assert(index < getNumElements() &&
2263 "index must be less than number of fields in enum");
2264 return getElements()[index].name;
2265}
2266
2267StringRef FEnumType::getElementName(size_t index) {
2268 return getElementNameAttr(index).getValue();
2269}
2270
2271IntegerAttr FEnumType::getElementValueAttr(size_t index) {
2272 return getElements()[index].value;
2273}
2274
2275APInt FEnumType::getElementValue(size_t index) {
2276 return getElementValueAttr(index).getValue();
2277}
2278
2279FIRRTLBaseType FEnumType::getElementType(size_t index) {
2280 return getElements()[index].type;
2281}
2282
2283std::optional<FEnumType::EnumElement> FEnumType::getElement(StringAttr name) {
2284 if (auto maybeIndex = getElementIndex(name))
2285 return getElements()[*maybeIndex];
2286 return std::nullopt;
2287}
2288
2289std::optional<FEnumType::EnumElement> FEnumType::getElement(StringRef name) {
2290 if (auto maybeIndex = getElementIndex(name))
2291 return getElements()[*maybeIndex];
2292 return std::nullopt;
2293}
2294
2295/// Look up an element by index.
2296FEnumType::EnumElement FEnumType::getElement(size_t index) {
2297 assert(index < getNumElements() &&
2298 "index must be less than number of fields in enum");
2299 return getElements()[index];
2300}
2301
2302FIRRTLBaseType FEnumType::getElementType(StringAttr name) {
2303 auto element = getElement(name);
2304 return element ? element->type : FIRRTLBaseType();
2305}
2306
2307FIRRTLBaseType FEnumType::getElementType(StringRef name) {
2308 auto element = getElement(name);
2309 return element ? element->type : FIRRTLBaseType();
2310}
2311
2312FIRRTLBaseType FEnumType::getElementType(size_t index) const {
2313 assert(index < getNumElements() &&
2314 "index must be less than number of fields in enum");
2315 return getElements()[index].type;
2316}
2317
2318FIRRTLBaseType FEnumType::getElementTypePreservingConst(size_t index) {
2319 auto type = getElementType(index);
2320 return type.getConstType(type.isConst() || isConst());
2321}
2322
2323LogicalResult FEnumType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2324 ArrayRef<EnumElement> elements, bool isConst) {
2325 bool first = true;
2326 IntegerAttr previous;
2327 SmallPtrSet<Attribute, 4> nameSet;
2328
2329 for (auto &elt : elements) {
2330 auto r = elt.type.getRecursiveTypeProperties();
2331 if (!r.isPassive)
2332 return emitErrorFn() << "enum field " << elt.name << " not passive";
2333 if (r.containsAnalog)
2334 return emitErrorFn() << "enum field " << elt.name << " contains analog";
2335 if (r.hasUninferredWidth)
2336 return emitErrorFn() << "enum field " << elt.name
2337 << " has uninferred width";
2338 if (r.hasUninferredReset)
2339 return emitErrorFn() << "enum field " << elt.name
2340 << " has uninferred reset";
2341 if (r.containsConst && !isConst)
2342 return emitErrorFn() << "enum with 'const' elements must be 'const'";
2343 // Ensure that each tag has a unique name.
2344 if (!nameSet.insert(elt.name).second)
2345 return emitErrorFn() << "duplicate variant name " << elt.name
2346 << " in enum";
2347 // Ensure that each tag is increasing and unique.
2348 if (first) {
2349 previous = elt.value;
2350 first = false;
2351 } else {
2352 auto current = elt.value;
2353 if (previous.getType() != current.getType())
2354 return emitErrorFn() << "enum variant " << elt.name << " has type"
2355 << current.getType()
2356 << " which is different than previous variant "
2357 << previous.getType();
2358
2359 if (previous.getValue().getBitWidth() != current.getValue().getBitWidth())
2360 return emitErrorFn() << "enum variant " << elt.name << " has bitwidth"
2361 << current.getValue().getBitWidth()
2362 << " which is different than previous variant "
2363 << previous.getValue().getBitWidth();
2364 if (previous.getValue().uge(current.getValue()))
2365 return emitErrorFn()
2366 << "enum variant " << elt.name << " has value " << current
2367 << " which is not greater than previous variant " << previous;
2368 }
2369 // TODO: exclude reference containing
2370 }
2371 return success();
2372}
2373
2374/// Return this type with any type aliases recursively removed from itself.
2375FIRRTLBaseType FEnumType::getAnonymousType() {
2376 auto *impl = getImpl();
2377
2378 if (impl->anonymousType)
2379 return impl->anonymousType;
2380
2381 if (!impl->recProps.containsTypeAlias)
2382 return impl->anonymousType = *this;
2383
2384 SmallVector<FEnumType::EnumElement, 4> elements;
2385
2386 for (auto element : getElements())
2387 elements.push_back(
2388 {element.name, element.value, element.type.getAnonymousType()});
2389 return impl->anonymousType = FEnumType::get(getContext(), elements);
2390}
2391
2392//===----------------------------------------------------------------------===//
2393// BaseTypeAliasType
2394//===----------------------------------------------------------------------===//
2395
2398 using KeyTy = std::tuple<StringAttr, FIRRTLBaseType>;
2399
2403
2404 bool operator==(const KeyTy &key) const { return key == getAsKey(); }
2405
2406 KeyTy getAsKey() const { return KeyTy(name, innerType); }
2407
2408 static llvm::hash_code hashKey(const KeyTy &key) {
2409 return llvm::hash_value(key);
2410 }
2411
2412 static BaseTypeAliasStorage *construct(TypeStorageAllocator &allocator,
2413 KeyTy key) {
2414 return new (allocator.allocate<BaseTypeAliasStorage>())
2415 BaseTypeAliasStorage(std::get<0>(key), std::get<1>(key));
2416 }
2417 StringAttr name;
2420};
2421
2422auto BaseTypeAliasType::get(StringAttr name, FIRRTLBaseType innerType)
2423 -> BaseTypeAliasType {
2424 return Base::get(name.getContext(), name, innerType);
2425}
2426
2427auto BaseTypeAliasType::getName() const -> StringAttr {
2428 return getImpl()->name;
2429}
2430
2431auto BaseTypeAliasType::getInnerType() const -> FIRRTLBaseType {
2432 return getImpl()->innerType;
2433}
2434
2435FIRRTLBaseType BaseTypeAliasType::getAnonymousType() {
2436 auto *impl = getImpl();
2437 if (impl->anonymousType)
2438 return impl->anonymousType;
2439 return impl->anonymousType = getInnerType().getAnonymousType();
2440}
2441
2442FIRRTLBaseType BaseTypeAliasType::getPassiveType() {
2443 return getModifiedType(getInnerType().getPassiveType());
2444}
2445
2446RecursiveTypeProperties BaseTypeAliasType::getRecursiveTypeProperties() const {
2447 auto rtp = getInnerType().getRecursiveTypeProperties();
2448 rtp.containsTypeAlias = true;
2449 return rtp;
2450}
2451
2452// If a given `newInnerType` is identical to innerType, return `*this`
2453// because we can reuse the type alias. Otherwise return `newInnerType`.
2455BaseTypeAliasType::getModifiedType(FIRRTLBaseType newInnerType) const {
2456 if (newInnerType == getInnerType())
2457 return *this;
2458 return newInnerType;
2459}
2460
2461// FieldIDTypeInterface implementation.
2462FIRRTLBaseType BaseTypeAliasType::getAllConstDroppedType() {
2463 return getModifiedType(getInnerType().getAllConstDroppedType());
2464}
2465
2466FIRRTLBaseType BaseTypeAliasType::getConstType(bool isConst) const {
2467 return getModifiedType(getInnerType().getConstType(isConst));
2468}
2469
2470std::pair<Type, uint64_t>
2471BaseTypeAliasType::getSubTypeByFieldID(uint64_t fieldID) const {
2472 return hw::FieldIdImpl::getSubTypeByFieldID(getInnerType(), fieldID);
2473}
2474
2475uint64_t BaseTypeAliasType::getMaxFieldID() const {
2476 return hw::FieldIdImpl::getMaxFieldID(getInnerType());
2477}
2478
2479std::pair<uint64_t, bool>
2480BaseTypeAliasType::projectToChildFieldID(uint64_t fieldID,
2481 uint64_t index) const {
2482 return hw::FieldIdImpl::projectToChildFieldID(getInnerType(), fieldID, index);
2483}
2484
2485uint64_t BaseTypeAliasType::getIndexForFieldID(uint64_t fieldID) const {
2486 return hw::FieldIdImpl::getIndexForFieldID(getInnerType(), fieldID);
2487}
2488
2489uint64_t BaseTypeAliasType::getFieldID(uint64_t index) const {
2490 return hw::FieldIdImpl::getFieldID(getInnerType(), index);
2491}
2492
2493std::pair<uint64_t, uint64_t>
2494BaseTypeAliasType::getIndexAndSubfieldID(uint64_t fieldID) const {
2495 return hw::FieldIdImpl::getIndexAndSubfieldID(getInnerType(), fieldID);
2496}
2497
2498//===----------------------------------------------------------------------===//
2499// LHSType
2500//===----------------------------------------------------------------------===//
2501
2502LHSType LHSType::get(FIRRTLBaseType type) {
2503 return LHSType::get(type.getContext(), type);
2504}
2505
2506LogicalResult LHSType::verify(function_ref<InFlightDiagnostic()> emitError,
2507 FIRRTLBaseType type) {
2508 if (type.containsAnalog())
2509 return emitError() << "lhs type cannot contain an AnalogType";
2510 if (!type.isPassive())
2511 return emitError() << "lhs type cannot contain a non-passive type";
2512 if (type.containsReference())
2513 return emitError() << "lhs type cannot contain a reference";
2514 if (type_isa<LHSType>(type))
2515 return emitError() << "lhs type cannot contain a lhs type";
2516
2517 return success();
2518}
2519
2520//===----------------------------------------------------------------------===//
2521// RefType
2522//===----------------------------------------------------------------------===//
2523
2524auto RefType::get(FIRRTLBaseType type, bool forceable, SymbolRefAttr layer)
2525 -> RefType {
2526 return Base::get(type.getContext(), type, forceable, layer);
2527}
2528
2529auto RefType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
2530 FIRRTLBaseType base, bool forceable, SymbolRefAttr layer)
2531 -> LogicalResult {
2532 if (!base.isPassive())
2533 return emitErrorFn() << "reference base type must be passive";
2534 if (forceable && base.containsConst())
2535 return emitErrorFn()
2536 << "forceable reference base type cannot contain const";
2537 return success();
2538}
2539
2540RecursiveTypeProperties RefType::getRecursiveTypeProperties() const {
2541 auto rtp = getType().getRecursiveTypeProperties();
2542 rtp.containsReference = true;
2543 // References are not "passive", per FIRRTL spec.
2544 rtp.isPassive = false;
2545 return rtp;
2546}
2547
2548//===----------------------------------------------------------------------===//
2549// AnalogType
2550//===----------------------------------------------------------------------===//
2551
2552AnalogType AnalogType::get(mlir::MLIRContext *context) {
2553 return AnalogType::get(context, -1, false);
2554}
2555
2556AnalogType AnalogType::get(mlir::MLIRContext *context,
2557 std::optional<int32_t> width, bool isConst) {
2558 return AnalogType::get(context, width ? *width : -1, isConst);
2559}
2560
2561LogicalResult AnalogType::verify(function_ref<InFlightDiagnostic()> emitError,
2562 int32_t widthOrSentinel, bool isConst) {
2563 if (widthOrSentinel < -1)
2564 return emitError() << "invalid width";
2565 return success();
2566}
2567
2568int32_t AnalogType::getWidthOrSentinel() const { return getImpl()->width; }
2569
2570AnalogType AnalogType::getConstType(bool isConst) const {
2571 if (isConst == this->isConst())
2572 return *this;
2573 return get(getContext(), getWidthOrSentinel(), isConst);
2574}
2575
2576//===----------------------------------------------------------------------===//
2577// ClockType
2578//===----------------------------------------------------------------------===//
2579
2580ClockType ClockType::getConstType(bool isConst) const {
2581 if (isConst == this->isConst())
2582 return *this;
2583 return get(getContext(), isConst);
2584}
2585
2586//===----------------------------------------------------------------------===//
2587// ResetType
2588//===----------------------------------------------------------------------===//
2589
2590ResetType ResetType::getConstType(bool isConst) const {
2591 if (isConst == this->isConst())
2592 return *this;
2593 return get(getContext(), isConst);
2594}
2595
2596//===----------------------------------------------------------------------===//
2597// AsyncResetType
2598//===----------------------------------------------------------------------===//
2599
2600AsyncResetType AsyncResetType::getConstType(bool isConst) const {
2601 if (isConst == this->isConst())
2602 return *this;
2603 return get(getContext(), isConst);
2604}
2605
2606//===----------------------------------------------------------------------===//
2607// ClassType
2608//===----------------------------------------------------------------------===//
2609
2611 using KeyTy = std::tuple<FlatSymbolRefAttr, ArrayRef<ClassElement>>;
2612
2613 static ClassTypeStorage *construct(TypeStorageAllocator &allocator,
2614 KeyTy key) {
2615 auto name = std::get<0>(key);
2616 auto elements = allocator.copyInto(std::get<1>(key));
2617
2618 // build the field ID table
2619 SmallVector<uint64_t, 4> ids;
2620 uint64_t id = 0;
2621 ids.reserve(elements.size());
2622 for (auto &element : elements) {
2623 id += 1;
2624 ids.push_back(id);
2625 id += hw::FieldIdImpl::getMaxFieldID(element.type);
2626 }
2627
2628 auto fieldIDs = allocator.copyInto(ArrayRef(ids));
2629 auto maxFieldID = id;
2630
2631 return new (allocator.allocate<ClassTypeStorage>())
2633 }
2634
2635 ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef<ClassElement> elements,
2636 ArrayRef<uint64_t> fieldIDs, uint64_t maxFieldID)
2639
2640 bool operator==(const KeyTy &key) const { return getAsKey() == key; }
2641
2642 KeyTy getAsKey() const { return KeyTy(name, elements); }
2643
2644 FlatSymbolRefAttr name;
2645 ArrayRef<ClassElement> elements;
2646 ArrayRef<uint64_t> fieldIDs;
2647 uint64_t maxFieldID;
2648};
2649
2650ClassType ClassType::get(FlatSymbolRefAttr name,
2651 ArrayRef<ClassElement> elements) {
2652 return get(name.getContext(), name, elements);
2653}
2654
2655StringRef ClassType::getName() const {
2656 return getNameAttr().getAttr().getValue();
2657}
2658
2659FlatSymbolRefAttr ClassType::getNameAttr() const { return getImpl()->name; }
2660
2661ArrayRef<ClassElement> ClassType::getElements() const {
2662 return getImpl()->elements;
2663}
2664
2665const ClassElement &ClassType::getElement(IntegerAttr index) const {
2666 return getElement(index.getValue().getZExtValue());
2667}
2668
2669const ClassElement &ClassType::getElement(size_t index) const {
2670 return getElements()[index];
2671}
2672
2673std::optional<uint64_t> ClassType::getElementIndex(StringRef fieldName) const {
2674 for (const auto [i, e] : llvm::enumerate(getElements()))
2675 if (fieldName == e.name)
2676 return i;
2677 return {};
2678}
2679
2680void ClassType::printInterface(AsmPrinter &p) const {
2681 p.printSymbolName(getName());
2682 p << "(";
2683 bool first = true;
2684 for (const auto &element : getElements()) {
2685 if (!first)
2686 p << ", ";
2687 p << element.direction << " ";
2688 p.printKeywordOrString(element.name);
2689 p << ": " << element.type;
2690 first = false;
2691 }
2692 p << ")";
2693}
2694
2695uint64_t ClassType::getFieldID(uint64_t index) const {
2696 return getImpl()->fieldIDs[index];
2697}
2698
2699uint64_t ClassType::getIndexForFieldID(uint64_t fieldID) const {
2700 assert(!getElements().empty() && "Class must have >0 fields");
2701 auto fieldIDs = getImpl()->fieldIDs;
2702 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
2703 return std::distance(fieldIDs.begin(), it);
2704}
2705
2706std::pair<uint64_t, uint64_t>
2707ClassType::getIndexAndSubfieldID(uint64_t fieldID) const {
2708 auto index = getIndexForFieldID(fieldID);
2709 auto elementFieldID = getFieldID(index);
2710 return {index, fieldID - elementFieldID};
2711}
2712
2713std::pair<Type, uint64_t>
2714ClassType::getSubTypeByFieldID(uint64_t fieldID) const {
2715 if (fieldID == 0)
2716 return {*this, 0};
2717 auto subfieldIndex = getIndexForFieldID(fieldID);
2718 auto subfieldType = getElement(subfieldIndex).type;
2719 auto subfieldID = fieldID - getFieldID(subfieldIndex);
2720 return {subfieldType, subfieldID};
2721}
2722
2723uint64_t ClassType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2724
2725std::pair<uint64_t, bool>
2726ClassType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
2727 auto childRoot = getFieldID(index);
2728 auto rangeEnd = index + 1 >= getNumElements() ? getMaxFieldID()
2729 : (getFieldID(index + 1) - 1);
2730 return std::make_pair(fieldID - childRoot,
2731 fieldID >= childRoot && fieldID <= rangeEnd);
2732}
2733
2734ParseResult ClassType::parseInterface(AsmParser &parser, ClassType &result) {
2735 StringAttr className;
2736 if (parser.parseSymbolName(className))
2737 return failure();
2738
2739 SmallVector<ClassElement> elements;
2740 if (parser.parseCommaSeparatedList(
2741 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
2742 // Parse port direction.
2743 Direction direction;
2744 if (succeeded(parser.parseOptionalKeyword("out")))
2745 direction = Direction::Out;
2746 else if (succeeded(parser.parseKeyword("in", "or 'out'")))
2747 direction = Direction::In;
2748 else
2749 return failure();
2750
2751 // Parse port name.
2752 std::string keyword;
2753 if (parser.parseKeywordOrString(&keyword))
2754 return failure();
2755 StringAttr name = StringAttr::get(parser.getContext(), keyword);
2756
2757 // Parse port type.
2758 Type type;
2759 if (parser.parseColonType(type))
2760 return failure();
2761
2762 elements.emplace_back(name, type, direction);
2763 return success();
2764 }))
2765 return failure();
2766
2767 result = ClassType::get(FlatSymbolRefAttr::get(className), elements);
2768 return success();
2769}
2770
2771//===----------------------------------------------------------------------===//
2772// FIRRTLDialect
2773//===----------------------------------------------------------------------===//
2774
2775void FIRRTLDialect::registerTypes() {
2776 addTypes<
2777#define GET_TYPEDEF_LIST
2778#include "circt/Dialect/FIRRTL/FIRRTLTypes.cpp.inc"
2779 >();
2780}
2781
2782// Get the bit width for this type, return None if unknown. Unlike
2783// getBitWidthOrSentinel(), this can recursively compute the bitwidth of
2784// aggregate types. For bundle and vectors, recursively get the width of each
2785// field element and return the total bit width of the aggregate type. This
2786// returns None, if any of the bundle fields is a flip type, or ground type with
2787// unknown bit width.
2788std::optional<int64_t> firrtl::getBitWidth(FIRRTLBaseType type,
2789 bool ignoreFlip) {
2790 std::function<std::optional<int64_t>(FIRRTLBaseType)> getWidth =
2791 [&](FIRRTLBaseType type) -> std::optional<int64_t> {
2792 return TypeSwitch<FIRRTLBaseType, std::optional<int64_t>>(type)
2793 .Case<BundleType>([&](BundleType bundle) -> std::optional<int64_t> {
2794 int64_t width = 0;
2795 for (auto &elt : bundle) {
2796 if (elt.isFlip && !ignoreFlip)
2797 return std::nullopt;
2798 auto w = getBitWidth(elt.type);
2799 if (!w.has_value())
2800 return std::nullopt;
2801 width += *w;
2802 }
2803 return width;
2804 })
2805 .Case<FEnumType>([&](FEnumType fenum) -> std::optional<int64_t> {
2806 int64_t width = 0;
2807 for (auto &elt : fenum) {
2808 auto w = getBitWidth(elt.type);
2809 if (!w.has_value())
2810 return std::nullopt;
2811 width = std::max(width, *w);
2812 }
2813 return width + fenum.getTagWidth();
2814 })
2815 .Case<FVectorType>([&](auto vector) -> std::optional<int64_t> {
2816 auto w = getBitWidth(vector.getElementType());
2817 if (!w.has_value())
2818 return std::nullopt;
2819 return *w * vector.getNumElements();
2820 })
2821 .Case<IntType>([&](IntType iType) { return iType.getWidth(); })
2822 .Case<ClockType, ResetType, AsyncResetType>([](Type) { return 1; })
2823 .Default([&](auto t) { return std::nullopt; });
2824 };
2825 return getWidth(type);
2826}
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)
std::tuple< ArrayRef< BundleType::BundleElement >, char > KeyTy
SmallVector< BundleType::BundleElement, 4 > elements
static llvm::hash_code hashKey(const KeyTy &key)
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
BundleTypeStorage(ArrayRef< BundleType::BundleElement > elements, bool isConst)
bool operator==(const KeyTy &key) const
bool operator==(const KeyTy &key) const
static ClassTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
std::tuple< FlatSymbolRefAttr, ArrayRef< ClassElement > > KeyTy
ClassTypeStorage(FlatSymbolRefAttr name, ArrayRef< ClassElement > elements, ArrayRef< uint64_t > fieldIDs, uint64_t maxFieldID)
SmallVector< FEnumType::EnumElement, 4 > elements
static llvm::hash_code hashKey(const KeyTy &key)
bool operator==(const KeyTy &key) const
static FEnumTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
FEnumTypeStorage(ArrayRef< FEnumType::EnumElement > elements, bool isConst)
std::tuple< ArrayRef< FEnumType::EnumElement >, char > KeyTy
bool operator==(const KeyTy &key) const
static FIRRTLBaseTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
bool operator==(const KeyTy &key) const
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
static FVectorTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
std::tuple< FIRRTLBaseType, size_t, char > KeyTy
FVectorTypeStorage(FIRRTLBaseType elementType, size_t numElements, bool isConst)
SmallVector< OpenBundleType::BundleElement, 4 > elements
static OpenBundleTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
static llvm::hash_code hashKey(const KeyTy &key)
RecursiveTypeProperties props
This holds the bits for the type's recursive properties, and can hold a pointer to a passive version ...
OpenBundleTypeStorage(ArrayRef< OpenBundleType::BundleElement > elements, bool isConst)
std::tuple< ArrayRef< OpenBundleType::BundleElement >, char > KeyTy
std::tuple< FIRRTLType, size_t, char > KeyTy
static OpenVectorTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
OpenVectorTypeStorage(FIRRTLType elementType, size_t numElements, bool isConst)
WidthTypeStorage(int32_t width, bool isConst)
std::tuple< int32_t, char > KeyTy
bool operator==(const KeyTy &key) const
static WidthTypeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)