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