30 #include "mlir/IR/Attributes.h"
31 #include "mlir/IR/ImplicitLocOpBuilder.h"
32 #include "mlir/Pass/Pass.h"
33 #include "mlir/Support/FileUtilities.h"
34 #include "llvm/ADT/SmallPtrSet.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/MemoryBuffer.h"
37 #include "llvm/Support/Path.h"
39 #define DEBUG_TYPE "firrtl-extract-instances"
43 #define GEN_PASS_DEF_EXTRACTINSTANCES
44 #include "circt/Dialect/FIRRTL/Passes.h.inc"
48 using namespace circt;
49 using namespace firrtl;
50 using hw::InnerRefAttr;
58 struct ExtractionInfo {
60 StringRef traceFilename;
64 StringRef wrapperModule;
70 struct ExtractInstancesPass
71 :
public circt::firrtl::impl::ExtractInstancesBase<ExtractInstancesPass> {
72 void runOnOperation()
override;
74 void collectAnno(InstanceOp inst,
Annotation anno);
75 void extractInstances();
76 void groupInstances();
77 void createTraceFiles(ClassOp &sifiveMetadata);
81 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
82 return moduleNamespaces.try_emplace(module, module).first->second;
89 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
90 return getModuleNamespace(mod);
95 hw::HierPathOp cloneWithNewNameAndPath(hw::HierPathOp pathOp,
96 ArrayRef<Attribute> newPath) {
97 OpBuilder builder(pathOp);
98 auto newPathOp = builder.cloneWithoutRegions(pathOp);
99 newPathOp.setSymNameAttr(builder.getStringAttr(
100 circuitNamespace.newName(newPathOp.getSymName())));
101 newPathOp.setNamepathAttr(builder.getArrayAttr(newPath));
106 emit::FileOp getOrCreateFile(StringRef fileName) {
107 auto [it, inserted] = files.try_emplace(fileName, emit::FileOp{});
109 auto builder = ImplicitLocOpBuilder::atBlockEnd(
111 it->second = builder.create<emit::FileOp>(fileName);
116 bool anythingChanged;
121 SymbolTable *symbolTable =
nullptr;
125 DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
129 DenseSet<Operation *> dutRootModules;
132 DenseSet<Operation *> dutModules;
134 DenseSet<Attribute> dutModuleNames;
137 StringRef dutPrefix =
"";
140 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
143 DenseMap<StringRef, emit::FileOp> files;
149 DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
153 DenseMap<Operation *, StringAttr> originalInstanceParents;
157 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
160 DenseMap<Operation *, SmallString<16>> instPrefices;
165 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
167 ClassOp extractMetadataClass, schemaClass;
168 const unsigned prefixNameFieldId = 0, pathFieldId = 2, fileNameFieldId = 4;
171 DenseMap<InnerRefAttr, InstanceOp> innerRefToInstances;
176 void ExtractInstancesPass::runOnOperation() {
177 circuitOp = getOperation();
178 anythingChanged =
false;
180 annotatedModules.clear();
181 dutRootModules.clear();
183 dutModuleNames.clear();
185 extractionWorklist.clear();
187 extractionPaths.clear();
188 originalInstanceParents.clear();
189 extractedInstances.clear();
190 instPrefices.clear();
191 moduleNamespaces.clear();
192 circuitNamespace.clear();
193 circuitNamespace.add(circuitOp);
194 innerRefToInstances.clear();
195 extractMetadataClass = {};
200 instanceGraph = &getAnalysis<InstanceGraph>();
201 symbolTable = &getAnalysis<SymbolTable>();
204 return signalPassFailure();
209 return signalPassFailure();
214 return signalPassFailure();
216 ClassOp sifiveMetadata =
217 dyn_cast_or_null<ClassOp>(symbolTable->lookup(
"SiFive_Metadata"));
220 createTraceFiles(sifiveMetadata);
222 return signalPassFailure();
225 LLVM_DEBUG(llvm::dbgs() <<
"\n");
226 if (!anythingChanged)
227 markAllAnalysesPreserved();
236 void ExtractInstancesPass::collectAnnos() {
237 CircuitOp circuit = getOperation();
240 StringRef clkgateFileName;
241 StringRef clkgateWrapperModule;
245 LLVM_DEBUG(llvm::dbgs()
246 <<
"Clock gate extraction config: " << anno.
getDict() <<
"\n");
247 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
248 auto groupAttr = anno.
getMember<StringAttr>(
"group");
250 circuit.emitError(
"missing `filename` attribute in `")
251 << anno.
getClass() <<
"` annotation";
256 if (!clkgateFileName.empty()) {
257 circuit.emitError(
"multiple `")
258 << anno.getClass() <<
"` annotations on circuit";
263 clkgateFileName = filenameAttr.getValue();
265 clkgateWrapperModule = groupAttr.getValue();
270 StringRef memoryFileName;
271 StringRef memoryWrapperModule;
275 LLVM_DEBUG(llvm::dbgs()
276 <<
"Memory extraction config: " << anno.
getDict() <<
"\n");
277 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
278 auto groupAttr = anno.
getMember<StringAttr>(
"group");
280 circuit.emitError(
"missing `filename` attribute in `")
281 << anno.
getClass() <<
"` annotation";
286 if (!memoryFileName.empty()) {
287 circuit.emitError(
"multiple `")
288 << anno.getClass() <<
"` annotations on circuit";
293 memoryFileName = filenameAttr.getValue();
295 memoryWrapperModule = groupAttr.getValue();
301 for (
auto module : circuit.getOps<FModuleLike>()) {
304 LLVM_DEBUG(llvm::dbgs()
305 <<
"Marking DUT `" << module.getModuleName() <<
"`\n");
306 dutRootModules.insert(module);
307 dutModules.insert(module);
308 if (auto prefix = anno.getMember<StringAttr>(
"prefix"))
314 LLVM_DEBUG(llvm::dbgs() <<
"Annotated module `" << module.getModuleName()
315 <<
"`:\n " << anno.
getDict() <<
"\n");
316 annotatedModules[module].push_back(anno);
322 circuit.walk([&](InstanceOp inst) {
323 SmallVector<Annotation, 1> instAnnos;
324 Operation *module = inst.getReferencedModule(*instanceGraph);
327 auto it = annotatedModules.find(module);
328 if (it != annotatedModules.end())
329 instAnnos.append(it->second);
335 LLVM_DEBUG(llvm::dbgs() <<
"Annotated instance `" << inst.getName()
336 <<
"`:\n " << anno.
getDict() <<
"\n");
337 instAnnos.push_back(anno);
342 if (instAnnos.empty())
346 if (instAnnos.size() > 1) {
347 auto d = inst.emitError(
"multiple extraction annotations on instance `")
348 << inst.getName() <<
"`";
349 d.attachNote(inst.getLoc()) <<
"instance has the following annotations, "
350 "but at most one is allowed:";
351 for (
auto anno : instAnnos)
352 d.attachNote(inst.getLoc()) << anno.
getDict();
358 collectAnno(inst, instAnnos[0]);
362 LLVM_DEBUG(llvm::dbgs() <<
"Marking DUT hierarchy\n");
363 SmallVector<InstanceGraphNode *> worklist;
364 for (Operation *op : dutModules)
366 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(op)));
367 while (!worklist.empty()) {
368 auto *module = worklist.pop_back_val();
369 dutModuleNames.insert(module->getModule().getModuleNameAttr());
370 LLVM_DEBUG(llvm::dbgs()
371 <<
"- " << module->getModule().getModuleName() <<
"\n");
372 for (
auto *instRecord : *module) {
373 auto *target = instRecord->getTarget();
374 if (dutModules.insert(target->getModule()).second)
375 worklist.push_back(target);
383 if (!clkgateFileName.empty()) {
384 auto clkgateDefNameAttr =
StringAttr::get(&getContext(),
"EICG_wrapper");
385 for (
auto module : circuit.getOps<FExtModuleOp>()) {
386 if (module.getDefnameAttr() != clkgateDefNameAttr)
388 LLVM_DEBUG(llvm::dbgs()
389 <<
"Clock gate `" << module.getModuleName() <<
"`\n");
390 if (!dutModules.contains(module)) {
391 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
396 info.traceFilename = clkgateFileName;
397 info.prefix =
"clock_gate";
398 info.wrapperModule = clkgateWrapperModule;
399 info.stopAtDUT = !info.wrapperModule.empty();
400 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
401 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
402 LLVM_DEBUG(llvm::dbgs()
404 << inst->getParentOfType<FModuleLike>().getModuleName()
405 <<
"." << inst.getName() <<
"`\n");
406 extractionWorklist.push_back({inst, info});
415 if (!memoryFileName.empty()) {
419 getOrCreateFile(memoryFileName);
421 for (
auto module : circuit.getOps<FMemModuleOp>()) {
422 LLVM_DEBUG(llvm::dbgs() <<
"Memory `" << module.getModuleName() <<
"`\n");
423 if (!dutModules.contains(module)) {
424 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
429 info.traceFilename = memoryFileName;
430 info.prefix =
"mem_wiring";
431 info.wrapperModule = memoryWrapperModule;
432 info.stopAtDUT = !info.wrapperModule.empty();
433 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
434 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
435 LLVM_DEBUG(llvm::dbgs()
437 << inst->getParentOfType<FModuleLike>().getModuleName()
438 <<
"." << inst.getName() <<
"`\n");
439 extractionWorklist.push_back({inst, info});
448 void ExtractInstancesPass::collectAnno(InstanceOp inst,
Annotation anno) {
449 LLVM_DEBUG(llvm::dbgs() <<
"Processing instance `" << inst.getName() <<
"` "
452 auto getStringOrError = [&](StringRef member) {
453 auto attr = anno.
getMember<StringAttr>(member);
455 inst.emitError(
"missing `")
456 << member <<
"` attribute in `" << anno.
getClass() <<
"` annotation";
463 auto filename = getStringOrError(
"filename");
464 auto prefix = getStringOrError(
"prefix");
465 auto dest = anno.
getMember<StringAttr>(
"dest");
470 info.traceFilename = filename;
471 info.prefix = prefix;
472 info.wrapperModule = (dest ? dest.getValue() :
"");
477 info.stopAtDUT = !info.wrapperModule.empty();
479 extractionWorklist.push_back({inst, info});
489 unsigned nlaLen = nla.getNamepath().size();
491 auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
492 for (
unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
493 auto refPart = nla.refPart(nlaIdx);
494 if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
503 void ExtractInstancesPass::extractInstances() {
506 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
508 DenseMap<StringRef, unsigned> prefixUniqueIDs;
510 SmallPtrSet<Operation *, 4> nlasToRemove;
512 auto &nlaTable = getAnalysis<NLATable>();
515 for (
auto &[inst, info] : extractionWorklist)
516 originalInstanceParents[inst] =
517 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
519 while (!extractionWorklist.empty()) {
522 std::tie(inst, info) = extractionWorklist.pop_back_val();
523 auto parent = inst->getParentOfType<FModuleOp>();
532 if (!info.prefix.empty()) {
533 auto &prefixSlot = instPrefices[inst];
534 if (prefixSlot.empty()) {
535 auto idx = prefixUniqueIDs[info.prefix]++;
536 (Twine(info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
545 if (!dutModules.contains(parent) ||
546 instanceGraph->lookup(parent)->noUses() ||
547 (info.stopAtDUT && dutRootModules.contains(parent))) {
548 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
549 extractedInstances.push_back({inst, info});
553 llvm::dbgs() <<
"\nMoving ";
555 llvm::dbgs() <<
"`" << prefix <<
"` ";
556 llvm::dbgs() << inst <<
"\n";
561 unsigned numParentPorts = parent.getNumPorts();
562 unsigned numInstPorts = inst.getNumResults();
564 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
567 auto name = inst.getPortNameStr(portIdx);
570 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
573 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
575 newPort.loc = inst.getResult(portIdx).getLoc();
576 newPorts.push_back({numParentPorts, newPort});
577 LLVM_DEBUG(llvm::dbgs()
578 <<
"- Adding port " << newPort.direction <<
" "
579 << newPort.name.getValue() <<
": " << newPort.type <<
"\n");
581 parent.insertPorts(newPorts);
582 anythingChanged =
true;
586 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
587 inst.getResult(portIdx).replaceAllUsesWith(
588 parent.getArgument(numParentPorts + portIdx));
590 assert(inst.use_empty() &&
"instance ports should have been detached");
591 DenseSet<hw::HierPathOp> instanceNLAs;
594 nlaTable.getInstanceNLAs(inst, instanceNLAs);
597 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
600 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
604 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
605 instNonlocalAnnos[nla].push_back(anno);
606 instanceNLAs.insert(nla);
613 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
615 llvm::sort(sortedInstanceNLAs,
616 [](
auto a,
auto b) {
return a.getSymName() < b.getSymName(); });
621 auto *instParentNode =
622 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
623 for (
auto *instRecord : instParentNode->uses()) {
624 auto oldParentInst = cast<InstanceOp>(*instRecord->getInstance());
625 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
626 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
627 auto newParentInst = oldParentInst.cloneAndInsertPorts(newPorts);
628 if (newParentInst.getInnerSymAttr())
629 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
632 for (
unsigned portIdx = 0; portIdx < numParentPorts; ++portIdx)
633 oldParentInst.getResult(portIdx).replaceAllUsesWith(
634 newParentInst.getResult(portIdx));
638 auto newInst = inst.cloneAndInsertPorts({});
645 getModuleNamespace(newParent).newName(instSym.getValue());
646 if (newName != instSym.getValue())
647 newInst.setInnerSymAttr(
652 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
653 builder.setInsertionPointAfter(newParentInst);
654 builder.insert(newInst);
655 if (newParentInst.getInnerSymAttr())
657 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
658 auto dst = newInst.getResult(portIdx);
659 auto src = newParentInst.getResult(numParentPorts + portIdx);
662 builder.create<MatchingConnectOp>(dst, src);
671 auto oldPrefix = instPrefices.find(inst);
672 if (oldPrefix != instPrefices.end()) {
673 LLVM_DEBUG(llvm::dbgs()
674 <<
" - Reusing prefix `" << oldPrefix->second <<
"`\n");
675 auto newPrefix = std::move(oldPrefix->second);
676 instPrefices.erase(oldPrefix);
677 instPrefices.insert({newInst, newPrefix});
681 extractionPaths.try_emplace(newInst);
682 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
684 innerRefToInstances[instInnerRef] = newParentInst;
685 extractionPath.push_back(instInnerRef);
686 originalInstanceParents.try_emplace(newInst);
687 originalInstanceParents[newInst] = originalInstanceParents[inst];
690 SmallVector<Annotation> newInstNonlocalAnnos;
693 for (
auto nla : sortedInstanceNLAs) {
694 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
698 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
699 nla.getNamepath().end());
708 if (nlaIdx >= nlaPath.size()) {
709 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
712 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
719 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
721 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
723 LLVM_DEBUG(llvm::dbgs()
724 <<
" - Ignored since NLA parent " << innerRef
725 <<
" does not pass through extraction parent\n");
743 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
744 assert(isa<InnerRefAttr>(nlaPath[0]) &&
745 "head of hierpath must be an InnerRefAttr");
749 if (instParentNode->hasOneUse()) {
754 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
755 for (
auto anno : instNonlocalAnnos.lookup(nla))
756 newInstNonlocalAnnos.push_back(anno);
757 nlaTable.addNLA(nla);
758 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
762 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
763 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
766 newInstNonlocalAnnos.push_back(anno);
769 nlaTable.addNLA(newNla);
770 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
780 inst.emitWarning(
"extraction of instance `")
781 << inst.getInstanceName()
782 <<
"` could break non-local annotations rooted at `"
783 << parent.getModuleName() <<
"`";
794 if (nlaPath.size() == 2) {
795 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
797 newInstNonlocalAnnos.push_back(anno);
798 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
802 nlasToRemove.insert(nla);
810 StringAttr parentName =
811 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
813 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
817 LLVM_DEBUG(llvm::dbgs()
818 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
819 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
820 nlaPath[nlaIdx] = newRef;
821 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
823 if (isa<FlatSymbolRefAttr>(newRef)) {
828 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
829 nlaTable.addNLA(newNla);
830 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
831 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
834 newInstNonlocalAnnos.push_back(anno);
837 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
838 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
839 for (
auto anno : instNonlocalAnnos.lookup(nla))
840 newInstNonlocalAnnos.push_back(anno);
848 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
849 newInstAnnos.applyToOperation(newInst);
853 extractionWorklist.push_back({newInst, info});
854 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
857 instanceGraph->replaceInstance(oldParentInst, newParentInst);
858 oldParentInst.erase();
863 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
871 for (Operation *op : nlasToRemove) {
872 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
880 void ExtractInstancesPass::groupInstances() {
885 llvm::MapVector<std::pair<Operation *, StringRef>, SmallVector<InstanceOp>>
887 for (
auto &[inst, info] : extractedInstances) {
888 if (!info.wrapperModule.empty())
889 instsByWrapper[{inst->getParentOfType<FModuleOp>(), info.wrapperModule}]
892 if (instsByWrapper.empty())
894 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
897 SmallVector<PortInfo> ports;
898 auto &nlaTable = getAnalysis<NLATable>();
900 for (
auto &[parentAndWrapperName, insts] : instsByWrapper) {
901 auto [parentOp, wrapperName] = parentAndWrapperName;
902 auto parent = cast<FModuleOp>(parentOp);
903 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
904 << parent.getModuleName() <<
"` with "
905 << insts.size() <<
" instances\n");
906 OpBuilder builder(parentOp);
909 auto wrapperModuleName = builder.getStringAttr(
910 circuitNamespace.newName(dutPrefix + wrapperName));
911 auto wrapperInstName =
912 builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
919 for (
auto inst : insts) {
921 StringRef prefix(instPrefices[inst]);
922 unsigned portNum = inst.getNumResults();
923 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
924 auto name = inst.getPortNameStr(portIdx);
925 auto nameAttr = builder.getStringAttr(
926 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
928 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
929 inst.getPortDirection(portIdx)};
930 port.
loc = inst.getResult(portIdx).getLoc();
931 ports.push_back(port);
935 DenseSet<hw::HierPathOp> instNlas;
937 nlaTable.getInstanceNLAs(inst, instNlas);
941 for (
auto anno : instAnnos) {
942 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
945 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
947 instNlas.insert(nla);
949 for (
auto nla : instNlas) {
950 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
954 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
955 nla.getNamepath().end());
957 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
958 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
965 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
969 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
970 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
971 nlaPath[nlaIdx] = ref1;
972 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
976 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
977 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
979 nlaTable.addNLAtoModule(nla, wrapperModuleName);
984 auto wrapper = builder.create<FModuleOp>(
985 builder.getUnknownLoc(), wrapperModuleName,
987 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
992 builder.setInsertionPointToStart(parent.getBodyBlock());
993 auto wrapperInst = builder.create<InstanceOp>(
994 wrapper.getLoc(), wrapper, wrapperName, NameKindEnum::DroppableName,
995 ArrayRef<Attribute>{},
996 ArrayRef<Attribute>{},
false,
998 unsigned portIdx = 0;
999 for (
auto inst : insts)
1000 for (
auto result : inst.getResults())
1001 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
1006 builder.setInsertionPointToStart(wrapper.getBodyBlock());
1007 for (
auto inst : insts) {
1009 builder.insert(inst);
1010 for (
auto result : inst.getResults()) {
1012 Value src = wrapper.getArgument(portIdx);
1014 std::swap(dst, src);
1015 builder.create<MatchingConnectOp>(result.getLoc(), dst, src);
1025 void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1026 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1029 llvm::MapVector<StringRef, SmallVector<InstanceOp>> instsByTraceFile;
1030 for (
auto &[inst, info] : extractedInstances)
1031 if (!info.traceFilename.empty())
1032 instsByTraceFile[info.traceFilename].push_back(inst);
1035 SmallVector<Attribute> symbols;
1037 if (sifiveMetadataClass && !extractMetadataClass)
1040 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1042 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1043 classOp.getLoc(), classOp.getBodyBlock());
1044 auto portIndex = classOp.getNumPorts();
1045 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1046 for (
auto [index, port] : enumerate(objFields)) {
1048 auto obj = port.first;
1049 newPorts.emplace_back(
1051 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1054 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1055 builderOM.create<PropAssignOp>(blockarg, obj);
1057 classOp.insertPorts(newPorts);
1061 SmallVector<std::pair<Value, Twine>> classFields;
1062 for (
auto &[fileName, insts] : instsByTraceFile) {
1063 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1065 llvm::raw_string_ostream os(buffer);
1067 symbolIndices.clear();
1069 auto addSymbol = [&](Attribute symbol) {
1071 auto it = symbolIndices.find(symbol);
1072 if (it != symbolIndices.end()) {
1075 id = symbols.size();
1076 symbols.push_back(symbol);
1077 symbolIndices.insert({symbol,
id});
1079 os <<
"{{" <<
id <<
"}}";
1082 auto file = getOrCreateFile(fileName);
1083 auto builder = OpBuilder::atBlockEnd(file.getBody());
1084 for (
auto inst : insts) {
1085 StringRef prefix(instPrefices[inst]);
1086 if (prefix.empty()) {
1087 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1088 <<
"` since it has no extraction prefix\n");
1091 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1093 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1094 <<
"` since it has not been moved\n");
1097 LLVM_DEBUG(llvm::dbgs()
1098 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1099 os << prefix <<
" -> ";
1101 if (sifiveMetadataClass) {
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);
1108 builderOM.create<ObjectSubfieldOp>(object, prefixNameFieldId);
1109 builderOM.create<PropAssignOp>(fPrefix, prefixName);
1111 auto targetInstance = innerRefToInstances[path.front()];
1112 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1113 auto nla = pathCache.getOpFor(
1116 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1117 auto fPath = builderOM.create<ObjectSubfieldOp>(object, pathFieldId);
1118 builderOM.create<PropAssignOp>(fPath, pathOp);
1120 builderOM.create<ObjectSubfieldOp>(object, fileNameFieldId);
1122 builderOM.create<StringConstantOp>(builder.getStringAttr(fileName));
1123 builderOM.create<PropAssignOp>(fFile, fileNameOp);
1126 classFields.emplace_back(
object, prefix +
"_field");
1131 while (!path.empty() &&
1132 !dutModuleNames.contains(path.back().getModule())) {
1133 LLVM_DEBUG(llvm::dbgs()
1134 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1135 path = path.drop_back();
1141 ? originalInstanceParents[inst]
1142 : path.back().getModule()));
1143 for (
auto sym : llvm::reverse(path)) {
1154 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), buffer,
1155 ValueRange{}, builder.getArrayAttr(symbols));
1157 if (!classFields.empty()) {
1158 addPortsToClass(classFields, extractMetadataClass);
1162 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1163 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1164 SmallVector<std::pair<Value, Twine>> classFields = {
1165 {builderOM.create<ObjectOp>(
1166 extractMetadataClass,
1167 builderOM.getStringAttr(
"extract_instances_metadata")),
1168 "extractedInstances_field"}};
1170 addPortsToClass(classFields, sifiveMetadataClass);
1171 auto *node = instanceGraph->lookup(sifiveMetadataClass);
1172 assert(node && node->hasOneUse());
1173 ObjectOp metadataObj =
1174 dyn_cast_or_null<ObjectOp>((*node->usesBegin())->getInstance());
1176 "expected the class to be instantiated by an object op");
1177 builderOM.setInsertionPoint(metadataObj);
1179 builderOM.create<ObjectOp>(sifiveMetadataClass, metadataObj.getName());
1180 metadataObj->replaceAllUsesWith(newObj);
1181 metadataObj->remove();
1185 void ExtractInstancesPass::createSchema() {
1187 auto *context = circuitOp->getContext();
1189 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1190 unknownLoc, circuitOp.getBodyBlock());
1191 mlir::Type portsType[] = {
1196 StringRef portFields[] = {
"name",
"path",
"filename"};
1198 schemaClass = builderOM.create<ClassOp>(
"ExtractInstancesSchema", portFields,
1202 SmallVector<PortInfo> mports;
1203 extractMetadataClass = builderOM.create<ClassOp>(
1204 builderOM.getStringAttr(
"ExtractInstancesMetadata"), mports);
1211 return std::make_unique<ExtractInstancesPass>();
assert(baseType &&"element must be base type")
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.
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
This holds the name and type that describes the module's ports.