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"
44#define DEBUG_TYPE "firrtl-extract-instances"
48#define GEN_PASS_DEF_EXTRACTINSTANCES
49#include "circt/Dialect/FIRRTL/Passes.h.inc"
54using namespace firrtl;
55using hw::InnerRefAttr;
63struct ExtractionInfo {
65 StringRef traceFilename;
69 StringRef wrapperModule;
75struct ExtractInstancesPass
76 :
public circt::firrtl::impl::ExtractInstancesBase<ExtractInstancesPass> {
77 void runOnOperation()
override;
79 void collectAnno(InstanceOp inst,
Annotation anno);
80 void extractInstances();
81 void groupInstances();
82 void createTraceFiles(ClassOp &sifiveMetadata);
87 return moduleNamespaces.try_emplace(module, module).first->second;
93 return ::getInnerRefTo(op,
95 return getModuleNamespace(mod);
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));
111 emit::FileOp getOrCreateFile(StringRef fileName) {
112 auto [it, inserted] = files.try_emplace(fileName, emit::FileOp{});
114 auto builder = ImplicitLocOpBuilder::atBlockEnd(
115 UnknownLoc::get(&getContext()), getOperation().
getBodyBlock());
116 it->second = emit::FileOp::create(builder, fileName);
121 bool anythingChanged;
127 SymbolTable *symbolTable =
nullptr;
131 DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
134 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
137 DenseMap<StringRef, emit::FileOp> files;
143 DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
147 DenseMap<Operation *, StringAttr> originalInstanceParents;
151 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
154 DenseMap<Operation *, std::pair<SmallString<16>, StringAttr>>
160 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
162 ClassOp extractMetadataClass, schemaClass;
163 const unsigned prefixNameFieldId = 0, pathFieldId = 2, fileNameFieldId = 4,
167 DenseMap<InnerRefAttr, InstanceOp> innerRefToInstances;
168 Type stringType, pathType;
178void ExtractInstancesPass::runOnOperation() {
179 circuitOp = getOperation();
180 anythingChanged =
false;
182 annotatedModules.clear();
183 extractionWorklist.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 = {};
195 auto *
context = circuitOp->getContext();
196 stringType = StringType::get(
context);
197 pathType = PathType::get(
context);
202 instanceGraph = &getAnalysis<InstanceGraph>();
203 instanceInfo = &getAnalysis<InstanceInfo>();
204 symbolTable = &getAnalysis<SymbolTable>();
207 return signalPassFailure();
212 return signalPassFailure();
217 return signalPassFailure();
219 ClassOp sifiveMetadata =
220 dyn_cast_or_null<ClassOp>(symbolTable->lookup(
"SiFive_Metadata"));
223 createTraceFiles(sifiveMetadata);
225 return signalPassFailure();
228 LLVM_DEBUG(llvm::dbgs() <<
"\n");
229 if (!anythingChanged)
230 markAllAnalysesPreserved();
234 return anno.
isClass(extractBlackBoxAnnoClass);
239void ExtractInstancesPass::collectAnnos() {
240 CircuitOp circuit = getOperation();
251 if (!anno.
isClass(injectDUTHierarchyAnnoClass))
254 if (
auto moveDutAnnoAttr = anno.
getMember<BoolAttr>(
"moveDut"))
255 moveDut = moveDutAnnoAttr.getValue();
260 StringRef clkgateFileName;
261 StringRef clkgateWrapperModule;
263 if (!anno.
isClass(extractClockGatesFileAnnoClass))
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");
270 circuit.emitError(
"missing `filename` attribute in `")
271 << anno.
getClass() <<
"` annotation";
276 if (!clkgateFileName.empty()) {
277 circuit.emitError(
"multiple `")
278 << anno.
getClass() <<
"` annotations on circuit";
283 clkgateFileName = filenameAttr.getValue();
285 clkgateWrapperModule = groupAttr.getValue();
290 StringRef memoryFileName;
291 StringRef memoryWrapperModule;
293 if (!anno.
isClass(extractSeqMemsFileAnnoClass))
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");
300 circuit.emitError(
"missing `filename` attribute in `")
301 << anno.
getClass() <<
"` annotation";
306 if (!memoryFileName.empty()) {
307 circuit.emitError(
"multiple `")
308 << anno.
getClass() <<
"` annotations on circuit";
313 memoryFileName = filenameAttr.getValue();
315 memoryWrapperModule = groupAttr.getValue();
321 for (
auto module : circuit.getOps<FModuleLike>()) {
325 LLVM_DEBUG(llvm::dbgs() <<
"Annotated module `" << module.getModuleName()
326 <<
"`:\n " << anno.
getDict() <<
"\n");
327 annotatedModules[module].push_back(anno);
333 circuit.walk([&](InstanceOp inst) {
334 SmallVector<Annotation, 1> instAnnos;
335 Operation *
module = inst.getReferencedModule(*instanceGraph);
338 auto it = annotatedModules.find(module);
339 if (it != annotatedModules.end())
340 instAnnos.append(it->second);
346 LLVM_DEBUG(llvm::dbgs() <<
"Annotated instance `" << inst.getName()
347 <<
"`:\n " << anno.
getDict() <<
"\n");
348 instAnnos.push_back(anno);
353 if (instAnnos.empty())
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();
369 collectAnno(inst, instAnnos[0]);
378 if (!clkgateFileName.empty()) {
379 for (
auto module : circuit.getOps<FExtModuleOp>()) {
380 if (!module.getDefnameAttr().getValue().ends_with(
"EICG_wrapper"))
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");
390 info.traceFilename = clkgateFileName;
391 info.prefix =
"clock_gate";
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()
397 << inst->getParentOfType<FModuleLike>().getModuleName()
398 <<
"." << inst.getName() <<
"`\n");
399 extractionWorklist.push_back({inst,
info});
401 instRecord->getInstance()->emitError()
402 <<
"cannot extract clock gate instances through non-InstanceOp";
412 if (!memoryFileName.empty()) {
416 getOrCreateFile(memoryFileName);
418 for (
auto module : circuit.getOps<FMemModuleOp>()) {
419 LLVM_DEBUG(llvm::dbgs() <<
"Memory `" << module.getModuleName() <<
"`\n");
420 if (!instanceInfo->anyInstanceInDesign(module)) {
421 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
426 info.traceFilename = memoryFileName;
427 info.prefix =
"mem_wiring";
428 info.wrapperModule = memoryWrapperModule;
429 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
430 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
431 LLVM_DEBUG(llvm::dbgs()
433 << inst->getParentOfType<FModuleLike>().getModuleName()
434 <<
"." << inst.getName() <<
"`\n");
435 extractionWorklist.push_back({inst,
info});
437 instRecord->getInstance()->emitError()
438 <<
"cannot extract memory instances through non-InstanceOp";
448void 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";
462 if (anno.
isClass(extractBlackBoxAnnoClass)) {
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() :
"");
478 extractionWorklist.push_back({inst,
info});
488 unsigned nlaLen = nla.getNamepath().size();
490 auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
491 for (
unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
492 auto refPart = nla.refPart(nlaIdx);
493 if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
502void ExtractInstancesPass::extractInstances() {
505 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
507 DenseMap<StringRef, unsigned> prefixUniqueIDs;
509 SmallPtrSet<Operation *, 4> nlasToRemove;
511 auto &nlaTable = getAnalysis<NLATable>();
514 for (
auto &[inst, info] : extractionWorklist)
515 originalInstanceParents[inst] =
516 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
518 while (!extractionWorklist.empty()) {
521 std::tie(inst, info) = extractionWorklist.pop_back_val();
523 auto parent = inst->getParentOfType<FModuleOp>();
532 auto &instPrefixEntry = instPrefixNamesPair[inst];
533 instPrefixEntry.second = inst.getInstanceNameAttr();
534 if (!
info.prefix.empty()) {
535 auto &prefixSlot = instPrefixEntry.first;
536 if (prefixSlot.empty()) {
537 auto idx = prefixUniqueIDs[
info.prefix]++;
538 (Twine(
info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
545 bool stopAtDUT = !moveDut && !
info.wrapperModule.empty();
551 if (inst->getParentOfType<LayerBlockOp>() ||
552 !instanceInfo->anyInstanceInDesign(parent) ||
553 instanceGraph->lookup(parent)->noUses() ||
554 (stopAtDUT && instanceInfo->isDut(parent))) {
555 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
556 extractedInstances.push_back({inst,
info});
560 llvm::dbgs() <<
"\nMoving ";
562 llvm::dbgs() <<
"`" << prefix <<
"` ";
563 llvm::dbgs() << inst <<
"\n";
568 unsigned numParentPorts = parent.getNumPorts();
569 unsigned numInstPorts = inst.getNumResults();
571 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
574 auto name = inst.getPortName(portIdx);
575 auto nameAttr = StringAttr::get(
577 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
580 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
582 newPort.
loc = inst.getResult(portIdx).getLoc();
583 newPorts.push_back({numParentPorts, newPort});
584 LLVM_DEBUG(llvm::dbgs()
585 <<
"- Adding port " << newPort.direction <<
" "
586 << newPort.name.getValue() <<
": " << newPort.type <<
"\n");
588 parent.insertPorts(newPorts);
589 anythingChanged =
true;
593 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
594 inst.getResult(portIdx).replaceAllUsesWith(
595 parent.getArgument(numParentPorts + portIdx));
597 assert(inst.use_empty() &&
"instance ports should have been detached");
598 DenseSet<hw::HierPathOp> instanceNLAs;
601 nlaTable.getInstanceNLAs(inst, instanceNLAs);
604 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
607 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
611 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
612 instNonlocalAnnos[nla].push_back(anno);
613 instanceNLAs.insert(nla);
620 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
622 llvm::sort(sortedInstanceNLAs,
623 [](
auto a,
auto b) {
return a.getSymName() <
b.getSymName(); });
628 auto *instParentNode =
629 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
630 for (
auto *instRecord : instParentNode->uses()) {
631 auto oldParentInst = dyn_cast<InstanceOp>(*instRecord->getInstance());
632 if (!oldParentInst) {
633 inst.emitError(
"cannot extract instance `")
634 << inst.getName() <<
"` through a non-InstanceOp parent";
638 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
639 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
640 auto newParentInst = cast<InstanceOp>(
641 oldParentInst.cloneWithInsertedPortsAndReplaceUses(newPorts));
642 if (newParentInst.getInnerSymAttr())
643 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
645 auto newInst = cast<InstanceOp>(inst->clone());
651 getModuleNamespace(newParent).newName(instSym.getValue());
652 if (newName != instSym.getValue())
653 newInst.setInnerSymAttr(
654 hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
658 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
659 builder.setInsertionPointAfter(newParentInst);
660 builder.insert(newInst);
661 if (newParentInst.getInnerSymAttr())
663 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
664 auto dst = newInst.getResult(portIdx);
665 auto src = newParentInst.getResult(numParentPorts + portIdx);
666 if (newPorts[portIdx].second.direction == Direction::In)
668 MatchingConnectOp::create(builder, dst, src);
677 auto oldPrefix = instPrefixNamesPair.find(inst);
678 if (oldPrefix != instPrefixNamesPair.end()) {
679 LLVM_DEBUG(llvm::dbgs() <<
" - Reusing prefix `"
680 << oldPrefix->second.first <<
"`\n");
681 auto newPrefix = std::move(oldPrefix->second);
682 instPrefixNamesPair.erase(oldPrefix);
683 instPrefixNamesPair.insert({newInst, newPrefix});
687 extractionPaths.try_emplace(newInst);
688 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
690 innerRefToInstances[instInnerRef] = newParentInst;
691 extractionPath.push_back(instInnerRef);
692 originalInstanceParents.try_emplace(newInst);
693 originalInstanceParents[newInst] = originalInstanceParents[inst];
696 SmallVector<Annotation> newInstNonlocalAnnos;
699 for (
auto nla : sortedInstanceNLAs) {
700 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
704 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
705 nla.getNamepath().end());
714 if (nlaIdx >= nlaPath.size()) {
715 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
718 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
725 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
727 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
729 LLVM_DEBUG(llvm::dbgs()
730 <<
" - Ignored since NLA parent " << innerRef
731 <<
" does not pass through extraction parent\n");
749 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
750 assert(isa<InnerRefAttr>(nlaPath[0]) &&
751 "head of hierpath must be an InnerRefAttr");
752 nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
755 if (instParentNode->hasOneUse()) {
760 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
761 for (
auto anno : instNonlocalAnnos.lookup(nla))
762 newInstNonlocalAnnos.push_back(anno);
763 nlaTable.addNLA(nla);
764 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
768 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
769 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
771 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
772 newInstNonlocalAnnos.push_back(anno);
775 nlaTable.addNLA(newNla);
776 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
786 inst.emitWarning(
"extraction of instance `")
787 << inst.getInstanceName()
788 <<
"` could break non-local annotations rooted at `"
789 << parent.getModuleName() <<
"`";
800 if (nlaPath.size() == 2) {
801 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
803 newInstNonlocalAnnos.push_back(anno);
804 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
808 nlasToRemove.insert(nla);
816 StringAttr parentName =
817 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
819 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
822 newRef = FlatSymbolRefAttr::get(parentName);
823 LLVM_DEBUG(llvm::dbgs()
824 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
825 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
826 nlaPath[nlaIdx] = newRef;
827 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
829 if (isa<FlatSymbolRefAttr>(newRef)) {
834 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
835 nlaTable.addNLA(newNla);
836 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
837 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
839 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
840 newInstNonlocalAnnos.push_back(anno);
843 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
844 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
845 for (
auto anno : instNonlocalAnnos.lookup(nla))
846 newInstNonlocalAnnos.push_back(anno);
854 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
855 newInstAnnos.applyToOperation(newInst);
859 extractionWorklist.push_back({newInst,
info});
860 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
863 instanceGraph->replaceInstance(oldParentInst, newParentInst);
864 oldParentInst.erase();
869 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
877 for (Operation *op : nlasToRemove) {
878 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
886void ExtractInstancesPass::groupInstances() {
893 for (
auto &[inst, info] : extractedInstances) {
894 if (!
info.wrapperModule.empty())
895 instsByWrapper[{inst->getParentOfType<FModuleOp>(),
info.wrapperModule}]
898 if (instsByWrapper.empty())
900 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
903 SmallVector<PortInfo> ports;
904 auto &nlaTable = getAnalysis<NLATable>();
906 for (
auto &[parentAndWrapperName, insts] : instsByWrapper) {
907 auto [parentOp, wrapperName] = parentAndWrapperName;
908 auto parent = cast<FModuleOp>(parentOp);
909 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
910 << parent.getModuleName() <<
"` with "
911 << insts.size() <<
" instances\n");
912 OpBuilder builder(parentOp);
915 auto wrapperModuleName =
916 builder.getStringAttr(circuitNamespace.newName(wrapperName));
917 auto wrapperInstName =
918 builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
925 for (
auto inst : insts) {
927 StringRef prefix(instPrefixNamesPair[inst].first);
928 unsigned portNum = inst.getNumResults();
929 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
930 auto name = inst.getPortName(portIdx);
931 auto nameAttr = builder.getStringAttr(
932 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
934 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
935 inst.getPortDirection(portIdx)};
936 port.
loc = inst.getResult(portIdx).getLoc();
937 ports.push_back(port);
941 DenseSet<hw::HierPathOp> instNlas;
943 nlaTable.getInstanceNLAs(inst, instNlas);
947 for (
auto anno : instAnnos) {
948 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
951 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
953 instNlas.insert(nla);
955 for (
auto nla : instNlas) {
956 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
960 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
961 nla.getNamepath().end());
963 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
964 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
969 InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
971 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
972 ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
974 ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
975 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
976 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
977 nlaPath[nlaIdx] = ref1;
978 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
982 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
983 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
985 nlaTable.addNLAtoModule(nla, wrapperModuleName);
990 auto wrapper = FModuleOp::create(
991 builder, builder.getUnknownLoc(), wrapperModuleName,
992 ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
993 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
998 builder.setInsertionPointToStart(parent.getBodyBlock());
999 auto wrapperInst = InstanceOp::create(
1000 builder, wrapper.getLoc(), wrapper, wrapperName,
1001 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
1002 ArrayRef<Attribute>{},
false,
1003 false, hw::InnerSymAttr::get(wrapperInstName));
1004 unsigned portIdx = 0;
1005 for (
auto inst : insts)
1006 for (auto result : inst.getResults())
1007 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
1012 builder.setInsertionPointToStart(wrapper.getBodyBlock());
1013 for (
auto inst : insts) {
1015 builder.insert(inst);
1016 for (
auto result : inst.getResults()) {
1018 Value src = wrapper.getArgument(portIdx);
1019 if (ports[portIdx].direction == Direction::Out)
1020 std::swap(dst, src);
1021 MatchingConnectOp::create(builder, result.getLoc(), dst, src);
1031void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1032 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1036 for (
auto &[inst, info] : extractedInstances)
1038 instsByTraceFile[
info.traceFilename].push_back(inst);
1041 SmallVector<Attribute> symbols;
1043 if (sifiveMetadataClass && !extractMetadataClass)
1046 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1048 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1049 classOp.getLoc(), classOp.getBodyBlock());
1050 auto portIndex = classOp.getNumPorts();
1051 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1052 for (
auto [index, port] : enumerate(objFields)) {
1054 auto obj = port.first;
1055 newPorts.emplace_back(
1057 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1058 obj.getType(), Direction::Out));
1060 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1061 PropAssignOp::create(builderOM, blockarg, obj);
1063 classOp.insertPorts(newPorts);
1067 SmallVector<std::pair<Value, Twine>> classFields;
1068 for (
auto &[fileName, insts] : instsByTraceFile) {
1069 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1071 llvm::raw_string_ostream os(buffer);
1073 symbolIndices.clear();
1075 auto addSymbol = [&](Attribute symbol) {
1077 auto it = symbolIndices.find(symbol);
1078 if (it != symbolIndices.end()) {
1081 id = symbols.size();
1082 symbols.push_back(symbol);
1083 symbolIndices.insert({symbol,
id});
1085 os <<
"{{" <<
id <<
"}}";
1088 auto file = getOrCreateFile(fileName);
1089 auto builder = OpBuilder::atBlockEnd(file.getBody());
1090 for (
auto inst : insts) {
1091 StringRef prefix(instPrefixNamesPair[inst].first);
1092 StringAttr origInstName(instPrefixNamesPair[inst].second);
1093 if (prefix.empty()) {
1094 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1095 <<
"` since it has no extraction prefix\n");
1098 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1100 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1101 <<
"` since it has not been moved\n");
1104 LLVM_DEBUG(llvm::dbgs()
1105 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1106 os << prefix <<
" -> ";
1108 if (sifiveMetadataClass) {
1110 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1111 inst.getLoc(), extractMetadataClass.getBodyBlock());
1112 auto prefixName = StringConstantOp::create(builderOM, prefix);
1113 auto object = ObjectOp::create(builderOM, schemaClass, prefix);
1115 ObjectSubfieldOp::create(builderOM,
object, prefixNameFieldId);
1116 PropAssignOp::create(builderOM, fPrefix, prefixName);
1118 auto targetInstance = innerRefToInstances[path.front()];
1119 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1120 auto nla = pathCache.getOpFor(
1121 ArrayAttr::get(circuitOp->getContext(), pathOpAttr));
1123 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1124 auto fPath = ObjectSubfieldOp::create(builderOM,
object, pathFieldId);
1125 PropAssignOp::create(builderOM, fPath, pathOp);
1127 ObjectSubfieldOp::create(builderOM,
object, fileNameFieldId);
1128 auto fileNameOp = StringConstantOp::create(
1129 builderOM, builder.getStringAttr(fileName));
1130 PropAssignOp::create(builderOM, fFile, fileNameOp);
1133 ObjectSubfieldOp::create(builderOM,
object, instNameFieldId);
1134 auto instNameOp = StringConstantOp::create(builderOM, origInstName);
1135 PropAssignOp::create(builderOM, finstName, instNameOp);
1138 classFields.emplace_back(
object, prefix +
"_field");
1143 while (!path.empty() &&
1144 !instanceInfo->anyInstanceInDesign(cast<igraph::ModuleOpInterface>(
1145 symbolTable->lookup(path.back().getModule())))) {
1146 LLVM_DEBUG(llvm::dbgs()
1147 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1148 path = path.drop_back();
1153 addSymbol(FlatSymbolRefAttr::get(path.empty()
1154 ? originalInstanceParents[inst]
1155 : path.back().getModule()));
1156 for (
auto sym :
llvm::reverse(path)) {
1160 os <<
"." << origInstName.getValue();
1168 sv::VerbatimOp::create(builder, builder.getUnknownLoc(), buffer,
1169 ValueRange{}, builder.getArrayAttr(symbols));
1171 if (!classFields.empty()) {
1172 addPortsToClass(classFields, extractMetadataClass);
1176 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1177 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1178 SmallVector<std::pair<Value, Twine>> classFields = {
1180 builderOM, extractMetadataClass,
1181 builderOM.getStringAttr(
"extract_instances_metadata")),
1182 "extractedInstances_field"}};
1184 addPortsToClass(classFields, sifiveMetadataClass);
1185 auto *node = instanceGraph->lookup(sifiveMetadataClass);
1186 assert(node && node->hasOneUse());
1187 ObjectOp metadataObj = (*node->usesBegin())->getInstance<ObjectOp>();
1189 "expected the class to be instantiated by an object op");
1190 builderOM.setInsertionPoint(metadataObj);
1192 ObjectOp::create(builderOM, sifiveMetadataClass, metadataObj.getName());
1193 metadataObj->replaceAllUsesWith(newObj);
1194 metadataObj->remove();
1198void ExtractInstancesPass::createSchema() {
1200 auto *
context = circuitOp->getContext();
1201 auto unknownLoc = mlir::UnknownLoc::get(
context);
1202 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1203 unknownLoc, circuitOp.getBodyBlock());
1204 mlir::Type portsType[] = {
1210 StringRef portFields[] = {
"name",
"path",
"filename",
"inst_name"};
1212 schemaClass = ClassOp::create(builderOM,
"ExtractInstancesSchema", portFields,
1216 SmallVector<PortInfo> mports;
1217 extractMetadataClass = ClassOp::create(
1218 builderOM, builderOM.getStringAttr(
"ExtractInstancesMetadata"), mports);
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
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.
PathOp createPathRef(Operation *op, hw::HierPathOp nla, mlir::ImplicitLocOpBuilder &builderOM)
Add the tracker annotation to the op and get a PathOp to the op.
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.
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.