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