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