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