CIRCT  20.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  sPortSymbols = StringAttr::get(context, "portSymbols");
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  sPortSymbols, 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() != "portSymbols" && 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  FModuleLike::fixupPortSymsArray(newArgSyms, context);
1128  extModule.setPortSymbols(newArgSyms);
1129  if (internalPaths)
1130  extModule.setInternalPathsAttr(builder.getArrayAttr(newInternalPaths));
1131 
1132  return false;
1133 }
1134 
1135 bool TypeLoweringVisitor::visitDecl(FModuleOp module) {
1136  auto *body = module.getBodyBlock();
1137 
1138  ImplicitLocOpBuilder theBuilder(module.getLoc(), context);
1139  builder = &theBuilder;
1140 
1141  // Lower the operations.
1142  lowerBlock(body);
1143 
1144  // Lower the module block arguments.
1145  llvm::BitVector argsToRemove;
1146  auto newArgs = llvm::map_to_vector(module.getPorts(), [](auto pi) {
1147  return PortInfoWithIP{pi, std::nullopt};
1148  });
1149  for (size_t argIndex = 0, argsRemoved = 0; argIndex < newArgs.size();
1150  ++argIndex) {
1151  SmallVector<Value> lowerings;
1152  if (lowerArg(module, argIndex, argsRemoved, newArgs, lowerings)) {
1153  auto arg = module.getArgument(argIndex);
1154  processUsers(arg, lowerings);
1155  argsToRemove.push_back(true);
1156  ++argsRemoved;
1157  } else
1158  argsToRemove.push_back(false);
1159  // lowerArg might have invalidated any reference to newArgs, be careful
1160  }
1161 
1162  // Remove block args that have been lowered.
1163  body->eraseArguments(argsToRemove);
1164  for (auto deadArg = argsToRemove.find_last(); deadArg != -1;
1165  deadArg = argsToRemove.find_prev(deadArg))
1166  newArgs.erase(newArgs.begin() + deadArg);
1167 
1168  SmallVector<NamedAttribute, 8> newModuleAttrs;
1169 
1170  // Copy over any attributes that weren't original argument attributes.
1171  for (auto attr : module->getAttrDictionary())
1172  // Drop old "portNames", directions, and argument attributes. These are
1173  // handled differently below.
1174  if (attr.getName() != "portNames" && attr.getName() != "portDirections" &&
1175  attr.getName() != "portTypes" && attr.getName() != "portAnnotations" &&
1176  attr.getName() != "portSymbols" && attr.getName() != "portLocations")
1177  newModuleAttrs.push_back(attr);
1178 
1179  SmallVector<Direction> newArgDirections;
1180  SmallVector<Attribute> newArgNames;
1181  SmallVector<Attribute> newArgTypes;
1182  SmallVector<Attribute> newArgSyms;
1183  SmallVector<Attribute> newArgLocations;
1184  SmallVector<Attribute, 8> newArgAnnotations;
1185  for (auto &port : newArgs) {
1186  newArgDirections.push_back(port.pi.direction);
1187  newArgNames.push_back(port.pi.name);
1188  newArgTypes.push_back(TypeAttr::get(port.pi.type));
1189  newArgSyms.push_back(port.pi.sym);
1190  newArgLocations.push_back(port.pi.loc);
1191  newArgAnnotations.push_back(port.pi.annotations.getArrayAttr());
1192  }
1193 
1194  newModuleAttrs.push_back(
1195  NamedAttribute(cache.sPortDirections,
1196  direction::packAttribute(context, newArgDirections)));
1197 
1198  newModuleAttrs.push_back(
1199  NamedAttribute(cache.sPortNames, builder->getArrayAttr(newArgNames)));
1200 
1201  newModuleAttrs.push_back(
1202  NamedAttribute(cache.sPortTypes, builder->getArrayAttr(newArgTypes)));
1203 
1204  newModuleAttrs.push_back(NamedAttribute(
1205  cache.sPortLocations, builder->getArrayAttr(newArgLocations)));
1206 
1207  newModuleAttrs.push_back(NamedAttribute(
1208  cache.sPortAnnotations, builder->getArrayAttr(newArgAnnotations)));
1209 
1210  // Update the module's attributes.
1211  module->setAttrs(newModuleAttrs);
1212  FModuleLike::fixupPortSymsArray(newArgSyms, context);
1213  module.setPortSymbols(newArgSyms);
1214  return false;
1215 }
1216 
1217 /// Lower a wire op with a bundle to multiple non-bundled wires.
1218 bool TypeLoweringVisitor::visitDecl(WireOp op) {
1219  if (op.isForceable())
1220  return false;
1221 
1222  auto clone = [&](const FlatBundleFieldEntry &field,
1223  ArrayAttr attrs) -> Value {
1224  return builder
1225  ->create<WireOp>(mapLoweredType(op.getDataRaw().getType(), field.type),
1226  "", NameKindEnum::DroppableName, attrs, StringAttr{})
1227  .getResult();
1228  };
1229  return lowerProducer(op, clone);
1230 }
1231 
1232 /// Lower a reg op with a bundle to multiple non-bundled regs.
1233 bool TypeLoweringVisitor::visitDecl(RegOp op) {
1234  if (op.isForceable())
1235  return false;
1236 
1237  auto clone = [&](const FlatBundleFieldEntry &field,
1238  ArrayAttr attrs) -> Value {
1239  return builder
1240  ->create<RegOp>(field.type, op.getClockVal(), "",
1241  NameKindEnum::DroppableName, attrs, StringAttr{})
1242  .getResult();
1243  };
1244  return lowerProducer(op, clone);
1245 }
1246 
1247 /// Lower a reg op with a bundle to multiple non-bundled regs.
1248 bool TypeLoweringVisitor::visitDecl(RegResetOp op) {
1249  if (op.isForceable())
1250  return false;
1251 
1252  auto clone = [&](const FlatBundleFieldEntry &field,
1253  ArrayAttr attrs) -> Value {
1254  auto resetVal = getSubWhatever(op.getResetValue(), field.index);
1255  return builder
1256  ->create<RegResetOp>(field.type, op.getClockVal(), op.getResetSignal(),
1257  resetVal, "", NameKindEnum::DroppableName, attrs,
1258  StringAttr{})
1259  .getResult();
1260  };
1261  return lowerProducer(op, clone);
1262 }
1263 
1264 /// Lower a wire op with a bundle to multiple non-bundled wires.
1265 bool TypeLoweringVisitor::visitDecl(NodeOp op) {
1266  if (op.isForceable())
1267  return false;
1268 
1269  auto clone = [&](const FlatBundleFieldEntry &field,
1270  ArrayAttr attrs) -> Value {
1271  auto input = getSubWhatever(op.getInput(), field.index);
1272  return builder
1273  ->create<NodeOp>(input, "", NameKindEnum::DroppableName, attrs)
1274  .getResult();
1275  };
1276  return lowerProducer(op, clone);
1277 }
1278 
1279 /// Lower an InvalidValue op with a bundle to multiple non-bundled InvalidOps.
1280 bool TypeLoweringVisitor::visitExpr(InvalidValueOp op) {
1281  auto clone = [&](const FlatBundleFieldEntry &field,
1282  ArrayAttr attrs) -> Value {
1283  return builder->create<InvalidValueOp>(field.type);
1284  };
1285  return lowerProducer(op, clone);
1286 }
1287 
1288 // Expand muxes of aggregates
1289 bool TypeLoweringVisitor::visitExpr(MuxPrimOp op) {
1290  auto clone = [&](const FlatBundleFieldEntry &field,
1291  ArrayAttr attrs) -> Value {
1292  auto high = getSubWhatever(op.getHigh(), field.index);
1293  auto low = getSubWhatever(op.getLow(), field.index);
1294  return builder->create<MuxPrimOp>(op.getSel(), high, low);
1295  };
1296  return lowerProducer(op, clone);
1297 }
1298 
1299 // Expand muxes of aggregates
1300 bool TypeLoweringVisitor::visitExpr(Mux2CellIntrinsicOp op) {
1301  auto clone = [&](const FlatBundleFieldEntry &field,
1302  ArrayAttr attrs) -> Value {
1303  auto high = getSubWhatever(op.getHigh(), field.index);
1304  auto low = getSubWhatever(op.getLow(), field.index);
1305  return builder->create<Mux2CellIntrinsicOp>(op.getSel(), high, low);
1306  };
1307  return lowerProducer(op, clone);
1308 }
1309 
1310 // Expand muxes of aggregates
1311 bool TypeLoweringVisitor::visitExpr(Mux4CellIntrinsicOp op) {
1312  auto clone = [&](const FlatBundleFieldEntry &field,
1313  ArrayAttr attrs) -> Value {
1314  auto v3 = getSubWhatever(op.getV3(), field.index);
1315  auto v2 = getSubWhatever(op.getV2(), field.index);
1316  auto v1 = getSubWhatever(op.getV1(), field.index);
1317  auto v0 = getSubWhatever(op.getV0(), field.index);
1318  return builder->create<Mux4CellIntrinsicOp>(op.getSel(), v3, v2, v1, v0);
1319  };
1320  return lowerProducer(op, clone);
1321 }
1322 
1323 // Expand UnrealizedConversionCastOp of aggregates
1324 bool TypeLoweringVisitor::visitUnrealizedConversionCast(
1325  mlir::UnrealizedConversionCastOp op) {
1326  auto clone = [&](const FlatBundleFieldEntry &field,
1327  ArrayAttr attrs) -> Value {
1328  auto input = getSubWhatever(op.getOperand(0), field.index);
1329  return builder->create<mlir::UnrealizedConversionCastOp>(field.type, input)
1330  .getResult(0);
1331  };
1332  // If the input to the cast is not a FIRRTL type, getSubWhatever cannot handle
1333  // it, donot lower the op.
1334  if (!type_isa<FIRRTLType>(op->getOperand(0).getType()))
1335  return false;
1336  return lowerProducer(op, clone);
1337 }
1338 
1339 // Expand BitCastOp of aggregates
1340 bool TypeLoweringVisitor::visitExpr(BitCastOp op) {
1341  Value srcLoweredVal = op.getInput();
1342  // If the input is of aggregate type, then cat all the leaf fields to form a
1343  // UInt type result. That is, first bitcast the aggregate type to a UInt.
1344  // Attempt to get the bundle types.
1345  SmallVector<FlatBundleFieldEntry> fields;
1346  if (peelType(op.getInput().getType(), fields, PreserveAggregate::None)) {
1347  size_t uptoBits = 0;
1348  // Loop over the leaf aggregates and concat each of them to get a UInt.
1349  // Bitcast the fields to handle nested aggregate types.
1350  for (const auto &field : llvm::enumerate(fields)) {
1351  auto fieldBitwidth = *getBitWidth(field.value().type);
1352  // Ignore zero width fields, like empty bundles.
1353  if (fieldBitwidth == 0)
1354  continue;
1355  Value src = getSubWhatever(op.getInput(), field.index());
1356  // The src could be an aggregate type, bitcast it to a UInt type.
1357  src = builder->createOrFold<BitCastOp>(
1358  UIntType::get(context, fieldBitwidth), src);
1359  // Take the first field, or else Cat the previous fields with this field.
1360  if (uptoBits == 0)
1361  srcLoweredVal = src;
1362  else {
1363  if (type_isa<BundleType>(op.getInput().getType())) {
1364  srcLoweredVal = builder->create<CatPrimOp>(srcLoweredVal, src);
1365  } else {
1366  srcLoweredVal = builder->create<CatPrimOp>(src, srcLoweredVal);
1367  }
1368  }
1369  // Record the total bits already accumulated.
1370  uptoBits += fieldBitwidth;
1371  }
1372  } else {
1373  srcLoweredVal = builder->createOrFold<AsUIntPrimOp>(srcLoweredVal);
1374  }
1375  // Now the input has been cast to srcLoweredVal, which is of UInt type.
1376  // If the result is an aggregate type, then use lowerProducer.
1377  if (type_isa<BundleType, FVectorType>(op.getResult().getType())) {
1378  // uptoBits is used to keep track of the bits that have been extracted.
1379  size_t uptoBits = 0;
1380  auto aggregateBits = *getBitWidth(op.getResult().getType());
1381  auto clone = [&](const FlatBundleFieldEntry &field,
1382  ArrayAttr attrs) -> Value {
1383  // All the fields must have valid bitwidth, a requirement for BitCastOp.
1384  auto fieldBits = *getBitWidth(field.type);
1385  // If empty field, then it doesnot have any use, so replace it with an
1386  // invalid op, which should be trivially removed.
1387  if (fieldBits == 0)
1388  return builder->create<InvalidValueOp>(field.type);
1389 
1390  // Assign the field to the corresponding bits from the input.
1391  // Bitcast the field, incase its an aggregate type.
1392  BitsPrimOp extractBits;
1393  if (type_isa<BundleType>(op.getResult().getType())) {
1394  extractBits = builder->create<BitsPrimOp>(
1395  srcLoweredVal, aggregateBits - uptoBits - 1,
1396  aggregateBits - uptoBits - fieldBits);
1397  } else {
1398  extractBits = builder->create<BitsPrimOp>(
1399  srcLoweredVal, uptoBits + fieldBits - 1, uptoBits);
1400  }
1401  uptoBits += fieldBits;
1402  return builder->create<BitCastOp>(field.type, extractBits);
1403  };
1404  return lowerProducer(op, clone);
1405  }
1406 
1407  // If ground type, then replace the result.
1408  if (type_isa<SIntType>(op.getType()))
1409  srcLoweredVal = builder->create<AsSIntPrimOp>(srcLoweredVal);
1410  op.getResult().replaceAllUsesWith(srcLoweredVal);
1411  return true;
1412 }
1413 
1414 bool TypeLoweringVisitor::visitExpr(RefSendOp op) {
1415  auto clone = [&](const FlatBundleFieldEntry &field,
1416  ArrayAttr attrs) -> Value {
1417  return builder->create<RefSendOp>(
1418  getSubWhatever(op.getBase(), field.index));
1419  };
1420  // Be careful re:what gets lowered, consider ref.send of non-passive
1421  // and whether we're using the ref or the base type to choose
1422  // whether this should be lowered.
1423  return lowerProducer(op, clone);
1424 }
1425 
1426 bool TypeLoweringVisitor::visitExpr(RefResolveOp op) {
1427  auto clone = [&](const FlatBundleFieldEntry &field,
1428  ArrayAttr attrs) -> Value {
1429  Value src = getSubWhatever(op.getRef(), field.index);
1430  return builder->create<RefResolveOp>(src);
1431  };
1432  // Lower according to lowering of the reference.
1433  // Particularly, preserve if rwprobe.
1434  return lowerProducer(op, clone, op.getRef().getType());
1435 }
1436 
1437 bool TypeLoweringVisitor::visitExpr(RefCastOp op) {
1438  auto clone = [&](const FlatBundleFieldEntry &field,
1439  ArrayAttr attrs) -> Value {
1440  auto input = getSubWhatever(op.getInput(), field.index);
1441  return builder->create<RefCastOp>(RefType::get(field.type,
1442  op.getType().getForceable(),
1443  op.getType().getLayer()),
1444  input);
1445  };
1446  return lowerProducer(op, clone);
1447 }
1448 
1449 bool TypeLoweringVisitor::visitDecl(InstanceOp op) {
1450  bool skip = true;
1451  SmallVector<Type, 8> resultTypes;
1452  SmallVector<int64_t, 8> endFields; // Compressed sparse row encoding
1453  auto oldPortAnno = op.getPortAnnotations();
1454  SmallVector<Direction> newDirs;
1455  SmallVector<Attribute> newNames;
1456  SmallVector<Attribute> newPortAnno;
1457  PreserveAggregate::PreserveMode mode = getPreservationModeForModule(
1458  cast<FModuleLike>(op.getReferencedOperation(symTbl)));
1459 
1460  endFields.push_back(0);
1461  for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
1462  auto srcType = type_cast<FIRRTLType>(op.getType(i));
1463 
1464  // Flatten any nested bundle types the usual way.
1465  SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
1466  if (!peelType(srcType, fieldTypes, mode)) {
1467  newDirs.push_back(op.getPortDirection(i));
1468  newNames.push_back(op.getPortName(i));
1469  resultTypes.push_back(srcType);
1470  newPortAnno.push_back(oldPortAnno[i]);
1471  } else {
1472  skip = false;
1473  auto oldName = op.getPortNameStr(i);
1474  auto oldDir = op.getPortDirection(i);
1475  // Store the flat type for the new bundle type.
1476  for (const auto &field : fieldTypes) {
1477  newDirs.push_back(direction::get((unsigned)oldDir ^ field.isOutput));
1478  newNames.push_back(builder->getStringAttr(oldName + field.suffix));
1479  resultTypes.push_back(mapLoweredType(srcType, field.type));
1480  auto annos = filterAnnotations(
1481  context, dyn_cast_or_null<ArrayAttr>(oldPortAnno[i]), srcType,
1482  field);
1483  newPortAnno.push_back(annos);
1484  }
1485  }
1486  endFields.push_back(resultTypes.size());
1487  }
1488 
1489  auto sym = getInnerSymName(op);
1490 
1491  if (skip) {
1492  return false;
1493  }
1494 
1495  // FIXME: annotation update
1496  auto newInstance = builder->create<InstanceOp>(
1497  resultTypes, op.getModuleNameAttr(), op.getNameAttr(),
1498  op.getNameKindAttr(), direction::packAttribute(context, newDirs),
1499  builder->getArrayAttr(newNames), op.getAnnotations(),
1500  builder->getArrayAttr(newPortAnno), op.getLayersAttr(),
1501  op.getLowerToBindAttr(),
1502  sym ? hw::InnerSymAttr::get(sym) : hw::InnerSymAttr());
1503 
1504  // Copy over any attributes which have not already been copied over by
1505  // arguments to the builder.
1506  auto attrNames = InstanceOp::getAttributeNames();
1507  DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1508  SmallVector<NamedAttribute> newAttrs(newInstance->getAttrs());
1509  for (auto i : llvm::make_filter_range(op->getAttrs(), [&](auto namedAttr) {
1510  return !attrSet.count(namedAttr.getName());
1511  }))
1512  newAttrs.push_back(i);
1513  newInstance->setAttrs(newAttrs);
1514 
1515  SmallVector<Value> lowered;
1516  for (size_t aggIndex = 0, eAgg = op.getNumResults(); aggIndex != eAgg;
1517  ++aggIndex) {
1518  lowered.clear();
1519  for (size_t fieldIndex = endFields[aggIndex],
1520  eField = endFields[aggIndex + 1];
1521  fieldIndex < eField; ++fieldIndex)
1522  lowered.push_back(newInstance.getResult(fieldIndex));
1523  if (lowered.size() != 1 ||
1524  op.getType(aggIndex) != resultTypes[endFields[aggIndex]])
1525  processUsers(op.getResult(aggIndex), lowered);
1526  else
1527  op.getResult(aggIndex).replaceAllUsesWith(lowered[0]);
1528  }
1529  return true;
1530 }
1531 
1532 bool TypeLoweringVisitor::visitExpr(SubaccessOp op) {
1533  auto input = op.getInput();
1534  FVectorType vType = input.getType();
1535 
1536  // Check for empty vectors
1537  if (vType.getNumElements() == 0) {
1538  Value inv = builder->create<InvalidValueOp>(vType.getElementType());
1539  op.replaceAllUsesWith(inv);
1540  return true;
1541  }
1542 
1543  // Check for constant instances
1544  if (ConstantOp arg =
1545  llvm::dyn_cast_or_null<ConstantOp>(op.getIndex().getDefiningOp())) {
1546  auto sio = builder->create<SubindexOp>(op.getInput(),
1547  arg.getValue().getExtValue());
1548  op.replaceAllUsesWith(sio.getResult());
1549  return true;
1550  }
1551 
1552  // Construct a multibit mux
1553  SmallVector<Value> inputs;
1554  inputs.reserve(vType.getNumElements());
1555  for (int index = vType.getNumElements() - 1; index >= 0; index--)
1556  inputs.push_back(builder->create<SubindexOp>(input, index));
1557 
1558  Value multibitMux = builder->create<MultibitMuxOp>(op.getIndex(), inputs);
1559  op.replaceAllUsesWith(multibitMux);
1560  return true;
1561 }
1562 
1563 bool TypeLoweringVisitor::visitExpr(VectorCreateOp op) {
1564  auto clone = [&](const FlatBundleFieldEntry &field,
1565  ArrayAttr attrs) -> Value {
1566  return op.getOperand(field.index);
1567  };
1568  return lowerProducer(op, clone);
1569 }
1570 
1571 bool TypeLoweringVisitor::visitExpr(BundleCreateOp op) {
1572  auto clone = [&](const FlatBundleFieldEntry &field,
1573  ArrayAttr attrs) -> Value {
1574  return op.getOperand(field.index);
1575  };
1576  return lowerProducer(op, clone);
1577 }
1578 
1579 bool TypeLoweringVisitor::visitExpr(ElementwiseOrPrimOp op) {
1580  auto clone = [&](const FlatBundleFieldEntry &field,
1581  ArrayAttr attrs) -> Value {
1582  Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1583  getSubWhatever(op.getRhs(), field.index)};
1584  return type_isa<BundleType, FVectorType>(field.type)
1585  ? (Value)builder->create<ElementwiseOrPrimOp>(field.type,
1586  operands)
1587  : (Value)builder->create<OrPrimOp>(operands);
1588  };
1589 
1590  return lowerProducer(op, clone);
1591 }
1592 
1593 bool TypeLoweringVisitor::visitExpr(ElementwiseAndPrimOp op) {
1594  auto clone = [&](const FlatBundleFieldEntry &field,
1595  ArrayAttr attrs) -> Value {
1596  Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1597  getSubWhatever(op.getRhs(), field.index)};
1598  return type_isa<BundleType, FVectorType>(field.type)
1599  ? (Value)builder->create<ElementwiseAndPrimOp>(field.type,
1600  operands)
1601  : (Value)builder->create<AndPrimOp>(operands);
1602  };
1603 
1604  return lowerProducer(op, clone);
1605 }
1606 
1607 bool TypeLoweringVisitor::visitExpr(ElementwiseXorPrimOp op) {
1608  auto clone = [&](const FlatBundleFieldEntry &field,
1609  ArrayAttr attrs) -> Value {
1610  Value operands[] = {getSubWhatever(op.getLhs(), field.index),
1611  getSubWhatever(op.getRhs(), field.index)};
1612  return type_isa<BundleType, FVectorType>(field.type)
1613  ? (Value)builder->create<ElementwiseXorPrimOp>(field.type,
1614  operands)
1615  : (Value)builder->create<XorPrimOp>(operands);
1616  };
1617 
1618  return lowerProducer(op, clone);
1619 }
1620 
1621 bool TypeLoweringVisitor::visitExpr(MultibitMuxOp op) {
1622  auto clone = [&](const FlatBundleFieldEntry &field,
1623  ArrayAttr attrs) -> Value {
1624  SmallVector<Value> newInputs;
1625  newInputs.reserve(op.getInputs().size());
1626  for (auto input : op.getInputs()) {
1627  auto inputSub = getSubWhatever(input, field.index);
1628  newInputs.push_back(inputSub);
1629  }
1630  return builder->create<MultibitMuxOp>(op.getIndex(), newInputs);
1631  };
1632  return lowerProducer(op, clone);
1633 }
1634 
1635 //===----------------------------------------------------------------------===//
1636 // Pass Infrastructure
1637 //===----------------------------------------------------------------------===//
1638 
1639 namespace {
1640 struct LowerTypesPass
1641  : public circt::firrtl::impl::LowerFIRRTLTypesBase<LowerTypesPass> {
1642  LowerTypesPass(
1643  circt::firrtl::PreserveAggregate::PreserveMode preserveAggregateFlag,
1644  circt::firrtl::PreserveAggregate::PreserveMode preserveMemoriesFlag) {
1645  preserveAggregate = preserveAggregateFlag;
1646  preserveMemories = preserveMemoriesFlag;
1647  }
1648  void runOnOperation() override;
1649 };
1650 } // end anonymous namespace
1651 
1652 // This is the main entrypoint for the lowering pass.
1653 void LowerTypesPass::runOnOperation() {
1654  LLVM_DEBUG(debugPassHeader(this) << "\n");
1655  std::vector<FModuleLike> ops;
1656  // Symbol Table
1657  auto &symTbl = getAnalysis<SymbolTable>();
1658  // Cached attr
1659  AttrCache cache(&getContext());
1660 
1661  DenseMap<FModuleLike, Convention> conventionTable;
1662  auto circuit = getOperation();
1663  for (auto module : circuit.getOps<FModuleLike>()) {
1664  conventionTable.insert({module, module.getConvention()});
1665  ops.push_back(module);
1666  }
1667 
1668  // This lambda, executes in parallel for each Op within the circt.
1669  auto lowerModules = [&](FModuleLike op) -> LogicalResult {
1670  auto tl =
1671  TypeLoweringVisitor(&getContext(), preserveAggregate, preserveMemories,
1672  symTbl, cache, conventionTable);
1673  tl.lowerModule(op);
1674 
1675  return LogicalResult::failure(tl.isFailed());
1676  };
1677 
1678  auto result = failableParallelForEach(&getContext(), ops, lowerModules);
1679 
1680  if (failed(result))
1681  signalPassFailure();
1682 }
1683 
1684 /// This is the pass constructor.
1685 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerFIRRTLTypesPass(
1687  PreserveAggregate::PreserveMode memoryMode) {
1688  return std::make_unique<LowerTypesPass>(mode, memoryMode);
1689 }
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:55
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
Definition: CalyxOps.cpp:59
@ 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:25
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.