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