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;
509 DenseMap<std::pair<Operation *, StringRef>,
unsigned> prefixUniqueIDs;
511 SmallPtrSet<Operation *, 4> nlasToRemove;
513 auto &nlaTable = getAnalysis<NLATable>();
516 for (
auto &[inst, info] : extractionWorklist)
517 originalInstanceParents[inst] =
518 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
520 while (!extractionWorklist.empty()) {
523 std::tie(inst, info) = extractionWorklist.pop_back_val();
525 auto parent = inst->getParentOfType<FModuleOp>();
535 auto &instPrefixEntry = instPrefixNamesPair[inst];
536 instPrefixEntry.second = inst.getInstanceNameAttr();
537 if (!
info.prefix.empty()) {
538 auto &prefixSlot = instPrefixEntry.first;
540 auto idx = prefixUniqueIDs[{parent,
info.prefix}]++;
541 (Twine(
info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
547 bool stopAtDUT = !moveDut && !
info.wrapperModule.empty();
553 if (inst->getParentOfType<LayerBlockOp>() ||
554 !instanceInfo->anyInstanceInDesign(parent) ||
555 instanceGraph->lookup(parent)->noUses() ||
556 (stopAtDUT && instanceInfo->isDut(parent))) {
557 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
558 extractedInstances.push_back({inst,
info});
562 llvm::dbgs() <<
"\nMoving ";
564 llvm::dbgs() <<
"`" << prefix <<
"` ";
565 llvm::dbgs() << inst <<
"\n";
570 unsigned numParentPorts = parent.getNumPorts();
571 unsigned numInstPorts = inst.getNumResults();
573 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
576 auto name = inst.getPortName(portIdx);
577 auto nameAttr = StringAttr::get(
579 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
582 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
584 newPort.
loc = inst.getResult(portIdx).getLoc();
585 newPorts.push_back({numParentPorts, newPort});
586 LLVM_DEBUG(llvm::dbgs()
587 <<
"- Adding port " << newPort.direction <<
" "
588 << newPort.name.getValue() <<
": " << newPort.type <<
"\n");
590 parent.insertPorts(newPorts);
591 anythingChanged =
true;
595 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
596 inst.getResult(portIdx).replaceAllUsesWith(
597 parent.getArgument(numParentPorts + portIdx));
599 assert(inst.use_empty() &&
"instance ports should have been detached");
600 DenseSet<hw::HierPathOp> instanceNLAs;
603 nlaTable.getInstanceNLAs(inst, instanceNLAs);
606 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
609 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
613 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
614 instNonlocalAnnos[nla].push_back(anno);
615 instanceNLAs.insert(nla);
622 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
624 llvm::sort(sortedInstanceNLAs,
625 [](
auto a,
auto b) {
return a.getSymName() < b.getSymName(); });
630 auto *instParentNode =
631 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
632 for (
auto *instRecord : instParentNode->uses()) {
633 auto oldParentInst = dyn_cast<InstanceOp>(*instRecord->getInstance());
634 if (!oldParentInst) {
635 inst.emitError(
"cannot extract instance `")
636 << inst.getName() <<
"` through a non-InstanceOp parent";
640 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
641 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
642 auto newParentInst = cast<InstanceOp>(
643 oldParentInst.cloneWithInsertedPortsAndReplaceUses(newPorts));
644 if (newParentInst.getInnerSymAttr())
645 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
647 auto newInst = cast<InstanceOp>(inst->clone());
653 getModuleNamespace(newParent).newName(instSym.getValue());
654 if (newName != instSym.getValue())
655 newInst.setInnerSymAttr(
656 hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
660 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
661 builder.setInsertionPointAfter(newParentInst);
662 builder.insert(newInst);
663 if (newParentInst.getInnerSymAttr())
665 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
666 auto dst = newInst.getResult(portIdx);
667 auto src = newParentInst.getResult(numParentPorts + portIdx);
668 if (newPorts[portIdx].second.direction == Direction::In)
670 MatchingConnectOp::create(builder, dst, src);
678 auto oldPrefix = instPrefixNamesPair.find(inst);
679 if (oldPrefix != instPrefixNamesPair.end()) {
680 LLVM_DEBUG(llvm::dbgs() <<
" - Moving prefix `"
681 << oldPrefix->second.first <<
"`\n");
682 auto newPrefix = std::move(oldPrefix->second);
683 instPrefixNamesPair.erase(oldPrefix);
684 instPrefixNamesPair.insert({newInst, newPrefix});
688 extractionPaths.try_emplace(newInst);
689 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
691 innerRefToInstances[instInnerRef] = newParentInst;
692 extractionPath.push_back(instInnerRef);
693 originalInstanceParents.try_emplace(newInst);
694 originalInstanceParents[newInst] = originalInstanceParents[inst];
697 SmallVector<Annotation> newInstNonlocalAnnos;
700 for (
auto nla : sortedInstanceNLAs) {
701 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
705 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
706 nla.getNamepath().end());
715 if (nlaIdx >= nlaPath.size()) {
716 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
719 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
726 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
728 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
730 LLVM_DEBUG(llvm::dbgs()
731 <<
" - Ignored since NLA parent " << innerRef
732 <<
" does not pass through extraction parent\n");
750 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
751 assert(isa<InnerRefAttr>(nlaPath[0]) &&
752 "head of hierpath must be an InnerRefAttr");
753 nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
756 if (instParentNode->hasOneUse()) {
761 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
762 for (
auto anno : instNonlocalAnnos.lookup(nla))
763 newInstNonlocalAnnos.push_back(anno);
764 nlaTable.addNLA(nla);
765 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
769 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
770 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
772 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
773 newInstNonlocalAnnos.push_back(anno);
776 nlaTable.addNLA(newNla);
777 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
787 inst.emitWarning(
"extraction of instance `")
788 << inst.getInstanceName()
789 <<
"` could break non-local annotations rooted at `"
790 << parent.getModuleName() <<
"`";
801 if (nlaPath.size() == 2) {
802 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
804 newInstNonlocalAnnos.push_back(anno);
805 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
809 nlasToRemove.insert(nla);
817 StringAttr parentName =
818 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
820 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
823 newRef = FlatSymbolRefAttr::get(parentName);
824 LLVM_DEBUG(llvm::dbgs()
825 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
826 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
827 nlaPath[nlaIdx] = newRef;
828 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
830 if (isa<FlatSymbolRefAttr>(newRef)) {
835 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
836 nlaTable.addNLA(newNla);
837 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
838 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
840 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
841 newInstNonlocalAnnos.push_back(anno);
844 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
845 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
846 for (
auto anno : instNonlocalAnnos.lookup(nla))
847 newInstNonlocalAnnos.push_back(anno);
855 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
856 newInstAnnos.applyToOperation(newInst);
860 extractionWorklist.push_back({newInst,
info});
861 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
864 instanceGraph->replaceInstance(oldParentInst, newParentInst);
865 oldParentInst.erase();
870 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
878 for (Operation *op : nlasToRemove) {
879 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
887void ExtractInstancesPass::groupInstances() {
894 for (
auto &[inst, info] : extractedInstances) {
895 if (!
info.wrapperModule.empty())
896 instsByWrapper[{inst->getParentOfType<FModuleOp>(),
info.wrapperModule}]
899 if (instsByWrapper.empty())
901 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
904 SmallVector<PortInfo> ports;
905 auto &nlaTable = getAnalysis<NLATable>();
907 for (
auto &[parentAndWrapperName, insts] : instsByWrapper) {
908 auto [parentOp, wrapperName] = parentAndWrapperName;
909 auto parent = cast<FModuleOp>(parentOp);
910 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
911 << parent.getModuleName() <<
"` with "
912 << insts.size() <<
" instances\n");
913 OpBuilder builder(parentOp);
916 auto wrapperModuleName =
917 builder.getStringAttr(circuitNamespace.newName(wrapperName));
918 auto wrapperInstName =
919 builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
926 for (
auto inst : insts) {
928 StringRef prefix(instPrefixNamesPair[inst].first);
929 unsigned portNum = inst.getNumResults();
930 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
931 auto name = inst.getPortName(portIdx);
932 auto nameAttr = builder.getStringAttr(
933 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
935 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
936 inst.getPortDirection(portIdx)};
937 port.
loc = inst.getResult(portIdx).getLoc();
938 ports.push_back(port);
942 DenseSet<hw::HierPathOp> instNlas;
944 nlaTable.getInstanceNLAs(inst, instNlas);
948 for (
auto anno : instAnnos) {
949 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
952 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
954 instNlas.insert(nla);
956 for (
auto nla : instNlas) {
957 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
961 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
962 nla.getNamepath().end());
964 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
965 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
970 InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
972 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
973 ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
975 ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
976 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
977 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
978 nlaPath[nlaIdx] = ref1;
979 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
983 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
984 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
986 nlaTable.addNLAtoModule(nla, wrapperModuleName);
991 auto wrapper = FModuleOp::create(
992 builder, builder.getUnknownLoc(), wrapperModuleName,
993 ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
994 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
999 builder.setInsertionPointToStart(parent.getBodyBlock());
1000 auto wrapperInst = InstanceOp::create(
1001 builder, wrapper.getLoc(), wrapper, wrapperName,
1002 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
1003 ArrayRef<Attribute>{},
false,
1004 false, hw::InnerSymAttr::get(wrapperInstName));
1005 unsigned portIdx = 0;
1006 for (
auto inst : insts)
1007 for (auto result : inst.getResults())
1008 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
1013 builder.setInsertionPointToStart(wrapper.getBodyBlock());
1014 for (
auto inst : insts) {
1016 builder.insert(inst);
1017 for (
auto result : inst.getResults()) {
1019 Value src = wrapper.getArgument(portIdx);
1020 if (ports[portIdx].direction == Direction::Out)
1021 std::swap(dst, src);
1022 MatchingConnectOp::create(builder, result.getLoc(), dst, src);
1032void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1033 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1037 for (
auto &[inst, info] : extractedInstances)
1039 instsByTraceFile[
info.traceFilename].push_back(inst);
1042 SmallVector<Attribute> symbols;
1044 if (sifiveMetadataClass && !extractMetadataClass)
1047 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1049 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1050 classOp.getLoc(), classOp.getBodyBlock());
1051 auto portIndex = classOp.getNumPorts();
1052 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1053 for (
auto [index, port] : enumerate(objFields)) {
1055 auto obj = port.first;
1056 newPorts.emplace_back(
1058 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1059 obj.getType(), Direction::Out));
1061 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1062 PropAssignOp::create(builderOM, blockarg, obj);
1064 classOp.insertPorts(newPorts);
1068 SmallVector<std::pair<Value, Twine>> classFields;
1069 for (
auto &[fileName, insts] : instsByTraceFile) {
1070 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1072 llvm::raw_string_ostream os(buffer);
1074 symbolIndices.clear();
1076 auto addSymbol = [&](Attribute symbol) {
1078 auto it = symbolIndices.find(symbol);
1079 if (it != symbolIndices.end()) {
1082 id = symbols.size();
1083 symbols.push_back(symbol);
1084 symbolIndices.insert({symbol,
id});
1086 os <<
"{{" <<
id <<
"}}";
1089 auto file = getOrCreateFile(fileName);
1090 auto builder = OpBuilder::atBlockEnd(file.getBody());
1091 for (
auto inst : insts) {
1092 StringRef prefix(instPrefixNamesPair[inst].first);
1093 StringAttr origInstName(instPrefixNamesPair[inst].second);
1094 if (prefix.empty()) {
1095 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1096 <<
"` since it has no extraction prefix\n");
1099 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1101 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1102 <<
"` since it has not been moved\n");
1105 LLVM_DEBUG(llvm::dbgs()
1106 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1107 os << prefix <<
" -> ";
1109 if (sifiveMetadataClass) {
1111 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1112 inst.getLoc(), extractMetadataClass.getBodyBlock());
1113 auto prefixName = StringConstantOp::create(builderOM, prefix);
1114 auto object = ObjectOp::create(builderOM, schemaClass, prefix);
1116 ObjectSubfieldOp::create(builderOM,
object, prefixNameFieldId);
1117 PropAssignOp::create(builderOM, fPrefix, prefixName);
1119 auto targetInstance = innerRefToInstances[path.front()];
1120 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1121 auto nla = pathCache.getOpFor(
1122 ArrayAttr::get(circuitOp->getContext(), pathOpAttr));
1124 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1125 auto fPath = ObjectSubfieldOp::create(builderOM,
object, pathFieldId);
1126 PropAssignOp::create(builderOM, fPath, pathOp);
1128 ObjectSubfieldOp::create(builderOM,
object, fileNameFieldId);
1129 auto fileNameOp = StringConstantOp::create(
1130 builderOM, builder.getStringAttr(fileName));
1131 PropAssignOp::create(builderOM, fFile, fileNameOp);
1134 ObjectSubfieldOp::create(builderOM,
object, instNameFieldId);
1135 auto instNameOp = StringConstantOp::create(builderOM, origInstName);
1136 PropAssignOp::create(builderOM, finstName, instNameOp);
1139 classFields.emplace_back(
object, prefix +
"_field");
1144 while (!path.empty() &&
1145 !instanceInfo->anyInstanceInDesign(cast<igraph::ModuleOpInterface>(
1146 symbolTable->lookup(path.back().getModule())))) {
1147 LLVM_DEBUG(llvm::dbgs()
1148 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1149 path = path.drop_back();
1154 addSymbol(FlatSymbolRefAttr::get(path.empty()
1155 ? originalInstanceParents[inst]
1156 : path.back().getModule()));
1157 for (
auto sym :
llvm::reverse(path)) {
1161 os <<
"." << origInstName.getValue();
1169 sv::VerbatimOp::create(builder, builder.getUnknownLoc(), buffer,
1170 ValueRange{}, builder.getArrayAttr(symbols));
1172 if (!classFields.empty()) {
1173 addPortsToClass(classFields, extractMetadataClass);
1177 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1178 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1179 SmallVector<std::pair<Value, Twine>> classFields = {
1181 builderOM, extractMetadataClass,
1182 builderOM.getStringAttr(
"extract_instances_metadata")),
1183 "extractedInstances_field"}};
1185 addPortsToClass(classFields, sifiveMetadataClass);
1186 auto *node = instanceGraph->lookup(sifiveMetadataClass);
1187 assert(node && node->hasOneUse());
1188 ObjectOp metadataObj = (*node->usesBegin())->getInstance<ObjectOp>();
1190 "expected the class to be instantiated by an object op");
1191 builderOM.setInsertionPoint(metadataObj);
1193 ObjectOp::create(builderOM, sifiveMetadataClass, metadataObj.getName());
1194 metadataObj->replaceAllUsesWith(newObj);
1195 metadataObj->remove();
1199void ExtractInstancesPass::createSchema() {
1201 auto *
context = circuitOp->getContext();
1202 auto unknownLoc = mlir::UnknownLoc::get(
context);
1203 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1204 unknownLoc, circuitOp.getBodyBlock());
1205 mlir::Type portsType[] = {
1211 StringRef portFields[] = {
"name",
"path",
"filename",
"inst_name"};
1213 schemaClass = ClassOp::create(builderOM,
"ExtractInstancesSchema", portFields,
1217 SmallVector<PortInfo> mports;
1218 extractMetadataClass = ClassOp::create(
1219 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.