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