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