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