CIRCT  19.0.0git
ExtractInstances.cpp
Go to the documentation of this file.
1 //===- ExtractInstances.cpp - Move instances up the hierarchy ---*- 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 // Moves annotated instances upwards in the module hierarchy. Corresponds to the
10 // `ExtractBlackBoxes`, `ExtractClockGates`, and `ExtractSeqMems` passes in the
11 // Scala FIRRTL implementation.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "PassDetails.h"
27 #include "circt/Dialect/SV/SVOps.h"
28 #include "circt/Support/Path.h"
29 #include "mlir/IR/Attributes.h"
30 #include "mlir/IR/ImplicitLocOpBuilder.h"
31 #include "mlir/Support/FileUtilities.h"
32 #include "llvm/ADT/SmallPtrSet.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/Path.h"
36 
37 #define DEBUG_TYPE "firrtl-extract-instances"
38 
39 using namespace circt;
40 using namespace firrtl;
41 using hw::InnerRefAttr;
42 
43 //===----------------------------------------------------------------------===//
44 // Pass Implementation
45 //===----------------------------------------------------------------------===//
46 
47 namespace {
48 /// All information necessary to move instances about.
49 struct ExtractionInfo {
50  /// A filename into which the performed hierarchy modifications are emitted.
51  StringRef traceFilename;
52  /// A prefix to attach to the wiring generated by the extraction.
53  StringRef prefix;
54  /// Optional name of the wrapper module that will hold the moved instance.
55  StringRef wrapperModule;
56  /// Whether the extraction should stop at the root of the DUT instead of going
57  /// past that and extracting into the test harness.
58  bool stopAtDUT;
59 };
60 
61 struct ExtractInstancesPass
62  : public ExtractInstancesBase<ExtractInstancesPass> {
63  void runOnOperation() override;
64  void collectAnnos();
65  void collectAnno(InstanceOp inst, Annotation anno);
66  void extractInstances();
67  void groupInstances();
68  void createTraceFiles();
69 
70  /// Get the cached namespace for a module.
71  hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
72  return moduleNamespaces.try_emplace(module, module).first->second;
73  }
74 
75  /// Obtain an inner reference to an operation, possibly adding an `inner_sym`
76  /// to that operation.
77  InnerRefAttr getInnerRefTo(Operation *op) {
79  [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
80  return getModuleNamespace(mod);
81  });
82  }
83 
84  /// Create a clone of a `HierPathOp` with a new uniquified name.
85  hw::HierPathOp cloneWithNewNameAndPath(hw::HierPathOp pathOp,
86  ArrayRef<Attribute> newPath) {
87  OpBuilder builder(pathOp);
88  auto newPathOp = builder.cloneWithoutRegions(pathOp);
89  newPathOp.setSymNameAttr(builder.getStringAttr(
90  circuitNamespace.newName(newPathOp.getSymName())));
91  newPathOp.setNamepathAttr(builder.getArrayAttr(newPath));
92  return newPathOp;
93  }
94 
95  /// Return a handle to the unique instance of file with a given name.
96  emit::FileOp getOrCreateFile(StringRef fileName) {
97  auto [it, inserted] = files.try_emplace(fileName, emit::FileOp{});
98  if (inserted) {
99  auto builder = ImplicitLocOpBuilder::atBlockEnd(
100  UnknownLoc::get(&getContext()), getOperation().getBodyBlock());
101  it->second = builder.create<emit::FileOp>(fileName);
102  }
103  return it->second;
104  }
105 
106  bool anythingChanged;
107  bool anyFailures;
108 
109  InstanceGraph *instanceGraph = nullptr;
110 
111  /// The modules in the design that are annotated with one or more annotations
112  /// relevant for instance extraction.
113  DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
114 
115  /// All modules that are marked as DUT themselves. Realistically this is only
116  /// ever one module in the design.
117  DenseSet<Operation *> dutRootModules;
118  /// All modules that are marked as DUT themselves, or that have a DUT parent
119  /// module.
120  DenseSet<Operation *> dutModules;
121  /// All DUT module names.
122  DenseSet<Attribute> dutModuleNames;
123  /// The prefix of the DUT module. This is used when creating new modules
124  /// under the DUT.
125  StringRef dutPrefix = "";
126 
127  /// A worklist of instances that need to be moved.
128  SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
129 
130  /// A mapping from file names to file ops for de-duplication.
131  DenseMap<StringRef, emit::FileOp> files;
132 
133  /// The path along which instances have been extracted. This essentially
134  /// documents the original location of the instance in reverse. Every push
135  /// upwards in the hierarchy adds another entry to this path documenting along
136  /// which instantiation path each instance was extracted.
137  DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
138 
139  /// A map of the original parent modules of instances before they were
140  /// extracted. This is used in a corner case during trace file emission.
141  DenseMap<Operation *, StringAttr> originalInstanceParents;
142 
143  /// All extracted instances in their position after moving upwards in the
144  /// hierarchy, but before being grouped into an optional submodule.
145  SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
146 
147  // The uniquified wiring prefix for each instance.
148  DenseMap<Operation *, SmallString<16>> instPrefices;
149 
150  /// The current circuit namespace valid within the call to `runOnOperation`.
151  CircuitNamespace circuitNamespace;
152  /// Cached module namespaces.
153  DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
154 };
155 } // end anonymous namespace
156 
157 /// Emit the annotated source code for black boxes in a circuit.
158 void ExtractInstancesPass::runOnOperation() {
159  CircuitOp circuitOp = getOperation();
160  anythingChanged = false;
161  anyFailures = false;
162  annotatedModules.clear();
163  dutRootModules.clear();
164  dutModules.clear();
165  dutModuleNames.clear();
166  dutPrefix = "";
167  extractionWorklist.clear();
168  files.clear();
169  extractionPaths.clear();
170  originalInstanceParents.clear();
171  extractedInstances.clear();
172  instPrefices.clear();
173  moduleNamespaces.clear();
174  circuitNamespace.clear();
175  circuitNamespace.add(circuitOp);
176 
177  // Walk the IR and gather all the annotations relevant for extraction that
178  // appear on instances and the instantiated modules.
179  instanceGraph = &getAnalysis<InstanceGraph>();
180  collectAnnos();
181  if (anyFailures)
182  return signalPassFailure();
183 
184  // Actually move instances upwards.
185  extractInstances();
186  if (anyFailures)
187  return signalPassFailure();
188 
189  // Group instances into submodules, if requested.
190  groupInstances();
191  if (anyFailures)
192  return signalPassFailure();
193 
194  // Generate the trace files that list where each instance was extracted from.
195  createTraceFiles();
196  if (anyFailures)
197  return signalPassFailure();
198 
199  // If nothing has changed we can preserve the analysis.
200  LLVM_DEBUG(llvm::dbgs() << "\n");
201  if (!anythingChanged)
202  markAllAnalysesPreserved();
203 }
204 
205 static bool isAnnoInteresting(Annotation anno) {
206  return anno.isClass(extractBlackBoxAnnoClass);
207 }
208 
209 /// Gather the modules and instances annotated to be moved by this pass. This
210 /// populates the corresponding lists and maps of the pass.
211 void ExtractInstancesPass::collectAnnos() {
212  CircuitOp circuit = getOperation();
213 
214  // Grab the clock gate extraction annotation on the circuit.
215  StringRef clkgateFileName;
216  StringRef clkgateWrapperModule;
217  AnnotationSet::removeAnnotations(circuit, [&](Annotation anno) {
219  return false;
220  LLVM_DEBUG(llvm::dbgs()
221  << "Clock gate extraction config: " << anno.getDict() << "\n");
222  auto filenameAttr = anno.getMember<StringAttr>("filename");
223  auto groupAttr = anno.getMember<StringAttr>("group");
224  if (!filenameAttr) {
225  circuit.emitError("missing `filename` attribute in `")
226  << anno.getClass() << "` annotation";
227  anyFailures = true;
228  return true;
229  }
230 
231  if (!clkgateFileName.empty()) {
232  circuit.emitError("multiple `")
233  << anno.getClass() << "` annotations on circuit";
234  anyFailures = true;
235  return true;
236  }
237 
238  clkgateFileName = filenameAttr.getValue();
239  if (groupAttr)
240  clkgateWrapperModule = groupAttr.getValue();
241  return true;
242  });
243 
244  // Grab the memory extraction annotation on the circuit.
245  StringRef memoryFileName;
246  StringRef memoryWrapperModule;
247  AnnotationSet::removeAnnotations(circuit, [&](Annotation anno) {
248  if (!anno.isClass(extractSeqMemsAnnoClass))
249  return false;
250  LLVM_DEBUG(llvm::dbgs()
251  << "Memory extraction config: " << anno.getDict() << "\n");
252  auto filenameAttr = anno.getMember<StringAttr>("filename");
253  auto groupAttr = anno.getMember<StringAttr>("group");
254  if (!filenameAttr) {
255  circuit.emitError("missing `filename` attribute in `")
256  << anno.getClass() << "` annotation";
257  anyFailures = true;
258  return true;
259  }
260 
261  if (!memoryFileName.empty()) {
262  circuit.emitError("multiple `")
263  << anno.getClass() << "` annotations on circuit";
264  anyFailures = true;
265  return true;
266  }
267 
268  memoryFileName = filenameAttr.getValue();
269  if (groupAttr)
270  memoryWrapperModule = groupAttr.getValue();
271  return true;
272  });
273 
274  // Gather the annotations on modules. These complement the later per-instance
275  // annotations.
276  for (auto module : circuit.getOps<FModuleLike>()) {
277  AnnotationSet::removeAnnotations(module, [&](Annotation anno) {
278  if (anno.isClass(dutAnnoClass)) {
279  LLVM_DEBUG(llvm::dbgs()
280  << "Marking DUT `" << module.getModuleName() << "`\n");
281  dutRootModules.insert(module);
282  dutModules.insert(module);
283  if (auto prefix = anno.getMember<StringAttr>("prefix"))
284  dutPrefix = prefix;
285  return false; // other passes may rely on this anno; keep it
286  }
287  if (!isAnnoInteresting(anno))
288  return false;
289  LLVM_DEBUG(llvm::dbgs() << "Annotated module `" << module.getModuleName()
290  << "`:\n " << anno.getDict() << "\n");
291  annotatedModules[module].push_back(anno);
292  return true;
293  });
294  }
295 
296  // Gather the annotations on instances to be extracted.
297  circuit.walk([&](InstanceOp inst) {
298  SmallVector<Annotation, 1> instAnnos;
299  Operation *module = inst.getReferencedModule(*instanceGraph);
300 
301  // Module-level annotations.
302  auto it = annotatedModules.find(module);
303  if (it != annotatedModules.end())
304  instAnnos.append(it->second);
305 
306  // Instance-level annotations.
308  if (!isAnnoInteresting(anno))
309  return false;
310  LLVM_DEBUG(llvm::dbgs() << "Annotated instance `" << inst.getName()
311  << "`:\n " << anno.getDict() << "\n");
312  instAnnos.push_back(anno);
313  return true;
314  });
315 
316  // No need to do anything about unannotated instances.
317  if (instAnnos.empty())
318  return;
319 
320  // Ensure there are no conflicting annotations.
321  if (instAnnos.size() > 1) {
322  auto d = inst.emitError("multiple extraction annotations on instance `")
323  << inst.getName() << "`";
324  d.attachNote(inst.getLoc()) << "instance has the following annotations, "
325  "but at most one is allowed:";
326  for (auto anno : instAnnos)
327  d.attachNote(inst.getLoc()) << anno.getDict();
328  anyFailures = true;
329  return;
330  }
331 
332  // Process the annotation.
333  collectAnno(inst, instAnnos[0]);
334  });
335 
336  // Propagate the DUT marking to all arbitrarily nested submodules of the DUT.
337  LLVM_DEBUG(llvm::dbgs() << "Marking DUT hierarchy\n");
338  SmallVector<InstanceGraphNode *> worklist;
339  for (Operation *op : dutModules)
340  worklist.push_back(
341  instanceGraph->lookup(cast<igraph::ModuleOpInterface>(op)));
342  while (!worklist.empty()) {
343  auto *module = worklist.pop_back_val();
344  dutModuleNames.insert(module->getModule().getModuleNameAttr());
345  LLVM_DEBUG(llvm::dbgs()
346  << "- " << module->getModule().getModuleName() << "\n");
347  for (auto *instRecord : *module) {
348  auto *target = instRecord->getTarget();
349  if (dutModules.insert(target->getModule()).second)
350  worklist.push_back(target);
351  }
352  }
353 
354  // If clock gate extraction is requested, find instances of extmodules with
355  // the corresponding `defname` and mark them as to be extracted.
356  // TODO: This defname really shouldn't be hardcoded here. Make this at least
357  // somewhat configurable.
358  if (!clkgateFileName.empty()) {
359  auto clkgateDefNameAttr = StringAttr::get(&getContext(), "EICG_wrapper");
360  for (auto module : circuit.getOps<FExtModuleOp>()) {
361  if (module.getDefnameAttr() != clkgateDefNameAttr)
362  continue;
363  LLVM_DEBUG(llvm::dbgs()
364  << "Clock gate `" << module.getModuleName() << "`\n");
365  if (!dutModules.contains(module)) {
366  LLVM_DEBUG(llvm::dbgs() << "- Ignored (outside DUT)\n");
367  continue;
368  }
369 
370  ExtractionInfo info;
371  info.traceFilename = clkgateFileName;
372  info.prefix = "clock_gate"; // TODO: Don't hardcode this
373  info.wrapperModule = clkgateWrapperModule;
374  info.stopAtDUT = !info.wrapperModule.empty();
375  for (auto *instRecord : instanceGraph->lookup(module)->uses()) {
376  if (auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
377  LLVM_DEBUG(llvm::dbgs()
378  << "- Marking `"
379  << inst->getParentOfType<FModuleLike>().getModuleName()
380  << "." << inst.getName() << "`\n");
381  extractionWorklist.push_back({inst, info});
382  }
383  }
384  }
385  }
386 
387  // If memory extraction is requested, find instances of `FMemModuleOp` and
388  // mark them as to be extracted.
389  // somewhat configurable.
390  if (!memoryFileName.empty()) {
391  // Create a potentially empty file if a name is specified. This is done to
392  // align with the SFC implementation of this pass where the file is always
393  // created. This does introduce an additional leading newline in the file.
394  getOrCreateFile(memoryFileName);
395 
396  for (auto module : circuit.getOps<FMemModuleOp>()) {
397  LLVM_DEBUG(llvm::dbgs() << "Memory `" << module.getModuleName() << "`\n");
398  if (!dutModules.contains(module)) {
399  LLVM_DEBUG(llvm::dbgs() << "- Ignored (outside DUT)\n");
400  continue;
401  }
402 
403  ExtractionInfo info;
404  info.traceFilename = memoryFileName;
405  info.prefix = "mem_wiring"; // TODO: Don't hardcode this
406  info.wrapperModule = memoryWrapperModule;
407  info.stopAtDUT = !info.wrapperModule.empty();
408  for (auto *instRecord : instanceGraph->lookup(module)->uses()) {
409  if (auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
410  LLVM_DEBUG(llvm::dbgs()
411  << "- Marking `"
412  << inst->getParentOfType<FModuleLike>().getModuleName()
413  << "." << inst.getName() << "`\n");
414  extractionWorklist.push_back({inst, info});
415  }
416  }
417  }
418  }
419 }
420 
421 /// Process an extraction annotation on an instance into a corresponding
422 /// `ExtractionInfo` and a spot on the worklist for later moving things around.
423 void ExtractInstancesPass::collectAnno(InstanceOp inst, Annotation anno) {
424  LLVM_DEBUG(llvm::dbgs() << "Processing instance `" << inst.getName() << "` "
425  << anno.getDict() << "\n");
426 
427  auto getStringOrError = [&](StringRef member) {
428  auto attr = anno.getMember<StringAttr>(member);
429  if (!attr) {
430  inst.emitError("missing `")
431  << member << "` attribute in `" << anno.getClass() << "` annotation";
432  anyFailures = true;
433  }
434  return attr;
435  };
436 
437  if (anno.isClass(extractBlackBoxAnnoClass)) {
438  auto filename = getStringOrError("filename");
439  auto prefix = getStringOrError("prefix");
440  auto dest = anno.getMember<StringAttr>("dest"); // optional
441  if (anyFailures)
442  return;
443 
444  ExtractionInfo info;
445  info.traceFilename = filename;
446  info.prefix = prefix;
447  info.wrapperModule = (dest ? dest.getValue() : "");
448 
449  // CAVEAT: If the instance has a wrapper module configured then extraction
450  // should stop at the DUT module instead of extracting past the DUT into the
451  // surrounding test harness. This is all very ugly and hacky.
452  info.stopAtDUT = !info.wrapperModule.empty();
453 
454  extractionWorklist.push_back({inst, info});
455  return;
456  }
457 }
458 
459 /// Find the location in an NLA that corresponds to a given instance (either by
460 /// mentioning exactly the instance, or the instance's parent module). Returns a
461 /// position within the NLA's path, or the length of the path if the instances
462 /// was not found.
463 static unsigned findInstanceInNLA(InstanceOp inst, hw::HierPathOp nla) {
464  unsigned nlaLen = nla.getNamepath().size();
465  auto instName = getInnerSymName(inst);
466  auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
467  for (unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
468  auto refPart = nla.refPart(nlaIdx);
469  if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
470  return nlaIdx;
471  }
472  return nlaLen;
473 }
474 
475 /// Move instances in the extraction worklist upwards in the hierarchy. This
476 /// iteratively pushes instances up one level of hierarchy until they have
477 /// arrived in the desired container module.
478 void ExtractInstancesPass::extractInstances() {
479  // The list of ports to be added to an instance's parent module. Cleared and
480  // reused across instances.
481  SmallVector<std::pair<unsigned, PortInfo>> newPorts;
482  // The number of instances with the same prefix. Used to uniquify prefices.
483  DenseMap<StringRef, unsigned> prefixUniqueIDs;
484 
485  SmallPtrSet<Operation *, 4> nlasToRemove;
486 
487  auto &nlaTable = getAnalysis<NLATable>();
488 
489  // Keep track of where the instance was originally.
490  for (auto &[inst, info] : extractionWorklist)
491  originalInstanceParents[inst] =
492  inst->getParentOfType<FModuleLike>().getModuleNameAttr();
493 
494  while (!extractionWorklist.empty()) {
495  InstanceOp inst;
496  ExtractionInfo info;
497  std::tie(inst, info) = extractionWorklist.pop_back_val();
498  auto parent = inst->getParentOfType<FModuleOp>();
499 
500  // Figure out the wiring prefix to use for this instance. If we are supposed
501  // to use a wiring prefix (`info.prefix` is non-empty), we assemble a
502  // `<prefix>_<N>` string, where `N` is an unsigned integer used to uniquifiy
503  // the prefix. This is very close to what the original Scala implementation
504  // of the pass does, which would group instances to be extracted by prefix
505  // and then iterate over them with the index in the group being used as `N`.
506  StringRef prefix;
507  if (!info.prefix.empty()) {
508  auto &prefixSlot = instPrefices[inst];
509  if (prefixSlot.empty()) {
510  auto idx = prefixUniqueIDs[info.prefix]++;
511  (Twine(info.prefix) + "_" + Twine(idx)).toVector(prefixSlot);
512  }
513  prefix = prefixSlot;
514  }
515 
516  // If the instance is already in the right place (outside the DUT or already
517  // in the root module), there's nothing left for us to do. Otherwise we
518  // proceed to bubble it up one level in the hierarchy and add the resulting
519  // instances back to the worklist.
520  if (!dutModules.contains(parent) ||
521  instanceGraph->lookup(parent)->noUses() ||
522  (info.stopAtDUT && dutRootModules.contains(parent))) {
523  LLVM_DEBUG(llvm::dbgs() << "\nNo need to further move " << inst << "\n");
524  extractedInstances.push_back({inst, info});
525  continue;
526  }
527  LLVM_DEBUG({
528  llvm::dbgs() << "\nMoving ";
529  if (!prefix.empty())
530  llvm::dbgs() << "`" << prefix << "` ";
531  llvm::dbgs() << inst << "\n";
532  });
533 
534  // Add additional ports to the parent module as a replacement for the
535  // instance port signals once the instance is extracted.
536  unsigned numParentPorts = parent.getNumPorts();
537  unsigned numInstPorts = inst.getNumResults();
538 
539  for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
540  // Assemble the new port name as "<prefix>_<name>", where the prefix is
541  // provided by the extraction annotation.
542  auto name = inst.getPortNameStr(portIdx);
543  auto nameAttr = StringAttr::get(
544  &getContext(),
545  prefix.empty() ? Twine(name) : Twine(prefix) + "_" + name);
546 
547  PortInfo newPort{nameAttr,
548  type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
549  direction::flip(inst.getPortDirection(portIdx))};
550  newPort.loc = inst.getResult(portIdx).getLoc();
551  newPorts.push_back({numParentPorts, newPort});
552  LLVM_DEBUG(llvm::dbgs()
553  << "- Adding port " << newPort.direction << " "
554  << newPort.name.getValue() << ": " << newPort.type << "\n");
555  }
556  parent.insertPorts(newPorts);
557  anythingChanged = true;
558 
559  // Replace all uses of the existing instance ports with the newly-created
560  // module ports.
561  for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
562  inst.getResult(portIdx).replaceAllUsesWith(
563  parent.getArgument(numParentPorts + portIdx));
564  }
565  assert(inst.use_empty() && "instance ports should have been detached");
566  DenseSet<hw::HierPathOp> instanceNLAs;
567  // Get the NLAs that pass through the InstanceOp `inst`.
568  // This does not returns NLAs that have the `inst` as the leaf.
569  nlaTable.getInstanceNLAs(inst, instanceNLAs);
570  // Map of the NLAs, that are applied to the InstanceOp. That is the NLA
571  // terminates on the InstanceOp.
572  DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
574  // Only consider annotations with a `circt.nonlocal` field.
575  auto nlaName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
576  if (!nlaName)
577  return false;
578  // Track the NLA.
579  if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
580  instNonlocalAnnos[nla].push_back(anno);
581  instanceNLAs.insert(nla);
582  }
583  return true;
584  });
585 
586  // Sort the instance NLAs we've collected by the NLA name to have a
587  // deterministic output.
588  SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
589  instanceNLAs.end());
590  llvm::sort(sortedInstanceNLAs,
591  [](auto a, auto b) { return a.getSymName() < b.getSymName(); });
592 
593  // Move the original instance one level up such that it is right next to
594  // the instances of the parent module, and wire the instance ports up to
595  // the newly added parent module ports.
596  auto *instParentNode =
597  instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
598  for (auto *instRecord : instParentNode->uses()) {
599  auto oldParentInst = cast<InstanceOp>(*instRecord->getInstance());
600  auto newParent = oldParentInst->getParentOfType<FModuleLike>();
601  LLVM_DEBUG(llvm::dbgs() << "- Updating " << oldParentInst << "\n");
602  auto newParentInst = oldParentInst.cloneAndInsertPorts(newPorts);
603 
604  // Migrate connections to existing ports.
605  for (unsigned portIdx = 0; portIdx < numParentPorts; ++portIdx)
606  oldParentInst.getResult(portIdx).replaceAllUsesWith(
607  newParentInst.getResult(portIdx));
608 
609  // Clone the existing instance and remove it from its current parent, such
610  // that we can insert it at its extracted location.
611  auto newInst = inst.cloneAndInsertPorts({});
612  newInst->remove();
613 
614  // Ensure that the `inner_sym` of the instance is unique within the parent
615  // module we're extracting it to.
616  if (auto instSym = getInnerSymName(inst)) {
617  auto newName =
618  getModuleNamespace(newParent).newName(instSym.getValue());
619  if (newName != instSym.getValue())
620  newInst.setInnerSymAttr(
621  hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
622  }
623 
624  // Add the moved instance and hook it up to the added ports.
625  ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
626  builder.setInsertionPointAfter(newParentInst);
627  builder.insert(newInst);
628  for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
629  auto dst = newInst.getResult(portIdx);
630  auto src = newParentInst.getResult(numParentPorts + portIdx);
631  if (newPorts[portIdx].second.direction == Direction::In)
632  std::swap(src, dst);
633  builder.create<StrictConnectOp>(dst, src);
634  }
635 
636  // Move the wiring prefix from the old to the new instance. We just look
637  // up the prefix for the old instance and if it exists, we remove it and
638  // assign it to the new instance. This has the effect of making the first
639  // new instance we create inherit the wiring prefix, and all additional
640  // new instances (e.g. through multiple instantiation of the parent) will
641  // pick a new prefix.
642  auto oldPrefix = instPrefices.find(inst);
643  if (oldPrefix != instPrefices.end()) {
644  LLVM_DEBUG(llvm::dbgs()
645  << " - Reusing prefix `" << oldPrefix->second << "`\n");
646  auto newPrefix = std::move(oldPrefix->second);
647  instPrefices.erase(oldPrefix);
648  instPrefices.insert({newInst, newPrefix});
649  }
650 
651  // Inherit the old instance's extraction path.
652  extractionPaths.try_emplace(newInst); // (create entry first)
653  auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
654  extractionPath.push_back(getInnerRefTo(newParentInst));
655  originalInstanceParents.try_emplace(newInst); // (create entry first)
656  originalInstanceParents[newInst] = originalInstanceParents[inst];
657  // Record the Nonlocal annotations that need to be applied to the new
658  // Inst.
659  SmallVector<Annotation> newInstNonlocalAnnos;
660 
661  // Update all NLAs that touch the moved instance.
662  for (auto nla : sortedInstanceNLAs) {
663  LLVM_DEBUG(llvm::dbgs() << " - Updating " << nla << "\n");
664 
665  // Find the position of the instance in the NLA path. This is going to
666  // be the position at which we have to modify the NLA.
667  SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
668  nla.getNamepath().end());
669  unsigned nlaIdx = findInstanceInNLA(inst, nla);
670 
671  // Handle the case where the instance no longer shows up in the NLA's
672  // path. This usually happens if the instance is extracted into multiple
673  // parents (because the current parent module is multiply instantiated).
674  // In that case NLAs that were specific to one instance may have been
675  // moved when we arrive at the second instance, and the NLA is already
676  // updated.
677  if (nlaIdx >= nlaPath.size()) {
678  LLVM_DEBUG(llvm::dbgs() << " - Instance no longer in path\n");
679  continue;
680  }
681  LLVM_DEBUG(llvm::dbgs() << " - Position " << nlaIdx << "\n");
682 
683  // Handle the case where the NLA's path doesn't go through the
684  // instance's new parent module, which happens if the current parent
685  // module is multiply instantiated. In that case, we only move over NLAs
686  // that actually affect the instance through the new parent module.
687  if (nlaIdx > 0) {
688  auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
689  if (innerRef &&
690  !(innerRef.getModule() == newParent.getModuleNameAttr() &&
691  innerRef.getName() == getInnerSymName(newParentInst))) {
692  LLVM_DEBUG(llvm::dbgs()
693  << " - Ignored since NLA parent " << innerRef
694  << " does not pass through extraction parent\n");
695  continue;
696  }
697  }
698 
699  // There are two interesting cases now:
700  // - If `nlaIdx == 0`, the NLA is rooted at the module the instance was
701  // located in prior to extraction. This indicates that the NLA applies
702  // to all instances of that parent module. Since we are extracting
703  // *out* of that module, we have to create a new NLA rooted at the new
704  // parent module after extraction.
705  // - If `nlaIdx > 0`, the NLA is rooted further up in the hierarchy and
706  // we can simply remove the old parent module from the path.
707 
708  // Handle the case where we need to come up with a new NLA for this
709  // instance since we've moved it past the module at which the old NLA
710  // was rooted at.
711  if (nlaIdx == 0) {
712  LLVM_DEBUG(llvm::dbgs() << " - Re-rooting " << nlaPath[0] << "\n");
713  assert(isa<InnerRefAttr>(nlaPath[0]) &&
714  "head of hierpath must be an InnerRefAttr");
715  nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
716  getInnerSymName(newInst));
717 
718  if (instParentNode->hasOneUse()) {
719  // Simply update the existing NLA since our parent is only
720  // instantiated once, and we therefore are not creating multiple
721  // instances through the extraction.
722  nlaTable.erase(nla);
723  nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
724  for (auto anno : instNonlocalAnnos.lookup(nla))
725  newInstNonlocalAnnos.push_back(anno);
726  nlaTable.addNLA(nla);
727  LLVM_DEBUG(llvm::dbgs() << " - Modified to " << nla << "\n");
728  } else {
729  // Since we are extracting to multiple parent locations, create a
730  // new NLA for each instantiation site.
731  auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
732  for (auto anno : instNonlocalAnnos.lookup(nla)) {
733  anno.setMember("circt.nonlocal",
734  FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
735  newInstNonlocalAnnos.push_back(anno);
736  }
737 
738  nlaTable.addNLA(newNla);
739  LLVM_DEBUG(llvm::dbgs() << " - Created " << newNla << "\n");
740  // CAVEAT(fschuiki): This results in annotations in the subhierarchy
741  // below `inst` with the old NLA symbol name, instead of those
742  // annotations duplicated for each of the newly-created NLAs. This
743  // shouldn't come up in our current use cases, but is a weakness of
744  // the current implementation. Instead, we should keep an NLA
745  // replication table that we fill with mappings from old NLA names
746  // to lists of new NLA names. A post-pass would then traverse the
747  // entire subhierarchy and go replicate all annotations with the old
748  // names.
749  inst.emitWarning("extraction of instance `")
750  << inst.getInstanceName()
751  << "` could break non-local annotations rooted at `"
752  << parent.getModuleName() << "`";
753  }
754  continue;
755  }
756 
757  // In the subequent code block we are going to remove one element from
758  // the NLA path, corresponding to the fact that the extracted instance
759  // has moved up in the hierarchy by one level. Removing that element may
760  // leave the NLA in a degenerate state, with only a single element in
761  // its path. If that is the case we have to convert the NLA into a
762  // regular local annotation.
763  if (nlaPath.size() == 2) {
764  for (auto anno : instNonlocalAnnos.lookup(nla)) {
765  anno.removeMember("circt.nonlocal");
766  newInstNonlocalAnnos.push_back(anno);
767  LLVM_DEBUG(llvm::dbgs() << " - Converted to local "
768  << anno.getDict() << "\n");
769  }
770  nlaTable.erase(nla);
771  nlasToRemove.insert(nla);
772  continue;
773  }
774 
775  // At this point the NLA looks like `NewParent::X, OldParent::BB`, and
776  // the `nlaIdx` points at `OldParent::BB`. To make our lives easier,
777  // since we know that `nlaIdx` is a `InnerRefAttr`, we'll modify
778  // `OldParent::BB` to be `NewParent::BB` and delete `NewParent::X`.
779  StringAttr parentName =
780  cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
781  Attribute newRef;
782  if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
783  newRef = InnerRefAttr::get(parentName, getInnerSymName(newInst));
784  else
785  newRef = FlatSymbolRefAttr::get(parentName);
786  LLVM_DEBUG(llvm::dbgs()
787  << " - Replacing " << nlaPath[nlaIdx - 1] << " and "
788  << nlaPath[nlaIdx] << " with " << newRef << "\n");
789  nlaPath[nlaIdx] = newRef;
790  nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
791 
792  if (isa<FlatSymbolRefAttr>(newRef)) {
793  // Since the original NLA ended at the instance's parent module, there
794  // is no guarantee that the instance is the sole user of the NLA (as
795  // opposed to the original NLA explicitly naming the instance). Create
796  // a new NLA.
797  auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
798  nlaTable.addNLA(newNla);
799  LLVM_DEBUG(llvm::dbgs() << " - Created " << newNla << "\n");
800  for (auto anno : instNonlocalAnnos.lookup(nla)) {
801  anno.setMember("circt.nonlocal",
802  FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
803  newInstNonlocalAnnos.push_back(anno);
804  }
805  } else {
806  nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
807  LLVM_DEBUG(llvm::dbgs() << " - Modified to " << nla << "\n");
808  for (auto anno : instNonlocalAnnos.lookup(nla))
809  newInstNonlocalAnnos.push_back(anno);
810  }
811 
812  // No update to NLATable required, since it will be deleted from the
813  // parent, and it should already exist in the new parent module.
814  continue;
815  }
816  AnnotationSet newInstAnnos(newInst);
817  newInstAnnos.addAnnotations(newInstNonlocalAnnos);
818  newInstAnnos.applyToOperation(newInst);
819 
820  // Add the moved instance to the extraction worklist such that it gets
821  // bubbled up further if needed.
822  extractionWorklist.push_back({newInst, info});
823  LLVM_DEBUG(llvm::dbgs() << " - Updated to " << newInst << "\n");
824 
825  // Keep instance graph up-to-date.
826  instanceGraph->replaceInstance(oldParentInst, newParentInst);
827  oldParentInst.erase();
828  }
829  // Remove the obsolete NLAs from the instance of the parent module, since
830  // the extracted instance no longer resides in that module and any NLAs to
831  // it no longer go through the parent module.
832  nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
833 
834  // Clean up the original instance.
835  inst.erase();
836  newPorts.clear();
837  }
838 
839  // Remove unused NLAs.
840  for (Operation *op : nlasToRemove) {
841  LLVM_DEBUG(llvm::dbgs() << "Removing obsolete " << *op << "\n");
842  op->erase();
843  }
844 }
845 
846 /// Group instances into submodules after they have been moved upwards. This
847 /// only occurs for instances that had the corresponding `dest` field of the
848 /// annotation set.
849 void ExtractInstancesPass::groupInstances() {
850  // Group the extracted instances by their wrapper module name and their parent
851  // module. Note that we cannot group instances that landed in different parent
852  // modules into the same submodule, so we use that parent module as a grouping
853  // key.
854  llvm::MapVector<std::pair<Operation *, StringRef>, SmallVector<InstanceOp>>
855  instsByWrapper;
856  for (auto &[inst, info] : extractedInstances) {
857  if (!info.wrapperModule.empty())
858  instsByWrapper[{inst->getParentOfType<FModuleOp>(), info.wrapperModule}]
859  .push_back(inst);
860  }
861  if (instsByWrapper.empty())
862  return;
863  LLVM_DEBUG(llvm::dbgs() << "\nGrouping instances into wrappers\n");
864 
865  // Generate the wrappers.
866  SmallVector<PortInfo> ports;
867  auto &nlaTable = getAnalysis<NLATable>();
868 
869  for (auto &[parentAndWrapperName, insts] : instsByWrapper) {
870  auto [parentOp, wrapperName] = parentAndWrapperName;
871  auto parent = cast<FModuleOp>(parentOp);
872  LLVM_DEBUG(llvm::dbgs() << "- Wrapper `" << wrapperName << "` in `"
873  << parent.getModuleName() << "` with "
874  << insts.size() << " instances\n");
875  OpBuilder builder(parentOp);
876 
877  // Uniquify the wrapper name.
878  auto wrapperModuleName = builder.getStringAttr(
879  circuitNamespace.newName(dutPrefix + wrapperName));
880  auto wrapperInstName =
881  builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
882 
883  // Assemble a list of ports for the wrapper module, which is basically just
884  // a concatenation of the wrapped instance ports. Also keep track of the
885  // NLAs that target the grouped instances since these will have to pass
886  // through the wrapper module.
887  ports.clear();
888  for (auto inst : insts) {
889  // Determine the ports for the wrapper.
890  StringRef prefix(instPrefices[inst]);
891  unsigned portNum = inst.getNumResults();
892  for (unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
893  auto name = inst.getPortNameStr(portIdx);
894  auto nameAttr = builder.getStringAttr(
895  prefix.empty() ? Twine(name) : Twine(prefix) + "_" + name);
896  PortInfo port{nameAttr,
897  type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
898  inst.getPortDirection(portIdx)};
899  port.loc = inst.getResult(portIdx).getLoc();
900  ports.push_back(port);
901  }
902 
903  // Set of NLAs that have a reference to this InstanceOp `inst`.
904  DenseSet<hw::HierPathOp> instNlas;
905  // Get the NLAs that pass through the `inst`, and not end at it.
906  nlaTable.getInstanceNLAs(inst, instNlas);
907  AnnotationSet instAnnos(inst);
908  // Get the NLAs that end at the InstanceOp, that is the Nonlocal
909  // annotations that apply to the InstanceOp.
910  for (auto anno : instAnnos) {
911  auto nlaName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
912  if (!nlaName)
913  continue;
914  hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
915  if (nla)
916  instNlas.insert(nla);
917  }
918  for (auto nla : instNlas) {
919  LLVM_DEBUG(llvm::dbgs() << " - Updating " << nla << "\n");
920 
921  // Find the position of the instance in the NLA path. This is going to
922  // be the position at which we have to modify the NLA.
923  SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
924  nla.getNamepath().end());
925  unsigned nlaIdx = findInstanceInNLA(inst, nla);
926  assert(nlaIdx < nlaPath.size() && "instance not found in its own NLA");
927  LLVM_DEBUG(llvm::dbgs() << " - Position " << nlaIdx << "\n");
928 
929  // The relevant part of the NLA is of the form `Top::bb`, which we want
930  // to expand to `Top::wrapperInst` and `Wrapper::bb`.
931  auto ref1 =
932  InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
933  Attribute ref2;
934  if (auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
935  ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
936  else
937  ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
938  LLVM_DEBUG(llvm::dbgs() << " - Expanding " << nlaPath[nlaIdx]
939  << " to (" << ref1 << ", " << ref2 << ")\n");
940  nlaPath[nlaIdx] = ref1;
941  nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
942  // CAVEAT: This is likely to conflict with additional users of `nla`
943  // that have nothing to do with this instance. Might need some NLATable
944  // machinery at some point to allow for these things to be updated.
945  nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
946  LLVM_DEBUG(llvm::dbgs() << " - Modified to " << nla << "\n");
947  // Add the NLA to the wrapper module.
948  nlaTable.addNLAtoModule(nla, wrapperModuleName);
949  }
950  }
951 
952  // Create the wrapper module.
953  auto wrapper = builder.create<FModuleOp>(
954  builder.getUnknownLoc(), wrapperModuleName,
955  ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
956  SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
957 
958  // Instantiate the wrapper module in the parent and replace uses of the
959  // extracted instances' ports with the corresponding wrapper module ports.
960  // This will essentially disconnect the extracted instances.
961  builder.setInsertionPointToStart(parent.getBodyBlock());
962  auto wrapperInst = builder.create<InstanceOp>(
963  wrapper.getLoc(), wrapper, wrapperName, NameKindEnum::DroppableName,
964  ArrayRef<Attribute>{},
965  /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/false,
966  hw::InnerSymAttr::get(wrapperInstName));
967  unsigned portIdx = 0;
968  for (auto inst : insts)
969  for (auto result : inst.getResults())
970  result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
971 
972  // Move all instances into the wrapper module and wire them up to the
973  // wrapper ports.
974  portIdx = 0;
975  builder.setInsertionPointToStart(wrapper.getBodyBlock());
976  for (auto inst : insts) {
977  inst->remove();
978  builder.insert(inst);
979  for (auto result : inst.getResults()) {
980  Value dst = result;
981  Value src = wrapper.getArgument(portIdx);
982  if (ports[portIdx].direction == Direction::Out)
983  std::swap(dst, src);
984  builder.create<StrictConnectOp>(result.getLoc(), dst, src);
985  ++portIdx;
986  }
987  }
988  }
989 }
990 
991 /// Generate trace files, which are plain text metadata files that list the
992 /// hierarchical path where each instance was extracted from. The file lists one
993 /// instance per line in the form `<prefix> -> <original-path>`.
994 void ExtractInstancesPass::createTraceFiles() {
995  LLVM_DEBUG(llvm::dbgs() << "\nGenerating trace files\n");
996 
997  // Group the extracted instances by their trace file name.
999  for (auto &[inst, info] : extractedInstances)
1000  if (!info.traceFilename.empty())
1001  instsByTraceFile[info.traceFilename].push_back(inst);
1002 
1003  // Generate the trace files.
1004  SmallVector<Attribute> symbols;
1005  SmallDenseMap<Attribute, unsigned> symbolIndices;
1006 
1007  for (auto &[fileName, insts] : instsByTraceFile) {
1008  LLVM_DEBUG(llvm::dbgs() << "- " << fileName << "\n");
1009  std::string buffer;
1010  llvm::raw_string_ostream os(buffer);
1011  symbols.clear();
1012  symbolIndices.clear();
1013 
1014  auto addSymbol = [&](Attribute symbol) {
1015  unsigned id;
1016  auto it = symbolIndices.find(symbol);
1017  if (it != symbolIndices.end()) {
1018  id = it->second;
1019  } else {
1020  id = symbols.size();
1021  symbols.push_back(symbol);
1022  symbolIndices.insert({symbol, id});
1023  }
1024  os << "{{" << id << "}}";
1025  };
1026 
1027  auto file = getOrCreateFile(fileName);
1028  auto builder = OpBuilder::atBlockEnd(file.getBody());
1029  for (auto inst : insts) {
1030  StringRef prefix(instPrefices[inst]);
1031  if (prefix.empty()) {
1032  LLVM_DEBUG(llvm::dbgs() << " - Skipping `" << inst.getName()
1033  << "` since it has no extraction prefix\n");
1034  continue;
1035  }
1036  ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1037  if (path.empty()) {
1038  LLVM_DEBUG(llvm::dbgs() << " - Skipping `" << inst.getName()
1039  << "` since it has not been moved\n");
1040  continue;
1041  }
1042  LLVM_DEBUG(llvm::dbgs()
1043  << " - " << prefix << ": " << inst.getName() << "\n");
1044  os << prefix << " -> ";
1045 
1046  // HACK: To match the Scala implementation, we strip all non-DUT modules
1047  // from the path and make the path look like it's rooted at the first DUT
1048  // module (so `TestHarness.dut.foo.bar` becomes `DUTModule.foo.bar`).
1049  while (!path.empty() &&
1050  !dutModuleNames.contains(path.back().getModule())) {
1051  LLVM_DEBUG(llvm::dbgs()
1052  << " - Dropping non-DUT segment " << path.back() << "\n");
1053  path = path.drop_back();
1054  }
1055  // HACK: This is extremely ugly. In case the instance was just moved by a
1056  // single level, the path may become empty. In that case we simply use the
1057  // instance's original parent before it was moved.
1058  addSymbol(FlatSymbolRefAttr::get(path.empty()
1059  ? originalInstanceParents[inst]
1060  : path.back().getModule()));
1061  for (auto sym : llvm::reverse(path)) {
1062  os << ".";
1063  addSymbol(sym);
1064  }
1065  // The final instance name is excluded as this does not provide useful
1066  // additional information and could conflict with a name inside the final
1067  // module.
1068  os << "\n";
1069  }
1070 
1071  // Put the information in a verbatim operation.
1072  builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), buffer,
1073  ValueRange{}, builder.getArrayAttr(symbols));
1074  }
1075 }
1076 
1077 //===----------------------------------------------------------------------===//
1078 // Pass Creation
1079 //===----------------------------------------------------------------------===//
1080 
1081 std::unique_ptr<mlir::Pass> circt::firrtl::createExtractInstancesPass() {
1082  return std::make_unique<ExtractInstancesPass>();
1083 }
assert(baseType &&"element must be base type")
static unsigned findInstanceInNLA(InstanceOp inst, hw::HierPathOp nla)
Find the location in an NLA that corresponds to a given instance (either by mentioning exactly the in...
static bool isAnnoInteresting(Annotation anno)
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
Builder builder
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
void setMember(StringAttr name, Attribute value)
Add or set a member of the annotation to a value.
void removeMember(StringAttr name)
Remove a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
Direction flip(Direction direction)
Flip a port direction.
constexpr const char * extractBlackBoxAnnoClass
constexpr const char * dutAnnoClass
constexpr const char * extractSeqMemsAnnoClass
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
std::unique_ptr< mlir::Pass > createExtractInstancesPass()
constexpr const char * extractClockGatesAnnoClass
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
Definition: FIRRTLOps.h:107
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24
This holds the name and type that describes the module's ports.