CIRCT  20.0.0git
InferResets.cpp
Go to the documentation of this file.
1 //===- InferResets.cpp - Infer resets and add full reset --------*- 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 InferResets pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
19 #include "circt/Support/Debug.h"
20 #include "circt/Support/FieldRef.h"
23 #include "mlir/IR/Dominance.h"
24 #include "mlir/IR/ImplicitLocOpBuilder.h"
25 #include "mlir/IR/Threading.h"
26 #include "mlir/Pass/Pass.h"
27 #include "llvm/ADT/EquivalenceClasses.h"
28 #include "llvm/ADT/SetVector.h"
29 #include "llvm/ADT/TinyPtrVector.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/Debug.h"
32 
33 #define DEBUG_TYPE "infer-resets"
34 
35 namespace circt {
36 namespace firrtl {
37 #define GEN_PASS_DEF_INFERRESETS
38 #include "circt/Dialect/FIRRTL/Passes.h.inc"
39 } // namespace firrtl
40 } // namespace circt
41 
42 using circt::igraph::InstanceOpInterface;
45 using llvm::BumpPtrAllocator;
46 using llvm::MapVector;
47 using llvm::SmallDenseSet;
48 using llvm::SmallSetVector;
49 using mlir::FailureOr;
50 using mlir::InferTypeOpInterface;
51 using mlir::WalkOrder;
52 
53 using namespace circt;
54 using namespace firrtl;
55 
56 //===----------------------------------------------------------------------===//
57 // Utilities
58 //===----------------------------------------------------------------------===//
59 
60 namespace {
61 /// A reset domain.
62 struct ResetDomain {
63  /// Whether this is the root of the reset domain.
64  bool isTop = false;
65  /// The reset signal for this domain. A null value indicates that this domain
66  /// explicitly has no reset.
67  Value reset;
68 
69  // Implementation details for this domain.
70  Value existingValue;
71  std::optional<unsigned> existingPort;
72  StringAttr newPortName;
73 
74  ResetDomain(Value reset) : reset(reset) {}
75 };
76 } // namespace
77 
78 inline bool operator==(const ResetDomain &a, const ResetDomain &b) {
79  return (a.isTop == b.isTop && a.reset == b.reset);
80 }
81 inline bool operator!=(const ResetDomain &a, const ResetDomain &b) {
82  return !(a == b);
83 }
84 
85 /// Return the name and parent module of a reset. The reset value must either be
86 /// a module port or a wire/node operation.
87 static std::pair<StringAttr, FModuleOp> getResetNameAndModule(Value reset) {
88  if (auto arg = dyn_cast<BlockArgument>(reset)) {
89  auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
90  return {module.getPortNameAttr(arg.getArgNumber()), module};
91  } else {
92  auto op = reset.getDefiningOp();
93  return {op->getAttrOfType<StringAttr>("name"),
94  op->getParentOfType<FModuleOp>()};
95  }
96 }
97 
98 /// Return the name of a reset. The reset value must either be a module port or
99 /// a wire/node operation.
100 static inline StringAttr getResetName(Value reset) {
101  return getResetNameAndModule(reset).first;
102 }
103 
104 /// Construct a zero value of the given type using the given builder.
105 static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type,
107  // The zero value's type is a const version of `type`.
108  type = type.getConstType(true);
109  auto it = cache.find(type);
110  if (it != cache.end())
111  return it->second;
112  auto nullBit = [&]() {
113  return createZeroValue(
114  builder, UIntType::get(builder.getContext(), 1, /*isConst=*/true),
115  cache);
116  };
117  auto value =
119  .Case<ClockType>([&](auto type) {
120  return builder.create<AsClockPrimOp>(nullBit());
121  })
122  .Case<AsyncResetType>([&](auto type) {
123  return builder.create<AsAsyncResetPrimOp>(nullBit());
124  })
125  .Case<SIntType, UIntType>([&](auto type) {
126  return builder.create<ConstantOp>(
127  type, APInt::getZero(type.getWidth().value_or(1)));
128  })
129  .Case<BundleType>([&](auto type) {
130  auto wireOp = builder.create<WireOp>(type);
131  for (unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
132  auto fieldType = type.getElementTypePreservingConst(i);
133  auto zero = createZeroValue(builder, fieldType, cache);
134  auto acc =
135  builder.create<SubfieldOp>(fieldType, wireOp.getResult(), i);
136  emitConnect(builder, acc, zero);
137  }
138  return wireOp.getResult();
139  })
140  .Case<FVectorType>([&](auto type) {
141  auto wireOp = builder.create<WireOp>(type);
142  auto zero = createZeroValue(
143  builder, type.getElementTypePreservingConst(), cache);
144  for (unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
145  auto acc = builder.create<SubindexOp>(zero.getType(),
146  wireOp.getResult(), i);
147  emitConnect(builder, acc, zero);
148  }
149  return wireOp.getResult();
150  })
151  .Case<ResetType, AnalogType>(
152  [&](auto type) { return builder.create<InvalidValueOp>(type); })
153  .Default([](auto) {
154  llvm_unreachable("switch handles all types");
155  return Value{};
156  });
157  cache.insert({type, value});
158  return value;
159 }
160 
161 /// Construct a null value of the given type using the given builder.
162 static Value createZeroValue(ImplicitLocOpBuilder &builder,
163  FIRRTLBaseType type) {
165  return createZeroValue(builder, type, cache);
166 }
167 
168 /// Helper function that inserts reset multiplexer into all `ConnectOp`s
169 /// with the given target. Looks through `SubfieldOp`, `SubindexOp`,
170 /// and `SubaccessOp`, and inserts multiplexers into connects to
171 /// these subaccesses as well. Modifies the insertion location of the builder.
172 /// Returns true if the `resetValue` was used in any way, false otherwise.
173 static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target,
174  Value reset, Value resetValue) {
175  // Indicates whether the `resetValue` was assigned to in some way. We use this
176  // to erase unused subfield/subindex/subaccess ops on the reset value if they
177  // end up unused.
178  bool resetValueUsed = false;
179 
180  for (auto &use : target.getUses()) {
181  Operation *useOp = use.getOwner();
182  builder.setInsertionPoint(useOp);
183  TypeSwitch<Operation *>(useOp)
184  // Insert a mux on the value connected to the target:
185  // connect(dst, src) -> connect(dst, mux(reset, resetValue, src))
186  .Case<ConnectOp, MatchingConnectOp>([&](auto op) {
187  if (op.getDest() != target)
188  return;
189  LLVM_DEBUG(llvm::dbgs() << " - Insert mux into " << op << "\n");
190  auto muxOp =
191  builder.create<MuxPrimOp>(reset, resetValue, op.getSrc());
192  op.getSrcMutable().assign(muxOp);
193  resetValueUsed = true;
194  })
195  // Look through subfields.
196  .Case<SubfieldOp>([&](auto op) {
197  auto resetSubValue =
198  builder.create<SubfieldOp>(resetValue, op.getFieldIndexAttr());
199  if (insertResetMux(builder, op, reset, resetSubValue))
200  resetValueUsed = true;
201  else
202  resetSubValue.erase();
203  })
204  // Look through subindices.
205  .Case<SubindexOp>([&](auto op) {
206  auto resetSubValue =
207  builder.create<SubindexOp>(resetValue, op.getIndexAttr());
208  if (insertResetMux(builder, op, reset, resetSubValue))
209  resetValueUsed = true;
210  else
211  resetSubValue.erase();
212  })
213  // Look through subaccesses.
214  .Case<SubaccessOp>([&](auto op) {
215  if (op.getInput() != target)
216  return;
217  auto resetSubValue =
218  builder.create<SubaccessOp>(resetValue, op.getIndex());
219  if (insertResetMux(builder, op, reset, resetSubValue))
220  resetValueUsed = true;
221  else
222  resetSubValue.erase();
223  });
224  }
225  return resetValueUsed;
226 }
227 
228 //===----------------------------------------------------------------------===//
229 // Reset Network
230 //===----------------------------------------------------------------------===//
231 
232 namespace {
233 
234 /// A reset signal.
235 ///
236 /// This essentially combines the exact `FieldRef` of the signal in question
237 /// with a type to be used for error reporting and inferring the reset kind.
238 struct ResetSignal {
239  ResetSignal(FieldRef field, FIRRTLBaseType type) : field(field), type(type) {}
240  bool operator<(const ResetSignal &other) const { return field < other.field; }
241  bool operator==(const ResetSignal &other) const {
242  return field == other.field;
243  }
244  bool operator!=(const ResetSignal &other) const { return !(*this == other); }
245 
246  FieldRef field;
247  FIRRTLBaseType type;
248 };
249 
250 /// A connection made to or from a reset network.
251 ///
252 /// These drives are tracked for each reset network, and are used for error
253 /// reporting to the user.
254 struct ResetDrive {
255  /// What's being driven.
256  ResetSignal dst;
257  /// What's driving.
258  ResetSignal src;
259  /// The location to use for diagnostics.
260  Location loc;
261 };
262 
263 /// A list of connections to a reset network.
264 using ResetDrives = SmallVector<ResetDrive, 1>;
265 
266 /// All signals connected together into a reset network.
267 using ResetNetwork = llvm::iterator_range<
268  llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
269 
270 /// Whether a reset is sync or async.
271 enum class ResetKind { Async, Sync };
272 
273 static StringRef resetKindToStringRef(const ResetKind &kind) {
274  switch (kind) {
275  case ResetKind::Async:
276  return "async";
277  case ResetKind::Sync:
278  return "sync";
279  }
280  llvm_unreachable("unhandled reset kind");
281 }
282 } // namespace
283 
284 namespace llvm {
285 template <>
286 struct DenseMapInfo<ResetSignal> {
287  static inline ResetSignal getEmptyKey() {
288  return ResetSignal{DenseMapInfo<FieldRef>::getEmptyKey(), {}};
289  }
290  static inline ResetSignal getTombstoneKey() {
291  return ResetSignal{DenseMapInfo<FieldRef>::getTombstoneKey(), {}};
292  }
293  static unsigned getHashValue(const ResetSignal &x) {
294  return circt::hash_value(x.field);
295  }
296  static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs) {
297  return lhs == rhs;
298  }
299 };
300 } // namespace llvm
301 
302 template <typename T>
303 static T &operator<<(T &os, const ResetKind &kind) {
304  switch (kind) {
305  case ResetKind::Async:
306  return os << "async";
307  case ResetKind::Sync:
308  return os << "sync";
309  }
310  return os;
311 }
312 
313 //===----------------------------------------------------------------------===//
314 // Pass Infrastructure
315 //===----------------------------------------------------------------------===//
316 
317 namespace {
318 /// Infer concrete reset types and insert full reset.
319 ///
320 /// This pass replaces `reset` types in the IR with a concrete `asyncreset` or
321 /// `uint<1>` depending on how the reset is used, and adds resets to registers
322 /// in modules marked with the corresponding `FullResetAnnotation`.
323 ///
324 /// On a high level, the first stage of the pass that deals with reset inference
325 /// operates as follows:
326 ///
327 /// 1. Build a global graph of the resets in the design by tracing reset signals
328 /// through instances. This uses the `ResetNetwork` utilities and boils down
329 /// to finding groups of values in the IR that are part of the same reset
330 /// network (i.e., somehow attached together through ports, wires, instances,
331 /// and connects). We use LLVM's `EquivalenceClasses` data structure to do
332 /// this efficiently.
333 ///
334 /// 2. Infer the type of each reset network found in step 1 by looking at the
335 /// type of values connected to the network. This results in the network
336 /// being declared a sync (`uint<1>`) or async (`asyncreset`) network. If the
337 /// reset is never driven by a concrete type, an error is emitted.
338 ///
339 /// 3. Walk the IR and update the type of wires and ports with the reset types
340 /// found in step 2. This will replace all `reset` types in the IR with
341 /// a concrete type.
342 ///
343 /// The second stage that deals with the addition of full resets operates as
344 /// follows:
345 ///
346 /// 4. Visit every module in the design and determine if it has an explicit
347 /// reset annotated. Ports of and wires in the module can have a
348 /// `FullResetAnnotation`, which marks that port or wire as the reset for
349 /// the module. A module may also carry a `ExcludeFromFullResetAnnotation`,
350 /// which marks it as being explicitly not in a reset domain. These
351 /// annotations are sparse; it is very much possible that just the top-level
352 /// module in the design has a full reset annotation. A module can only
353 /// ever carry one of these annotations, which puts it into one of three
354 /// categories from a full reset inference perspective:
355 ///
356 /// a. unambiguously marks a port or wire as the module's full reset
357 /// b. explicitly marks it as not to have any full resets added
358 /// c. inherit reset
359 ///
360 /// 5. For every module in the design, determine the full full reset domain it
361 /// is in. Note that this very narrowly deals with the inference of a
362 /// "default" full reset, which basically goes through the IR and attaches
363 /// all non-reset registers to a default full reset signal. If a module
364 /// carries one of the annotations mentioned in (4), the annotated port or
365 /// wire is used as its reset domain. Otherwise, it inherits the reset domain
366 /// from parent modules. This conceptually involves looking at all the places
367 /// where a module is instantiated, and recursively determining the reset
368 /// domain at the instantiation site. A module can only ever be in one reset
369 /// domain. In case it is inferred to lie in multiple ones, e.g., if it is
370 /// instantiated in different reset domains, an error is emitted. If
371 /// successful, every module is associated with a reset signal, either one of
372 /// its local ports or wires, or a port or wire within one of its parent
373 /// modules.
374 ///
375 /// 6. For every module in the design, determine how full resets shall be
376 /// implemented. This step handles the following distinct cases:
377 ///
378 /// a. Skip a module because it is marked as having no reset domain.
379 /// b. Use a port or wire in the module itself as reset. This is possible
380 /// if the module is at the "top" of its reset domain, which means that
381 /// it itself carried a reset annotation, and the reset value is either
382 /// a port or wire of the module itself.
383 /// c. Route a parent module's reset through a module port and use that
384 /// port as the reset. This happens if the module is *not* at the "top"
385 /// of its reset domain, but rather refers to a value in a parent module
386 /// as its reset.
387 ///
388 /// As a result, a module's reset domain is annotated with the existing local
389 /// value to reuse (port or wire), the index of an existing port to reuse,
390 /// and the name of an additional port to insert into its port list.
391 ///
392 /// 7. For every module in the design, full resets are implemented. This
393 /// determines the local value to use as the reset signal and updates the
394 /// `reg` and `regreset` operations in the design. If the register already
395 /// has an async reset, or if the type of the full reset is sync, the
396 /// register's reset is left unchanged. If it has a sync reset and the full
397 /// reset is async, the sync reset is moved into a `mux` operation on all
398 /// `connect`s to the register (which the Scala code base called the
399 /// `RemoveResets` pass). Finally the register is replaced with a `regreset`
400 /// operation, with the reset signal determined earlier, and a "zero" value
401 /// constructed for the register's type.
402 ///
403 /// Determining the local reset value is trivial if step 6 found a module to
404 /// be of case a or b. Case c is the non-trivial one, because it requires
405 /// modifying the port list of the module. This is done by first determining
406 /// the name of the reset signal in the parent module, which is either the
407 /// name of the port or wire declaration. We then look for an existing
408 /// port of the same type in the port list and reuse that as reset. If no
409 /// port with that name was found, or the existing port is of the wrong type,
410 /// a new port is inserted into the port list.
411 ///
412 /// TODO: This logic is *very* brittle and error-prone. It may make sense to
413 /// just add an additional port for the inferred reset in any case, with an
414 /// optimization to use an existing port if all of the module's
415 /// instantiations have that port connected to the desired signal already.
416 ///
417 struct InferResetsPass
418  : public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
419  void runOnOperation() override;
420  void runOnOperationInner();
421 
422  // Copy creates a new empty pass (because ResetMap has no copy constructor).
423  using InferResetsBase::InferResetsBase;
424  InferResetsPass(const InferResetsPass &other) : InferResetsBase(other) {}
425 
426  //===--------------------------------------------------------------------===//
427  // Reset type inference
428 
429  void traceResets(CircuitOp circuit);
430  void traceResets(InstanceOp inst);
431  void traceResets(Value dst, Value src, Location loc);
432  void traceResets(Value value);
433  void traceResets(Type dstType, Value dst, unsigned dstID, Type srcType,
434  Value src, unsigned srcID, Location loc);
435 
436  LogicalResult inferAndUpdateResets();
437  FailureOr<ResetKind> inferReset(ResetNetwork net);
438  LogicalResult updateReset(ResetNetwork net, ResetKind kind);
439  bool updateReset(FieldRef field, FIRRTLBaseType resetType);
440 
441  //===--------------------------------------------------------------------===//
442  // Full reset implementation
443 
444  LogicalResult collectAnnos(CircuitOp circuit);
445  // Collect reset annotations in the module and return a reset signal.
446  // Return `failure()` if there was an error in the annotation processing.
447  // Return `std::nullopt` if there was no reset annotation.
448  // Return `nullptr` if there was `ignore` annotation.
449  // Return a non-null Value if the reset was actually provided.
450  FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
451 
452  LogicalResult buildDomains(CircuitOp circuit);
453  void buildDomains(FModuleOp module, const InstancePath &instPath,
454  Value parentReset, InstanceGraph &instGraph,
455  unsigned indent = 0);
456 
457  void determineImpl();
458  void determineImpl(FModuleOp module, ResetDomain &domain);
459 
460  LogicalResult implementFullReset();
461  LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
462  void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
463 
464  LogicalResult verifyNoAbstractReset();
465 
466  //===--------------------------------------------------------------------===//
467  // Utilities
468 
469  /// Get the reset network a signal belongs to.
470  ResetNetwork getResetNetwork(ResetSignal signal) {
471  return llvm::make_range(resetClasses.findLeader(signal),
472  resetClasses.member_end());
473  }
474 
475  /// Get the drives of a reset network.
476  ResetDrives &getResetDrives(ResetNetwork net) {
477  return resetDrives[*net.begin()];
478  }
479 
480  /// Guess the root node of a reset network, such that we have something for
481  /// the user to make sense of.
482  ResetSignal guessRoot(ResetNetwork net);
483  ResetSignal guessRoot(ResetSignal signal) {
484  return guessRoot(getResetNetwork(signal));
485  }
486 
487  //===--------------------------------------------------------------------===//
488  // Analysis data
489 
490  /// A map of all traced reset networks in the circuit.
491  llvm::EquivalenceClasses<ResetSignal> resetClasses;
492 
493  /// A map of all connects to and from a reset.
494  DenseMap<ResetSignal, ResetDrives> resetDrives;
495 
496  /// The annotated reset for a module. A null value indicates that the module
497  /// is explicitly annotated with `ignore`. Otherwise the port/wire/node
498  /// annotated as reset within the module is stored.
499  DenseMap<Operation *, Value> annotatedResets;
500 
501  /// The reset domain for a module. In case of conflicting domain membership,
502  /// the vector for a module contains multiple elements.
503  MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
504  domains;
505 
506  /// Cache of modules symbols
507  InstanceGraph *instanceGraph;
508 
509  /// Cache of instance paths.
510  std::unique_ptr<InstancePathCache> instancePathCache;
511 };
512 } // namespace
513 
514 void InferResetsPass::runOnOperation() {
515  runOnOperationInner();
516  resetClasses = llvm::EquivalenceClasses<ResetSignal>();
517  resetDrives.clear();
518  annotatedResets.clear();
519  domains.clear();
520  instancePathCache.reset(nullptr);
521  markAnalysesPreserved<InstanceGraph>();
522 }
523 
524 void InferResetsPass::runOnOperationInner() {
525  instanceGraph = &getAnalysis<InstanceGraph>();
526  instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
527 
528  // Trace the uninferred reset networks throughout the design.
529  traceResets(getOperation());
530 
531  // Infer the type of the traced resets and update the IR.
532  if (failed(inferAndUpdateResets()))
533  return signalPassFailure();
534 
535  // Gather the reset annotations throughout the modules.
536  if (failed(collectAnnos(getOperation())))
537  return signalPassFailure();
538 
539  // Build the reset domains in the design.
540  if (failed(buildDomains(getOperation())))
541  return signalPassFailure();
542 
543  // Determine how each reset shall be implemented.
544  determineImpl();
545 
546  // Implement the full resets.
547  if (failed(implementFullReset()))
548  return signalPassFailure();
549 
550  // Require that no Abstract Resets exist on ports in the design.
551  if (failed(verifyNoAbstractReset()))
552  return signalPassFailure();
553 }
554 
555 std::unique_ptr<mlir::Pass> circt::firrtl::createInferResetsPass() {
556  return std::make_unique<InferResetsPass>();
557 }
558 
559 ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
560  ResetDrives &drives = getResetDrives(net);
561  ResetSignal bestSignal = *net.begin();
562  unsigned bestNumDrives = -1;
563 
564  for (auto signal : net) {
565  // Don't consider `invalidvalue` for reporting as a root.
566  if (isa_and_nonnull<InvalidValueOp>(
567  signal.field.getValue().getDefiningOp()))
568  continue;
569 
570  // Count the number of times this particular signal in the reset network is
571  // assigned to.
572  unsigned numDrives = 0;
573  for (auto &drive : drives)
574  if (drive.dst == signal)
575  ++numDrives;
576 
577  // Keep track of the signal with the lowest number of assigns. These tend to
578  // be the signals further up the reset tree. This will usually resolve to
579  // the root of the reset tree far up in the design hierarchy.
580  if (numDrives < bestNumDrives) {
581  bestNumDrives = numDrives;
582  bestSignal = signal;
583  }
584  }
585  return bestSignal;
586 }
587 
588 //===----------------------------------------------------------------------===//
589 // Custom Field IDs
590 //===----------------------------------------------------------------------===//
591 
592 // The following functions implement custom field IDs specifically for the use
593 // in reset inference. They look much more like tracking fields on types than
594 // individual values. For example, vectors don't carry separate IDs for each of
595 // their elements. Instead they have one set of IDs for the entire vector, since
596 // the element type is uniform across all elements.
597 
598 static unsigned getMaxFieldID(FIRRTLBaseType type) {
600  .Case<BundleType>([](auto type) {
601  unsigned id = 0;
602  for (auto e : type.getElements())
603  id += getMaxFieldID(e.type) + 1;
604  return id;
605  })
606  .Case<FVectorType>(
607  [](auto type) { return getMaxFieldID(type.getElementType()) + 1; })
608  .Default([](auto) { return 0; });
609 }
610 
611 static unsigned getFieldID(BundleType type, unsigned index) {
612  assert(index < type.getNumElements());
613  unsigned id = 1;
614  for (unsigned i = 0; i < index; ++i)
615  id += getMaxFieldID(type.getElementType(i)) + 1;
616  return id;
617 }
618 
619 static unsigned getFieldID(FVectorType type) { return 1; }
620 
621 static unsigned getIndexForFieldID(BundleType type, unsigned fieldID) {
622  assert(type.getNumElements() && "Bundle must have >0 fields");
623  --fieldID;
624  for (const auto &e : llvm::enumerate(type.getElements())) {
625  auto numSubfields = getMaxFieldID(e.value().type) + 1;
626  if (fieldID < numSubfields)
627  return e.index();
628  fieldID -= numSubfields;
629  }
630  assert(false && "field id outside bundle");
631  return 0;
632 }
633 
634 // If a field is pointing to a child of a zero-length vector, it is useless.
635 static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID) {
636  if (oldType.isGround()) {
637  assert(fieldID == 0);
638  return false;
639  }
640 
641  // If this is a bundle type, recurse.
642  if (auto bundleType = type_dyn_cast<BundleType>(oldType)) {
643  unsigned index = getIndexForFieldID(bundleType, fieldID);
644  return isUselessVec(bundleType.getElementType(index),
645  fieldID - getFieldID(bundleType, index));
646  }
647 
648  // If this is a vector type, check if it is zero length. Anything in a
649  // zero-length vector is useless.
650  if (auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
651  if (vectorType.getNumElements() == 0)
652  return true;
653  return isUselessVec(vectorType.getElementType(),
654  fieldID - getFieldID(vectorType));
655  }
656 
657  return false;
658 }
659 
660 // If a field is pointing to a child of a zero-length vector, it is useless.
661 static bool isUselessVec(FieldRef field) {
662  return isUselessVec(
663  getBaseType(type_cast<FIRRTLType>(field.getValue().getType())),
664  field.getFieldID());
665 }
666 
667 static bool getDeclName(Value value, SmallString<32> &string) {
668  if (auto arg = dyn_cast<BlockArgument>(value)) {
669  auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
670  string += module.getPortName(arg.getArgNumber());
671  return true;
672  }
673 
674  auto *op = value.getDefiningOp();
675  return TypeSwitch<Operation *, bool>(op)
676  .Case<InstanceOp, MemOp>([&](auto op) {
677  string += op.getName();
678  string += ".";
679  string +=
680  op.getPortName(cast<OpResult>(value).getResultNumber()).getValue();
681  return true;
682  })
683  .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](auto op) {
684  string += op.getName();
685  return true;
686  })
687  .Default([](auto) { return false; });
688 }
689 
690 static bool getFieldName(const FieldRef &fieldRef, SmallString<32> &string) {
691  SmallString<64> name;
692  auto value = fieldRef.getValue();
693  if (!getDeclName(value, string))
694  return false;
695 
696  auto type = value.getType();
697  auto localID = fieldRef.getFieldID();
698  while (localID) {
699  if (auto bundleType = type_dyn_cast<BundleType>(type)) {
700  auto index = getIndexForFieldID(bundleType, localID);
701  // Add the current field string, and recurse into a subfield.
702  auto &element = bundleType.getElements()[index];
703  if (!string.empty())
704  string += ".";
705  string += element.name.getValue();
706  // Recurse in to the element type.
707  type = element.type;
708  localID = localID - getFieldID(bundleType, index);
709  } else if (auto vecType = type_dyn_cast<FVectorType>(type)) {
710  string += "[]";
711  // Recurse in to the element type.
712  type = vecType.getElementType();
713  localID = localID - getFieldID(vecType);
714  } else {
715  // If we reach here, the field ref is pointing inside some aggregate type
716  // that isn't a bundle or a vector. If the type is a ground type, then the
717  // localID should be 0 at this point, and we should have broken from the
718  // loop.
719  llvm_unreachable("unsupported type");
720  }
721  }
722  return true;
723 }
724 
725 //===----------------------------------------------------------------------===//
726 // Reset Tracing
727 //===----------------------------------------------------------------------===//
728 
729 /// Check whether a type contains a `ResetType`.
730 static bool typeContainsReset(Type type) {
731  return TypeSwitch<Type, bool>(type)
732  .Case<FIRRTLType>([](auto type) {
733  return type.getRecursiveTypeProperties().hasUninferredReset;
734  })
735  .Default([](auto) { return false; });
736 }
737 
738 /// Iterate over a circuit and follow all signals with `ResetType`, aggregating
739 /// them into reset nets. After this function returns, the `resetMap` is
740 /// populated with the reset networks in the circuit, alongside information on
741 /// drivers and their types that contribute to the reset.
742 void InferResetsPass::traceResets(CircuitOp circuit) {
743  LLVM_DEBUG({
744  llvm::dbgs() << "\n";
745  debugHeader("Tracing uninferred resets") << "\n\n";
746  });
747 
748  SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
749 
750  for (auto module : circuit.getOps<FModuleOp>())
751  moduleToOps.push_back({module, {}});
752 
753  hw::InnerRefNamespace irn{getAnalysis<SymbolTable>(),
754  getAnalysis<hw::InnerSymbolTableCollection>()};
755 
756  mlir::parallelForEach(circuit.getContext(), moduleToOps, [](auto &e) {
757  e.first.walk([&](Operation *op) {
758  // We are only interested in operations which are related to abstract
759  // reset.
760  if (llvm::any_of(
761  op->getResultTypes(),
762  [](mlir::Type type) { return typeContainsReset(type); }) ||
763  llvm::any_of(op->getOperandTypes(), typeContainsReset))
764  e.second.push_back(op);
765  });
766  });
767 
768  for (auto &[_, ops] : moduleToOps)
769  for (auto *op : ops) {
770  TypeSwitch<Operation *>(op)
771  .Case<FConnectLike>([&](auto op) {
772  traceResets(op.getDest(), op.getSrc(), op.getLoc());
773  })
774  .Case<InstanceOp>([&](auto op) { traceResets(op); })
775  .Case<RefSendOp>([&](auto op) {
776  // Trace using base types.
777  traceResets(op.getType().getType(), op.getResult(), 0,
778  op.getBase().getType().getPassiveType(), op.getBase(),
779  0, op.getLoc());
780  })
781  .Case<RefResolveOp>([&](auto op) {
782  // Trace using base types.
783  traceResets(op.getType(), op.getResult(), 0,
784  op.getRef().getType().getType(), op.getRef(), 0,
785  op.getLoc());
786  })
787  .Case<Forceable>([&](Forceable op) {
788  if (auto node = dyn_cast<NodeOp>(op.getOperation()))
789  traceResets(node.getResult(), node.getInput(), node.getLoc());
790  // Trace reset into rwprobe. Avoid invalid IR.
791  if (op.isForceable())
792  traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
793  op.getDataRef(), 0, op.getLoc());
794  })
795  .Case<RWProbeOp>([&](RWProbeOp op) {
796  auto ist = irn.lookup(op.getTarget());
797  assert(ist);
798  auto ref = getFieldRefForTarget(ist);
799  auto baseType = op.getType().getType();
800  traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
801  ref.getValue(), ref.getFieldID(), op.getLoc());
802  })
803  .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](auto op) {
804  traceResets(op.getResult(), op.getInput(), op.getLoc());
805  })
806  .Case<InvalidValueOp>([&](auto op) {
807  // Uniquify `InvalidValueOp`s that are contributing to multiple
808  // reset networks. These are tricky to handle because passes
809  // like CSE will generally ensure that there is only a single
810  // `InvalidValueOp` per type. However, a `reset` invalid value
811  // may be connected to two reset networks that end up being
812  // inferred as `asyncreset` and `uint<1>`. In that case, we need
813  // a distinct `InvalidValueOp` for each reset network in order
814  // to assign it the correct type.
815  auto type = op.getType();
816  if (!typeContainsReset(type) || op->hasOneUse() || op->use_empty())
817  return;
818  LLVM_DEBUG(llvm::dbgs() << "Uniquify " << op << "\n");
819  ImplicitLocOpBuilder builder(op->getLoc(), op);
820  for (auto &use :
821  llvm::make_early_inc_range(llvm::drop_begin(op->getUses()))) {
822  // - `make_early_inc_range` since `getUses()` is invalidated
823  // upon
824  // `use.set(...)`.
825  // - `drop_begin` such that the first use can keep the
826  // original op.
827  auto newOp = builder.create<InvalidValueOp>(type);
828  use.set(newOp);
829  }
830  })
831 
832  .Case<SubfieldOp>([&](auto op) {
833  // Associate the input bundle's resets with the output field's
834  // resets.
835  BundleType bundleType = op.getInput().getType();
836  auto index = op.getFieldIndex();
837  traceResets(op.getType(), op.getResult(), 0,
838  bundleType.getElements()[index].type, op.getInput(),
839  getFieldID(bundleType, index), op.getLoc());
840  })
841 
842  .Case<SubindexOp, SubaccessOp>([&](auto op) {
843  // Associate the input vector's resets with the output field's
844  // resets.
845  //
846  // This collapses all elements in vectors into one shared
847  // element which will ensure that reset inference provides a
848  // uniform result for all elements.
849  //
850  // CAVEAT: This may infer reset networks that are too big, since
851  // unrelated resets in the same vector end up looking as if they
852  // were connected. However for the sake of type inference, this
853  // is indistinguishable from them having to share the same type
854  // (namely the vector element type).
855  FVectorType vectorType = op.getInput().getType();
856  traceResets(op.getType(), op.getResult(), 0,
857  vectorType.getElementType(), op.getInput(),
858  getFieldID(vectorType), op.getLoc());
859  })
860 
861  .Case<RefSubOp>([&](RefSubOp op) {
862  // Trace through ref.sub.
863  auto aggType = op.getInput().getType().getType();
864  uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
865  .Case<FVectorType>([](auto type) {
866  return getFieldID(type);
867  })
868  .Case<BundleType>([&](auto type) {
869  return getFieldID(type, op.getIndex());
870  });
871  traceResets(op.getType(), op.getResult(), 0,
872  op.getResult().getType(), op.getInput(), fieldID,
873  op.getLoc());
874  });
875  }
876 }
877 
878 /// Trace reset signals through an instance. This essentially associates the
879 /// instance's port values with the target module's port values.
880 void InferResetsPass::traceResets(InstanceOp inst) {
881  // Lookup the referenced module. Nothing to do if its an extmodule.
882  auto module = inst.getReferencedModule<FModuleOp>(*instanceGraph);
883  if (!module)
884  return;
885  LLVM_DEBUG(llvm::dbgs() << "Visiting instance " << inst.getName() << "\n");
886 
887  // Establish a connection between the instance ports and module ports.
888  for (const auto &it : llvm::enumerate(inst.getResults())) {
889  auto dir = module.getPortDirection(it.index());
890  Value dstPort = module.getArgument(it.index());
891  Value srcPort = it.value();
892  if (dir == Direction::Out)
893  std::swap(dstPort, srcPort);
894  traceResets(dstPort, srcPort, it.value().getLoc());
895  }
896 }
897 
898 /// Analyze a connect of one (possibly aggregate) value to another.
899 /// Each drive involving a `ResetType` is recorded.
900 void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
901  // Analyze the actual connection.
902  traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
903 }
904 
905 /// Analyze a connect of one (possibly aggregate) value to another.
906 /// Each drive involving a `ResetType` is recorded.
907 void InferResetsPass::traceResets(Type dstType, Value dst, unsigned dstID,
908  Type srcType, Value src, unsigned srcID,
909  Location loc) {
910  if (auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
911  auto srcBundle = type_cast<BundleType>(srcType);
912  for (unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
913  ++dstIdx) {
914  auto dstField = dstBundle.getElements()[dstIdx].name;
915  auto srcIdx = srcBundle.getElementIndex(dstField);
916  if (!srcIdx)
917  continue;
918  auto &dstElt = dstBundle.getElements()[dstIdx];
919  auto &srcElt = srcBundle.getElements()[*srcIdx];
920  if (dstElt.isFlip) {
921  traceResets(srcElt.type, src, srcID + getFieldID(srcBundle, *srcIdx),
922  dstElt.type, dst, dstID + getFieldID(dstBundle, dstIdx),
923  loc);
924  } else {
925  traceResets(dstElt.type, dst, dstID + getFieldID(dstBundle, dstIdx),
926  srcElt.type, src, srcID + getFieldID(srcBundle, *srcIdx),
927  loc);
928  }
929  }
930  return;
931  }
932 
933  if (auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
934  auto srcVector = type_cast<FVectorType>(srcType);
935  auto srcElType = srcVector.getElementType();
936  auto dstElType = dstVector.getElementType();
937  // Collapse all elements into one shared element. See comment in traceResets
938  // above for some context. Note that we are directly passing on the field ID
939  // of the vector itself as a stand-in for its element type. This is not
940  // really what `FieldRef` is designed to do, but tends to work since all the
941  // places that need to reason about the resulting weird IDs are inside this
942  // file. Normally you would pick a specific index from the vector, which
943  // would also move the field ID forward by some amount. However, we can't
944  // distinguish individual elements for the sake of type inference *and* we
945  // have to support zero-length vectors for which the only available ID is
946  // the vector itself. Therefore we always just pick the vector itself for
947  // the field ID and make sure in `updateType` that we handle vectors
948  // accordingly.
949  traceResets(dstElType, dst, dstID + getFieldID(dstVector), srcElType, src,
950  srcID + getFieldID(srcVector), loc);
951  return;
952  }
953 
954  // Handle connecting ref's. Other uses trace using base type.
955  if (auto dstRef = type_dyn_cast<RefType>(dstType)) {
956  auto srcRef = type_cast<RefType>(srcType);
957  return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
958  srcID, loc);
959  }
960 
961  // Handle reset connections.
962  auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
963  auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
964  if (!dstBase || !srcBase)
965  return;
966  if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
967  return;
968 
969  FieldRef dstField(dst, dstID);
970  FieldRef srcField(src, srcID);
971  LLVM_DEBUG(llvm::dbgs() << "Visiting driver '" << dstField << "' = '"
972  << srcField << "' (" << dstType << " = " << srcType
973  << ")\n");
974 
975  // Determine the leaders for the dst and src reset networks before we make
976  // the connection. This will allow us to later detect if dst got merged
977  // into src, or src into dst.
978  ResetSignal dstLeader =
979  *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
980  ResetSignal srcLeader =
981  *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
982 
983  // Unify the two reset networks.
984  ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
985  assert(unionLeader == dstLeader || unionLeader == srcLeader);
986 
987  // If dst got merged into src, append dst's drives to src's, or vice
988  // versa. Also, remove dst's or src's entry in resetDrives, because they
989  // will never come up as a leader again.
990  if (dstLeader != srcLeader) {
991  auto &unionDrives = resetDrives[unionLeader]; // needed before finds
992  auto mergedDrivesIt =
993  resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
994  if (mergedDrivesIt != resetDrives.end()) {
995  unionDrives.append(mergedDrivesIt->second);
996  resetDrives.erase(mergedDrivesIt);
997  }
998  }
999 
1000  // Keep note of this drive so we can point the user at the right location
1001  // in case something goes wrong.
1002  resetDrives[unionLeader].push_back(
1003  {{dstField, dstBase}, {srcField, srcBase}, loc});
1004 }
1005 
1006 //===----------------------------------------------------------------------===//
1007 // Reset Inference
1008 //===----------------------------------------------------------------------===//
1009 
1010 LogicalResult InferResetsPass::inferAndUpdateResets() {
1011  LLVM_DEBUG({
1012  llvm::dbgs() << "\n";
1013  debugHeader("Infer reset types") << "\n\n";
1014  });
1015  for (auto it = resetClasses.begin(), end = resetClasses.end(); it != end;
1016  ++it) {
1017  if (!it->isLeader())
1018  continue;
1019  ResetNetwork net = llvm::make_range(resetClasses.member_begin(it),
1020  resetClasses.member_end());
1021 
1022  // Infer whether this should be a sync or async reset.
1023  auto kind = inferReset(net);
1024  if (failed(kind))
1025  return failure();
1026 
1027  // Update the types in the IR to match the inferred kind.
1028  if (failed(updateReset(net, *kind)))
1029  return failure();
1030  }
1031  return success();
1032 }
1033 
1034 FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1035  LLVM_DEBUG(llvm::dbgs() << "Inferring reset network with "
1036  << std::distance(net.begin(), net.end())
1037  << " nodes\n");
1038 
1039  // Go through the nodes and track the involved types.
1040  unsigned asyncDrives = 0;
1041  unsigned syncDrives = 0;
1042  unsigned invalidDrives = 0;
1043  for (ResetSignal signal : net) {
1044  // Keep track of whether this signal contributes a vote for async or sync.
1045  if (type_isa<AsyncResetType>(signal.type))
1046  ++asyncDrives;
1047  else if (type_isa<UIntType>(signal.type))
1048  ++syncDrives;
1049  else if (isUselessVec(signal.field) ||
1050  isa_and_nonnull<InvalidValueOp>(
1051  signal.field.getValue().getDefiningOp()))
1052  ++invalidDrives;
1053  }
1054  LLVM_DEBUG(llvm::dbgs() << "- Found " << asyncDrives << " async, "
1055  << syncDrives << " sync, " << invalidDrives
1056  << " invalid drives\n");
1057 
1058  // Handle the case where we have no votes for either kind.
1059  if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1060  ResetSignal root = guessRoot(net);
1061  auto diag = mlir::emitError(root.field.getValue().getLoc())
1062  << "reset network never driven with concrete type";
1063  for (ResetSignal signal : net)
1064  diag.attachNote(signal.field.getLoc()) << "here: ";
1065  return failure();
1066  }
1067 
1068  // Handle the case where we have votes for both kinds.
1069  if (asyncDrives > 0 && syncDrives > 0) {
1070  ResetSignal root = guessRoot(net);
1071  bool majorityAsync = asyncDrives >= syncDrives;
1072  auto diag = mlir::emitError(root.field.getValue().getLoc())
1073  << "reset network";
1074  SmallString<32> fieldName;
1075  if (getFieldName(root.field, fieldName))
1076  diag << " \"" << fieldName << "\"";
1077  diag << " simultaneously connected to async and sync resets";
1078  diag.attachNote(root.field.getValue().getLoc())
1079  << "majority of connections to this reset are "
1080  << (majorityAsync ? "async" : "sync");
1081  for (auto &drive : getResetDrives(net)) {
1082  if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1083  (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1084  (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1085  (type_isa<UIntType>(drive.src.type) && majorityAsync))
1086  diag.attachNote(drive.loc)
1087  << (type_isa<AsyncResetType>(drive.src.type) ? "async" : "sync")
1088  << " drive here:";
1089  }
1090  return failure();
1091  }
1092 
1093  // At this point we know that the type of the reset is unambiguous. If there
1094  // are any votes for async, we make the reset async. Otherwise we make it
1095  // sync.
1096  auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1097  LLVM_DEBUG(llvm::dbgs() << "- Inferred as " << kind << "\n");
1098  return kind;
1099 }
1100 
1101 //===----------------------------------------------------------------------===//
1102 // Reset Updating
1103 //===----------------------------------------------------------------------===//
1104 
1105 LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1106  LLVM_DEBUG(llvm::dbgs() << "Updating reset network with "
1107  << std::distance(net.begin(), net.end())
1108  << " nodes to " << kind << "\n");
1109 
1110  // Determine the final type the reset should have.
1111  FIRRTLBaseType resetType;
1112  if (kind == ResetKind::Async)
1113  resetType = AsyncResetType::get(&getContext());
1114  else
1115  resetType = UIntType::get(&getContext(), 1);
1116 
1117  // Update all those values in the network that cannot be inferred from
1118  // operands. If we change the type of a module port (i.e. BlockArgument), add
1119  // the module to a module worklist since we need to update its function type.
1120  SmallSetVector<Operation *, 16> worklist;
1121  SmallDenseSet<Operation *> moduleWorklist;
1122  SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1123  for (auto signal : net) {
1124  Value value = signal.field.getValue();
1125  if (!isa<BlockArgument>(value) &&
1126  !isa_and_nonnull<WireOp, RegOp, RegResetOp, InstanceOp, InvalidValueOp,
1127  ConstCastOp, RefCastOp, UninferredResetCastOp,
1128  RWProbeOp>(value.getDefiningOp()))
1129  continue;
1130  if (updateReset(signal.field, resetType)) {
1131  for (auto user : value.getUsers())
1132  worklist.insert(user);
1133  if (auto blockArg = dyn_cast<BlockArgument>(value))
1134  moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1135  else if (auto instOp = value.getDefiningOp<InstanceOp>()) {
1136  if (auto extmodule =
1137  instOp.getReferencedModule<FExtModuleOp>(*instanceGraph))
1138  extmoduleWorklist.insert({extmodule, instOp});
1139  } else if (auto uncast = value.getDefiningOp<UninferredResetCastOp>()) {
1140  uncast.replaceAllUsesWith(uncast.getInput());
1141  uncast.erase();
1142  }
1143  }
1144  }
1145 
1146  // Process the worklist of operations that have their type changed, pushing
1147  // types down the SSA dataflow graph. This is important because we change the
1148  // reset types in aggregates, and then need all the subindex, subfield, and
1149  // subaccess operations to be updated as appropriate.
1150  while (!worklist.empty()) {
1151  auto *wop = worklist.pop_back_val();
1152  SmallVector<Type, 2> types;
1153  if (auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1154  // Determine the new result types.
1155  SmallVector<Type, 2> types;
1156  if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1157  op->getOperands(), op->getAttrDictionary(),
1158  op->getPropertiesStorage(),
1159  op->getRegions(), types)))
1160  return failure();
1161 
1162  // Update the results and add the changed ones to the
1163  // worklist.
1164  for (auto it : llvm::zip(op->getResults(), types)) {
1165  auto newType = std::get<1>(it);
1166  if (std::get<0>(it).getType() == newType)
1167  continue;
1168  std::get<0>(it).setType(newType);
1169  for (auto *user : std::get<0>(it).getUsers())
1170  worklist.insert(user);
1171  }
1172  LLVM_DEBUG(llvm::dbgs() << "- Inferred " << *op << "\n");
1173  } else if (auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1174  for (auto *user : uop.getResult().getUsers())
1175  worklist.insert(user);
1176  uop.replaceAllUsesWith(uop.getInput());
1177  LLVM_DEBUG(llvm::dbgs() << "- Inferred " << uop << "\n");
1178  uop.erase();
1179  }
1180  }
1181 
1182  // Update module types based on the type of the block arguments.
1183  for (auto *op : moduleWorklist) {
1184  auto module = dyn_cast<FModuleOp>(op);
1185  if (!module)
1186  continue;
1187 
1188  SmallVector<Attribute> argTypes;
1189  argTypes.reserve(module.getNumPorts());
1190  for (auto arg : module.getArguments())
1191  argTypes.push_back(TypeAttr::get(arg.getType()));
1192 
1193  module->setAttr(FModuleLike::getPortTypesAttrName(),
1194  ArrayAttr::get(op->getContext(), argTypes));
1195  LLVM_DEBUG(llvm::dbgs()
1196  << "- Updated type of module '" << module.getName() << "'\n");
1197  }
1198 
1199  // Update extmodule types based on their instantiation.
1200  for (auto pair : extmoduleWorklist) {
1201  auto module = cast<FExtModuleOp>(pair.first);
1202  auto instOp = cast<InstanceOp>(pair.second);
1203 
1204  SmallVector<Attribute> types;
1205  for (auto type : instOp.getResultTypes())
1206  types.push_back(TypeAttr::get(type));
1207 
1208  module->setAttr(FModuleLike::getPortTypesAttrName(),
1209  ArrayAttr::get(module->getContext(), types));
1210  LLVM_DEBUG(llvm::dbgs()
1211  << "- Updated type of extmodule '" << module.getName() << "'\n");
1212  }
1213 
1214  return success();
1215 }
1216 
1217 /// Update the type of a single field within a type.
1218 static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID,
1219  FIRRTLBaseType fieldType) {
1220  // If this is a ground type, simply replace it, preserving constness.
1221  if (oldType.isGround()) {
1222  assert(fieldID == 0);
1223  return fieldType.getConstType(oldType.isConst());
1224  }
1225 
1226  // If this is a bundle type, update the corresponding field.
1227  if (auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1228  unsigned index = getIndexForFieldID(bundleType, fieldID);
1229  SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1230  bundleType.end());
1231  fields[index].type = updateType(
1232  fields[index].type, fieldID - getFieldID(bundleType, index), fieldType);
1233  return BundleType::get(oldType.getContext(), fields, bundleType.isConst());
1234  }
1235 
1236  // If this is a vector type, update the element type.
1237  if (auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1238  auto newType = updateType(vectorType.getElementType(),
1239  fieldID - getFieldID(vectorType), fieldType);
1240  return FVectorType::get(newType, vectorType.getNumElements(),
1241  vectorType.isConst());
1242  }
1243 
1244  llvm_unreachable("unknown aggregate type");
1245  return oldType;
1246 }
1247 
1248 /// Update the reset type of a specific field.
1249 bool InferResetsPass::updateReset(FieldRef field, FIRRTLBaseType resetType) {
1250  // Compute the updated type.
1251  auto oldType = type_cast<FIRRTLType>(field.getValue().getType());
1252  FIRRTLType newType = mapBaseType(oldType, [&](auto base) {
1253  return updateType(base, field.getFieldID(), resetType);
1254  });
1255 
1256  // Update the type if necessary.
1257  if (oldType == newType)
1258  return false;
1259  LLVM_DEBUG(llvm::dbgs() << "- Updating '" << field << "' from " << oldType
1260  << " to " << newType << "\n");
1261  field.getValue().setType(newType);
1262  return true;
1263 }
1264 
1265 //===----------------------------------------------------------------------===//
1266 // Reset Annotations
1267 //===----------------------------------------------------------------------===//
1268 
1269 LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1270  LLVM_DEBUG({
1271  llvm::dbgs() << "\n";
1272  debugHeader("Gather reset annotations") << "\n\n";
1273  });
1274  SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1275  for (auto module : circuit.getOps<FModuleOp>())
1276  results.push_back({module, {}});
1277  // Collect annotations parallelly.
1278  if (failed(mlir::failableParallelForEach(
1279  circuit.getContext(), results, [&](auto &moduleAndResult) {
1280  auto result = collectAnnos(moduleAndResult.first);
1281  if (failed(result))
1282  return failure();
1283  moduleAndResult.second = *result;
1284  return success();
1285  })))
1286  return failure();
1287 
1288  for (auto [module, reset] : results)
1289  if (reset.has_value())
1290  annotatedResets.insert({module, *reset});
1291  return success();
1292 }
1293 
1294 FailureOr<std::optional<Value>>
1295 InferResetsPass::collectAnnos(FModuleOp module) {
1296  bool anyFailed = false;
1297  SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1298 
1299  // Consume a possible "ignore" annotation on the module itself, which
1300  // explicitly assigns it no reset domain.
1301  bool ignore = false;
1302  AnnotationSet::removeAnnotations(module, [&](Annotation anno) {
1304  ignore = true;
1305  conflictingAnnos.insert({anno, module.getLoc()});
1306  return true;
1307  }
1308  if (anno.isClass(fullResetAnnoClass)) {
1309  anyFailed = true;
1310  module.emitError("''FullResetAnnotation' cannot target module; must "
1311  "target port or wire/node instead");
1312  return true;
1313  }
1314  return false;
1315  });
1316  if (anyFailed)
1317  return failure();
1318 
1319  // Consume any reset annotations on module ports.
1320  Value reset;
1321  // Helper for checking annotations and determining the reset
1322  auto checkAnnotations = [&](Annotation anno, Value arg) {
1323  if (anno.isClass(fullResetAnnoClass)) {
1324  ResetKind expectedResetKind;
1325  if (auto rt = anno.getMember<StringAttr>("resetType")) {
1326  if (rt == "sync") {
1327  expectedResetKind = ResetKind::Sync;
1328  } else if (rt == "async") {
1329  expectedResetKind = ResetKind::Async;
1330  } else {
1331  mlir::emitError(arg.getLoc(),
1332  "'FullResetAnnotation' requires resetType == 'sync' "
1333  "| 'async', but got resetType == ")
1334  << rt;
1335  anyFailed = true;
1336  return true;
1337  }
1338  } else {
1339  mlir::emitError(arg.getLoc(),
1340  "'FullResetAnnotation' requires resetType == "
1341  "'sync' | 'async', but got no resetType");
1342  anyFailed = true;
1343  return true;
1344  }
1345  // Check that the type is well-formed
1346  bool isAsync = expectedResetKind == ResetKind::Async;
1347  bool validUint = false;
1348  if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1349  validUint = uintT.getWidth() == 1;
1350  if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1351  (!isAsync && !validUint)) {
1352  auto kind = resetKindToStringRef(expectedResetKind);
1353  mlir::emitError(arg.getLoc(),
1354  "'FullResetAnnotation' with resetType == '")
1355  << kind << "' must target " << kind << " reset, but targets "
1356  << arg.getType();
1357  anyFailed = true;
1358  return true;
1359  }
1360 
1361  reset = arg;
1362  conflictingAnnos.insert({anno, reset.getLoc()});
1363 
1364  return false;
1365  }
1367  anyFailed = true;
1368  mlir::emitError(arg.getLoc(),
1369  "'ExcludeFromFullResetAnnotation' cannot "
1370  "target port/wire/node; must target module instead");
1371  return true;
1372  }
1373  return false;
1374  };
1375 
1376  AnnotationSet::removePortAnnotations(module,
1377  [&](unsigned argNum, Annotation anno) {
1378  Value arg = module.getArgument(argNum);
1379  return checkAnnotations(anno, arg);
1380  });
1381  if (anyFailed)
1382  return failure();
1383 
1384  // Consume any reset annotations on wires in the module body.
1385  module.getBody().walk([&](Operation *op) {
1386  // Reset annotations must target wire/node ops.
1387  if (!isa<WireOp, NodeOp>(op)) {
1388  if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1390  anyFailed = true;
1391  op->emitError(
1392  "reset annotations must target module, port, or wire/node");
1393  }
1394  return;
1395  }
1396 
1397  // At this point we know that we have a WireOp/NodeOp. Process the reset
1398  // annotations.
1399  AnnotationSet::removeAnnotations(op, [&](Annotation anno) {
1400  auto arg = op->getResult(0);
1401  return checkAnnotations(anno, arg);
1402  });
1403  });
1404  if (anyFailed)
1405  return failure();
1406 
1407  // If we have found no annotations, there is nothing to do. We just leave
1408  // this module unannotated, which will cause it to inherit a reset domain
1409  // from its instantiation sites.
1410  if (!ignore && !reset) {
1411  LLVM_DEBUG(llvm::dbgs()
1412  << "No reset annotation for " << module.getName() << "\n");
1413  return std::optional<Value>();
1414  }
1415 
1416  // If we have found multiple annotations, emit an error and abort.
1417  if (conflictingAnnos.size() > 1) {
1418  auto diag = module.emitError("multiple reset annotations on module '")
1419  << module.getName() << "'";
1420  for (auto &annoAndLoc : conflictingAnnos)
1421  diag.attachNote(annoAndLoc.second)
1422  << "conflicting " << annoAndLoc.first.getClassAttr() << ":";
1423  return failure();
1424  }
1425 
1426  // Dump some information in debug builds.
1427  LLVM_DEBUG({
1428  llvm::dbgs() << "Annotated reset for " << module.getName() << ": ";
1429  if (ignore)
1430  llvm::dbgs() << "no domain\n";
1431  else if (auto arg = dyn_cast<BlockArgument>(reset))
1432  llvm::dbgs() << "port " << module.getPortName(arg.getArgNumber()) << "\n";
1433  else
1434  llvm::dbgs() << "wire "
1435  << reset.getDefiningOp()->getAttrOfType<StringAttr>("name")
1436  << "\n";
1437  });
1438 
1439  // Store the annotated reset for this module.
1440  assert(ignore || reset);
1441  return std::optional<Value>(reset);
1442 }
1443 
1444 //===----------------------------------------------------------------------===//
1445 // Domain Construction
1446 //===----------------------------------------------------------------------===//
1447 
1448 /// Gather the reset domains present in a circuit. This traverses the instance
1449 /// hierarchy of the design, making instances either live in a new reset
1450 /// domain if so annotated, or inherit their parent's domain. This can go
1451 /// wrong in some cases, mainly when a module is instantiated multiple times
1452 /// within different reset domains.
1453 LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1454  LLVM_DEBUG({
1455  llvm::dbgs() << "\n";
1456  debugHeader("Build full reset domains") << "\n\n";
1457  });
1458 
1459  // Gather the domains.
1460  auto &instGraph = getAnalysis<InstanceGraph>();
1461  auto module = dyn_cast<FModuleOp>(*instGraph.getTopLevelNode()->getModule());
1462  if (!module) {
1463  LLVM_DEBUG(llvm::dbgs()
1464  << "Skipping circuit because main module is no `firrtl.module`");
1465  return success();
1466  }
1467  buildDomains(module, InstancePath{}, Value{}, instGraph);
1468 
1469  // Report any domain conflicts among the modules.
1470  bool anyFailed = false;
1471  for (auto &it : domains) {
1472  auto module = cast<FModuleOp>(it.first);
1473  auto &domainConflicts = it.second;
1474  if (domainConflicts.size() <= 1)
1475  continue;
1476 
1477  anyFailed = true;
1478  SmallDenseSet<Value> printedDomainResets;
1479  auto diag = module.emitError("module '")
1480  << module.getName()
1481  << "' instantiated in different reset domains";
1482  for (auto &it : domainConflicts) {
1483  ResetDomain &domain = it.first;
1484  const auto &path = it.second;
1485  auto inst = path.leaf();
1486  auto loc = path.empty() ? module.getLoc() : inst.getLoc();
1487  auto &note = diag.attachNote(loc);
1488 
1489  // Describe the instance itself.
1490  if (path.empty())
1491  note << "root instance";
1492  else {
1493  note << "instance '";
1494  llvm::interleave(
1495  path,
1496  [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1497  [&]() { note << "/"; });
1498  note << "'";
1499  }
1500 
1501  // Describe the reset domain the instance is in.
1502  note << " is in";
1503  if (domain.reset) {
1504  auto nameAndModule = getResetNameAndModule(domain.reset);
1505  note << " reset domain rooted at '" << nameAndModule.first.getValue()
1506  << "' of module '" << nameAndModule.second.getName() << "'";
1507 
1508  // Show where the domain reset is declared (once per reset).
1509  if (printedDomainResets.insert(domain.reset).second) {
1510  diag.attachNote(domain.reset.getLoc())
1511  << "reset domain '" << nameAndModule.first.getValue()
1512  << "' of module '" << nameAndModule.second.getName()
1513  << "' declared here:";
1514  }
1515  } else
1516  note << " no reset domain";
1517  }
1518  }
1519  return failure(anyFailed);
1520 }
1521 
1522 void InferResetsPass::buildDomains(FModuleOp module,
1523  const InstancePath &instPath,
1524  Value parentReset, InstanceGraph &instGraph,
1525  unsigned indent) {
1526  LLVM_DEBUG({
1527  llvm::dbgs().indent(indent * 2) << "Visiting ";
1528  if (instPath.empty())
1529  llvm::dbgs() << "$root";
1530  else
1531  llvm::dbgs() << instPath.leaf().getInstanceName();
1532  llvm::dbgs() << " (" << module.getName() << ")\n";
1533  });
1534 
1535  // Assemble the domain for this module.
1536  ResetDomain domain(parentReset);
1537  auto it = annotatedResets.find(module);
1538  if (it != annotatedResets.end()) {
1539  domain.isTop = true;
1540  domain.reset = it->second;
1541  }
1542 
1543  // Associate the domain with this module. If the module already has an
1544  // associated domain, it must be identical. Otherwise we'll have to report
1545  // the conflicting domains to the user.
1546  auto &entries = domains[module];
1547  if (llvm::all_of(entries,
1548  [&](const auto &entry) { return entry.first != domain; }))
1549  entries.push_back({domain, instPath});
1550 
1551  // Traverse the child instances.
1552  for (auto *record : *instGraph[module]) {
1553  auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1554  if (!submodule)
1555  continue;
1556  auto childPath =
1557  instancePathCache->appendInstance(instPath, record->getInstance());
1558  buildDomains(submodule, childPath, domain.reset, instGraph, indent + 1);
1559  }
1560 }
1561 
1562 /// Determine how the reset for each module shall be implemented.
1563 void InferResetsPass::determineImpl() {
1564  LLVM_DEBUG({
1565  llvm::dbgs() << "\n";
1566  debugHeader("Determine implementation") << "\n\n";
1567  });
1568  for (auto &it : domains) {
1569  auto module = cast<FModuleOp>(it.first);
1570  auto &domain = it.second.back().first;
1571  determineImpl(module, domain);
1572  }
1573 }
1574 
1575 /// Determine how the reset for a module shall be implemented. This function
1576 /// fills in the `existingValue`, `existingPort`, and `newPortName` fields of
1577 /// the given reset domain.
1578 ///
1579 /// Generally it does the following:
1580 /// - If the domain has explicitly no reset ("ignore"), leaves everything
1581 /// empty.
1582 /// - If the domain is the place where the reset is defined ("top"), fills in
1583 /// the existing port/wire/node as reset.
1584 /// - If the module already has a port with the reset's name:
1585 /// - If the port has the same type as the reset domain, reuses that port.
1586 /// - Otherwise appends a `_N` suffix with increasing N to create a
1587 /// yet-unused
1588 /// port name, and marks that as to be created.
1589 /// - Otherwise indicates that a port with the reset's name should be created.
1590 ///
1591 void InferResetsPass::determineImpl(FModuleOp module, ResetDomain &domain) {
1592  if (!domain.reset)
1593  return; // nothing to do if the module needs no reset
1594  LLVM_DEBUG(llvm::dbgs() << "Planning reset for " << module.getName() << "\n");
1595 
1596  // If this is the root of a reset domain, we don't need to add any ports
1597  // and can just simply reuse the existing values.
1598  if (domain.isTop) {
1599  LLVM_DEBUG(llvm::dbgs() << "- Rooting at local value "
1600  << getResetName(domain.reset) << "\n");
1601  domain.existingValue = domain.reset;
1602  if (auto blockArg = dyn_cast<BlockArgument>(domain.reset))
1603  domain.existingPort = blockArg.getArgNumber();
1604  return;
1605  }
1606 
1607  // Otherwise, check if a port with this name and type already exists and
1608  // reuse that where possible.
1609  auto neededName = getResetName(domain.reset);
1610  auto neededType = domain.reset.getType();
1611  LLVM_DEBUG(llvm::dbgs() << "- Looking for existing port " << neededName
1612  << "\n");
1613  auto portNames = module.getPortNames();
1614  auto ports = llvm::zip(portNames, module.getArguments());
1615  auto portIt = llvm::find_if(
1616  ports, [&](auto port) { return std::get<0>(port) == neededName; });
1617  if (portIt != ports.end() && std::get<1>(*portIt).getType() == neededType) {
1618  LLVM_DEBUG(llvm::dbgs()
1619  << "- Reusing existing port " << neededName << "\n");
1620  domain.existingValue = std::get<1>(*portIt);
1621  domain.existingPort = std::distance(ports.begin(), portIt);
1622  return;
1623  }
1624 
1625  // If we have found a port but the types don't match, pick a new name for
1626  // the reset port.
1627  //
1628  // CAVEAT: The Scala FIRRTL compiler just throws an error in this case. This
1629  // seems unnecessary though, since the compiler can just insert a new reset
1630  // signal as needed.
1631  if (portIt != ports.end()) {
1632  LLVM_DEBUG(llvm::dbgs()
1633  << "- Existing " << neededName << " has incompatible type "
1634  << std::get<1>(*portIt).getType() << "\n");
1635  StringAttr newName;
1636  unsigned suffix = 0;
1637  do {
1638  newName =
1639  StringAttr::get(&getContext(), Twine(neededName.getValue()) +
1640  Twine("_") + Twine(suffix++));
1641  } while (llvm::is_contained(portNames, newName));
1642  LLVM_DEBUG(llvm::dbgs()
1643  << "- Creating uniquified port " << newName << "\n");
1644  domain.newPortName = newName;
1645  return;
1646  }
1647 
1648  // At this point we know that there is no such port, and we can safely
1649  // create one as needed.
1650  LLVM_DEBUG(llvm::dbgs() << "- Creating new port " << neededName << "\n");
1651  domain.newPortName = neededName;
1652 }
1653 
1654 //===----------------------------------------------------------------------===//
1655 // Full Reset Implementation
1656 //===----------------------------------------------------------------------===//
1657 
1658 /// Implement the annotated resets gathered in the pass' `domains` map.
1659 LogicalResult InferResetsPass::implementFullReset() {
1660  LLVM_DEBUG({
1661  llvm::dbgs() << "\n";
1662  debugHeader("Implement full resets") << "\n\n";
1663  });
1664  for (auto &it : domains)
1665  if (failed(implementFullReset(cast<FModuleOp>(it.first),
1666  it.second.back().first)))
1667  return failure();
1668  return success();
1669 }
1670 
1671 /// Implement the async resets for a specific module.
1672 ///
1673 /// This will add ports to the module as appropriate, update the register ops
1674 /// in the module, and update any instantiated submodules with their
1675 /// corresponding reset implementation details.
1676 LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1677  ResetDomain &domain) {
1678  LLVM_DEBUG(llvm::dbgs() << "Implementing full reset for " << module.getName()
1679  << "\n");
1680 
1681  // Nothing to do if the module was marked explicitly with no reset domain.
1682  if (!domain.reset) {
1683  LLVM_DEBUG(llvm::dbgs()
1684  << "- Skipping because module explicitly has no domain\n");
1685  return success();
1686  }
1687 
1688  // If needed, add a reset port to the module.
1689  Value actualReset = domain.existingValue;
1690  if (domain.newPortName) {
1691  PortInfo portInfo{domain.newPortName,
1692  domain.reset.getType(),
1693  Direction::In,
1694  {},
1695  domain.reset.getLoc()};
1696  module.insertPorts({{0, portInfo}});
1697  actualReset = module.getArgument(0);
1698  LLVM_DEBUG(llvm::dbgs()
1699  << "- Inserted port " << domain.newPortName << "\n");
1700  }
1701  assert(actualReset);
1702  LLVM_DEBUG({
1703  llvm::dbgs() << "- Using ";
1704  if (auto blockArg = dyn_cast<BlockArgument>(actualReset))
1705  llvm::dbgs() << "port #" << blockArg.getArgNumber() << " ";
1706  else
1707  llvm::dbgs() << "wire/node ";
1708  llvm::dbgs() << getResetName(actualReset) << "\n";
1709  });
1710 
1711  // Gather a list of operations in the module that need to be updated with
1712  // the new reset.
1713  SmallVector<Operation *> opsToUpdate;
1714  module.walk([&](Operation *op) {
1715  if (isa<InstanceOp, RegOp, RegResetOp>(op))
1716  opsToUpdate.push_back(op);
1717  });
1718 
1719  // If the reset is a local wire or node, move it upwards such that it
1720  // dominates all the operations that it will need to attach to. In the case
1721  // of a node this might not be easily possible, so we just spill into a wire
1722  // in that case.
1723  if (!isa<BlockArgument>(actualReset)) {
1724  mlir::DominanceInfo dom(module);
1725  // The first op in `opsToUpdate` is the top-most op in the module, since
1726  // the ops and blocks are traversed in a depth-first, top-to-bottom order
1727  // in `walk`. So we can simply check if the local reset declaration is
1728  // before the first op to find out if we need to move anything.
1729  auto *resetOp = actualReset.getDefiningOp();
1730  if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1731  LLVM_DEBUG(llvm::dbgs()
1732  << "- Reset doesn't dominate all uses, needs to be moved\n");
1733 
1734  // If the node can't be moved because its input doesn't dominate the
1735  // target location, convert it to a wire.
1736  auto nodeOp = dyn_cast<NodeOp>(resetOp);
1737  if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1738  LLVM_DEBUG(llvm::dbgs()
1739  << "- Promoting node to wire for move: " << nodeOp << "\n");
1740  auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1741  nodeOp->getBlock());
1742  auto wireOp = builder.create<WireOp>(
1743  nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1744  nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1745  nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1746  // Don't delete the node, since it might be in use in worklists.
1747  nodeOp->replaceAllUsesWith(wireOp);
1748  nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1749  nodeOp.setName("");
1750  // Leave forcable alone, since we cannot remove a result. It will be
1751  // cleaned up in canonicalization since it is dead. As will this node.
1752  nodeOp.setNameKind(NameKindEnum::DroppableName);
1753  nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1754  builder.setInsertionPointAfter(nodeOp);
1755  emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1756  resetOp = wireOp;
1757  actualReset = wireOp.getResult();
1758  domain.existingValue = wireOp.getResult();
1759  }
1760 
1761  // Determine the block into which the reset declaration needs to be
1762  // moved.
1763  Block *targetBlock = dom.findNearestCommonDominator(
1764  resetOp->getBlock(), opsToUpdate[0]->getBlock());
1765  LLVM_DEBUG({
1766  if (targetBlock != resetOp->getBlock())
1767  llvm::dbgs() << "- Needs to be moved to different block\n";
1768  });
1769 
1770  // At this point we have to figure out in front of which operation in
1771  // the target block the reset declaration has to be moved. The reset
1772  // declaration and the first op it needs to dominate may be buried
1773  // inside blocks of other operations (e.g. `WhenOp`), so we have to look
1774  // through their parent operations until we find the one that lies
1775  // within the target block.
1776  auto getParentInBlock = [](Operation *op, Block *block) {
1777  while (op && op->getBlock() != block)
1778  op = op->getParentOp();
1779  return op;
1780  };
1781  auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1782  auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1783 
1784  // Move the operation upwards. Since there are situations where the
1785  // reset declaration does not dominate the first use, but the `WhenOp`
1786  // it is nested within actually *does* come before that use, we have to
1787  // consider moving the reset declaration in front of its parent op.
1788  if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1789  resetOp->moveBefore(resetOpInTarget);
1790  else
1791  resetOp->moveBefore(firstOpInTarget);
1792  }
1793  }
1794 
1795  // Update the operations.
1796  for (auto *op : opsToUpdate)
1797  implementFullReset(op, module, actualReset);
1798 
1799  return success();
1800 }
1801 
1802 /// Modify an operation in a module to implement an full reset for that
1803 /// module.
1804 void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1805  Value actualReset) {
1806  ImplicitLocOpBuilder builder(op->getLoc(), op);
1807 
1808  // Handle instances.
1809  if (auto instOp = dyn_cast<InstanceOp>(op)) {
1810  // Lookup the reset domain of the instantiated module. If there is no
1811  // reset domain associated with that module, or the module is explicitly
1812  // marked as being in no domain, simply skip.
1813  auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1814  if (!refModule)
1815  return;
1816  auto domainIt = domains.find(refModule);
1817  if (domainIt == domains.end())
1818  return;
1819  auto &domain = domainIt->second.back().first;
1820  if (!domain.reset)
1821  return;
1822  LLVM_DEBUG(llvm::dbgs()
1823  << "- Update instance '" << instOp.getName() << "'\n");
1824 
1825  // If needed, add a reset port to the instance.
1826  Value instReset;
1827  if (domain.newPortName) {
1828  LLVM_DEBUG(llvm::dbgs() << " - Adding new result as reset\n");
1829 
1830  auto newInstOp = instOp.cloneAndInsertPorts(
1831  {{/*portIndex=*/0,
1832  {domain.newPortName,
1833  type_cast<FIRRTLBaseType>(actualReset.getType()),
1834  Direction::In}}});
1835  instReset = newInstOp.getResult(0);
1836 
1837  // Update the uses over to the new instance and drop the old instance.
1838  instOp.replaceAllUsesWith(newInstOp.getResults().drop_front());
1839  instanceGraph->replaceInstance(instOp, newInstOp);
1840  instOp->erase();
1841  instOp = newInstOp;
1842  } else if (domain.existingPort.has_value()) {
1843  auto idx = *domain.existingPort;
1844  instReset = instOp.getResult(idx);
1845  LLVM_DEBUG(llvm::dbgs() << " - Using result #" << idx << " as reset\n");
1846  }
1847 
1848  // If there's no reset port on the instance to connect, we're done. This
1849  // can happen if the instantiated module has a reset domain, but that
1850  // domain is e.g. rooted at an internal wire.
1851  if (!instReset)
1852  return;
1853 
1854  // Connect the instance's reset to the actual reset.
1855  assert(instReset && actualReset);
1856  builder.setInsertionPointAfter(instOp);
1857  emitConnect(builder, instReset, actualReset);
1858  return;
1859  }
1860 
1861  // Handle reset-less registers.
1862  if (auto regOp = dyn_cast<RegOp>(op)) {
1863  if (AnnotationSet::removeAnnotations(regOp, excludeMemToRegAnnoClass))
1864  return;
1865 
1866  LLVM_DEBUG(llvm::dbgs() << "- Adding full reset to " << regOp << "\n");
1867  auto zero = createZeroValue(builder, regOp.getResult().getType());
1868  auto newRegOp = builder.create<RegResetOp>(
1869  regOp.getResult().getType(), regOp.getClockVal(), actualReset, zero,
1870  regOp.getNameAttr(), regOp.getNameKindAttr(), regOp.getAnnotations(),
1871  regOp.getInnerSymAttr(), regOp.getForceableAttr());
1872  regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1873  if (regOp.getForceable())
1874  regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1875  regOp->erase();
1876  return;
1877  }
1878 
1879  // Handle registers with reset.
1880  if (auto regOp = dyn_cast<RegResetOp>(op)) {
1881  // If the register already has an async reset or if the type of the added
1882  // reset is sync, leave it alone.
1883  if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1884  type_isa<UIntType>(actualReset.getType())) {
1885  LLVM_DEBUG(llvm::dbgs() << "- Skipping (has reset) " << regOp << "\n");
1886  // The following performs the logic of `CheckResets` in the original
1887  // Scala source code.
1888  if (failed(regOp.verifyInvariants()))
1889  signalPassFailure();
1890  return;
1891  }
1892  LLVM_DEBUG(llvm::dbgs() << "- Updating reset of " << regOp << "\n");
1893 
1894  auto reset = regOp.getResetSignal();
1895  auto value = regOp.getResetValue();
1896 
1897  // If we arrive here, the register has a sync reset and the added reset is
1898  // async. In order to add an async reset, we have to move the sync reset
1899  // into a mux in front of the register.
1900  insertResetMux(builder, regOp.getResult(), reset, value);
1901  builder.setInsertionPointAfterValue(regOp.getResult());
1902  auto mux = builder.create<MuxPrimOp>(reset, value, regOp.getResult());
1903  emitConnect(builder, regOp.getResult(), mux);
1904 
1905  // Replace the existing reset with the async reset.
1906  builder.setInsertionPoint(regOp);
1907  auto zero = createZeroValue(builder, regOp.getResult().getType());
1908  regOp.getResetSignalMutable().assign(actualReset);
1909  regOp.getResetValueMutable().assign(zero);
1910  }
1911 }
1912 
1913 LogicalResult InferResetsPass::verifyNoAbstractReset() {
1914  bool hasAbstractResetPorts = false;
1915  for (FModuleLike module :
1916  getOperation().getBodyBlock()->getOps<FModuleLike>()) {
1917  for (PortInfo port : module.getPorts()) {
1918  if (getBaseOfType<ResetType>(port.type)) {
1919  auto diag = emitError(port.loc)
1920  << "a port \"" << port.getName()
1921  << "\" with abstract reset type was unable to be "
1922  "inferred by InferResets (is this a top-level port?)";
1923  diag.attachNote(module->getLoc())
1924  << "the module with this uninferred reset port was defined here";
1925  hasAbstractResetPorts = true;
1926  }
1927  }
1928  }
1929 
1930  if (hasAbstractResetPorts)
1931  return failure();
1932  return success();
1933 }
assert(baseType &&"element must be base type")
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static unsigned getFieldID(BundleType type, unsigned index)
static std::pair< StringAttr, FModuleOp > getResetNameAndModule(Value reset)
Return the name and parent module of a reset.
Definition: InferResets.cpp:87
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID, FIRRTLBaseType fieldType)
Update the type of a single field within a type.
static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID)
static StringAttr getResetName(Value reset)
Return the name of a reset.
static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target, Value reset, Value resetValue)
Helper function that inserts reset multiplexer into all ConnectOps with the given target.
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static bool typeContainsReset(Type type)
Check whether a type contains a ResetType.
static bool getDeclName(Value value, SmallString< 32 > &string)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static InstancePath empty
static Block * getBodyBlock(FModuleLike mod)
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Definition: FieldRef.h:59
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
FIRRTLBaseType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
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
This graph tracks modules and where they are instantiated.
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
constexpr const char * excludeMemToRegAnnoClass
igraph::InstancePathCache InstancePathCache
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
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
Definition: FIRRTLUtils.cpp:25
std::unique_ptr< mlir::Pass > createInferResetsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
llvm::raw_ostream & debugHeader(llvm::StringRef str, int width=80)
Write a "header"-like string to the debug stream with a certain width.
Definition: Debug.cpp:18
inline ::llvm::hash_code hash_value(const FieldRef &fieldRef)
Get a hash code for a FieldRef.
Definition: FieldRef.h:92
bool operator==(uint64_t a, const FVInt &b)
Definition: FVInt.h:649
bool operator!=(uint64_t a, const FVInt &b)
Definition: FVInt.h:650
bool operator<(const AppID &a, const AppID &b)
Definition: Manifest.cpp:696
This holds the name and type that describes the module's ports.
static ResetSignal getEmptyKey()
static ResetSignal getTombstoneKey()
static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)