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