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 = builder.create<emit::FileOp>(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;
173void ExtractInstancesPass::runOnOperation() {
174 circuitOp = getOperation();
175 anythingChanged =
false;
177 annotatedModules.clear();
178 extractionWorklist.clear();
180 extractionPaths.clear();
181 originalInstanceParents.clear();
182 extractedInstances.clear();
183 instPrefixNamesPair.clear();
184 moduleNamespaces.clear();
185 circuitNamespace.clear();
186 circuitNamespace.add(circuitOp);
187 innerRefToInstances.clear();
188 extractMetadataClass = {};
190 auto *context = circuitOp->getContext();
191 stringType = StringType::get(context);
192 pathType = PathType::get(context);
196 instanceGraph = &getAnalysis<InstanceGraph>();
197 instanceInfo = &getAnalysis<InstanceInfo>();
198 symbolTable = &getAnalysis<SymbolTable>();
201 return signalPassFailure();
206 return signalPassFailure();
211 return signalPassFailure();
213 ClassOp sifiveMetadata =
214 dyn_cast_or_null<ClassOp>(symbolTable->lookup(
"SiFive_Metadata"));
217 createTraceFiles(sifiveMetadata);
219 return signalPassFailure();
222 LLVM_DEBUG(llvm::dbgs() <<
"\n");
223 if (!anythingChanged)
224 markAllAnalysesPreserved();
233void ExtractInstancesPass::collectAnnos() {
234 CircuitOp circuit = getOperation();
237 StringRef clkgateFileName;
238 StringRef clkgateWrapperModule;
242 LLVM_DEBUG(llvm::dbgs()
243 <<
"Clock gate extraction config: " << anno.
getDict() <<
"\n");
244 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
245 auto groupAttr = anno.
getMember<StringAttr>(
"group");
247 circuit.emitError(
"missing `filename` attribute in `")
248 << anno.
getClass() <<
"` annotation";
253 if (!clkgateFileName.empty()) {
254 circuit.emitError(
"multiple `")
255 << anno.
getClass() <<
"` annotations on circuit";
260 clkgateFileName = filenameAttr.getValue();
262 clkgateWrapperModule = groupAttr.getValue();
267 StringRef memoryFileName;
268 StringRef memoryWrapperModule;
272 LLVM_DEBUG(llvm::dbgs()
273 <<
"Memory extraction config: " << anno.
getDict() <<
"\n");
274 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
275 auto groupAttr = anno.
getMember<StringAttr>(
"group");
277 circuit.emitError(
"missing `filename` attribute in `")
278 << anno.
getClass() <<
"` annotation";
283 if (!memoryFileName.empty()) {
284 circuit.emitError(
"multiple `")
285 << anno.
getClass() <<
"` annotations on circuit";
290 memoryFileName = filenameAttr.getValue();
292 memoryWrapperModule = groupAttr.getValue();
298 for (
auto module : circuit.getOps<FModuleLike>()) {
302 LLVM_DEBUG(llvm::dbgs() <<
"Annotated module `" << module.getModuleName()
303 <<
"`:\n " << anno.
getDict() <<
"\n");
304 annotatedModules[module].push_back(anno);
310 circuit.walk([&](InstanceOp inst) {
311 SmallVector<Annotation, 1> instAnnos;
312 Operation *
module = inst.getReferencedModule(*instanceGraph);
315 auto it = annotatedModules.find(module);
316 if (it != annotatedModules.end())
317 instAnnos.append(it->second);
323 LLVM_DEBUG(llvm::dbgs() <<
"Annotated instance `" << inst.getName()
324 <<
"`:\n " << anno.
getDict() <<
"\n");
325 instAnnos.push_back(anno);
330 if (instAnnos.empty())
334 if (instAnnos.size() > 1) {
335 auto d = inst.emitError(
"multiple extraction annotations on instance `")
336 << inst.getName() <<
"`";
337 d.attachNote(inst.getLoc()) <<
"instance has the following annotations, "
338 "but at most one is allowed:";
339 for (
auto anno : instAnnos)
340 d.attachNote(inst.getLoc()) << anno.getDict();
346 collectAnno(inst, instAnnos[0]);
355 if (!clkgateFileName.empty()) {
356 for (
auto module : circuit.getOps<FExtModuleOp>()) {
357 if (!module.getDefnameAttr().getValue().ends_with(
"EICG_wrapper"))
359 LLVM_DEBUG(llvm::dbgs()
360 <<
"Clock gate `" << module.getModuleName() <<
"`\n");
361 if (!instanceInfo->anyInstanceInDesign(module)) {
362 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
367 info.traceFilename = clkgateFileName;
368 info.prefix =
"clock_gate";
369 info.wrapperModule = clkgateWrapperModule;
370 info.stopAtDUT = !info.wrapperModule.empty();
371 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
372 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
373 LLVM_DEBUG(llvm::dbgs()
375 << inst->getParentOfType<FModuleLike>().getModuleName()
376 <<
"." << inst.getName() <<
"`\n");
377 extractionWorklist.push_back({inst, info});
386 if (!memoryFileName.empty()) {
390 getOrCreateFile(memoryFileName);
392 for (
auto module : circuit.getOps<FMemModuleOp>()) {
393 LLVM_DEBUG(llvm::dbgs() <<
"Memory `" << module.getModuleName() <<
"`\n");
394 if (!instanceInfo->anyInstanceInDesign(module)) {
395 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
400 info.traceFilename = memoryFileName;
401 info.prefix =
"mem_wiring";
402 info.wrapperModule = memoryWrapperModule;
403 info.stopAtDUT = !info.wrapperModule.empty();
404 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
405 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
406 LLVM_DEBUG(llvm::dbgs()
408 << inst->getParentOfType<FModuleLike>().getModuleName()
409 <<
"." << inst.getName() <<
"`\n");
410 extractionWorklist.push_back({inst, info});
419void ExtractInstancesPass::collectAnno(InstanceOp inst,
Annotation anno) {
420 LLVM_DEBUG(llvm::dbgs() <<
"Processing instance `" << inst.getName() <<
"` "
423 auto getStringOrError = [&](StringRef member) {
424 auto attr = anno.
getMember<StringAttr>(member);
426 inst.emitError(
"missing `")
427 << member <<
"` attribute in `" << anno.
getClass() <<
"` annotation";
434 auto filename = getStringOrError(
"filename");
435 auto prefix = getStringOrError(
"prefix");
436 auto dest = anno.
getMember<StringAttr>(
"dest");
441 info.traceFilename = filename;
442 info.prefix = prefix;
443 info.wrapperModule = (dest ? dest.getValue() :
"");
448 info.stopAtDUT = !info.wrapperModule.empty();
450 extractionWorklist.push_back({inst, info});
460 unsigned nlaLen = nla.getNamepath().size();
462 auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
463 for (
unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
464 auto refPart = nla.refPart(nlaIdx);
465 if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
474void ExtractInstancesPass::extractInstances() {
477 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
479 DenseMap<StringRef, unsigned> prefixUniqueIDs;
481 SmallPtrSet<Operation *, 4> nlasToRemove;
483 auto &nlaTable = getAnalysis<NLATable>();
486 for (
auto &[inst, info] : extractionWorklist)
487 originalInstanceParents[inst] =
488 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
490 while (!extractionWorklist.empty()) {
493 std::tie(inst, info) = extractionWorklist.pop_back_val();
495 auto parent = inst->getParentOfType<FModuleOp>();
504 auto &instPrefixEntry = instPrefixNamesPair[inst];
505 instPrefixEntry.second = inst.getInstanceNameAttr();
506 if (!info.prefix.empty()) {
507 auto &prefixSlot = instPrefixEntry.first;
508 if (prefixSlot.empty()) {
509 auto idx = prefixUniqueIDs[info.prefix]++;
510 (Twine(info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
519 if (inst->getParentOfType<LayerBlockOp>() ||
520 !instanceInfo->anyInstanceInDesign(parent) ||
521 instanceGraph->lookup(parent)->noUses() ||
522 (info.stopAtDUT && instanceInfo->isDut(parent))) {
523 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
524 extractedInstances.push_back({inst, info});
528 llvm::dbgs() <<
"\nMoving ";
530 llvm::dbgs() <<
"`" << prefix <<
"` ";
531 llvm::dbgs() << inst <<
"\n";
536 unsigned numParentPorts = parent.getNumPorts();
537 unsigned numInstPorts = inst.getNumResults();
539 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
542 auto name = inst.getPortNameStr(portIdx);
543 auto nameAttr = StringAttr::get(
545 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
548 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
550 newPort.
loc = inst.getResult(portIdx).getLoc();
551 newPorts.push_back({numParentPorts, newPort});
552 LLVM_DEBUG(llvm::dbgs()
553 <<
"- Adding port " << newPort.direction <<
" "
554 << newPort.name.getValue() <<
": " << newPort.type <<
"\n");
556 parent.insertPorts(newPorts);
557 anythingChanged =
true;
561 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
562 inst.getResult(portIdx).replaceAllUsesWith(
563 parent.getArgument(numParentPorts + portIdx));
565 assert(inst.use_empty() &&
"instance ports should have been detached");
566 DenseSet<hw::HierPathOp> instanceNLAs;
569 nlaTable.getInstanceNLAs(inst, instanceNLAs);
572 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
575 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
579 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
580 instNonlocalAnnos[nla].push_back(anno);
581 instanceNLAs.insert(nla);
588 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
590 llvm::sort(sortedInstanceNLAs,
591 [](
auto a,
auto b) {
return a.getSymName() < b.getSymName(); });
596 auto *instParentNode =
597 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
598 for (
auto *instRecord : instParentNode->uses()) {
599 auto oldParentInst = cast<InstanceOp>(*instRecord->getInstance());
600 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
601 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
602 auto newParentInst = oldParentInst.cloneAndInsertPorts(newPorts);
603 if (newParentInst.getInnerSymAttr())
604 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
607 for (
unsigned portIdx = 0; portIdx < numParentPorts; ++portIdx)
608 oldParentInst.getResult(portIdx).replaceAllUsesWith(
609 newParentInst.getResult(portIdx));
613 auto newInst = inst.cloneAndInsertPorts({});
620 getModuleNamespace(newParent).newName(instSym.getValue());
621 if (newName != instSym.getValue())
622 newInst.setInnerSymAttr(
623 hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
627 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
628 builder.setInsertionPointAfter(newParentInst);
629 builder.insert(newInst);
630 if (newParentInst.getInnerSymAttr())
632 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
633 auto dst = newInst.getResult(portIdx);
634 auto src = newParentInst.getResult(numParentPorts + portIdx);
635 if (newPorts[portIdx].second.direction == Direction::In)
637 builder.create<MatchingConnectOp>(dst, src);
646 auto oldPrefix = instPrefixNamesPair.find(inst);
647 if (oldPrefix != instPrefixNamesPair.end()) {
648 LLVM_DEBUG(llvm::dbgs() <<
" - Reusing prefix `"
649 << oldPrefix->second.first <<
"`\n");
650 auto newPrefix = std::move(oldPrefix->second);
651 instPrefixNamesPair.erase(oldPrefix);
652 instPrefixNamesPair.insert({newInst, newPrefix});
656 extractionPaths.try_emplace(newInst);
657 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
659 innerRefToInstances[instInnerRef] = newParentInst;
660 extractionPath.push_back(instInnerRef);
661 originalInstanceParents.try_emplace(newInst);
662 originalInstanceParents[newInst] = originalInstanceParents[inst];
665 SmallVector<Annotation> newInstNonlocalAnnos;
668 for (
auto nla : sortedInstanceNLAs) {
669 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
673 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
674 nla.getNamepath().end());
683 if (nlaIdx >= nlaPath.size()) {
684 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
687 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
694 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
696 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
698 LLVM_DEBUG(llvm::dbgs()
699 <<
" - Ignored since NLA parent " << innerRef
700 <<
" does not pass through extraction parent\n");
718 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
719 assert(isa<InnerRefAttr>(nlaPath[0]) &&
720 "head of hierpath must be an InnerRefAttr");
721 nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
724 if (instParentNode->hasOneUse()) {
729 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
730 for (
auto anno : instNonlocalAnnos.lookup(nla))
731 newInstNonlocalAnnos.push_back(anno);
732 nlaTable.addNLA(nla);
733 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
737 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
738 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
740 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
741 newInstNonlocalAnnos.push_back(anno);
744 nlaTable.addNLA(newNla);
745 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
755 inst.emitWarning(
"extraction of instance `")
756 << inst.getInstanceName()
757 <<
"` could break non-local annotations rooted at `"
758 << parent.getModuleName() <<
"`";
769 if (nlaPath.size() == 2) {
770 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
772 newInstNonlocalAnnos.push_back(anno);
773 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
777 nlasToRemove.insert(nla);
785 StringAttr parentName =
786 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
788 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
791 newRef = FlatSymbolRefAttr::get(parentName);
792 LLVM_DEBUG(llvm::dbgs()
793 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
794 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
795 nlaPath[nlaIdx] = newRef;
796 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
798 if (isa<FlatSymbolRefAttr>(newRef)) {
803 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
804 nlaTable.addNLA(newNla);
805 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
806 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
808 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
809 newInstNonlocalAnnos.push_back(anno);
812 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
813 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
814 for (
auto anno : instNonlocalAnnos.lookup(nla))
815 newInstNonlocalAnnos.push_back(anno);
823 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
824 newInstAnnos.applyToOperation(newInst);
828 extractionWorklist.push_back({newInst, info});
829 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
832 instanceGraph->replaceInstance(oldParentInst, newParentInst);
833 oldParentInst.erase();
838 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
846 for (Operation *op : nlasToRemove) {
847 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
855void ExtractInstancesPass::groupInstances() {
860 llvm::MapVector<std::pair<Operation *, StringRef>, SmallVector<InstanceOp>>
862 for (
auto &[inst, info] : extractedInstances) {
863 if (!info.wrapperModule.empty())
864 instsByWrapper[{inst->getParentOfType<FModuleOp>(), info.wrapperModule}]
867 if (instsByWrapper.empty())
869 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
872 SmallVector<PortInfo> ports;
873 auto &nlaTable = getAnalysis<NLATable>();
875 for (
auto &[parentAndWrapperName, insts] : instsByWrapper) {
876 auto [parentOp, wrapperName] = parentAndWrapperName;
877 auto parent = cast<FModuleOp>(parentOp);
878 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
879 << parent.getModuleName() <<
"` with "
880 << insts.size() <<
" instances\n");
881 OpBuilder builder(parentOp);
884 auto wrapperModuleName =
885 builder.getStringAttr(circuitNamespace.newName(wrapperName));
886 auto wrapperInstName =
887 builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
894 for (
auto inst : insts) {
896 StringRef prefix(instPrefixNamesPair[inst].first);
897 unsigned portNum = inst.getNumResults();
898 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
899 auto name = inst.getPortNameStr(portIdx);
900 auto nameAttr = builder.getStringAttr(
901 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
903 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
904 inst.getPortDirection(portIdx)};
905 port.
loc = inst.getResult(portIdx).getLoc();
906 ports.push_back(port);
910 DenseSet<hw::HierPathOp> instNlas;
912 nlaTable.getInstanceNLAs(inst, instNlas);
916 for (
auto anno : instAnnos) {
917 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
920 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
922 instNlas.insert(nla);
924 for (
auto nla : instNlas) {
925 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
929 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
930 nla.getNamepath().end());
932 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
933 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
938 InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
940 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
941 ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
943 ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
944 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
945 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
946 nlaPath[nlaIdx] = ref1;
947 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
951 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
952 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
954 nlaTable.addNLAtoModule(nla, wrapperModuleName);
959 auto wrapper = builder.create<FModuleOp>(
960 builder.getUnknownLoc(), wrapperModuleName,
961 ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
962 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
967 builder.setInsertionPointToStart(parent.getBodyBlock());
968 auto wrapperInst = builder.create<InstanceOp>(
969 wrapper.getLoc(), wrapper, wrapperName, NameKindEnum::DroppableName,
970 ArrayRef<Attribute>{},
971 ArrayRef<Attribute>{},
false,
972 hw::InnerSymAttr::get(wrapperInstName));
973 unsigned portIdx = 0;
974 for (
auto inst : insts)
975 for (auto result : inst.getResults())
976 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
981 builder.setInsertionPointToStart(wrapper.getBodyBlock());
982 for (
auto inst : insts) {
984 builder.insert(inst);
985 for (
auto result : inst.getResults()) {
987 Value src = wrapper.getArgument(portIdx);
988 if (ports[portIdx].direction == Direction::Out)
990 builder.create<MatchingConnectOp>(result.getLoc(), dst, src);
1000void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1001 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1004 llvm::MapVector<StringRef, SmallVector<InstanceOp>> instsByTraceFile;
1005 for (
auto &[inst, info] : extractedInstances)
1006 if (!info.traceFilename.
empty())
1007 instsByTraceFile[info.traceFilename].push_back(inst);
1010 SmallVector<Attribute> symbols;
1012 if (sifiveMetadataClass && !extractMetadataClass)
1015 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1017 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1018 classOp.getLoc(), classOp.getBodyBlock());
1019 auto portIndex = classOp.getNumPorts();
1020 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1021 for (
auto [index, port] : enumerate(objFields)) {
1023 auto obj = port.first;
1024 newPorts.emplace_back(
1026 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1027 obj.getType(), Direction::Out));
1029 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1030 builderOM.create<PropAssignOp>(blockarg, obj);
1032 classOp.insertPorts(newPorts);
1036 SmallVector<std::pair<Value, Twine>> classFields;
1037 for (
auto &[fileName, insts] : instsByTraceFile) {
1038 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1040 llvm::raw_string_ostream os(buffer);
1042 symbolIndices.clear();
1044 auto addSymbol = [&](Attribute symbol) {
1046 auto it = symbolIndices.find(symbol);
1047 if (it != symbolIndices.end()) {
1050 id = symbols.size();
1051 symbols.push_back(symbol);
1052 symbolIndices.insert({symbol,
id});
1054 os <<
"{{" <<
id <<
"}}";
1057 auto file = getOrCreateFile(fileName);
1058 auto builder = OpBuilder::atBlockEnd(file.getBody());
1059 for (
auto inst : insts) {
1060 StringRef prefix(instPrefixNamesPair[inst].first);
1061 StringAttr origInstName(instPrefixNamesPair[inst].second);
1062 if (prefix.empty()) {
1063 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1064 <<
"` since it has no extraction prefix\n");
1067 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1069 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1070 <<
"` since it has not been moved\n");
1073 LLVM_DEBUG(llvm::dbgs()
1074 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1075 os << prefix <<
" -> ";
1077 if (sifiveMetadataClass) {
1079 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1080 inst.getLoc(), extractMetadataClass.getBodyBlock());
1081 auto prefixName = builderOM.create<StringConstantOp>(prefix);
1082 auto object = builderOM.create<ObjectOp>(schemaClass, prefix);
1084 builderOM.create<ObjectSubfieldOp>(object, prefixNameFieldId);
1085 builderOM.create<PropAssignOp>(fPrefix, prefixName);
1087 auto targetInstance = innerRefToInstances[path.front()];
1088 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1089 auto nla = pathCache.getOpFor(
1090 ArrayAttr::get(circuitOp->getContext(), pathOpAttr));
1092 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1093 auto fPath = builderOM.create<ObjectSubfieldOp>(object, pathFieldId);
1094 builderOM.create<PropAssignOp>(fPath, pathOp);
1096 builderOM.create<ObjectSubfieldOp>(object, fileNameFieldId);
1098 builderOM.create<StringConstantOp>(builder.getStringAttr(fileName));
1099 builderOM.create<PropAssignOp>(fFile, fileNameOp);
1102 builderOM.create<ObjectSubfieldOp>(object, instNameFieldId);
1103 auto instNameOp = builderOM.create<StringConstantOp>(origInstName);
1104 builderOM.create<PropAssignOp>(finstName, instNameOp);
1107 classFields.emplace_back(
object, prefix +
"_field");
1112 while (!path.empty() &&
1113 !instanceInfo->anyInstanceInDesign(cast<igraph::ModuleOpInterface>(
1114 symbolTable->lookup(path.back().getModule())))) {
1115 LLVM_DEBUG(llvm::dbgs()
1116 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1117 path = path.drop_back();
1122 addSymbol(FlatSymbolRefAttr::get(path.empty()
1123 ? originalInstanceParents[inst]
1124 : path.back().getModule()));
1125 for (
auto sym :
llvm::reverse(path)) {
1129 os <<
"." << origInstName.getValue();
1137 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), buffer,
1138 ValueRange{}, builder.getArrayAttr(symbols));
1140 if (!classFields.empty()) {
1141 addPortsToClass(classFields, extractMetadataClass);
1145 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1146 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1147 SmallVector<std::pair<Value, Twine>> classFields = {
1148 {builderOM.create<ObjectOp>(
1149 extractMetadataClass,
1150 builderOM.getStringAttr(
"extract_instances_metadata")),
1151 "extractedInstances_field"}};
1153 addPortsToClass(classFields, sifiveMetadataClass);
1154 auto *node = instanceGraph->lookup(sifiveMetadataClass);
1155 assert(node && node->hasOneUse());
1156 ObjectOp metadataObj =
1157 dyn_cast_or_null<ObjectOp>((*node->usesBegin())->getInstance());
1159 "expected the class to be instantiated by an object op");
1160 builderOM.setInsertionPoint(metadataObj);
1162 builderOM.create<ObjectOp>(sifiveMetadataClass, metadataObj.getName());
1163 metadataObj->replaceAllUsesWith(newObj);
1164 metadataObj->remove();
1168void ExtractInstancesPass::createSchema() {
1170 auto *context = circuitOp->getContext();
1171 auto unknownLoc = mlir::UnknownLoc::get(context);
1172 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1173 unknownLoc, circuitOp.getBodyBlock());
1174 mlir::Type portsType[] = {
1180 StringRef portFields[] = {
"name",
"path",
"filename",
"inst_name"};
1182 schemaClass = builderOM.create<ClassOp>(
"ExtractInstancesSchema", portFields,
1186 SmallVector<PortInfo> mports;
1187 extractMetadataClass = builderOM.create<ClassOp>(
1188 builderOM.getStringAttr(
"ExtractInstancesMetadata"), mports);
1195 return std::make_unique<ExtractInstancesPass>();
assert(baseType &&"element must be base type")
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static InstancePath empty
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 * 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.
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.