CIRCT 24.0.0git
Loading...
Searching...
No Matches
HWTypes.cpp
Go to the documentation of this file.
1//===- HWTypes.cpp - HW types code defs -----------------------------------===//
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// Implementation logic for HW data types.
10//
11//===----------------------------------------------------------------------===//
12
18#include "circt/Support/LLVM.h"
19#include "mlir/IR/AttrTypeSubElements.h"
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/BuiltinTypes.h"
22#include "mlir/IR/Diagnostics.h"
23#include "mlir/IR/DialectImplementation.h"
24#include "mlir/IR/StorageUniquerSupport.h"
25#include "mlir/IR/Types.h"
26#include "mlir/Interfaces/MemorySlotInterfaces.h"
27#include "llvm/ADT/SmallSet.h"
28#include "llvm/ADT/StringExtras.h"
29#include "llvm/ADT/StringSet.h"
30#include "llvm/ADT/TypeSwitch.h"
31
32using namespace circt;
33using namespace circt::hw;
34using namespace circt::hw::detail;
35
36static ParseResult parseHWArray(AsmParser &parser, Attribute &dim,
37 Type &elementType);
38static void printHWArray(AsmPrinter &printer, Attribute dim, Type elementType);
39
40static ParseResult parseHWElementType(AsmParser &parser, Type &elementType);
41static void printHWElementType(AsmPrinter &printer, Type dim);
42
43#define GET_TYPEDEF_CLASSES
44#include "circt/Dialect/HW/HWTypes.cpp.inc"
45
46//===----------------------------------------------------------------------===//
47// Type Helpers
48//===----------------------------------------------------------------------===/
49
50mlir::Type circt::hw::getCanonicalType(mlir::Type type) {
51 mlir::AttrTypeReplacer replacer;
52 replacer.addReplacement(
53 [](TypeAliasType alias) { return alias.getCanonicalType(); });
54 return replacer.replace(type);
55}
56
57/// Return true if the specified type is a value HW Integer type. This checks
58/// that it is a signless standard dialect type or a hw::IntType.
59bool circt::hw::isHWIntegerType(mlir::Type type) {
60 Type canonicalType = getCanonicalType(type);
61
62 if (isa<hw::IntType>(canonicalType))
63 return true;
64
65 auto intType = dyn_cast<IntegerType>(canonicalType);
66 if (!intType || !intType.isSignless())
67 return false;
68
69 return true;
70}
71
72bool circt::hw::isHWEnumType(mlir::Type type) {
73 return isa<hw::EnumType>(getCanonicalType(type));
74}
75
76/// Return true if the specified type can be used as an HW value type, that is
77/// the set of types that can be composed together to represent synthesized,
78/// hardware but not marker types like InOutType.
79bool circt::hw::isHWValueType(Type type) {
80 // Signless and signed integer types are both valid.
81 if (isa<IntegerType, IntType, EnumType>(type))
82 return true;
83
84 if (auto array = dyn_cast<ArrayType>(type))
85 return isHWValueType(array.getElementType());
86
87 if (auto array = dyn_cast<UnpackedArrayType>(type))
88 return isHWValueType(array.getElementType());
89
90 if (auto t = dyn_cast<StructType>(type))
91 return llvm::all_of(t.getElements(),
92 [](auto f) { return isHWValueType(f.type); });
93
94 if (auto t = dyn_cast<UnionType>(type))
95 return llvm::all_of(t.getElements(),
96 [](auto f) { return isHWValueType(f.type); });
97
98 if (auto t = dyn_cast<TypeAliasType>(type))
99 return isHWValueType(t.getCanonicalType());
100
101 return false;
102}
103
105 if (!type)
106 return false;
107
108 if (isa<IntegerType>(type))
109 return true;
110
111 auto *interface =
112 type.getDialect().getRegisteredInterface<ProbeTypeDialectInterface>();
113 return interface && interface->isValidProbeElementType(type);
114}
115
116/// Return the hardware bit width of a type. Does not reflect any encoding,
117/// padding, or storage scheme, just the bit (and wire width) of a
118/// statically-size type. Reflects the number of wires needed to transmit a
119/// value of this type. Returns -1 if the type is not known or cannot be
120/// statically computed.
121int64_t circt::hw::getBitWidth(mlir::Type type) {
122 // Handle built-in types that don't implement the interface. Do this first
123 // since it is faster than downcasting to an interface.
124 return llvm::TypeSwitch<::mlir::Type, int64_t>(type)
125 .Case<IntegerType>(
126 [](IntegerType t) { return t.getIntOrFloatBitWidth(); })
127 .Default([](Type type) -> int64_t {
128 // If type implements the BitWidthTypeInterface, use it.
129 if (auto iface = dyn_cast<BitWidthTypeInterface>(type)) {
130 std::optional<int64_t> width = iface.getBitWidth();
131 return width.has_value() ? *width : -1;
132 }
133 return -1;
134 });
135}
136
137/// Return true if the specified type contains known marker types like
138/// InOutType. Unlike isHWValueType, this is not conservative, it only returns
139/// false on known InOut types, rather than any unknown types.
140bool circt::hw::hasHWInOutType(Type type) {
141 if (auto array = dyn_cast<ArrayType>(type))
142 return hasHWInOutType(array.getElementType());
143
144 if (auto array = dyn_cast<UnpackedArrayType>(type))
145 return hasHWInOutType(array.getElementType());
146
147 if (auto t = dyn_cast<StructType>(type)) {
148 return std::any_of(t.getElements().begin(), t.getElements().end(),
149 [](const auto &f) { return hasHWInOutType(f.type); });
150 }
151
152 if (auto t = dyn_cast<TypeAliasType>(type))
153 return hasHWInOutType(t.getCanonicalType());
154
155 return isa<InOutType>(type);
156}
157
158namespace {
159struct AggregateAttrFrame {
160 SmallVector<Attribute> attrs;
161 SmallVector<Type> types;
162 unsigned remaining;
163
164 AggregateAttrFrame(SmallVector<Type> &&types)
165 : attrs(types.size()), types(std::move(types)), remaining(attrs.size()) {}
166
167 void addChild(Attribute attr) { attrs[--remaining] = attr; }
168 Type getNextChildType() { return types[remaining - 1]; }
169 bool isFinished() const { return remaining == 0; }
170};
171} // namespace
172
173/// Convert an APInt value into a nested aggregate attribute matching the given
174/// HWAggregateType. Returns failure() if the type is not an HWAggregateType or
175/// recursively contains a type other than HWAggregateType or IntegerType.
176LogicalResult circt::hw::apIntToAggregateAttr(Type aggregateType,
177 const APInt &intVal,
178 ArrayAttr &result) {
179 auto *ctx = aggregateType.getContext();
180 SmallVector<AggregateAttrFrame> stack;
181 unsigned nextExtraction = 0;
182
183 auto pushToStack = [&](Type type) -> bool {
184 return TypeSwitch<Type, bool>(type)
185 .Case<StructType>([&](auto structType) {
186 auto len = structType.getElements().size();
187 SmallVector<Type> types;
188 types.reserve(len);
189 for (auto &element : structType.getElements())
190 types.push_back(getCanonicalType(element.type));
191 stack.push_back(std::move(types));
192 return true;
193 })
194 .Case<ArrayType, UnpackedArrayType>([&](auto arrayType) {
195 SmallVector<Type> types(arrayType.getNumElements(),
196 getCanonicalType(arrayType.getElementType()));
197 stack.push_back(std::move(types));
198 return true;
199 })
200 .Default([](Type) {
201 // Unsupported type
202 return false;
203 });
204 };
205
206 if (!pushToStack(getCanonicalType(aggregateType)))
207 return failure();
208
209 while (!stack.empty()) {
210 if (stack.back().isFinished()) {
211 auto frame = stack.pop_back_val();
212 result = ArrayAttr::get(ctx, frame.attrs);
213 if (!stack.empty())
214 stack.back().addChild(result);
215 continue;
216 }
217
218 auto curType = stack.back().getNextChildType();
219 if (auto intType = dyn_cast<IntegerType>(curType)) {
220 auto width = intType.getWidth();
221 auto elemValue = width ? intVal.extractBits(width, nextExtraction)
222 : APInt(0, 0, false);
223 nextExtraction += width;
224 stack.back().addChild(IntegerAttr::get(intType, elemValue));
225 } else {
226 if (!pushToStack(curType))
227 return failure();
228 }
229 }
230
231 assert(nextExtraction == intVal.getBitWidth() &&
232 "constant wasn't fully processed");
233 return success();
234}
235
236/// Convert an ArrayAttr into an APInt value matching the given type.
237/// The type is used to determine the bit width of the resulting APInt.
238/// Returns failure() if the attribute recursively contains anything other than
239/// ArrayAttr or IntegerAttr.
240LogicalResult circt::hw::aggregateAttrToAPInt(Type type, ArrayAttr attr,
241 APInt &result) {
242 SmallVector<Attribute> worklist;
243 worklist.push_back(attr);
244 auto bitWidth = hw::getBitWidth(type);
245 assert(bitWidth >= 0 && "bit width must be known for constant");
246 result = APInt(bitWidth, 0);
247 unsigned nextInsertion = 0;
248
249 while (!worklist.empty()) {
250 auto current = worklist.pop_back_val();
251 if (auto innerArray = dyn_cast<ArrayAttr>(current)) {
252 worklist.append(innerArray.begin(), innerArray.end());
253 continue;
254 }
255
256 if (auto intAttr = dyn_cast<IntegerAttr>(current)) {
257 auto chunk = intAttr.getValue();
258 result.insertBits(chunk, nextInsertion);
259 nextInsertion += chunk.getBitWidth();
260 continue;
261 }
262
263 return failure();
264 }
265
266 assert(nextInsertion == bitWidth && "constant wasn't fully processed");
267 return success();
268}
269
270/// Parse and print nested HW types nicely. These helper methods allow eliding
271/// the "hw." prefix on array, inout, and other types when in a context that
272/// expects HW subelement types.
273static ParseResult parseHWElementType(AsmParser &p, Type &result) {
274 // If this is an HW dialect type, then we don't need/want the !hw. prefix
275 // redundantly specified.
276 auto fullString = static_cast<DialectAsmParser &>(p).getFullSymbolSpec();
277 auto *curPtr = p.getCurrentLocation().getPointer();
278 auto typeString =
279 StringRef(curPtr, fullString.size() - (curPtr - fullString.data()));
280
281 if (typeString.starts_with("array<") || typeString.starts_with("inout<") ||
282 typeString.starts_with("uarray<") || typeString.starts_with("struct<") ||
283 typeString.starts_with("typealias<") || typeString.starts_with("int<") ||
284 typeString.starts_with("enum<") || typeString.starts_with("union<")) {
285 llvm::StringRef mnemonic;
286 if (auto parseResult = generatedTypeParser(p, &mnemonic, result);
287 parseResult.has_value())
288 return *parseResult;
289 return p.emitError(p.getNameLoc(), "invalid type `") << typeString << "`";
290 }
291
292 return p.parseType(result);
293}
294
295static void printHWElementType(AsmPrinter &p, Type element) {
296 if (succeeded(generatedTypePrinter(element, p)))
297 return;
298 p.printType(element);
299}
300
301//===----------------------------------------------------------------------===//
302// Int Type
303//===----------------------------------------------------------------------===//
304
305Type IntType::get(mlir::TypedAttr width) {
306 // The width expression must always be a 32-bit wide integer type itself.
307 auto widthWidth = llvm::dyn_cast<IntegerType>(width.getType());
308 assert(widthWidth && widthWidth.getWidth() == 32 &&
309 "!hw.int width must be 32-bits");
310 (void)widthWidth;
311
312 if (auto cstWidth = llvm::dyn_cast<IntegerAttr>(width))
313 return IntegerType::get(width.getContext(),
314 cstWidth.getValue().getZExtValue());
315
316 return Base::get(width.getContext(), width);
317}
318
319Type IntType::parse(AsmParser &p) {
320 // The bitwidth of the parameter size is always 32 bits.
321 auto int32Type = p.getBuilder().getIntegerType(32);
322
323 mlir::TypedAttr width;
324 if (p.parseLess() || p.parseAttribute(width, int32Type) || p.parseGreater())
325 return Type();
326 return get(width);
327}
328
329void IntType::print(AsmPrinter &p) const {
330 p << "<";
331 p.printAttributeWithoutType(getWidth());
332 p << '>';
333}
334
335//===----------------------------------------------------------------------===//
336// Struct Type
337//===----------------------------------------------------------------------===//
338
339Type StructType::parse(AsmParser &p) {
340 llvm::SmallVector<FieldInfo, 4> parameters;
341 SmallVector<std::pair<llvm::SMLoc, StringAttr>> duplicates;
342 auto parseResult = parseFieldList(p, parameters, duplicates);
343 for (auto [loc, name] : duplicates)
344 p.emitError(loc) << "duplicate field name '" << name.getValue() << "'";
345 if (failed(parseResult) || !duplicates.empty())
346 return Type();
347 return get(p.getContext(), parameters);
348}
349
350LogicalResult StructType::verify(function_ref<InFlightDiagnostic()> emitError,
351 ArrayRef<StructType::FieldInfo> elements) {
352 llvm::SmallDenseSet<StringAttr> fieldNameSet;
353 LogicalResult result = success();
354 fieldNameSet.reserve(elements.size());
355 for (const auto &elt : elements)
356 if (!fieldNameSet.insert(elt.name).second) {
357 result = failure();
358 emitError() << "duplicate field name '" << elt.name.getValue()
359 << "' in hw.struct type";
360 }
361 return result;
362}
363
364void StructType::print(AsmPrinter &p) const {
365 printFieldList(p, getElements());
366}
367
368Type StructType::getFieldType(mlir::StringRef fieldName) {
369 for (const auto &field : getElements())
370 if (field.name == fieldName)
371 return field.type;
372 return Type();
373}
374
375std::optional<uint32_t> StructType::getFieldIndex(mlir::StringRef fieldName) {
376 ArrayRef<hw::StructType::FieldInfo> elems = getElements();
377 for (size_t idx = 0, numElems = elems.size(); idx < numElems; ++idx)
378 if (elems[idx].name == fieldName)
379 return idx;
380 return {};
381}
382
383std::optional<uint32_t> StructType::getFieldIndex(mlir::StringAttr fieldName) {
384 ArrayRef<hw::StructType::FieldInfo> elems = getElements();
385 for (size_t idx = 0, numElems = elems.size(); idx < numElems; ++idx)
386 if (elems[idx].name == fieldName)
387 return idx;
388 return {};
389}
390
391static std::pair<uint64_t, SmallVector<uint64_t>>
392getFieldIDsStruct(const StructType &st) {
393 uint64_t fieldID = 0;
394 auto elements = st.getElements();
395 SmallVector<uint64_t> fieldIDs;
396 fieldIDs.reserve(elements.size());
397 for (auto &element : elements) {
398 auto type = element.type;
399 fieldID += 1;
400 fieldIDs.push_back(fieldID);
401 // Increment the field ID for the next field by the number of subfields.
402 fieldID += hw::FieldIdImpl::getMaxFieldID(type);
403 }
404 return {fieldID, fieldIDs};
405}
406
407void StructType::getInnerTypes(SmallVectorImpl<Type> &types) {
408 for (const auto &field : getElements())
409 types.push_back(field.type);
410}
411
412uint64_t StructType::getMaxFieldID() const {
413 uint64_t fieldID = 0;
414 for (const auto &field : getElements())
415 fieldID += 1 + hw::FieldIdImpl::getMaxFieldID(field.type);
416 return fieldID;
417}
418
419std::pair<Type, uint64_t>
420StructType::getSubTypeByFieldID(uint64_t fieldID) const {
421 if (fieldID == 0)
422 return {*this, 0};
423 auto [maxId, fieldIDs] = getFieldIDsStruct(*this);
424 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
425 auto subfieldIndex = std::distance(fieldIDs.begin(), it);
426 auto subfieldType = getElements()[subfieldIndex].type;
427 auto subfieldID = fieldID - fieldIDs[subfieldIndex];
428 return {subfieldType, subfieldID};
429}
430
431std::pair<uint64_t, bool>
432StructType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
433 auto [maxId, fieldIDs] = getFieldIDsStruct(*this);
434 auto childRoot = fieldIDs[index];
435 auto rangeEnd =
436 index + 1 >= getElements().size() ? maxId : (fieldIDs[index + 1] - 1);
437 return std::make_pair(fieldID - childRoot,
438 fieldID >= childRoot && fieldID <= rangeEnd);
439}
440
441uint64_t StructType::getFieldID(uint64_t index) const {
442 auto [maxId, fieldIDs] = getFieldIDsStruct(*this);
443 return fieldIDs[index];
444}
445
446uint64_t StructType::getIndexForFieldID(uint64_t fieldID) const {
447 assert(!getElements().empty() && "Bundle must have >0 fields");
448 auto [maxId, fieldIDs] = getFieldIDsStruct(*this);
449 auto *it = std::prev(llvm::upper_bound(fieldIDs, fieldID));
450 return std::distance(fieldIDs.begin(), it);
451}
452
453std::pair<uint64_t, uint64_t>
454StructType::getIndexAndSubfieldID(uint64_t fieldID) const {
455 auto index = getIndexForFieldID(fieldID);
456 auto elementFieldID = getFieldID(index);
457 return {index, fieldID - elementFieldID};
458}
459
460std::optional<DenseMap<Attribute, Type>>
461hw::StructType::getSubelementIndexMap() const {
462 DenseMap<Attribute, Type> destructured;
463 for (auto [i, field] : llvm::enumerate(getElements()))
464 destructured.insert(
465 {IntegerAttr::get(IndexType::get(getContext()), i), field.type});
466 return destructured;
467}
468
469Type hw::StructType::getTypeAtIndex(Attribute index) const {
470 auto indexAttr = llvm::dyn_cast<IntegerAttr>(index);
471 if (!indexAttr)
472 return {};
473
474 return getSubTypeByFieldID(indexAttr.getInt()).first;
475}
476
477std::optional<int64_t> StructType::getBitWidth() const {
478 int64_t total = 0;
479 for (auto field : getElements()) {
480 int64_t fieldSize = hw::getBitWidth(field.type);
481 if (fieldSize < 0)
482 return std::nullopt;
483 total += fieldSize;
484 }
485 return total;
486}
487
488//===----------------------------------------------------------------------===//
489// Union Type
490//===----------------------------------------------------------------------===//
491
492namespace circt {
493namespace hw {
494namespace detail {
496 return a.name == b.name && a.type == b.type && a.offset == b.offset;
497}
498// NOLINTNEXTLINE
499llvm::hash_code hash_value(const OffsetFieldInfo &fi) {
500 return llvm::hash_combine(fi.name, fi.type, fi.offset);
501}
502} // namespace detail
503} // namespace hw
504} // namespace circt
505
506Type UnionType::parse(AsmParser &p) {
507 llvm::SmallVector<FieldInfo, 4> parameters;
508 llvm::StringSet<> nameSet;
509 bool hasDuplicateName = false;
510 if (p.parseCommaSeparatedList(
511 mlir::AsmParser::Delimiter::LessGreater, [&]() -> ParseResult {
512 StringRef name;
513 Type type;
514
515 auto fieldLoc = p.getCurrentLocation();
516 if (p.parseKeyword(&name) || p.parseColon() || p.parseType(type))
517 return failure();
518
519 if (!nameSet.insert(name).second) {
520 p.emitError(fieldLoc, "duplicate field name \'" + name +
521 "\' in hw.union type");
522 // Continue parsing to print all duplicates, but make sure to
523 // error eventually
524 hasDuplicateName = true;
525 }
526
527 size_t offset = 0;
528 if (succeeded(p.parseOptionalKeyword("offset")))
529 if (p.parseInteger(offset))
530 return failure();
531 parameters.push_back(UnionType::FieldInfo{
532 StringAttr::get(p.getContext(), name), type, offset});
533 return success();
534 }))
535 return Type();
536
537 if (hasDuplicateName)
538 return Type();
539
540 return get(p.getContext(), parameters);
541}
542
543void UnionType::print(AsmPrinter &odsPrinter) const {
544 odsPrinter << '<';
545 llvm::interleaveComma(
546 getElements(), odsPrinter, [&](const UnionType::FieldInfo &field) {
547 odsPrinter << field.name.getValue() << ": " << field.type;
548 if (field.offset)
549 odsPrinter << " offset " << field.offset;
550 });
551 odsPrinter << ">";
552}
553
554LogicalResult UnionType::verify(function_ref<InFlightDiagnostic()> emitError,
555 ArrayRef<UnionType::FieldInfo> elements) {
556 llvm::SmallDenseSet<StringAttr> fieldNameSet;
557 LogicalResult result = success();
558 fieldNameSet.reserve(elements.size());
559 for (const auto &elt : elements)
560 if (!fieldNameSet.insert(elt.name).second) {
561 result = failure();
562 emitError() << "duplicate field name '" << elt.name.getValue()
563 << "' in hw.union type";
564 }
565 return result;
566}
567
568std::optional<uint32_t> UnionType::getFieldIndex(mlir::StringAttr fieldName) {
569 ArrayRef<hw::UnionType::FieldInfo> elems = getElements();
570 for (size_t idx = 0, numElems = elems.size(); idx < numElems; ++idx)
571 if (elems[idx].name == fieldName)
572 return idx;
573 return {};
574}
575
576std::optional<uint32_t> UnionType::getFieldIndex(mlir::StringRef fieldName) {
577 return getFieldIndex(StringAttr::get(getContext(), fieldName));
578}
579
580UnionType::FieldInfo UnionType::getFieldInfo(::mlir::StringRef fieldName) {
581 if (auto fieldIndex = getFieldIndex(fieldName))
582 return getElements()[*fieldIndex];
583 return FieldInfo();
584}
585
586Type UnionType::getFieldType(mlir::StringRef fieldName) {
587 return getFieldInfo(fieldName).type;
588}
589
590std::optional<int64_t> UnionType::getBitWidth() const {
591 int64_t maxSize = 0;
592 for (auto field : getElements()) {
593 int64_t fieldSize = hw::getBitWidth(field.type);
594 if (fieldSize < 0)
595 return std::nullopt;
596 fieldSize += field.offset;
597 if (fieldSize > maxSize)
598 maxSize = fieldSize;
599 }
600 return maxSize;
601}
602
603//===----------------------------------------------------------------------===//
604// Enum Type
605//===----------------------------------------------------------------------===//
606
607Type EnumType::parse(AsmParser &p) {
608 llvm::SmallVector<Attribute> fields;
609
610 if (p.parseCommaSeparatedList(AsmParser::Delimiter::LessGreater, [&]() {
611 StringRef name;
612 if (p.parseKeyword(&name))
613 return failure();
614 fields.push_back(StringAttr::get(p.getContext(), name));
615 return success();
616 }))
617 return Type();
618
619 return get(p.getContext(), ArrayAttr::get(p.getContext(), fields));
620}
621
622void EnumType::print(AsmPrinter &p) const {
623 p << '<';
624 llvm::interleaveComma(getFields(), p, [&](Attribute enumerator) {
625 p << llvm::cast<StringAttr>(enumerator).getValue();
626 });
627 p << ">";
628}
629
630bool EnumType::contains(mlir::StringRef field) {
631 return indexOf(field).has_value();
632}
633
634std::optional<size_t> EnumType::indexOf(mlir::StringRef field) {
635 for (auto it : llvm::enumerate(getFields()))
636 if (llvm::cast<StringAttr>(it.value()).getValue() == field)
637 return it.index();
638 return {};
639}
640
641std::optional<int64_t> EnumType::getBitWidth() const {
642 auto w = getFields().size();
643 if (w > 1)
644 return llvm::Log2_64_Ceil(w);
645 return 1;
646}
647
648//===----------------------------------------------------------------------===//
649// ArrayType
650//===----------------------------------------------------------------------===//
651
652static ParseResult parseHWArray(AsmParser &p, Attribute &dim, Type &inner) {
653 uint64_t dimLiteral;
654 auto int64Type = p.getBuilder().getIntegerType(64);
655
656 if (auto res = p.parseOptionalInteger(dimLiteral); res.has_value()) {
657 if (failed(*res))
658 return failure();
659 dim = p.getBuilder().getI64IntegerAttr(dimLiteral);
660 } else if (auto res64 = p.parseOptionalAttribute(dim, int64Type);
661 res64.has_value()) {
662 if (failed(*res64))
663 return failure();
664 } else
665 return p.emitError(p.getNameLoc(), "expected integer");
666
667 if (!isa<IntegerAttr, ParamExprAttr, ParamDeclRefAttr>(dim)) {
668 p.emitError(p.getNameLoc(), "unsupported dimension kind in hw.array");
669 return failure();
670 }
671
672 if (p.parseXInDimensionList() || parseHWElementType(p, inner))
673 return failure();
674
675 return success();
676}
677
678static void printHWArray(AsmPrinter &p, Attribute dim, Type elementType) {
679 p.printAttributeWithoutType(dim);
680 p << "x";
682}
683
684size_t ArrayType::getNumElements() const {
685 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(getSizeAttr()))
686 return intAttr.getInt();
687 return -1;
688}
689
690LogicalResult ArrayType::verify(function_ref<InFlightDiagnostic()> emitError,
691 Type innerType, Attribute size) {
692 if (hasHWInOutType(innerType))
693 return emitError() << "hw.array cannot contain InOut types";
694 return success();
695}
696
697uint64_t ArrayType::getMaxFieldID() const {
698 return getNumElements() *
699 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
700}
701
702std::pair<Type, uint64_t>
703ArrayType::getSubTypeByFieldID(uint64_t fieldID) const {
704 if (fieldID == 0)
705 return {*this, 0};
706 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
707}
708
709std::pair<uint64_t, bool>
710ArrayType::projectToChildFieldID(uint64_t fieldID, uint64_t index) const {
711 auto childRoot = getFieldID(index);
712 auto rangeEnd =
713 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
714 return std::make_pair(fieldID - childRoot,
715 fieldID >= childRoot && fieldID <= rangeEnd);
716}
717
718uint64_t ArrayType::getIndexForFieldID(uint64_t fieldID) const {
719 assert(fieldID && "fieldID must be at least 1");
720 // Divide the field ID by the number of fieldID's per element.
721 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
722}
723
724std::pair<uint64_t, uint64_t>
725ArrayType::getIndexAndSubfieldID(uint64_t fieldID) const {
726 auto index = getIndexForFieldID(fieldID);
727 auto elementFieldID = getFieldID(index);
728 return {index, fieldID - elementFieldID};
729}
730
731uint64_t ArrayType::getFieldID(uint64_t index) const {
732 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
733}
734
735std::optional<DenseMap<Attribute, Type>>
736hw::ArrayType::getSubelementIndexMap() const {
737 DenseMap<Attribute, Type> destructured;
738 for (unsigned i = 0; i < getNumElements(); ++i)
739 destructured.insert(
740 {IntegerAttr::get(IndexType::get(getContext()), i), getElementType()});
741 return destructured;
742}
743
744Type hw::ArrayType::getTypeAtIndex(Attribute index) const {
745 return getElementType();
746}
747
748std::optional<int64_t> hw::ArrayType::getBitWidth() const {
749 auto elementBitWidth = hw::getBitWidth(getElementType());
750 if (elementBitWidth < 0)
751 return std::nullopt;
752 int64_t numElements = getNumElements();
753 if (numElements < 0)
754 return std::nullopt;
755 return numElements * elementBitWidth;
756}
757
758//===----------------------------------------------------------------------===//
759// UnpackedArrayType
760//===----------------------------------------------------------------------===//
761
762LogicalResult
763UnpackedArrayType::verify(function_ref<InFlightDiagnostic()> emitError,
764 Type innerType, Attribute size) {
765 if (!isHWValueType(innerType))
766 return emitError() << "invalid element for uarray type";
767 return success();
768}
769
770size_t UnpackedArrayType::getNumElements() const {
771 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(getSizeAttr()))
772 return intAttr.getInt();
773 return -1;
774}
775
776uint64_t UnpackedArrayType::getMaxFieldID() const {
777 return getNumElements() *
778 (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
779}
780
781std::pair<Type, uint64_t>
782UnpackedArrayType::getSubTypeByFieldID(uint64_t fieldID) const {
783 if (fieldID == 0)
784 return {*this, 0};
785 return {getElementType(), getIndexAndSubfieldID(fieldID).second};
786}
787
788std::pair<uint64_t, bool>
789UnpackedArrayType::projectToChildFieldID(uint64_t fieldID,
790 uint64_t index) const {
791 auto childRoot = getFieldID(index);
792 auto rangeEnd =
793 index >= getNumElements() ? getMaxFieldID() : (getFieldID(index + 1) - 1);
794 return std::make_pair(fieldID - childRoot,
795 fieldID >= childRoot && fieldID <= rangeEnd);
796}
797
798uint64_t UnpackedArrayType::getIndexForFieldID(uint64_t fieldID) const {
799 assert(fieldID && "fieldID must be at least 1");
800 // Divide the field ID by the number of fieldID's per element.
801 return (fieldID - 1) / (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
802}
803
804std::pair<uint64_t, uint64_t>
805UnpackedArrayType::getIndexAndSubfieldID(uint64_t fieldID) const {
806 auto index = getIndexForFieldID(fieldID);
807 auto elementFieldID = getFieldID(index);
808 return {index, fieldID - elementFieldID};
809}
810
811uint64_t UnpackedArrayType::getFieldID(uint64_t index) const {
812 return 1 + index * (hw::FieldIdImpl::getMaxFieldID(getElementType()) + 1);
813}
814
815std::optional<int64_t> UnpackedArrayType::getBitWidth() const {
816 auto elementBitWidth = hw::getBitWidth(getElementType());
817 if (elementBitWidth < 0)
818 return std::nullopt;
819 int64_t dimBitWidth = getNumElements();
820 if (dimBitWidth < 0)
821 return std::nullopt;
822 return (int64_t)getNumElements() * elementBitWidth;
823}
824
825//===----------------------------------------------------------------------===//
826// InOutType
827//===----------------------------------------------------------------------===//
828
829LogicalResult InOutType::verify(function_ref<InFlightDiagnostic()> emitError,
830 Type innerType) {
831 if (!isHWValueType(innerType))
832 return emitError() << "invalid element for hw.inout type " << innerType;
833 return success();
834}
835
836//===----------------------------------------------------------------------===//
837// TypeAliasType
838//===----------------------------------------------------------------------===//
839
840TypeAliasType TypeAliasType::get(SymbolRefAttr ref, Type innerType) {
841 return get(ref.getContext(), ref, innerType, hw::getCanonicalType(innerType));
842}
843
844TypeAliasType
845TypeAliasType::getChecked(function_ref<InFlightDiagnostic()> emitError,
846 SymbolRefAttr ref, Type innerType) {
847 return getChecked(emitError, ref.getContext(), ref, innerType,
848 hw::getCanonicalType(innerType));
849}
850
851LogicalResult
852TypeAliasType::verify(function_ref<InFlightDiagnostic()> emitError,
853 SymbolRefAttr ref, Type innerType, Type canonicalType) {
854 if (ref.getNestedReferences().size() != 1)
855 return emitError()
856 << "expected exactly one nested reference in hw.typealias";
857 return success();
858}
859
860Type TypeAliasType::parse(AsmParser &p) {
861 SymbolRefAttr ref;
862 Type type;
863 if (p.parseLess() || p.parseAttribute(ref) || p.parseComma() ||
864 p.parseType(type) || p.parseGreater())
865 return Type();
866
867 return p.getChecked<TypeAliasType>(ref, type);
868}
869
870void TypeAliasType::print(AsmPrinter &p) const {
871 p << "<" << getRef() << ", " << getInnerType() << ">";
872}
873
874/// Return the Typedecl referenced by this TypeAlias, given the module to look
875/// in. This returns null when the IR is malformed.
876TypedeclOp TypeAliasType::getTypeDecl(const HWSymbolCache &cache) {
877 SymbolRefAttr ref = getRef();
878 auto typeScope = ::dyn_cast_or_null<TypeScopeLike>(
879 cache.getDefinition(ref.getRootReference()));
880 if (!typeScope)
881 return {};
882
883 return dyn_cast_or_null<TypedeclOp>(
884 SymbolTable::lookupSymbolIn(typeScope, ref.getLeafReference()));
885}
886
887std::optional<int64_t> TypeAliasType::getBitWidth() const {
888 auto width = hw::getBitWidth(getCanonicalType());
889 if (width < 0)
890 return std::nullopt;
891 return width;
892}
893
894//===----------------------------------------------------------------------===//
895// ModuleType
896//===----------------------------------------------------------------------===//
897
898LogicalResult ModuleType::verify(function_ref<InFlightDiagnostic()> emitError,
899 ArrayRef<ModulePort> ports) {
900 if (llvm::any_of(ports, [](const ModulePort &port) {
901 return hasHWInOutType(port.type);
902 }))
903 return emitError() << "Ports cannot be inout types";
904 return success();
905}
906
907size_t ModuleType::getPortIdForInputId(size_t idx) {
908 assert(idx < getImpl()->inputToAbs.size() && "input port out of range");
909 return getImpl()->inputToAbs[idx];
910}
911
912size_t ModuleType::getPortIdForOutputId(size_t idx) {
913 assert(idx < getImpl()->outputToAbs.size() && " output port out of range");
914 return getImpl()->outputToAbs[idx];
915}
916
917size_t ModuleType::getInputIdForPortId(size_t idx) {
918 auto nIdx = getImpl()->absToInput[idx];
919 assert(nIdx != ~0ULL);
920 return nIdx;
921}
922
923size_t ModuleType::getOutputIdForPortId(size_t idx) {
924 auto nIdx = getImpl()->absToOutput[idx];
925 assert(nIdx != ~0ULL);
926 return nIdx;
927}
928
929size_t ModuleType::getNumInputs() { return getImpl()->inputToAbs.size(); }
930
931size_t ModuleType::getNumOutputs() { return getImpl()->outputToAbs.size(); }
932
933size_t ModuleType::getNumPorts() { return getPorts().size(); }
934
935SmallVector<Type> ModuleType::getInputTypes() {
936 SmallVector<Type> retval;
937 for (auto &p : getPorts()) {
938 if (p.dir == ModulePort::Direction::Input)
939 retval.push_back(p.type);
940 else if (p.dir == ModulePort::Direction::InOut) {
941 retval.push_back(hw::InOutType::get(p.type));
942 }
943 }
944 return retval;
945}
946
947SmallVector<Type> ModuleType::getOutputTypes() {
948 SmallVector<Type> retval;
949 for (auto &p : getPorts())
950 if (p.dir == ModulePort::Direction::Output)
951 retval.push_back(p.type);
952 return retval;
953}
954
955SmallVector<Type> ModuleType::getPortTypes() {
956 SmallVector<Type> retval;
957 for (auto &p : getPorts())
958 retval.push_back(p.type);
959 return retval;
960}
961
962Type ModuleType::getInputType(size_t idx) {
963 const auto &portInfo = getPorts()[getPortIdForInputId(idx)];
964 if (portInfo.dir != ModulePort::InOut)
965 return portInfo.type;
966 return InOutType::get(portInfo.type);
967}
968
969Type ModuleType::getOutputType(size_t idx) {
970 return getPorts()[getPortIdForOutputId(idx)].type;
971}
972
973SmallVector<Attribute> ModuleType::getInputNames() {
974 SmallVector<Attribute> retval;
975 for (auto &p : getPorts())
976 if (p.dir != ModulePort::Direction::Output)
977 retval.push_back(p.name);
978 return retval;
979}
980
981SmallVector<Attribute> ModuleType::getOutputNames() {
982 SmallVector<Attribute> retval;
983 for (auto &p : getPorts())
984 if (p.dir == ModulePort::Direction::Output)
985 retval.push_back(p.name);
986 return retval;
987}
988
989StringAttr ModuleType::getPortNameAttr(size_t idx) {
990 return getPorts()[idx].name;
991}
992
993StringRef ModuleType::getPortName(size_t idx) {
994 auto sa = getPortNameAttr(idx);
995 if (sa)
996 return sa.getValue();
997 return {};
998}
999
1000StringAttr ModuleType::getInputNameAttr(size_t idx) {
1001 return getPorts()[getPortIdForInputId(idx)].name;
1002}
1003
1004StringRef ModuleType::getInputName(size_t idx) {
1005 auto sa = getInputNameAttr(idx);
1006 if (sa)
1007 return sa.getValue();
1008 return {};
1009}
1010
1011StringAttr ModuleType::getOutputNameAttr(size_t idx) {
1012 return getPorts()[getPortIdForOutputId(idx)].name;
1013}
1014
1015StringRef ModuleType::getOutputName(size_t idx) {
1016 auto sa = getOutputNameAttr(idx);
1017 if (sa)
1018 return sa.getValue();
1019 return {};
1020}
1021
1022bool ModuleType::isOutput(size_t idx) {
1023 auto &p = getPorts()[idx];
1024 return p.dir == ModulePort::Direction::Output;
1025}
1026
1027FunctionType ModuleType::getFuncType() {
1028 SmallVector<Type> inputs, outputs;
1029 for (auto p : getPorts())
1030 if (p.dir == ModulePort::Input)
1031 inputs.push_back(p.type);
1032 else if (p.dir == ModulePort::InOut)
1033 inputs.push_back(InOutType::get(p.type));
1034 else
1035 outputs.push_back(p.type);
1036 return FunctionType::get(getContext(), inputs, outputs);
1037}
1038
1039ArrayRef<ModulePort> ModuleType::getPorts() const {
1040 return getImpl()->getPorts();
1041}
1042
1043FailureOr<ModuleType> ModuleType::resolveParametricTypes(ArrayAttr parameters,
1044 LocationAttr loc,
1045 bool emitErrors) {
1046 SmallVector<ModulePort, 8> resolvedPorts;
1047 for (ModulePort port : getPorts()) {
1048 FailureOr<Type> resolvedType =
1049 evaluateParametricType(loc, parameters, port.type, emitErrors);
1050 if (failed(resolvedType))
1051 return failure();
1052 port.type = *resolvedType;
1053 resolvedPorts.push_back(port);
1054 }
1055 return ModuleType::get(getContext(), resolvedPorts);
1056}
1057
1058static StringRef dirToStr(ModulePort::Direction dir) {
1059 switch (dir) {
1060 case ModulePort::Direction::Input:
1061 return "input";
1062 case ModulePort::Direction::Output:
1063 return "output";
1064 case ModulePort::Direction::InOut:
1065 return "inout";
1066 }
1067}
1068
1069static ModulePort::Direction strToDir(StringRef str) {
1070 if (str == "input")
1071 return ModulePort::Direction::Input;
1072 if (str == "output")
1073 return ModulePort::Direction::Output;
1074 if (str == "inout")
1075 return ModulePort::Direction::InOut;
1076 llvm::report_fatal_error("invalid direction");
1077}
1078
1079/// Parse a list of field names and types within <>. E.g.:
1080/// <input foo: i7, output bar: i8>
1081static ParseResult parsePorts(AsmParser &p,
1082 SmallVectorImpl<ModulePort> &ports) {
1083 return p.parseCommaSeparatedList(
1084 mlir::AsmParser::Delimiter::LessGreater, [&]() -> ParseResult {
1085 StringRef dir;
1086 std::string name;
1087 Type type;
1088 if (p.parseKeyword(&dir) || p.parseKeywordOrString(&name) ||
1089 p.parseColon() || p.parseType(type))
1090 return failure();
1091 ports.push_back(
1092 {StringAttr::get(p.getContext(), name), type, strToDir(dir)});
1093 return success();
1094 });
1095}
1096
1097/// Print out a list of named fields surrounded by <>.
1098static void printPorts(AsmPrinter &p, ArrayRef<ModulePort> ports) {
1099 p << '<';
1100 llvm::interleaveComma(ports, p, [&](const ModulePort &port) {
1101 p << dirToStr(port.dir) << " ";
1102 p.printKeywordOrString(port.name.getValue());
1103 p << " : " << port.type;
1104 });
1105 p << ">";
1106}
1107
1108Type ModuleType::parse(AsmParser &odsParser) {
1109 llvm::SmallVector<ModulePort, 4> ports;
1110 if (parsePorts(odsParser, ports))
1111 return Type();
1112 return get(odsParser.getContext(), ports);
1113}
1114
1115void ModuleType::print(AsmPrinter &odsPrinter) const {
1116 printPorts(odsPrinter, getPorts());
1117}
1118
1119ModuleType circt::hw::detail::fnToMod(Operation *op,
1120 ArrayRef<Attribute> inputNames,
1121 ArrayRef<Attribute> outputNames) {
1122 return fnToMod(
1123 cast<FunctionType>(cast<mlir::FunctionOpInterface>(op).getFunctionType()),
1124 inputNames, outputNames);
1125}
1126
1127ModuleType circt::hw::detail::fnToMod(FunctionType fnty,
1128 ArrayRef<Attribute> inputNames,
1129 ArrayRef<Attribute> outputNames) {
1130 SmallVector<ModulePort> ports;
1131 if (!inputNames.empty()) {
1132 for (auto [t, n] : llvm::zip_equal(fnty.getInputs(), inputNames))
1133 if (auto iot = dyn_cast<hw::InOutType>(t))
1134 ports.push_back({cast<StringAttr>(n), iot.getElementType(),
1135 ModulePort::Direction::InOut});
1136 else
1137 ports.push_back({cast<StringAttr>(n), t, ModulePort::Direction::Input});
1138 } else {
1139 for (auto t : fnty.getInputs())
1140 if (auto iot = dyn_cast<hw::InOutType>(t))
1141 ports.push_back(
1142 {{}, iot.getElementType(), ModulePort::Direction::InOut});
1143 else
1144 ports.push_back({{}, t, ModulePort::Direction::Input});
1145 }
1146 if (!outputNames.empty()) {
1147 for (auto [t, n] : llvm::zip_equal(fnty.getResults(), outputNames))
1148 ports.push_back({cast<StringAttr>(n), t, ModulePort::Direction::Output});
1149 } else {
1150 for (auto t : fnty.getResults())
1151 ports.push_back({{}, t, ModulePort::Direction::Output});
1152 }
1153 return ModuleType::get(fnty.getContext(), ports);
1154}
1155
1157 : ports(inPorts) {
1158 size_t nextInput = 0;
1159 size_t nextOutput = 0;
1160 for (auto [idx, p] : llvm::enumerate(ports)) {
1161 if (p.dir == ModulePort::Direction::Output) {
1162 outputToAbs.push_back(idx);
1163 absToOutput.push_back(nextOutput);
1164 absToInput.push_back(~0ULL);
1165 ++nextOutput;
1166 } else {
1167 inputToAbs.push_back(idx);
1168 absToInput.push_back(nextInput);
1169 absToOutput.push_back(~0ULL);
1170 ++nextInput;
1171 }
1172 }
1173}
1174
1175//===----------------------------------------------------------------------===//
1176// BoilerPlate
1177//===----------------------------------------------------------------------===//
1178
1179void HWDialect::registerTypes() {
1180 addTypes<
1181#define GET_TYPEDEF_LIST
1182#include "circt/Dialect/HW/HWTypes.cpp.inc"
1183 >();
1184}
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition CHIRRTL.cpp:30
MlirType elementType
Definition CHIRRTL.cpp:29
static ModulePort::Direction strToDir(StringRef str)
Definition HWTypes.cpp:1069
static void printPorts(AsmPrinter &p, ArrayRef< ModulePort > ports)
Print out a list of named fields surrounded by <>.
Definition HWTypes.cpp:1098
static StringRef dirToStr(ModulePort::Direction dir)
Definition HWTypes.cpp:1058
static ParseResult parseHWArray(AsmParser &parser, Attribute &dim, Type &elementType)
Definition HWTypes.cpp:652
static ParseResult parseHWElementType(AsmParser &parser, Type &elementType)
Parse and print nested HW types nicely.
Definition HWTypes.cpp:273
static ParseResult parsePorts(AsmParser &p, SmallVectorImpl< ModulePort > &ports)
Parse a list of field names and types within <>.
Definition HWTypes.cpp:1081
static void printHWArray(AsmPrinter &printer, Attribute dim, Type elementType)
Definition HWTypes.cpp:678
static std::pair< uint64_t, SmallVector< uint64_t > > getFieldIDsStruct(const StructType &st)
Definition HWTypes.cpp:392
static void printHWElementType(AsmPrinter &printer, Type dim)
Definition HWTypes.cpp:295
@ Input
Definition HW.h:42
@ Output
Definition HW.h:42
static unsigned getFieldID(BundleType type, unsigned index)
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static InstancePath empty
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition HWSymCache.h:57
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
Direction
The direction of a Component or Cell port.
Definition CalyxOps.h:76
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:423
std::pair< uint64_t, uint64_t > getIndexAndSubfieldID(Type type, uint64_t fieldID)
std::pair<::mlir::Type, uint64_t > getSubTypeByFieldID(Type, uint64_t fieldID)
llvm::hash_code hash_value(const OffsetFieldInfo &fi)
Definition HWTypes.cpp:499
bool operator==(const OffsetFieldInfo &a, const OffsetFieldInfo &b)
Definition HWTypes.cpp:495
ModuleType fnToMod(Operation *op, ArrayRef< Attribute > inputNames, ArrayRef< Attribute > outputNames)
Definition HWTypes.cpp:1119
bool isHWIntegerType(mlir::Type type)
Return true if the specified type is a value HW Integer type.
Definition HWTypes.cpp:59
bool isHWValueType(mlir::Type type)
Return true if the specified type can be used as an HW value type, that is the set of types that can ...
bool isValidProbeElementType(mlir::Type type)
Return true if type is a valid probe payload.
LogicalResult aggregateAttrToAPInt(mlir::Type type, ArrayAttr attr, APInt &result)
Convert an ArrayAttr into an APInt value matching the given type.
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:121
LogicalResult apIntToAggregateAttr(mlir::Type aggregateType, const APInt &intVal, ArrayAttr &result)
Convert an APInt value into a nested aggregate attribute matching the given HWAggregateType.
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
Definition HWTypes.cpp:72
mlir::Type getCanonicalType(mlir::Type type)
Recursively remove HW type aliases from a type and its subelements.
Definition HWTypes.cpp:50
bool hasHWInOutType(mlir::Type type)
Return true if the specified type contains known marker types like InOutType.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseFieldList(AsmParser &p, SmallVectorImpl< FieldInfo > &fields, SmallVectorImpl< std::pair< llvm::SMLoc, StringAttr > > &duplicates)
Parse a list of named fields within <>, e.g.
Definition FieldInfo.cpp:15
void printFieldList(AsmPrinter &p, ArrayRef< FieldInfo > fields)
Print a list of fields within <>, e.g. <foo: i7, bar: i8>.
Definition FieldInfo.cpp:38
Definition hw.py:1
A named and typed field of an aggregate type.
Definition FieldInfo.h:27
mlir::Type type
Definition HWTypes.h:34
mlir::StringAttr name
Definition HWTypes.h:33
Interface for dialects to classify their types as valid probe payloads.
Definition HWTypes.h:54
virtual bool isValidProbeElementType(mlir::Type type) const =0
SmallVector< ModulePort > ports
The parametric data held by the storage class.
Definition HWTypes.h:99
ModuleTypeStorage(ArrayRef< ModulePort > inPorts)
Definition HWTypes.cpp:1156
SmallVector< size_t > absToInput
Definition HWTypes.h:103
SmallVector< size_t > outputToAbs
Definition HWTypes.h:102
SmallVector< size_t > inputToAbs
Definition HWTypes.h:101
SmallVector< size_t > absToOutput
Definition HWTypes.h:104
Struct defining a field with an offset. Used in unions.
Definition HWTypes.h:124