CIRCT 22.0.0git
Loading...
Searching...
No Matches
LowerTypes.cpp
Go to the documentation of this file.
1//===- LowerTypes.cpp - Lower Aggregate Types -------------------*- C++ -*-===//
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 defines the LowerTypes pass. This pass replaces aggregate types
10// with expanded values.
11//
12// This pass walks the operations in reverse order. This lets it visit users
13// before defs. Users can usually be expanded out to multiple operations (think
14// mux of a bundle to muxes of each field) with a temporary subWhatever op
15// inserted. When processing an aggregate producer, we blow out the op as
16// appropriate, then walk the users, often those are subWhatever ops which can
17// be bypassed and deleted. Function arguments are logically last on the
18// operation visit order and walked left to right, being peeled one layer at a
19// time with replacements inserted to the right of the original argument.
20//
21// Each processing of an op peels one layer of aggregate type off. Because new
22// ops are inserted immediately above the current up, the walk will visit them
23// next, effectively recusing on the aggregate types, without recusing. These
24// potentially temporary ops(if the aggregate is complex) effectively serve as
25// the worklist. Often aggregates are shallow, so the new ops are the final
26// ones.
27//
28//===----------------------------------------------------------------------===//
29
38#include "circt/Support/Debug.h"
39#include "mlir/IR/ImplicitLocOpBuilder.h"
40#include "mlir/IR/Threading.h"
41#include "mlir/Pass/Pass.h"
42#include "llvm/ADT/APSInt.h"
43#include "llvm/ADT/BitVector.h"
44#include "llvm/Support/Debug.h"
45
46#define DEBUG_TYPE "firrtl-lower-types"
47
48namespace circt {
49namespace firrtl {
50#define GEN_PASS_DEF_LOWERFIRRTLTYPES
51#include "circt/Dialect/FIRRTL/Passes.h.inc"
52} // namespace firrtl
53} // namespace circt
54
55using namespace circt;
56using namespace firrtl;
57
58// TODO: check all argument types
59namespace {
60/// This represents a flattened bundle field element.
61struct FlatBundleFieldEntry {
62 /// This is the underlying ground type of the field.
63 FIRRTLBaseType type;
64 /// The index in the parent type
65 size_t index;
66 /// The fieldID
67 unsigned fieldID;
68 /// This is a suffix to add to the field name to make it unique.
69 SmallString<16> suffix;
70 /// This indicates whether the field was flipped to be an output.
71 bool isOutput;
72
73 FlatBundleFieldEntry(const FIRRTLBaseType &type, size_t index,
74 unsigned fieldID, StringRef suffix, bool isOutput)
75 : type(type), index(index), fieldID(fieldID), suffix(suffix),
76 isOutput(isOutput) {}
77
78 void dump() const {
79 llvm::errs() << "FBFE{" << type << " index<" << index << "> fieldID<"
80 << fieldID << "> suffix<" << suffix << "> isOutput<"
81 << isOutput << ">}\n";
82 }
83};
84
85/// Extended PortInfo including the (optional) internalPath attribute.
86struct PortInfoWithIP {
87 PortInfo pi;
88 std::optional<InternalPathAttr> internalPath;
89};
90
91} // end anonymous namespace
92
93/// Return fieldType or fieldType as same ref as type.
95 return mapBaseType(type, [&](auto) { return fieldType; });
96}
97
98/// Return fieldType or fieldType as same ref as type.
99static Type mapLoweredType(Type type, FIRRTLBaseType fieldType) {
100 auto ftype = type_dyn_cast<FIRRTLType>(type);
101 if (!ftype)
102 return type;
103 return mapLoweredType(ftype, fieldType);
104}
105
106/// Return true if the type is a 1d vector type or ground type.
109 .Case<BundleType>([&](auto bundle) { return false; })
110 .Case<FVectorType>([&](FVectorType vector) {
111 // When the size is 1, lower the vector into a scalar.
112 return vector.getElementType().isGround() &&
113 vector.getNumElements() > 1;
114 })
115 .Default([](auto groundType) { return true; });
116}
117
118// NOLINTBEGIN(misc-no-recursion)
119/// Return true if the type has a bundle type as subtype.
122 .Case<BundleType>([&](auto bundle) { return true; })
123 .Case<FVectorType>([&](FVectorType vector) {
124 return containsBundleType(vector.getElementType());
125 })
126 .Default([](auto groundType) { return false; });
127}
128// NOLINTEND(misc-no-recursion)
129
130/// Return true if we can preserve the type.
131static bool isPreservableAggregateType(Type type,
133 if (auto refType = type_dyn_cast<RefType>(type)) {
134 // Always preserve rwprobe's.
135 if (refType.getForceable())
136 return true;
137 // FIXME: Don't preserve read-only RefType for now. This is workaround for
138 // MemTap which causes type mismatches (issue 4479).
139 return false;
140 }
141
142 // Return false if no aggregate value is preserved.
143 if (mode == PreserveAggregate::None)
144 return false;
145
146 auto firrtlType = type_dyn_cast<FIRRTLBaseType>(type);
147 if (!firrtlType)
148 return false;
149
150 // We can a preserve the type iff (i) the type is not passive, (ii) the type
151 // doesn't contain analog and (iii) type don't contain zero bitwidth.
152 if (!firrtlType.isPassive() || firrtlType.containsAnalog() ||
153 hasZeroBitWidth(firrtlType))
154 return false;
155
156 switch (mode) {
158 return true;
160 return isOneDimVectorType(firrtlType);
162 return !containsBundleType(firrtlType);
163 default:
164 llvm_unreachable("unexpected mode");
165 }
166}
167
168/// Peel one layer of an aggregate type into its components. Type may be
169/// complex, but empty, in which case fields is empty, but the return is true.
170static bool peelType(Type type, SmallVectorImpl<FlatBundleFieldEntry> &fields,
172 // If the aggregate preservation is enabled and the type is preservable,
173 // then just return.
174 if (isPreservableAggregateType(type, mode))
175 return false;
176
177 if (auto refType = type_dyn_cast<RefType>(type))
178 type = refType.getType();
180 .Case<BundleType>([&](auto bundle) {
181 SmallString<16> tmpSuffix;
182 // Otherwise, we have a bundle type. Break it down.
183 for (size_t i = 0, e = bundle.getNumElements(); i < e; ++i) {
184 auto elt = bundle.getElement(i);
185 // Construct the suffix to pass down.
186 tmpSuffix.resize(0);
187 tmpSuffix.push_back('_');
188 tmpSuffix.append(elt.name.getValue());
189 fields.emplace_back(elt.type, i, bundle.getFieldID(i), tmpSuffix,
190 elt.isFlip);
191 }
192 return true;
193 })
194 .Case<FVectorType>([&](auto vector) {
195 // Increment the field ID to point to the first element.
196 for (size_t i = 0, e = vector.getNumElements(); i != e; ++i) {
197 fields.emplace_back(vector.getElementType(), i, vector.getFieldID(i),
198 "_" + std::to_string(i), false);
199 }
200 return true;
201 })
202 .Default([](auto op) { return false; });
203}
204
205/// Return if something is not a normal subaccess. Non-normal includes
206/// zero-length vectors and constant indexes (which are really subindexes).
207static bool isNotSubAccess(Operation *op) {
208 SubaccessOp sao = llvm::dyn_cast<SubaccessOp>(op);
209 if (!sao)
210 return true;
211 ConstantOp arg =
212 llvm::dyn_cast_or_null<ConstantOp>(sao.getIndex().getDefiningOp());
213 return arg && sao.getInput().getType().base().getNumElements() != 0;
214}
215
216/// Look through and collect subfields leading to a subaccess.
217static SmallVector<Operation *> getSAWritePath(Operation *op) {
218 SmallVector<Operation *> retval;
219 auto defOp = op->getOperand(0).getDefiningOp();
220 while (isa_and_nonnull<SubfieldOp, SubindexOp, SubaccessOp>(defOp)) {
221 retval.push_back(defOp);
222 defOp = defOp->getOperand(0).getDefiningOp();
223 }
224 // Trim to the subaccess
225 while (!retval.empty() && isNotSubAccess(retval.back()))
226 retval.pop_back();
227 return retval;
228}
229
230/// Clone memory for the specified field. Returns null op on error.
231static MemOp cloneMemWithNewType(ImplicitLocOpBuilder *b, MemOp op,
232 FlatBundleFieldEntry field) {
233 SmallVector<Type, 8> ports;
234 SmallVector<Attribute, 8> portNames;
235 SmallVector<Attribute, 8> portLocations;
236
237 auto oldPorts = op.getPorts();
238 for (size_t portIdx = 0, e = oldPorts.size(); portIdx < e; ++portIdx) {
239 auto port = oldPorts[portIdx];
240 ports.push_back(
241 MemOp::getTypeForPort(op.getDepth(), field.type, port.second));
242 portNames.push_back(port.first);
243 }
244
245 // It's easier to duplicate the old annotations, then fix and filter them.
246 auto newMem = b->create<MemOp>(
247 ports, op.getReadLatency(), op.getWriteLatency(), op.getDepth(),
248 op.getRuw(), b->getArrayAttr(portNames),
249 (op.getName() + field.suffix).str(), op.getNameKind(),
250 op.getAnnotations(), op.getPortAnnotations(), op.getInnerSymAttr(),
251 op.getInitAttr(), op.getPrefixAttr());
252
253 if (op.getInnerSym()) {
254 op.emitError("cannot split memory with symbol present");
255 return {};
256 }
257
258 SmallVector<Attribute> newAnnotations;
259 for (size_t portIdx = 0, e = newMem.getNumResults(); portIdx < e; ++portIdx) {
260 auto portType = type_cast<BundleType>(newMem.getResult(portIdx).getType());
261 auto oldPortType = type_cast<BundleType>(op.getResult(portIdx).getType());
262 SmallVector<Attribute> portAnno;
263 for (auto attr : newMem.getPortAnnotation(portIdx)) {
264 Annotation anno(attr);
265 if (auto annoFieldID = anno.getFieldID()) {
266 auto targetIndex = oldPortType.getIndexForFieldID(annoFieldID);
267
268 // Apply annotations to all elements if the target is the whole
269 // sub-field.
270 if (annoFieldID == oldPortType.getFieldID(targetIndex)) {
271 anno.setMember(
272 "circt.fieldID",
273 b->getI32IntegerAttr(portType.getFieldID(targetIndex)));
274 portAnno.push_back(anno.getDict());
275 continue;
276 }
277
278 // Handle aggregate sub-fields, including `(r/w)data` and `(w)mask`.
279 if (type_isa<BundleType>(oldPortType.getElement(targetIndex).type)) {
280 // Check whether the annotation falls into the range of the current
281 // field. Note that the `field` here is peeled from the `data`
282 // sub-field of the memory port, thus we need to add the fieldID of
283 // `data` or `mask` sub-field to get the "real" fieldID.
284 auto fieldID = field.fieldID + oldPortType.getFieldID(targetIndex);
285 if (annoFieldID >= fieldID &&
286 annoFieldID <=
287 fieldID + hw::FieldIdImpl::getMaxFieldID(field.type)) {
288 // Set the field ID of the new annotation.
289 auto newFieldID =
290 annoFieldID - fieldID + portType.getFieldID(targetIndex);
291 anno.setMember("circt.fieldID", b->getI32IntegerAttr(newFieldID));
292 portAnno.push_back(anno.getDict());
293 }
294 }
295 } else
296 portAnno.push_back(attr);
297 }
298 newAnnotations.push_back(b->getArrayAttr(portAnno));
299 }
300 newMem.setAllPortAnnotations(newAnnotations);
301 return newMem;
302}
303
304//===----------------------------------------------------------------------===//
305// Module Type Lowering
306//===----------------------------------------------------------------------===//
307namespace {
308
309struct AttrCache {
310 AttrCache(MLIRContext *context) {
311 i64ty = IntegerType::get(context, 64);
312 nameAttr = StringAttr::get(context, "name");
313 nameKindAttr = StringAttr::get(context, "nameKind");
314 sPortDirections = StringAttr::get(context, "portDirections");
315 sPortNames = StringAttr::get(context, "portNames");
316 sPortTypes = StringAttr::get(context, "portTypes");
317 sPortSymbols = StringAttr::get(context, "portSymbols");
318 sPortLocations = StringAttr::get(context, "portLocations");
319 sPortAnnotations = StringAttr::get(context, "portAnnotations");
320 sPortDomains = StringAttr::get(context, "domainInfo");
321 sEmpty = StringAttr::get(context, "");
322 aEmpty = ArrayAttr::get(context, {});
323 }
324 AttrCache(const AttrCache &) = default;
325
326 Type i64ty;
327 StringAttr nameAttr, nameKindAttr, sPortDirections, sPortNames, sPortTypes,
328 sPortSymbols, sPortLocations, sPortAnnotations, sPortDomains, sEmpty;
329 ArrayAttr aEmpty;
330};
331
332// The visitors all return true if the operation should be deleted, false if
333// not.
334struct TypeLoweringVisitor : public FIRRTLVisitor<TypeLoweringVisitor, bool> {
335
336 TypeLoweringVisitor(
337 MLIRContext *context, PreserveAggregate::PreserveMode preserveAggregate,
338 Convention bodyConvention,
339 PreserveAggregate::PreserveMode memoryPreservationMode,
340 SymbolTable &symTbl, const AttrCache &cache,
341 const llvm::DenseMap<FModuleLike, Convention> &conventionTable)
342 : context(context), defaultAggregatePreservationMode(preserveAggregate),
343 memoryPreservationMode(memoryPreservationMode), symTbl(symTbl),
344 cache(cache), conventionTable(conventionTable) {
345 bodyAggregatePreservationMode = bodyConvention == Convention::Scalarized
347 : defaultAggregatePreservationMode;
348 }
349 using FIRRTLVisitor<TypeLoweringVisitor, bool>::visitDecl;
350 using FIRRTLVisitor<TypeLoweringVisitor, bool>::visitExpr;
351 using FIRRTLVisitor<TypeLoweringVisitor, bool>::visitStmt;
352
353 /// If the referenced operation is a FModuleOp or an FExtModuleOp, perform
354 /// type lowering on all operations.
355 void lowerModule(FModuleLike op);
356
357 bool lowerArg(FModuleLike module, size_t argIndex, size_t argsRemoved,
358 SmallVectorImpl<PortInfoWithIP> &newArgs,
359 SmallVectorImpl<Value> &lowering);
360 std::pair<Value, PortInfoWithIP>
361 addArg(Operation *module, unsigned insertPt, unsigned insertPtOffset,
362 FIRRTLType srcType, const FlatBundleFieldEntry &field,
363 PortInfoWithIP &oldArg, hw::InnerSymAttr newSym);
364
365 // Helpers to manage state.
366 bool visitDecl(FExtModuleOp op);
367 bool visitDecl(FModuleOp op);
368 bool visitDecl(InstanceOp op);
369 bool visitDecl(MemOp op);
370 bool visitDecl(NodeOp op);
371 bool visitDecl(RegOp op);
372 bool visitDecl(WireOp op);
373 bool visitDecl(RegResetOp op);
374 bool visitExpr(InvalidValueOp op);
375 bool visitExpr(SubaccessOp op);
376 bool visitExpr(VectorCreateOp op);
377 bool visitExpr(BundleCreateOp op);
378 bool visitExpr(ElementwiseAndPrimOp op);
379 bool visitExpr(ElementwiseOrPrimOp op);
380 bool visitExpr(ElementwiseXorPrimOp op);
381 bool visitExpr(MultibitMuxOp op);
382 bool visitExpr(MuxPrimOp op);
383 bool visitExpr(Mux2CellIntrinsicOp op);
384 bool visitExpr(Mux4CellIntrinsicOp op);
385 bool visitExpr(BitCastOp op);
386 bool visitExpr(RefSendOp op);
387 bool visitExpr(RefResolveOp op);
388 bool visitExpr(RefCastOp op);
389 bool visitStmt(ConnectOp op);
390 bool visitStmt(MatchingConnectOp op);
391 bool visitStmt(RefDefineOp op);
392 bool visitStmt(WhenOp op);
393 bool visitStmt(LayerBlockOp op);
394 bool visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
395
396 bool isFailed() const { return encounteredError; }
397
398 bool visitInvalidOp(Operation *op) {
399 if (auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
400 return visitUnrealizedConversionCast(castOp);
401 return false;
402 }
403
404private:
405 void processUsers(Value val, ArrayRef<Value> mapping);
406 bool processSAPath(Operation *);
407 void lowerBlock(Block *);
408 void lowerSAWritePath(Operation *, ArrayRef<Operation *> writePath);
409
410 /// Lower a "producer" operation one layer based on policy.
411 /// Use the provided \p clone function to generate individual ops for
412 /// the expanded subelements/fields. The type used to determine if lowering
413 /// is needed is either \p srcType if provided or from the assumed-to-exist
414 /// first result of the operation. When lowering, the clone callback will be
415 /// invoked with each subelement/field of this type.
416 bool lowerProducer(
417 Operation *op,
418 llvm::function_ref<Value(const FlatBundleFieldEntry &, ArrayAttr)> clone,
419 Type srcType = {});
420
421 /// Filter out and return \p annotations that target includes \field,
422 /// modifying as needed to adjust fieldID's relative to to \field.
423 ArrayAttr filterAnnotations(MLIRContext *ctxt, ArrayAttr annotations,
424 FIRRTLType srcType, FlatBundleFieldEntry field);
425
426 /// Partition inner symbols on given type. Fails if any symbols
427 /// cannot be assigned to a field, such as inner symbol on root.
428 LogicalResult partitionSymbols(hw::InnerSymAttr sym, FIRRTLType parentType,
429 SmallVectorImpl<hw::InnerSymAttr> &newSyms,
430 Location errorLoc);
431
433 getPreservationModeForPorts(FModuleLike moduleLike);
434 Value getSubWhatever(Value val, size_t index);
435
436 size_t uniqueIdx = 0;
437 std::string uniqueName() {
438 auto myID = uniqueIdx++;
439 return (Twine("__GEN_") + Twine(myID)).str();
440 }
441
442 MLIRContext *context;
443
444 /// Aggregate preservation mode.
445 PreserveAggregate::PreserveMode defaultAggregatePreservationMode;
446 PreserveAggregate::PreserveMode bodyAggregatePreservationMode;
447 PreserveAggregate::PreserveMode memoryPreservationMode;
448
449 /// The builder is set and maintained in the main loop.
450 ImplicitLocOpBuilder *builder;
451
452 // Keep a symbol table around for resolving symbols
453 SymbolTable &symTbl;
454
455 // Cache some attributes
456 const AttrCache &cache;
457
458 const llvm::DenseMap<FModuleLike, Convention> &conventionTable;
459
460 // Set true if the lowering failed.
461 bool encounteredError = false;
462};
463} // namespace
464
465/// Return aggregate preservation mode for the module ports. If the module has a
466/// scalarized linkage, then we may not preserve it's aggregate ports.
468TypeLoweringVisitor::getPreservationModeForPorts(FModuleLike module) {
469 auto lookup = conventionTable.find(module);
470 if (lookup == conventionTable.end())
471 return defaultAggregatePreservationMode;
472 switch (lookup->second) {
473 case Convention::Scalarized:
475 case Convention::Internal:
476 return defaultAggregatePreservationMode;
477 }
478 llvm_unreachable("Unknown convention");
479 return defaultAggregatePreservationMode;
480}
481
482Value TypeLoweringVisitor::getSubWhatever(Value val, size_t index) {
483 if (type_isa<BundleType>(val.getType()))
484 return builder->create<SubfieldOp>(val, index);
485 if (type_isa<FVectorType>(val.getType()))
486 return builder->create<SubindexOp>(val, index);
487 if (type_isa<RefType>(val.getType()))
488 return builder->create<RefSubOp>(val, index);
489 llvm_unreachable("Unknown aggregate type");
490 return nullptr;
491}
492
493/// Conditionally expand a subaccessop write path
494bool TypeLoweringVisitor::processSAPath(Operation *op) {
495 // Does this LHS have a subaccessop?
496 SmallVector<Operation *> writePath = getSAWritePath(op);
497 if (writePath.empty())
498 return false;
499
500 lowerSAWritePath(op, writePath);
501 // Unhook the writePath from the connect. This isn't the right type, but we
502 // are deleting the op anyway.
503 op->eraseOperands(0, 2);
504 // See how far up the tree we can delete things.
505 for (size_t i = 0; i < writePath.size(); ++i) {
506 if (writePath[i]->use_empty()) {
507 writePath[i]->erase();
508 } else {
509 break;
510 }
511 }
512 return true;
513}
514
515void TypeLoweringVisitor::lowerBlock(Block *block) {
516 // Lower the operations bottom up.
517 for (auto it = block->rbegin(), e = block->rend(); it != e;) {
518 auto &iop = *it;
519 builder->setInsertionPoint(&iop);
520 builder->setLoc(iop.getLoc());
521 bool removeOp = dispatchVisitor(&iop);
522 ++it;
523 // Erase old ops eagerly so we don't have dangling uses we've already
524 // lowered.
525 if (removeOp)
526 iop.erase();
527 }
528}
529
530ArrayAttr TypeLoweringVisitor::filterAnnotations(MLIRContext *ctxt,
531 ArrayAttr annotations,
532 FIRRTLType srcType,
533 FlatBundleFieldEntry field) {
534 SmallVector<Attribute> retval;
535 if (!annotations || annotations.empty())
536 return ArrayAttr::get(ctxt, retval);
537 for (auto opAttr : annotations) {
538 Annotation anno(opAttr);
539 auto fieldID = anno.getFieldID();
540 anno.removeMember("circt.fieldID");
541
542 // If no fieldID set, or points to root, forward the annotation without the
543 // fieldID field (which was removed above).
544 if (fieldID == 0) {
545 retval.push_back(anno.getAttr());
546 continue;
547 }
548 // Check whether the annotation falls into the range of the current field.
549
550 if (fieldID < field.fieldID ||
551 fieldID > field.fieldID + hw::FieldIdImpl::getMaxFieldID(field.type))
552 continue;
553
554 // Add fieldID back if non-zero relative to this field.
555 if (auto newFieldID = fieldID - field.fieldID) {
556 // If the target is a subfield/subindex of the current field, create a
557 // new annotation with the correct circt.fieldID.
558 anno.setMember("circt.fieldID", builder->getI32IntegerAttr(newFieldID));
559 }
560
561 retval.push_back(anno.getAttr());
562 }
563 return ArrayAttr::get(ctxt, retval);
564}
565
566LogicalResult TypeLoweringVisitor::partitionSymbols(
567 hw::InnerSymAttr sym, FIRRTLType parentType,
568 SmallVectorImpl<hw::InnerSymAttr> &newSyms, Location errorLoc) {
569
570 // No symbol, nothing to partition.
571 if (!sym || sym.empty())
572 return success();
573
574 auto *context = sym.getContext();
575
576 auto baseType = getBaseType(parentType);
577 if (!baseType)
578 return mlir::emitError(errorLoc,
579 "unable to partition symbol on unsupported type ")
580 << parentType;
581
582 return TypeSwitch<FIRRTLType, LogicalResult>(baseType)
583 .Case<BundleType, FVectorType>([&](auto aggType) -> LogicalResult {
584 struct BinningInfo {
585 uint64_t index;
586 uint64_t relFieldID;
587 hw::InnerSymPropertiesAttr prop;
588 };
589
590 // Walk each inner symbol, compute binning information/assignment.
591 SmallVector<BinningInfo> binning;
592 for (auto prop : sym) {
593 auto fieldID = prop.getFieldID();
594 // Special-case fieldID == 0, helper methods require non-zero fieldID.
595 if (fieldID == 0)
596 return mlir::emitError(errorLoc, "unable to lower due to symbol ")
597 << prop.getName()
598 << " with target not preserved by lowering";
599 auto [index, relFieldID] = aggType.getIndexAndSubfieldID(fieldID);
600 binning.push_back({index, relFieldID, prop});
601 }
602
603 // Sort by index, fieldID.
604 llvm::stable_sort(binning, [&](auto &lhs, auto &rhs) {
605 return std::tuple(lhs.index, lhs.relFieldID) <
606 std::tuple(rhs.index, rhs.relFieldID);
607 });
608 assert(!binning.empty());
609
610 // Populate newSyms, group all symbols on same index.
611 newSyms.resize(aggType.getNumElements());
612 for (auto binIt = binning.begin(), binEnd = binning.end();
613 binIt != binEnd;) {
614 auto curIndex = binIt->index;
615 SmallVector<hw::InnerSymPropertiesAttr> propsForIndex;
616 // Gather all adjacent symbols for this index.
617 while (binIt != binEnd && binIt->index == curIndex) {
618 propsForIndex.push_back(hw::InnerSymPropertiesAttr::get(
619 context, binIt->prop.getName(), binIt->relFieldID,
620 binIt->prop.getSymVisibility()));
621 ++binIt;
622 }
623
624 assert(!newSyms[curIndex]);
625 newSyms[curIndex] = hw::InnerSymAttr::get(context, propsForIndex);
626 }
627 return success();
628 })
629 .Default([&](auto ty) {
630 return mlir::emitError(
631 errorLoc, "unable to partition symbol on unsupported type ")
632 << ty;
633 });
634}
635
636bool TypeLoweringVisitor::lowerProducer(
637 Operation *op,
638 llvm::function_ref<Value(const FlatBundleFieldEntry &, ArrayAttr)> clone,
639 Type srcType) {
640
641 if (!srcType)
642 srcType = op->getResult(0).getType();
643 auto srcFType = type_dyn_cast<FIRRTLType>(srcType);
644 if (!srcFType)
645 return false;
646 SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
647
648 if (!peelType(srcFType, fieldTypes, bodyAggregatePreservationMode))
649 return false;
650
651 SmallVector<Value> lowered;
652 // Loop over the leaf aggregates.
653 SmallString<16> loweredName;
654 auto nameKindAttr = op->getAttrOfType<NameKindEnumAttr>(cache.nameKindAttr);
655
656 if (auto nameAttr = op->getAttrOfType<StringAttr>(cache.nameAttr))
657 loweredName = nameAttr.getValue();
658 auto baseNameLen = loweredName.size();
659 auto oldAnno = dyn_cast_or_null<ArrayAttr>(op->getAttr("annotations"));
660
661 SmallVector<hw::InnerSymAttr> fieldSyms(fieldTypes.size());
662 if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
663 if (failed(partitionSymbols(symOp.getInnerSymAttr(), srcFType, fieldSyms,
664 symOp.getLoc()))) {
665 encounteredError = true;
666 return false;
667 }
668 }
669
670 for (const auto &[field, sym] : llvm::zip_equal(fieldTypes, fieldSyms)) {
671 if (!loweredName.empty()) {
672 loweredName.resize(baseNameLen);
673 loweredName += field.suffix;
674 }
675
676 // For all annotations on the parent op, filter them based on the target
677 // attribute.
678 ArrayAttr loweredAttrs =
679 filterAnnotations(context, oldAnno, srcFType, field);
680 auto newVal = clone(field, loweredAttrs);
681
682 // If inner symbols on this field, add to new op.
683 if (sym) {
684 // Splitting up something with symbols on it should lower to ops
685 // that also can have symbols on them.
686 auto newSymOp = newVal.getDefiningOp<hw::InnerSymbolOpInterface>();
687 assert(
688 newSymOp &&
689 "op with inner symbol lowered to op that cannot take inner symbol");
690 newSymOp.setInnerSymbolAttr(sym);
691 }
692
693 // Carry over the name, if present.
694 if (auto *newOp = newVal.getDefiningOp()) {
695 if (!loweredName.empty())
696 newOp->setAttr(cache.nameAttr, StringAttr::get(context, loweredName));
697 if (nameKindAttr)
698 newOp->setAttr(cache.nameKindAttr, nameKindAttr);
699
700 // Clone discardable attributes as well.
701 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
702 }
703 lowered.push_back(newVal);
704 }
705
706 processUsers(op->getResult(0), lowered);
707 return true;
708}
709
710void TypeLoweringVisitor::processUsers(Value val, ArrayRef<Value> mapping) {
711 for (auto *user : llvm::make_early_inc_range(val.getUsers())) {
712 TypeSwitch<Operation *, void>(user)
713 .Case<SubindexOp>([mapping](SubindexOp sio) {
714 Value repl = mapping[sio.getIndex()];
715 sio.replaceAllUsesWith(repl);
716 sio.erase();
717 })
718 .Case<SubfieldOp>([mapping](SubfieldOp sfo) {
719 // Get the input bundle type.
720 Value repl = mapping[sfo.getFieldIndex()];
721 sfo.replaceAllUsesWith(repl);
722 sfo.erase();
723 })
724 .Case<RefSubOp>([mapping](RefSubOp refSub) {
725 Value repl = mapping[refSub.getIndex()];
726 refSub.replaceAllUsesWith(repl);
727 refSub.erase();
728 })
729 .Default([&](auto op) {
730 // This means we have already processed the user, and it didn't lower
731 // its inputs. This is an opaque user, which will continue to have
732 // aggregate type as input, even after LowerTypes. So, construct the
733 // vector/bundle back from the lowered elements to ensure a valid
734 // input into the opaque op. This only supports Bundles and Vectors.
735
736 // This builder ensures that the aggregate construction happens at the
737 // user location, and the LowerTypes algorithm will not touch them any
738 // more, because LowerTypes was reverse iterating on the block and the
739 // user has already been processed.
740 ImplicitLocOpBuilder b(user->getLoc(), user);
741
742 // This shouldn't happen (non-FIRRTLBaseType's in lowered types, or
743 // refs), check explicitly here for clarity/early detection.
744 assert(llvm::none_of(mapping, [](auto v) {
745 auto fbasetype = type_dyn_cast<FIRRTLBaseType>(v.getType());
746 return !fbasetype || fbasetype.containsReference();
747 }));
748
749 Value input =
750 TypeSwitch<Type, Value>(val.getType())
751 .template Case<FVectorType>([&](auto vecType) {
752 return b.createOrFold<VectorCreateOp>(vecType, mapping);
753 })
754 .template Case<BundleType>([&](auto bundleType) {
755 return b.createOrFold<BundleCreateOp>(bundleType, mapping);
756 })
757 .Default([&](auto _) -> Value { return {}; });
758 if (!input) {
759 user->emitError("unable to reconstruct source of type ")
760 << val.getType();
761 encounteredError = true;
762 return;
763 }
764 user->replaceUsesOfWith(val, input);
765 });
766 }
767}
768
769void TypeLoweringVisitor::lowerModule(FModuleLike op) {
770 if (auto module = llvm::dyn_cast<FModuleOp>(*op))
771 visitDecl(module);
772 else if (auto extModule = llvm::dyn_cast<FExtModuleOp>(*op))
773 visitDecl(extModule);
774}
775
776// Creates and returns a new block argument of the specified type to the
777// module. This also maintains the name attribute for the new argument,
778// possibly with a new suffix appended.
779std::pair<Value, PortInfoWithIP>
780TypeLoweringVisitor::addArg(Operation *module, unsigned insertPt,
781 unsigned insertPtOffset, FIRRTLType srcType,
782 const FlatBundleFieldEntry &field,
783 PortInfoWithIP &oldArg, hw::InnerSymAttr newSym) {
784 Value newValue;
785 FIRRTLType fieldType = mapLoweredType(srcType, field.type);
786 if (auto mod = llvm::dyn_cast<FModuleOp>(module)) {
787 Block *body = mod.getBodyBlock();
788 // Append the new argument.
789 newValue = body->insertArgument(insertPt, fieldType, oldArg.pi.loc);
790 }
791
792 // Save the name attribute for the new argument.
793 auto name = builder->getStringAttr(oldArg.pi.name.getValue() + field.suffix);
794
795 // Populate the new arg attributes.
796 auto newAnnotations = filterAnnotations(
797 context, oldArg.pi.annotations.getArrayAttr(), srcType, field);
798 // Flip the direction if the field is an output.
799 auto direction = (Direction)((unsigned)oldArg.pi.direction ^ field.isOutput);
800
801 return std::make_pair(
802 newValue,
803 PortInfoWithIP{PortInfo{name, fieldType, direction, newSym, oldArg.pi.loc,
804 AnnotationSet(newAnnotations), oldArg.pi.domains},
805 oldArg.internalPath});
806}
807
808// Lower arguments with bundle type by flattening them.
809bool TypeLoweringVisitor::lowerArg(FModuleLike module, size_t argIndex,
810 size_t argsRemoved,
811 SmallVectorImpl<PortInfoWithIP> &newArgs,
812 SmallVectorImpl<Value> &lowering) {
813
814 // Flatten any bundle types.
815 SmallVector<FlatBundleFieldEntry> fieldTypes;
816 auto srcType = type_cast<FIRRTLType>(newArgs[argIndex].pi.type);
817 if (!peelType(srcType, fieldTypes, getPreservationModeForPorts(module)))
818 return false;
819
820 // Ports with internalPath set cannot be lowered.
821 if (auto ip = newArgs[argIndex].internalPath; ip && ip->getPath()) {
822 ::mlir::emitError(newArgs[argIndex].pi.loc,
823 "cannot lower port with internal path");
824 encounteredError = true;
825 return false;
826 }
827
828 SmallVector<hw::InnerSymAttr> fieldSyms(fieldTypes.size());
829 if (failed(partitionSymbols(newArgs[argIndex].pi.sym, srcType, fieldSyms,
830 newArgs[argIndex].pi.loc))) {
831 encounteredError = true;
832 return false;
833 }
834
835 for (const auto &[idx, field, fieldSym] :
836 llvm::enumerate(fieldTypes, fieldSyms)) {
837 auto newValue = addArg(module, 1 + argIndex + idx, argsRemoved, srcType,
838 field, newArgs[argIndex], fieldSym);
839 newArgs.insert(newArgs.begin() + 1 + argIndex + idx, newValue.second);
840 // Lower any other arguments by copying them to keep the relative order.
841 lowering.push_back(newValue.first);
842 }
843 return true;
844}
845
846static Value cloneAccess(ImplicitLocOpBuilder *builder, Operation *op,
847 Value rhs) {
848 if (auto rop = llvm::dyn_cast<SubfieldOp>(op))
849 return builder->create<SubfieldOp>(rhs, rop.getFieldIndex());
850 if (auto rop = llvm::dyn_cast<SubindexOp>(op))
851 return builder->create<SubindexOp>(rhs, rop.getIndex());
852 if (auto rop = llvm::dyn_cast<SubaccessOp>(op))
853 return builder->create<SubaccessOp>(rhs, rop.getIndex());
854 op->emitError("Unknown accessor");
855 return nullptr;
856}
857
858void TypeLoweringVisitor::lowerSAWritePath(Operation *op,
859 ArrayRef<Operation *> writePath) {
860 SubaccessOp sao = cast<SubaccessOp>(writePath.back());
861 FVectorType saoType = sao.getInput().getType();
862 auto selectWidth = llvm::Log2_64_Ceil(saoType.getNumElements());
863
864 for (size_t index = 0, e = saoType.getNumElements(); index < e; ++index) {
865 auto cond = builder->create<EQPrimOp>(
866 sao.getIndex(),
867 builder->createOrFold<ConstantOp>(UIntType::get(context, selectWidth),
868 APInt(selectWidth, index)));
869 builder->create<WhenOp>(cond, false, [&]() {
870 // Recreate the write Path
871 Value leaf = builder->create<SubindexOp>(sao.getInput(), index);
872 for (int i = writePath.size() - 2; i >= 0; --i) {
873 if (auto access = cloneAccess(builder, writePath[i], leaf))
874 leaf = access;
875 else {
876 encounteredError = true;
877 return;
878 }
879 }
880
881 emitConnect(*builder, leaf, op->getOperand(1));
882 });
883 }
884}
885
886// Expand connects of aggregates
887bool TypeLoweringVisitor::visitStmt(ConnectOp op) {
888 if (processSAPath(op))
889 return true;
890
891 // Attempt to get the bundle types.
892 SmallVector<FlatBundleFieldEntry> fields;
893
894 // We have to expand connections even if the aggregate preservation is true.
895 if (!peelType(op.getDest().getType(), fields, PreserveAggregate::None))
896 return false;
897
898 // Loop over the leaf aggregates.
899 for (const auto &field : llvm::enumerate(fields)) {
900 Value src = getSubWhatever(op.getSrc(), field.index());
901 Value dest = getSubWhatever(op.getDest(), field.index());
902 if (field.value().isOutput)
903 std::swap(src, dest);
904 emitConnect(*builder, dest, src);
905 }
906 return true;
907}
908
909// Expand connects of aggregates
910bool TypeLoweringVisitor::visitStmt(MatchingConnectOp op) {
911 if (processSAPath(op))
912 return true;
913
914 // Attempt to get the bundle types.
915 SmallVector<FlatBundleFieldEntry> fields;
916
917 // We have to expand connections even if the aggregate preservation is true.
918 if (!peelType(op.getDest().getType(), fields, PreserveAggregate::None))
919 return false;
920
921 // Loop over the leaf aggregates.
922 for (const auto &field : llvm::enumerate(fields)) {
923 Value src = getSubWhatever(op.getSrc(), field.index());
924 Value dest = getSubWhatever(op.getDest(), field.index());
925 if (field.value().isOutput)
926 std::swap(src, dest);
927 builder->create<MatchingConnectOp>(dest, src);
928 }
929 return true;
930}
931
932// Expand connects of references-of-aggregates
933bool TypeLoweringVisitor::visitStmt(RefDefineOp op) {
934 // Attempt to get the bundle types.
935 SmallVector<FlatBundleFieldEntry> fields;
936
937 if (!peelType(op.getDest().getType(), fields, bodyAggregatePreservationMode))
938 return false;
939
940 // Loop over the leaf aggregates.
941 for (const auto &field : llvm::enumerate(fields)) {
942 Value src = getSubWhatever(op.getSrc(), field.index());
943 Value dest = getSubWhatever(op.getDest(), field.index());
944 assert(!field.value().isOutput && "unexpected flip in reftype destination");
945 builder->create<RefDefineOp>(dest, src);
946 }
947 return true;
948}
949
950bool TypeLoweringVisitor::visitStmt(WhenOp op) {
951 // The WhenOp itself does not require any lowering, the only value it uses
952 // is a one-bit predicate. Recursively visit all regions so internal
953 // operations are lowered.
954
955 // Visit operations in the then block.
956 lowerBlock(&op.getThenBlock());
957
958 // Visit operations in the else block.
959 if (op.hasElseRegion())
960 lowerBlock(&op.getElseBlock());
961 return false; // don't delete the when!
962}
963
964/// Lower any types declared in layer blocks.
965bool TypeLoweringVisitor::visitStmt(LayerBlockOp op) {
966 lowerBlock(op.getBody());
967 return false;
968}
969
970/// Lower memory operations. A new memory is created for every leaf
971/// element in a memory's data type.
972bool TypeLoweringVisitor::visitDecl(MemOp op) {
973 // Attempt to get the bundle types.
974 SmallVector<FlatBundleFieldEntry> fields;
975
976 // MemOp should have ground types so we can't preserve aggregates.
977 if (!peelType(op.getDataType(), fields, memoryPreservationMode))
978 return false;
979
980 if (op.getInnerSym()) {
981 op->emitError() << "has a symbol, but no symbols may exist on aggregates "
982 "passed through LowerTypes";
983 encounteredError = true;
984 return false;
985 }
986
987 SmallVector<MemOp> newMemories;
988 SmallVector<WireOp> oldPorts;
989
990 // Wires for old ports
991 for (unsigned int index = 0, end = op.getNumResults(); index < end; ++index) {
992 auto result = op.getResult(index);
993 if (op.getPortKind(index) == MemOp::PortKind::Debug) {
994 op.emitOpError("cannot lower memory with debug port");
995 encounteredError = true;
996 return false;
997 }
998 auto wire = builder->create<WireOp>(
999 result.getType(),
1000 (op.getName() + "_" + op.getPortName(index).getValue()).str());
1001 oldPorts.push_back(wire);
1002 result.replaceAllUsesWith(wire.getResult());
1003 }
1004 // If annotations targeting fields of an aggregate are present, we cannot
1005 // flatten the memory. It must be split into one memory per aggregate field.
1006 // Do not overwrite the pass flag!
1007
1008 // Memory for each field
1009 for (const auto &field : fields) {
1010 auto newMemForField = cloneMemWithNewType(builder, op, field);
1011 if (!newMemForField) {
1012 op.emitError("failed cloning memory for field");
1013 encounteredError = true;
1014 return false;
1015 }
1016 newMemories.push_back(newMemForField);
1017 }
1018 // Hook up the new memories to the wires the old memory was replaced with.
1019 for (size_t index = 0, rend = op.getNumResults(); index < rend; ++index) {
1020 auto result = oldPorts[index].getResult();
1021 auto rType = type_cast<BundleType>(result.getType());
1022 for (size_t fieldIndex = 0, fend = rType.getNumElements();
1023 fieldIndex != fend; ++fieldIndex) {
1024 auto name = rType.getElement(fieldIndex).name.getValue();
1025 auto oldField = builder->create<SubfieldOp>(result, fieldIndex);
1026 // data and mask depend on the memory type which was split. They can also
1027 // go both directions, depending on the port direction.
1028 if (name == "data" || name == "mask" || name == "wdata" ||
1029 name == "wmask" || name == "rdata") {
1030 for (const auto &field : fields) {
1031 auto realOldField = getSubWhatever(oldField, field.index);
1032 auto newField = getSubWhatever(
1033 newMemories[field.index].getResult(index), fieldIndex);
1034 if (rType.getElement(fieldIndex).isFlip)
1035 std::swap(realOldField, newField);
1036 emitConnect(*builder, newField, realOldField);
1037 }
1038 } else {
1039 for (auto mem : newMemories) {
1040 auto newField =
1041 builder->create<SubfieldOp>(mem.getResult(index), fieldIndex);
1042 emitConnect(*builder, newField, oldField);
1043 }
1044 }
1045 }
1046 }
1047 return true;
1048}
1049
1050bool TypeLoweringVisitor::visitDecl(FExtModuleOp extModule) {
1051 ImplicitLocOpBuilder theBuilder(extModule.getLoc(), context);
1052 builder = &theBuilder;
1053
1054 // Top level builder
1055 OpBuilder builder(context);
1056
1057 auto internalPaths = extModule.getInternalPaths();
1058
1059 // Lower the module block arguments.
1060 SmallVector<unsigned> argsToRemove;
1061 SmallVector<PortInfoWithIP> newArgs;
1062 for (auto [idx, pi] : llvm::enumerate(extModule.getPorts())) {
1063 std::optional<InternalPathAttr> internalPath;
1064 if (internalPaths)
1065 internalPath = cast<InternalPathAttr>(internalPaths->getValue()[idx]);
1066 newArgs.push_back({pi, internalPath});
1067 }
1068
1069 for (size_t argIndex = 0, argsRemoved = 0; argIndex < newArgs.size();
1070 ++argIndex) {
1071 SmallVector<Value> lowering;
1072 if (lowerArg(extModule, argIndex, argsRemoved, newArgs, lowering)) {
1073 argsToRemove.push_back(argIndex);
1074 ++argsRemoved;
1075 }
1076 // lowerArg might have invalidated any reference to newArgs, be careful
1077 }
1078
1079 // Remove block args that have been lowered
1080 for (auto toRemove : llvm::reverse(argsToRemove))
1081 newArgs.erase(newArgs.begin() + toRemove);
1082
1083 SmallVector<NamedAttribute, 8> newModuleAttrs;
1084
1085 // Copy over any attributes that weren't original argument attributes.
1086 for (auto attr : extModule->getAttrDictionary())
1087 // Drop old "portNames", directions, and argument attributes. These are
1088 // handled differently below.
1089 if (attr.getName() != "portDirections" && attr.getName() != "portNames" &&
1090 attr.getName() != "portTypes" && attr.getName() != "portAnnotations" &&
1091 attr.getName() != "portSymbols" && attr.getName() != "portLocations")
1092 newModuleAttrs.push_back(attr);
1093
1094 SmallVector<Direction> newArgDirections;
1095 SmallVector<Attribute> newArgNames;
1096 SmallVector<Attribute, 8> newArgTypes;
1097 SmallVector<Attribute, 8> newArgSyms;
1098 SmallVector<Attribute, 8> newArgLocations;
1099 SmallVector<Attribute, 8> newArgAnnotations;
1100 SmallVector<Attribute, 8> newArgDomains;
1101 SmallVector<Attribute, 8> newInternalPaths;
1102
1103 auto emptyInternalPath = InternalPathAttr::get(context);
1104 for (auto &port : newArgs) {
1105 newArgDirections.push_back(port.pi.direction);
1106 newArgNames.push_back(port.pi.name);
1107 newArgTypes.push_back(TypeAttr::get(port.pi.type));
1108 newArgSyms.push_back(port.pi.sym);
1109 newArgLocations.push_back(port.pi.loc);
1110 newArgAnnotations.push_back(port.pi.annotations.getArrayAttr());
1111 if (auto domains = port.pi.domains)
1112 newArgDomains.push_back(domains);
1113 else
1114 newArgDomains.push_back(cache.aEmpty);
1115 if (internalPaths)
1116 newInternalPaths.push_back(port.internalPath.value_or(emptyInternalPath));
1117 }
1118
1119 newModuleAttrs.push_back(
1120 NamedAttribute(cache.sPortDirections,
1121 direction::packAttribute(context, newArgDirections)));
1122
1123 newModuleAttrs.push_back(
1124 NamedAttribute(cache.sPortNames, builder.getArrayAttr(newArgNames)));
1125
1126 newModuleAttrs.push_back(
1127 NamedAttribute(cache.sPortTypes, builder.getArrayAttr(newArgTypes)));
1128
1129 newModuleAttrs.push_back(NamedAttribute(
1130 cache.sPortLocations, builder.getArrayAttr(newArgLocations)));
1131
1132 newModuleAttrs.push_back(NamedAttribute(
1133 cache.sPortAnnotations, builder.getArrayAttr(newArgAnnotations)));
1134
1135 newModuleAttrs.push_back(
1136 NamedAttribute(cache.sPortDomains, builder.getArrayAttr(newArgDomains)));
1137
1138 // Update the module's attributes.
1139 extModule->setAttrs(newModuleAttrs);
1140 FModuleLike::fixupPortSymsArray(newArgSyms, context);
1141 extModule.setPortSymbols(newArgSyms);
1142 if (internalPaths)
1143 extModule.setInternalPathsAttr(builder.getArrayAttr(newInternalPaths));
1144
1145 return false;
1146}
1147
1148bool TypeLoweringVisitor::visitDecl(FModuleOp module) {
1149 auto *body = module.getBodyBlock();
1150
1151 ImplicitLocOpBuilder theBuilder(module.getLoc(), context);
1152 builder = &theBuilder;
1153
1154 // Lower the operations.
1155 lowerBlock(body);
1156
1157 // Lower the module block arguments.
1158 llvm::BitVector argsToRemove;
1159 auto newArgs = llvm::map_to_vector(module.getPorts(), [](auto pi) {
1160 return PortInfoWithIP{pi, std::nullopt};
1161 });
1162
1163 size_t argsRemoved = 0;
1164 for (size_t argIndex = 0; argIndex < newArgs.size(); ++argIndex) {
1165 SmallVector<Value> lowerings;
1166 if (lowerArg(module, argIndex, argsRemoved, newArgs, lowerings)) {
1167 auto arg = module.getArgument(argIndex);
1168 processUsers(arg, lowerings);
1169 argsToRemove.push_back(true);
1170 ++argsRemoved;
1171 } else
1172 argsToRemove.push_back(false);
1173 // lowerArg might have invalidated any reference to newArgs, be careful
1174 }
1175
1176 // Remove block args that have been lowered.
1177 if (argsRemoved != 0) {
1178 body->eraseArguments(argsToRemove);
1179 size_t size = newArgs.size();
1180 for (size_t src = 0, dst = 0; src < size; ++src) {
1181 if (argsToRemove[src])
1182 continue;
1183 newArgs[dst] = newArgs[src];
1184 ++dst;
1185 }
1186 newArgs.erase(newArgs.end() - argsRemoved, newArgs.end());
1187 }
1188
1189 SmallVector<NamedAttribute, 8> newModuleAttrs;
1190
1191 // Copy over any attributes that weren't original argument attributes.
1192 for (auto attr : module->getAttrDictionary())
1193 // Drop old "portNames", directions, and argument attributes. These are
1194 // handled differently below.
1195 if (attr.getName() != "portNames" && attr.getName() != "portDirections" &&
1196 attr.getName() != "portTypes" && attr.getName() != "portAnnotations" &&
1197 attr.getName() != "portSymbols" && attr.getName() != "portLocations")
1198 newModuleAttrs.push_back(attr);
1199
1200 SmallVector<Direction> newArgDirections;
1201 SmallVector<Attribute> newArgNames;
1202 SmallVector<Attribute> newArgTypes;
1203 SmallVector<Attribute> newArgSyms;
1204 SmallVector<Attribute> newArgLocations;
1205 SmallVector<Attribute, 8> newArgAnnotations;
1206 SmallVector<Attribute> newPortDomains;
1207 for (auto &port : newArgs) {
1208 newArgDirections.push_back(port.pi.direction);
1209 newArgNames.push_back(port.pi.name);
1210 newArgTypes.push_back(TypeAttr::get(port.pi.type));
1211 newArgSyms.push_back(port.pi.sym);
1212 newArgLocations.push_back(port.pi.loc);
1213 newArgAnnotations.push_back(port.pi.annotations.getArrayAttr());
1214 if (auto domains = port.pi.domains)
1215 newPortDomains.push_back(domains);
1216 else
1217 newPortDomains.push_back(cache.aEmpty);
1218 }
1219
1220 newModuleAttrs.push_back(
1221 NamedAttribute(cache.sPortDirections,
1222 direction::packAttribute(context, newArgDirections)));
1223
1224 newModuleAttrs.push_back(
1225 NamedAttribute(cache.sPortNames, builder->getArrayAttr(newArgNames)));
1226
1227 newModuleAttrs.push_back(
1228 NamedAttribute(cache.sPortTypes, builder->getArrayAttr(newArgTypes)));
1229
1230 newModuleAttrs.push_back(NamedAttribute(
1231 cache.sPortLocations, builder->getArrayAttr(newArgLocations)));
1232
1233 newModuleAttrs.push_back(NamedAttribute(
1234 cache.sPortAnnotations, builder->getArrayAttr(newArgAnnotations)));
1235
1236 newModuleAttrs.push_back(NamedAttribute(
1237 cache.sPortDomains, builder->getArrayAttr(newPortDomains)));
1238
1239 // Update the module's attributes.
1240 module->setAttrs(newModuleAttrs);
1241 FModuleLike::fixupPortSymsArray(newArgSyms, context);
1242 module.setPortSymbols(newArgSyms);
1243 return false;
1244}
1245
1246/// Lower a wire op with a bundle to multiple non-bundled wires.
1247bool TypeLoweringVisitor::visitDecl(WireOp op) {
1248 if (op.isForceable())
1249 return false;
1250
1251 auto clone = [&](const FlatBundleFieldEntry &field,
1252 ArrayAttr attrs) -> Value {
1253 return builder
1254 ->create<WireOp>(mapLoweredType(op.getDataRaw().getType(), field.type),
1255 "", NameKindEnum::DroppableName, attrs, StringAttr{})
1256 .getResult();
1257 };
1258 return lowerProducer(op, clone);
1259}
1260
1261/// Lower a reg op with a bundle to multiple non-bundled regs.
1262bool TypeLoweringVisitor::visitDecl(RegOp op) {
1263 if (op.isForceable())
1264 return false;
1265
1266 auto clone = [&](const FlatBundleFieldEntry &field,
1267 ArrayAttr attrs) -> Value {
1268 return builder
1269 ->create<RegOp>(field.type, op.getClockVal(), "",
1270 NameKindEnum::DroppableName, attrs, StringAttr{})
1271 .getResult();
1272 };
1273 return lowerProducer(op, clone);
1274}
1275
1276/// Lower a reg op with a bundle to multiple non-bundled regs.
1277bool TypeLoweringVisitor::visitDecl(RegResetOp op) {
1278 if (op.isForceable())
1279 return false;
1280
1281 auto clone = [&](const FlatBundleFieldEntry &field,
1282 ArrayAttr attrs) -> Value {
1283 auto resetVal = getSubWhatever(op.getResetValue(), field.index);
1284 return builder
1285 ->create<RegResetOp>(field.type, op.getClockVal(), op.getResetSignal(),
1286 resetVal, "", NameKindEnum::DroppableName, attrs,
1287 StringAttr{})
1288 .getResult();
1289 };
1290 return lowerProducer(op, clone);
1291}
1292
1293/// Lower a wire op with a bundle to multiple non-bundled wires.
1294bool TypeLoweringVisitor::visitDecl(NodeOp op) {
1295 if (op.isForceable())
1296 return false;
1297
1298 auto clone = [&](const FlatBundleFieldEntry &field,
1299 ArrayAttr attrs) -> Value {
1300 auto input = getSubWhatever(op.getInput(), field.index);
1301 return builder
1302 ->create<NodeOp>(input, "", NameKindEnum::DroppableName, attrs)
1303 .getResult();
1304 };
1305 return lowerProducer(op, clone);
1306}
1307
1308/// Lower an InvalidValue op with a bundle to multiple non-bundled InvalidOps.
1309bool TypeLoweringVisitor::visitExpr(InvalidValueOp op) {
1310 auto clone = [&](const FlatBundleFieldEntry &field,
1311 ArrayAttr attrs) -> Value {
1312 return builder->create<InvalidValueOp>(field.type);
1313 };
1314 return lowerProducer(op, clone);
1315}
1316
1317// Expand muxes of aggregates
1318bool TypeLoweringVisitor::visitExpr(MuxPrimOp op) {
1319 auto clone = [&](const FlatBundleFieldEntry &field,
1320 ArrayAttr attrs) -> Value {
1321 auto high = getSubWhatever(op.getHigh(), field.index);
1322 auto low = getSubWhatever(op.getLow(), field.index);
1323 return builder->create<MuxPrimOp>(op.getSel(), high, low);
1324 };
1325 return lowerProducer(op, clone);
1326}
1327
1328// Expand muxes of aggregates
1329bool TypeLoweringVisitor::visitExpr(Mux2CellIntrinsicOp op) {
1330 auto clone = [&](const FlatBundleFieldEntry &field,
1331 ArrayAttr attrs) -> Value {
1332 auto high = getSubWhatever(op.getHigh(), field.index);
1333 auto low = getSubWhatever(op.getLow(), field.index);
1334 return builder->create<Mux2CellIntrinsicOp>(op.getSel(), high, low);
1335 };
1336 return lowerProducer(op, clone);
1337}
1338
1339// Expand muxes of aggregates
1340bool TypeLoweringVisitor::visitExpr(Mux4CellIntrinsicOp op) {
1341 auto clone = [&](const FlatBundleFieldEntry &field,
1342 ArrayAttr attrs) -> Value {
1343 auto v3 = getSubWhatever(op.getV3(), field.index);
1344 auto v2 = getSubWhatever(op.getV2(), field.index);
1345 auto v1 = getSubWhatever(op.getV1(), field.index);
1346 auto v0 = getSubWhatever(op.getV0(), field.index);
1347 return builder->create<Mux4CellIntrinsicOp>(op.getSel(), v3, v2, v1, v0);
1348 };
1349 return lowerProducer(op, clone);
1350}
1351
1352// Expand UnrealizedConversionCastOp of aggregates
1353bool TypeLoweringVisitor::visitUnrealizedConversionCast(
1354 mlir::UnrealizedConversionCastOp op) {
1355 auto clone = [&](const FlatBundleFieldEntry &field,
1356 ArrayAttr attrs) -> Value {
1357 auto input = getSubWhatever(op.getOperand(0), field.index);
1358 return builder->create<mlir::UnrealizedConversionCastOp>(field.type, input)
1359 .getResult(0);
1360 };
1361 // If the input to the cast is not a FIRRTL type, getSubWhatever cannot handle
1362 // it, donot lower the op.
1363 if (!type_isa<FIRRTLType>(op->getOperand(0).getType()))
1364 return false;
1365 return lowerProducer(op, clone);
1366}
1367
1368// Expand BitCastOp of aggregates
1369bool TypeLoweringVisitor::visitExpr(BitCastOp op) {
1370 Value srcLoweredVal = op.getInput();
1371 // If the input is of aggregate type, then cat all the leaf fields to form a
1372 // UInt type result. That is, first bitcast the aggregate type to a UInt.
1373 // Attempt to get the bundle types.
1374 SmallVector<FlatBundleFieldEntry> fields;
1375 if (peelType(op.getInput().getType(), fields, PreserveAggregate::None)) {
1376 size_t uptoBits = 0;
1377 // Loop over the leaf aggregates and concat each of them to get a UInt.
1378 // Bitcast the fields to handle nested aggregate types.
1379 for (const auto &field : llvm::enumerate(fields)) {
1380 auto fieldBitwidth = *getBitWidth(field.value().type);
1381 // Ignore zero width fields, like empty bundles.
1382 if (fieldBitwidth == 0)
1383 continue;
1384 Value src = getSubWhatever(op.getInput(), field.index());
1385 // The src could be an aggregate type, bitcast it to a UInt type.
1386 src = builder->createOrFold<BitCastOp>(
1387 UIntType::get(context, fieldBitwidth), src);
1388 // Take the first field, or else Cat the previous fields with this field.
1389 if (uptoBits == 0)
1390 srcLoweredVal = src;
1391 else {
1392 if (type_isa<BundleType>(op.getInput().getType())) {
1393 srcLoweredVal =
1394 builder->create<CatPrimOp>(ValueRange{srcLoweredVal, src});
1395 } else {
1396 srcLoweredVal =
1397 builder->create<CatPrimOp>(ValueRange{src, srcLoweredVal});
1398 }
1399 }
1400 // Record the total bits already accumulated.
1401 uptoBits += fieldBitwidth;
1402 }
1403 } else {
1404 srcLoweredVal = builder->createOrFold<AsUIntPrimOp>(srcLoweredVal);
1405 }
1406 // Now the input has been cast to srcLoweredVal, which is of UInt type.
1407 // If the result is an aggregate type, then use lowerProducer.
1408 if (type_isa<BundleType, FVectorType>(op.getResult().getType())) {
1409 // uptoBits is used to keep track of the bits that have been extracted.
1410 size_t uptoBits = 0;
1411 auto aggregateBits = *getBitWidth(op.getResult().getType());
1412 auto clone = [&](const FlatBundleFieldEntry &field,
1413 ArrayAttr attrs) -> Value {
1414 // All the fields must have valid bitwidth, a requirement for BitCastOp.
1415 auto fieldBits = *getBitWidth(field.type);
1416 // If empty field, then it doesnot have any use, so replace it with an
1417 // invalid op, which should be trivially removed.
1418 if (fieldBits == 0)
1419 return builder->create<InvalidValueOp>(field.type);
1420
1421 // Assign the field to the corresponding bits from the input.
1422 // Bitcast the field, incase its an aggregate type.
1423 BitsPrimOp extractBits;
1424 if (type_isa<BundleType>(op.getResult().getType())) {
1425 extractBits = builder->create<BitsPrimOp>(
1426 srcLoweredVal, aggregateBits - uptoBits - 1,
1427 aggregateBits - uptoBits - fieldBits);
1428 } else {
1429 extractBits = builder->create<BitsPrimOp>(
1430 srcLoweredVal, uptoBits + fieldBits - 1, uptoBits);
1431 }
1432 uptoBits += fieldBits;
1433 return builder->create<BitCastOp>(field.type, extractBits);
1434 };
1435 return lowerProducer(op, clone);
1436 }
1437
1438 // If ground type, then replace the result.
1439 if (type_isa<SIntType>(op.getType()))
1440 srcLoweredVal = builder->create<AsSIntPrimOp>(srcLoweredVal);
1441 op.getResult().replaceAllUsesWith(srcLoweredVal);
1442 return true;
1443}
1444
1445bool TypeLoweringVisitor::visitExpr(RefSendOp op) {
1446 auto clone = [&](const FlatBundleFieldEntry &field,
1447 ArrayAttr attrs) -> Value {
1448 return builder->create<RefSendOp>(
1449 getSubWhatever(op.getBase(), field.index));
1450 };
1451 // Be careful re:what gets lowered, consider ref.send of non-passive
1452 // and whether we're using the ref or the base type to choose
1453 // whether this should be lowered.
1454 return lowerProducer(op, clone);
1455}
1456
1457bool TypeLoweringVisitor::visitExpr(RefResolveOp op) {
1458 auto clone = [&](const FlatBundleFieldEntry &field,
1459 ArrayAttr attrs) -> Value {
1460 Value src = getSubWhatever(op.getRef(), field.index);
1461 return builder->create<RefResolveOp>(src);
1462 };
1463 // Lower according to lowering of the reference.
1464 // Particularly, preserve if rwprobe.
1465 return lowerProducer(op, clone, op.getRef().getType());
1466}
1467
1468bool TypeLoweringVisitor::visitExpr(RefCastOp op) {
1469 auto clone = [&](const FlatBundleFieldEntry &field,
1470 ArrayAttr attrs) -> Value {
1471 auto input = getSubWhatever(op.getInput(), field.index);
1472 return builder->create<RefCastOp>(RefType::get(field.type,
1473 op.getType().getForceable(),
1474 op.getType().getLayer()),
1475 input);
1476 };
1477 return lowerProducer(op, clone);
1478}
1479
1480bool TypeLoweringVisitor::visitDecl(InstanceOp op) {
1481 bool skip = true;
1482 SmallVector<Type, 8> resultTypes;
1483 SmallVector<int64_t, 8> endFields; // Compressed sparse row encoding
1484 auto oldPortAnno = op.getPortAnnotations();
1485 SmallVector<Direction> newDirs;
1486 SmallVector<Attribute> newNames;
1487 // TODO: Properly lower `newDomains`.
1488 SmallVector<Attribute> newDomains;
1489 SmallVector<Attribute> newPortAnno;
1490 PreserveAggregate::PreserveMode mode = getPreservationModeForPorts(
1491 cast<FModuleLike>(op.getReferencedOperation(symTbl)));
1492
1493 endFields.push_back(0);
1494 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
1495 auto srcType = type_cast<FIRRTLType>(op.getType(i));
1496
1497 // Flatten any nested bundle types the usual way.
1498 SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
1499 if (!peelType(srcType, fieldTypes, mode)) {
1500 newDirs.push_back(op.getPortDirection(i));
1501 newNames.push_back(op.getPortName(i));
1502 newDomains.push_back(builder->getArrayAttr({}));
1503 resultTypes.push_back(srcType);
1504 newPortAnno.push_back(oldPortAnno[i]);
1505 } else {
1506 skip = false;
1507 auto oldName = op.getPortNameStr(i);
1508 auto oldDir = op.getPortDirection(i);
1509 // Store the flat type for the new bundle type.
1510 for (const auto &field : fieldTypes) {
1511 newDirs.push_back(direction::get((unsigned)oldDir ^ field.isOutput));
1512 newNames.push_back(builder->getStringAttr(oldName + field.suffix));
1513 newDomains.push_back(builder->getArrayAttr({}));
1514 resultTypes.push_back(mapLoweredType(srcType, field.type));
1515 auto annos = filterAnnotations(
1516 context, dyn_cast_or_null<ArrayAttr>(oldPortAnno[i]), srcType,
1517 field);
1518 newPortAnno.push_back(annos);
1519 }
1520 }
1521 endFields.push_back(resultTypes.size());
1522 }
1523
1524 auto sym = getInnerSymName(op);
1525
1526 if (skip) {
1527 return false;
1528 }
1529
1530 // FIXME: annotation update
1531 auto newInstance = builder->create<InstanceOp>(
1532 resultTypes, op.getModuleNameAttr(), op.getNameAttr(),
1533 op.getNameKindAttr(), direction::packAttribute(context, newDirs),
1534 builder->getArrayAttr(newNames), builder->getArrayAttr(newDomains),
1535 op.getAnnotations(), builder->getArrayAttr(newPortAnno),
1536 op.getLayersAttr(), op.getLowerToBindAttr(), op.getDoNotPrintAttr(),
1537 sym ? hw::InnerSymAttr::get(sym) : hw::InnerSymAttr());
1538
1539 newInstance->setDiscardableAttrs(op->getDiscardableAttrDictionary());
1540
1541 SmallVector<Value> lowered;
1542 for (size_t aggIndex = 0, eAgg = op.getNumResults(); aggIndex != eAgg;
1543 ++aggIndex) {
1544 lowered.clear();
1545 for (size_t fieldIndex = endFields[aggIndex],
1546 eField = endFields[aggIndex + 1];
1547 fieldIndex < eField; ++fieldIndex)
1548 lowered.push_back(newInstance.getResult(fieldIndex));
1549 if (lowered.size() != 1 ||
1550 op.getType(aggIndex) != resultTypes[endFields[aggIndex]])
1551 processUsers(op.getResult(aggIndex), lowered);
1552 else
1553 op.getResult(aggIndex).replaceAllUsesWith(lowered[0]);
1554 }
1555 return true;
1556}
1557
1558bool TypeLoweringVisitor::visitExpr(SubaccessOp op) {
1559 auto input = op.getInput();
1560 FVectorType vType = input.getType();
1561
1562 // Check for empty vectors
1563 if (vType.getNumElements() == 0) {
1564 Value inv = builder->create<InvalidValueOp>(vType.getElementType());
1565 op.replaceAllUsesWith(inv);
1566 return true;
1567 }
1568
1569 // Check for constant instances
1570 if (ConstantOp arg =
1571 llvm::dyn_cast_or_null<ConstantOp>(op.getIndex().getDefiningOp())) {
1572 auto sio = builder->create<SubindexOp>(op.getInput(),
1573 arg.getValue().getExtValue());
1574 op.replaceAllUsesWith(sio.getResult());
1575 return true;
1576 }
1577
1578 // Construct a multibit mux
1579 SmallVector<Value> inputs;
1580 inputs.reserve(vType.getNumElements());
1581 for (int index = vType.getNumElements() - 1; index >= 0; index--)
1582 inputs.push_back(builder->create<SubindexOp>(input, index));
1583
1584 Value multibitMux = builder->create<MultibitMuxOp>(op.getIndex(), inputs);
1585 op.replaceAllUsesWith(multibitMux);
1586 return true;
1587}
1588
1589bool TypeLoweringVisitor::visitExpr(VectorCreateOp op) {
1590 auto clone = [&](const FlatBundleFieldEntry &field,
1591 ArrayAttr attrs) -> Value {
1592 return op.getOperand(field.index);
1593 };
1594 return lowerProducer(op, clone);
1595}
1596
1597bool TypeLoweringVisitor::visitExpr(BundleCreateOp op) {
1598 auto clone = [&](const FlatBundleFieldEntry &field,
1599 ArrayAttr attrs) -> Value {
1600 return op.getOperand(field.index);
1601 };
1602 return lowerProducer(op, clone);
1603}
1604
1605bool TypeLoweringVisitor::visitExpr(ElementwiseOrPrimOp op) {
1606 auto clone = [&](const FlatBundleFieldEntry &field,
1607 ArrayAttr attrs) -> Value {
1608 Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1609 getSubWhatever(op.getRhs(), field.index)};
1610 return type_isa<BundleType, FVectorType>(field.type)
1611 ? (Value)builder->create<ElementwiseOrPrimOp>(field.type,
1612 operands)
1613 : (Value)builder->create<OrPrimOp>(operands);
1614 };
1615
1616 return lowerProducer(op, clone);
1617}
1618
1619bool TypeLoweringVisitor::visitExpr(ElementwiseAndPrimOp op) {
1620 auto clone = [&](const FlatBundleFieldEntry &field,
1621 ArrayAttr attrs) -> Value {
1622 Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1623 getSubWhatever(op.getRhs(), field.index)};
1624 return type_isa<BundleType, FVectorType>(field.type)
1625 ? (Value)builder->create<ElementwiseAndPrimOp>(field.type,
1626 operands)
1627 : (Value)builder->create<AndPrimOp>(operands);
1628 };
1629
1630 return lowerProducer(op, clone);
1631}
1632
1633bool TypeLoweringVisitor::visitExpr(ElementwiseXorPrimOp op) {
1634 auto clone = [&](const FlatBundleFieldEntry &field,
1635 ArrayAttr attrs) -> Value {
1636 Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1637 getSubWhatever(op.getRhs(), field.index)};
1638 return type_isa<BundleType, FVectorType>(field.type)
1639 ? (Value)builder->create<ElementwiseXorPrimOp>(field.type,
1640 operands)
1641 : (Value)builder->create<XorPrimOp>(operands);
1642 };
1643
1644 return lowerProducer(op, clone);
1645}
1646
1647bool TypeLoweringVisitor::visitExpr(MultibitMuxOp op) {
1648 auto clone = [&](const FlatBundleFieldEntry &field,
1649 ArrayAttr attrs) -> Value {
1650 SmallVector<Value> newInputs;
1651 newInputs.reserve(op.getInputs().size());
1652 for (auto input : op.getInputs()) {
1653 auto inputSub = getSubWhatever(input, field.index);
1654 newInputs.push_back(inputSub);
1655 }
1656 return builder->create<MultibitMuxOp>(op.getIndex(), newInputs);
1657 };
1658 return lowerProducer(op, clone);
1659}
1660
1661//===----------------------------------------------------------------------===//
1662// Pass Infrastructure
1663//===----------------------------------------------------------------------===//
1664
1665namespace {
1666struct LowerTypesPass
1667 : public circt::firrtl::impl::LowerFIRRTLTypesBase<LowerTypesPass> {
1668 using Base::Base;
1669
1670 void runOnOperation() override;
1671};
1672} // end anonymous namespace
1673
1674// This is the main entrypoint for the lowering pass.
1675void LowerTypesPass::runOnOperation() {
1676 LLVM_DEBUG(debugPassHeader(this) << "\n");
1677 std::vector<FModuleLike> ops;
1678 // Symbol Table
1679 auto &symTbl = getAnalysis<SymbolTable>();
1680 // Cached attr
1681 AttrCache cache(&getContext());
1682
1683 DenseMap<FModuleLike, Convention> conventionTable;
1684 auto circuit = getOperation();
1685 for (auto module : circuit.getOps<FModuleLike>()) {
1686 conventionTable.insert({module, module.getConvention()});
1687 ops.push_back(module);
1688 }
1689
1690 // This lambda, executes in parallel for each Op within the circt.
1691 auto lowerModules = [&](FModuleLike op) -> LogicalResult {
1692 // Use body type lowering attribute if it exists, otherwise use internal.
1693 Convention convention = Convention::Internal;
1694 if (auto conventionAttr = dyn_cast_or_null<ConventionAttr>(
1695 op->getDiscardableAttr("body_type_lowering")))
1696 convention = conventionAttr.getValue();
1697
1698 auto tl =
1699 TypeLoweringVisitor(&getContext(), preserveAggregate, convention,
1700 preserveMemories, symTbl, cache, conventionTable);
1701 tl.lowerModule(op);
1702
1703 return LogicalResult::failure(tl.isFailed());
1704 };
1705
1706 auto result = failableParallelForEach(&getContext(), ops, lowerModules);
1707
1708 if (failed(result))
1709 signalPassFailure();
1710}
assert(baseType &&"element must be base type")
static SmallVector< Value > extractBits(OpBuilder &builder, Value val)
Definition CombToAIG.cpp:57
static void dump(DIModule &module, raw_indented_ostream &os)
static bool isPreservableAggregateType(Type type, PreserveAggregate::PreserveMode mode)
Return true if we can preserve the type.
static FIRRTLType mapLoweredType(FIRRTLType type, FIRRTLBaseType fieldType)
Return fieldType or fieldType as same ref as type.
static MemOp cloneMemWithNewType(ImplicitLocOpBuilder *b, MemOp op, FlatBundleFieldEntry field)
Clone memory for the specified field. Returns null op on error.
static bool containsBundleType(FIRRTLType type)
Return true if the type has a bundle type as subtype.
static Value cloneAccess(ImplicitLocOpBuilder *builder, Operation *op, Value rhs)
static bool peelType(Type type, SmallVectorImpl< FlatBundleFieldEntry > &fields, PreserveAggregate::PreserveMode mode)
Peel one layer of an aggregate type into its components.
static bool isNotSubAccess(Operation *op)
Return if something is not a normal subaccess.
static SmallVector< Operation * > getSAWritePath(Operation *op)
Look through and collect subfields leading to a subaccess.
static bool isOneDimVectorType(FIRRTLType type)
Return true if the type is a 1d vector type or ground type.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
void setMember(StringAttr name, Attribute value)
Add or set a member of the annotation to a value.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
@ All
Preserve all aggregate values.
Definition Passes.h:40
@ OneDimVec
Preserve only 1d vectors of ground type (e.g. UInt<2>[3]).
Definition Passes.h:34
@ Vec
Preserve only vectors (e.g. UInt<2>[3][3]).
Definition Passes.h:37
@ None
Don't preserve aggregate at all.
Definition Passes.h:31
mlir::DenseBoolArrayAttr packAttribute(MLIRContext *context, ArrayRef< Direction > directions)
Return a DenseBoolArrayAttr containing the packed representation of an array of directions.
static Direction get(bool isOutput)
Return an output direction if isOutput is true, otherwise return an input direction.
Direction
This represents the direction of a single port.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
FIRRTLType mapBaseType(FIRRTLType type, function_ref< FIRRTLBaseType(FIRRTLBaseType)> fn)
Return a FIRRTLType with its base type component mutated by the given function.
bool hasZeroBitWidth(FIRRTLType type)
Return true if the type has zero bit width.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
Definition FIRRTLOps.h:108
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
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.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition Debug.cpp:31
Definition hw.py:1
This holds the name and type that describes the module's ports.