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