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