CIRCT  19.0.0git
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 
22 using namespace circt;
23 using namespace firrtl;
24 
25 using mlir::OptionalParseResult;
26 using 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.
45 static 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.
148 void 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 /// ```
186 static 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") {
311  FIRRTLBaseType elementType;
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") {
322  FIRRTLType elementType;
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.
478 static 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.
494 static 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 
507 static 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 
520 static 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.
573 void FIRRTLDialect::printType(Type type, DialectAsmPrinter &os) const {
574  printNestedType(type, os);
575 }
576 
577 /// Parse a type registered to this dialect.
578 Type 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 
590 enum {
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 
620  char isConst;
621 };
622 
623 /// Return true if this is a 'ground' type, aka a non-aggregate type.
624 bool 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 
641 bool FIRRTLType::isConst() {
642  return TypeSwitch<FIRRTLType, bool>(*this)
643  .Case<FIRRTLBaseType, OpenBundleType, OpenVectorType>(
644  [](auto type) { return type.isConst(); })
645  .Default(false);
646 }
647 
648 bool 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");
680  return RecursiveTypeProperties{};
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 
836 bool 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 
843 bool 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.
879 static 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 
1054 bool 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 
1183  FIRRTLBaseType rhs) {
1184  return lhs.getAnonymousType() == rhs.getAnonymousType();
1185 }
1186 
1187 bool 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
1202 Type firrtl::getPassiveType(Type anyBaseFIRRTLType) {
1203  return type_cast<FIRRTLBaseType>(anyBaseFIRRTLType).getPassiveType();
1204 }
1205 
1206 bool 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
1221 IntType 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 
1269 SIntType SIntType::get(MLIRContext *context) { return get(context, -1, false); }
1270 
1271 SIntType SIntType::get(MLIRContext *context, std::optional<int32_t> width,
1272  bool isConst) {
1273  return get(context, width ? *width : -1, isConst);
1274 }
1275 
1276 LogicalResult 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 
1283 int32_t SIntType::getWidthOrSentinel() const { return getImpl()->width; }
1284 
1285 SIntType 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 
1295 UIntType UIntType::get(MLIRContext *context) { return get(context, -1, false); }
1296 
1297 UIntType UIntType::get(MLIRContext *context, std::optional<int32_t> width,
1298  bool isConst) {
1299  return get(context, width ? *width : -1, isConst);
1300 }
1301 
1302 LogicalResult 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 
1309 int32_t UIntType::getWidthOrSentinel() const { return getImpl()->width; }
1310 
1311 UIntType 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 
1377 BundleType BundleType::get(MLIRContext *context,
1378  ArrayRef<BundleElement> elements, bool isConst) {
1379  return Base::get(context, elements, isConst);
1380 }
1381 
1382 auto BundleType::getElements() const -> ArrayRef<BundleElement> {
1383  return getImpl()->elements;
1384 }
1385 
1386 /// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1387 RecursiveTypeProperties BundleType::getRecursiveTypeProperties() const {
1388  return getImpl()->props;
1389 }
1390 
1391 /// Return this type with any flip types recursively removed from itself.
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 
1417 BundleType BundleType::getConstType(bool isConst) {
1418  if (isConst == this->isConst())
1419  return *this;
1420  return get(getContext(), getElements(), isConst);
1421 }
1422 
1423 BundleType 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 
1435 std::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 
1445 std::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 
1455 StringAttr 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 
1461 StringRef BundleType::getElementName(size_t index) {
1462  return getElementNameAttr(index).getValue();
1463 }
1464 
1465 std::optional<BundleType::BundleElement>
1466 BundleType::getElement(StringAttr name) {
1467  if (auto maybeIndex = getElementIndex(name))
1468  return getElements()[*maybeIndex];
1469  return std::nullopt;
1470 }
1471 
1472 std::optional<BundleType::BundleElement>
1473 BundleType::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.
1480 BundleType::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 
1486 FIRRTLBaseType BundleType::getElementType(StringAttr name) {
1487  auto element = getElement(name);
1488  return element ? element->type : FIRRTLBaseType();
1489 }
1490 
1491 FIRRTLBaseType BundleType::getElementType(StringRef name) {
1492  auto element = getElement(name);
1493  return element ? element->type : FIRRTLBaseType();
1494 }
1495 
1496 FIRRTLBaseType 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 
1502 uint64_t BundleType::getFieldID(uint64_t index) const {
1503  return getImpl()->fieldIDs[index];
1504 }
1505 
1506 uint64_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 
1513 std::pair<uint64_t, uint64_t>
1514 BundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1515  auto index = getIndexForFieldID(fieldID);
1516  auto elementFieldID = getFieldID(index);
1517  return {index, fieldID - elementFieldID};
1518 }
1519 
1520 std::pair<Type, uint64_t>
1521 BundleType::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 
1530 uint64_t BundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1531 
1532 std::pair<uint64_t, bool>
1533 BundleType::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 
1541 bool BundleType::isConst() { return getImpl()->isConst; }
1542 
1543 BundleType::ElementType
1544 BundleType::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.
1550 FIRRTLBaseType 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'.
1634  char isConst;
1635 };
1636 
1637 OpenBundleType OpenBundleType::get(MLIRContext *context,
1638  ArrayRef<BundleElement> elements,
1639  bool isConst) {
1640  return Base::get(context, elements, isConst);
1641 }
1642 
1643 auto OpenBundleType::getElements() const -> ArrayRef<BundleElement> {
1644  return getImpl()->elements;
1645 }
1646 
1647 /// Return a pair with the 'isPassive' and 'containsAnalog' bits.
1648 RecursiveTypeProperties OpenBundleType::getRecursiveTypeProperties() const {
1649  return getImpl()->props;
1650 }
1651 
1652 OpenBundleType OpenBundleType::getConstType(bool isConst) {
1653  if (isConst == this->isConst())
1654  return *this;
1655  return get(getContext(), getElements(), isConst);
1656 }
1657 
1658 std::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 
1668 std::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 
1678 StringAttr 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 
1684 StringRef OpenBundleType::getElementName(size_t index) {
1685  return getElementNameAttr(index).getValue();
1686 }
1687 
1688 std::optional<OpenBundleType::BundleElement>
1689 OpenBundleType::getElement(StringAttr name) {
1690  if (auto maybeIndex = getElementIndex(name))
1691  return getElements()[*maybeIndex];
1692  return std::nullopt;
1693 }
1694 
1695 std::optional<OpenBundleType::BundleElement>
1696 OpenBundleType::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.
1703 OpenBundleType::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 
1709 OpenBundleType::ElementType OpenBundleType::getElementType(StringAttr name) {
1710  auto element = getElement(name);
1711  return element ? element->type : FIRRTLBaseType();
1712 }
1713 
1714 OpenBundleType::ElementType OpenBundleType::getElementType(StringRef name) {
1715  auto element = getElement(name);
1716  return element ? element->type : FIRRTLBaseType();
1717 }
1718 
1719 OpenBundleType::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 
1725 uint64_t OpenBundleType::getFieldID(uint64_t index) const {
1726  return getImpl()->fieldIDs[index];
1727 }
1728 
1729 uint64_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 
1736 std::pair<uint64_t, uint64_t>
1737 OpenBundleType::getIndexAndSubfieldID(uint64_t fieldID) const {
1738  auto index = getIndexForFieldID(fieldID);
1739  auto elementFieldID = getFieldID(index);
1740  return {index, fieldID - elementFieldID};
1741 }
1742 
1743 std::pair<Type, uint64_t>
1744 OpenBundleType::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 
1753 uint64_t OpenBundleType::getMaxFieldID() const { return getImpl()->maxFieldID; }
1754 
1755 std::pair<uint64_t, bool>
1756 OpenBundleType::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 
1764 bool OpenBundleType::isConst() { return getImpl()->isConst; }
1765 
1766 OpenBundleType::ElementType
1767 OpenBundleType::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 
1777 LogicalResult
1778 OpenBundleType::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 
1802  bool isConst)
1805  props(elementType.getRecursiveTypeProperties()) {
1807  }
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 
1821  size_t numElements;
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 
1831  bool isConst) {
1832  return Base::get(elementType.getContext(), elementType, numElements, isConst);
1833 }
1834 
1835 FIRRTLBaseType FVectorType::getElementType() const {
1836  return getImpl()->elementType;
1837 }
1838 
1839 size_t FVectorType::getNumElements() const { return getImpl()->numElements; }
1840 
1841 /// Return the recursive properties of the type.
1842 RecursiveTypeProperties FVectorType::getRecursiveTypeProperties() const {
1843  return getImpl()->props;
1844 }
1845 
1846 /// Return this type with any flip types recursively removed from itself.
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 
1865 FVectorType FVectorType::getConstType(bool isConst) {
1866  if (isConst == this->isConst())
1867  return *this;
1868  return get(getElementType(), getNumElements(), isConst);
1869 }
1870 
1871 FVectorType 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.
1879 FIRRTLBaseType 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 
1896 uint64_t FVectorType::getFieldID(uint64_t index) const {
1897  return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
1898 }
1899 
1900 uint64_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 
1906 std::pair<uint64_t, uint64_t>
1907 FVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
1908  auto index = getIndexForFieldID(fieldID);
1909  auto elementFieldID = getFieldID(index);
1910  return {index, fieldID - elementFieldID};
1911 }
1912 
1913 std::pair<Type, uint64_t>
1914 FVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
1915  if (fieldID == 0)
1916  return {*this, 0};
1917  return {getElementType(), getIndexAndSubfieldID(fieldID).second};
1918 }
1919 
1920 uint64_t FVectorType::getMaxFieldID() const {
1921  return getNumElements() *
1922  (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
1923 }
1924 
1925 std::pair<uint64_t, bool>
1926 FVectorType::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 
1934 bool FVectorType::isConst() { return getImpl()->isConst; }
1935 
1936 FVectorType::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 
1949  bool isConst)
1951  isConst(static_cast<char>(isConst)) {
1954  }
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 
1968  size_t numElements;
1969 
1971  char isConst;
1972 };
1973 
1974 OpenVectorType OpenVectorType::get(FIRRTLType elementType, size_t numElements,
1975  bool isConst) {
1976  return Base::get(elementType.getContext(), elementType, numElements, isConst);
1977 }
1978 
1979 FIRRTLType OpenVectorType::getElementType() const {
1980  return getImpl()->elementType;
1981 }
1982 
1983 size_t OpenVectorType::getNumElements() const { return getImpl()->numElements; }
1984 
1985 /// Return the recursive properties of the type.
1986 RecursiveTypeProperties OpenVectorType::getRecursiveTypeProperties() const {
1987  return getImpl()->props;
1988 }
1989 
1990 OpenVectorType OpenVectorType::getConstType(bool isConst) {
1991  if (isConst == this->isConst())
1992  return *this;
1993  return get(getElementType(), getNumElements(), isConst);
1994 }
1995 
1996 uint64_t OpenVectorType::getFieldID(uint64_t index) const {
1997  return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
1998 }
1999 
2000 uint64_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 
2006 std::pair<uint64_t, uint64_t>
2007 OpenVectorType::getIndexAndSubfieldID(uint64_t fieldID) const {
2008  auto index = getIndexForFieldID(fieldID);
2009  auto elementFieldID = getFieldID(index);
2010  return {index, fieldID - elementFieldID};
2011 }
2012 
2013 std::pair<Type, uint64_t>
2014 OpenVectorType::getSubTypeByFieldID(uint64_t fieldID) const {
2015  if (fieldID == 0)
2016  return {*this, 0};
2017  return {getElementType(), getIndexAndSubfieldID(fieldID).second};
2018 }
2019 
2020 uint64_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 
2026 std::pair<uint64_t, bool>
2027 OpenVectorType::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 
2035 bool OpenVectorType::isConst() { return getImpl()->isConst; }
2036 
2037 OpenVectorType::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 
2047 LogicalResult
2048 OpenVectorType::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 
2115 FEnumType FEnumType::get(::mlir::MLIRContext *context,
2116  ArrayRef<EnumElement> elements, bool isConst) {
2117  return Base::get(context, elements, isConst);
2118 }
2119 
2120 ArrayRef<FEnumType::EnumElement> FEnumType::getElements() const {
2121  return getImpl()->elements;
2122 }
2123 
2124 FEnumType FEnumType::getConstType(bool isConst) {
2125  return get(getContext(), getElements(), isConst);
2126 }
2127 
2128 FEnumType 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.
2141 RecursiveTypeProperties FEnumType::getRecursiveTypeProperties() const {
2142  return getImpl()->recProps;
2143 }
2144 
2145 std::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 
2155 std::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 
2165 StringAttr 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 
2171 StringRef FEnumType::getElementName(size_t index) {
2172  return getElementNameAttr(index).getValue();
2173 }
2174 
2175 std::optional<FEnumType::EnumElement> FEnumType::getElement(StringAttr name) {
2176  if (auto maybeIndex = getElementIndex(name))
2177  return getElements()[*maybeIndex];
2178  return std::nullopt;
2179 }
2180 
2181 std::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.
2188 FEnumType::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 
2194 FIRRTLBaseType FEnumType::getElementType(StringAttr name) {
2195  auto element = getElement(name);
2196  return element ? element->type : FIRRTLBaseType();
2197 }
2198 
2199 FIRRTLBaseType FEnumType::getElementType(StringRef name) {
2200  auto element = getElement(name);
2201  return element ? element->type : FIRRTLBaseType();
2202 }
2203 
2204 FIRRTLBaseType 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 
2210 FIRRTLBaseType FEnumType::getElementTypePreservingConst(size_t index) {
2211  auto type = getElementType(index);
2212  return type.getConstType(type.isConst() || isConst());
2213 }
2214 
2215 uint64_t FEnumType::getFieldID(uint64_t index) const {
2216  return getImpl()->fieldIDs[index];
2217 }
2218 
2219 uint64_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 
2226 std::pair<uint64_t, uint64_t>
2227 FEnumType::getIndexAndSubfieldID(uint64_t fieldID) const {
2228  auto index = getIndexForFieldID(fieldID);
2229  auto elementFieldID = getFieldID(index);
2230  return {index, fieldID - elementFieldID};
2231 }
2232 
2233 std::pair<Type, uint64_t>
2234 FEnumType::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 
2243 uint64_t FEnumType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2244 
2245 std::pair<uint64_t, bool>
2246 FEnumType::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 
2254 auto 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.
2271 FIRRTLBaseType 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 
2297  innerType(innerType) {}
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 
2317 auto BaseTypeAliasType::get(StringAttr name, FIRRTLBaseType innerType)
2318  -> BaseTypeAliasType {
2319  return Base::get(name.getContext(), name, innerType);
2320 }
2321 
2322 auto BaseTypeAliasType::getName() const -> StringAttr {
2323  return getImpl()->name;
2324 }
2325 
2326 auto BaseTypeAliasType::getInnerType() const -> FIRRTLBaseType {
2327  return getImpl()->innerType;
2328 }
2329 
2330 FIRRTLBaseType BaseTypeAliasType::getAnonymousType() {
2331  auto *impl = getImpl();
2332  if (impl->anonymousType)
2333  return impl->anonymousType;
2334  return impl->anonymousType = getInnerType().getAnonymousType();
2335 }
2336 
2338  return getModifiedType(getInnerType().getPassiveType());
2339 }
2340 
2341 RecursiveTypeProperties 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`.
2349 FIRRTLBaseType BaseTypeAliasType::getModifiedType(FIRRTLBaseType newInnerType) {
2350  if (newInnerType == getInnerType())
2351  return *this;
2352  return newInnerType;
2353 }
2354 
2355 // FieldIDTypeInterface implementation.
2356 FIRRTLBaseType BaseTypeAliasType::getAllConstDroppedType() {
2357  return getModifiedType(getInnerType().getAllConstDroppedType());
2358 }
2359 
2360 FIRRTLBaseType BaseTypeAliasType::getConstType(bool isConst) {
2361  return getModifiedType(getInnerType().getConstType(isConst));
2362 }
2363 
2364 std::pair<Type, uint64_t>
2365 BaseTypeAliasType::getSubTypeByFieldID(uint64_t fieldID) const {
2366  return hw::FieldIdImpl::getSubTypeByFieldID(getInnerType(), fieldID);
2367 }
2368 
2369 uint64_t BaseTypeAliasType::getMaxFieldID() const {
2370  return hw::FieldIdImpl::getMaxFieldID(getInnerType());
2371 }
2372 
2373 std::pair<uint64_t, bool>
2375  uint64_t index) const {
2376  return hw::FieldIdImpl::projectToChildFieldID(getInnerType(), fieldID, index);
2377 }
2378 
2379 uint64_t BaseTypeAliasType::getIndexForFieldID(uint64_t fieldID) const {
2380  return hw::FieldIdImpl::getIndexForFieldID(getInnerType(), fieldID);
2381 }
2382 
2383 uint64_t BaseTypeAliasType::getFieldID(uint64_t index) const {
2384  return hw::FieldIdImpl::getFieldID(getInnerType(), index);
2385 }
2386 
2387 std::pair<uint64_t, uint64_t>
2388 BaseTypeAliasType::getIndexAndSubfieldID(uint64_t fieldID) const {
2389  return hw::FieldIdImpl::getIndexAndSubfieldID(getInnerType(), fieldID);
2390 }
2391 
2392 //===----------------------------------------------------------------------===//
2393 // LHSType
2394 //===----------------------------------------------------------------------===//
2395 
2396 LHSType LHSType::get(FIRRTLBaseType type) {
2397  return LHSType::get(type.getContext(), type);
2398 }
2399 
2400 LogicalResult 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 
2418 auto RefType::get(FIRRTLBaseType type, bool forceable, SymbolRefAttr layer)
2419  -> RefType {
2420  return Base::get(type.getContext(), type, forceable, layer);
2421 }
2422 
2423 auto 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 
2434 RecursiveTypeProperties 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 
2446 AnalogType AnalogType::get(mlir::MLIRContext *context) {
2447  return AnalogType::get(context, -1, false);
2448 }
2449 
2450 AnalogType AnalogType::get(mlir::MLIRContext *context,
2451  std::optional<int32_t> width, bool isConst) {
2452  return AnalogType::get(context, width ? *width : -1, isConst);
2453 }
2454 
2455 LogicalResult 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 
2462 int32_t AnalogType::getWidthOrSentinel() const { return getImpl()->width; }
2463 
2464 AnalogType 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 
2474 ClockType ClockType::getConstType(bool isConst) {
2475  if (isConst == this->isConst())
2476  return *this;
2477  return get(getContext(), isConst);
2478 }
2479 
2480 //===----------------------------------------------------------------------===//
2481 // ResetType
2482 //===----------------------------------------------------------------------===//
2483 
2484 ResetType ResetType::getConstType(bool isConst) {
2485  if (isConst == this->isConst())
2486  return *this;
2487  return get(getContext(), isConst);
2488 }
2489 
2490 //===----------------------------------------------------------------------===//
2491 // AsyncResetType
2492 //===----------------------------------------------------------------------===//
2493 
2494 AsyncResetType AsyncResetType::getConstType(bool isConst) {
2495  if (isConst == this->isConst())
2496  return *this;
2497  return get(getContext(), isConst);
2498 }
2499 
2500 //===----------------------------------------------------------------------===//
2501 // ClassType
2502 //===----------------------------------------------------------------------===//
2503 
2504 struct circt::firrtl::detail::ClassTypeStorage : mlir::TypeStorage {
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 
2544 ClassType ClassType::get(FlatSymbolRefAttr name,
2545  ArrayRef<ClassElement> elements) {
2546  return get(name.getContext(), name, elements);
2547 }
2548 
2549 StringRef ClassType::getName() const {
2550  return getNameAttr().getAttr().getValue();
2551 }
2552 
2553 FlatSymbolRefAttr ClassType::getNameAttr() const { return getImpl()->name; }
2554 
2555 ArrayRef<ClassElement> ClassType::getElements() const {
2556  return getImpl()->elements;
2557 }
2558 
2559 const ClassElement &ClassType::getElement(IntegerAttr index) const {
2560  return getElement(index.getValue().getZExtValue());
2561 }
2562 
2563 const ClassElement &ClassType::getElement(size_t index) const {
2564  return getElements()[index];
2565 }
2566 
2567 std::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 
2574 void 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 
2589 uint64_t ClassType::getFieldID(uint64_t index) const {
2590  return getImpl()->fieldIDs[index];
2591 }
2592 
2593 uint64_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 
2600 std::pair<uint64_t, uint64_t>
2601 ClassType::getIndexAndSubfieldID(uint64_t fieldID) const {
2602  auto index = getIndexForFieldID(fieldID);
2603  auto elementFieldID = getFieldID(index);
2604  return {index, fieldID - elementFieldID};
2605 }
2606 
2607 std::pair<Type, uint64_t>
2608 ClassType::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 
2617 uint64_t ClassType::getMaxFieldID() const { return getImpl()->maxFieldID; }
2618 
2619 std::pair<uint64_t, bool>
2620 ClassType::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 
2628 ParseResult 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 
2669 void 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.
2682 std::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.
Definition: FIRRTLTypes.cpp:45
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.
int32_t width
Definition: FIRRTL.cpp:36
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...
Definition: FIRRTLTypes.h:154
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 ...
Definition: FIRRTLTypes.h:520
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:530
bool containsReference()
Return true if this is or contains a Reference type.
Definition: FIRRTLTypes.h:107
bool isGround()
Return true if this is a 'ground' type, aka a non-aggregate 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.
Definition: FIRRTLTypes.h:296
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).
Definition: FIRRTLTypes.h:275
Represents a limited word-length unsigned integer in SystemC as described in IEEE 1666-2011 ยง7....
Definition: SystemCTypes.h:141
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition: SVOps.cpp:2443
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
uint64_t getWidth(Type t)
Definition: ESIPasses.cpp:32
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
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)
uint64_t getMaxFieldID(Type)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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)
SmallVector< BundleType::BundleElement, 4 > elements
static BundleTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
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
SmallVector< uint64_t, 4 > fieldIDs
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
FEnumTypeStorage(ArrayRef< FEnumType::EnumElement > elements, bool isConst)
std::pair< ArrayRef< FEnumType::EnumElement >, char > KeyTy
static FEnumTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
SmallVector< uint64_t, 4 > fieldIDs
static FIRRTLBaseTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
bool operator==(const KeyTy &key) const
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)
bool operator==(const KeyTy &key) const
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
bool operator==(const KeyTy &key) const
OpenVectorTypeStorage(FIRRTLType elementType, size_t numElements, bool isConst)
static OpenVectorTypeStorage * construct(TypeStorageAllocator &allocator, KeyTy key)
WidthTypeStorage(int32_t width, bool isConst)
static WidthTypeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)
bool operator==(const KeyTy &key) const
std::pair< int32_t, char > KeyTy