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