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"
55using hw::InnerRefAttr;
63struct ExtractionInfo {
65 StringRef traceFilename;
69 StringRef wrapperModule;
71 StringRef wrapperInstanceName;
77struct ExtractInstancesPass
78 :
public circt::firrtl::impl::ExtractInstancesBase<ExtractInstancesPass> {
79 void runOnOperation()
override;
81 void collectAnno(InstanceOp inst, Annotation anno);
82 void extractInstances();
83 void groupInstances();
84 void createTraceFiles(ClassOp &sifiveMetadata);
88 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
89 return moduleNamespaces.try_emplace(module, module).first->second;
95 return ::getInnerRefTo(op,
96 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
97 return getModuleNamespace(mod);
102 hw::HierPathOp cloneWithNewNameAndPath(hw::HierPathOp pathOp,
103 ArrayRef<Attribute> newPath) {
104 OpBuilder builder(pathOp);
105 auto newPathOp = builder.cloneWithoutRegions(pathOp);
106 newPathOp.setSymNameAttr(
107 builder.getStringAttr(circuitNamespace.newName(newPathOp.getName())));
108 newPathOp.setNamepathAttr(builder.getArrayAttr(newPath));
113 emit::FileOp getOrCreateFile(StringRef fileName) {
114 auto [it, inserted] = files.try_emplace(fileName, emit::FileOp{});
116 auto builder = ImplicitLocOpBuilder::atBlockEnd(
117 UnknownLoc::get(&getContext()), getOperation().
getBodyBlock());
118 it->second = emit::FileOp::create(builder, fileName);
123 bool anythingChanged;
128 InstanceInfo *instanceInfo =
nullptr;
129 SymbolTable *symbolTable =
nullptr;
133 DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
136 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
139 DenseMap<StringRef, emit::FileOp> files;
145 DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
149 DenseMap<Operation *, StringAttr> originalInstanceParents;
153 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
156 DenseMap<Operation *, std::pair<SmallString<16>, StringAttr>>
160 CircuitNamespace circuitNamespace;
162 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
164 ClassOp extractMetadataClass, schemaClass;
165 const unsigned prefixNameFieldId = 0, pathFieldId = 2, fileNameFieldId = 4,
169 DenseMap<InnerRefAttr, InstanceOp> innerRefToInstances;
170 Type stringType, pathType;
180void ExtractInstancesPass::runOnOperation() {
181 circuitOp = getOperation();
182 anythingChanged =
false;
184 annotatedModules.clear();
185 extractionWorklist.clear();
187 extractionPaths.clear();
188 originalInstanceParents.clear();
189 extractedInstances.clear();
190 instPrefixNamesPair.clear();
191 moduleNamespaces.clear();
192 circuitNamespace.
clear();
193 circuitNamespace.
add(circuitOp);
194 innerRefToInstances.clear();
195 extractMetadataClass = {};
197 auto *
context = circuitOp->getContext();
198 stringType = StringType::get(
context);
199 pathType = PathType::get(
context);
204 instanceGraph = &getAnalysis<InstanceGraph>();
205 instanceInfo = &getAnalysis<InstanceInfo>();
206 symbolTable = &getAnalysis<SymbolTable>();
209 return signalPassFailure();
214 return signalPassFailure();
219 return signalPassFailure();
221 ClassOp sifiveMetadata =
222 dyn_cast_or_null<ClassOp>(symbolTable->lookup(
"SiFive_Metadata"));
225 createTraceFiles(sifiveMetadata);
227 return signalPassFailure();
230 LLVM_DEBUG(llvm::dbgs() <<
"\n");
231 if (!anythingChanged)
232 markAllAnalysesPreserved();
236 return anno.
isClass(extractBlackBoxAnnoClass);
241void ExtractInstancesPass::collectAnnos() {
242 CircuitOp circuit = getOperation();
253 if (!anno.
isClass(injectDUTHierarchyAnnoClass))
256 if (
auto moveDutAnnoAttr = anno.
getMember<BoolAttr>(
"moveDut"))
257 moveDut = moveDutAnnoAttr.getValue();
262 StringRef clkgateFileName;
263 StringRef clkgateWrapperModule;
264 StringRef clkgateWrapperInstanceName;
266 if (!anno.
isClass(extractClockGatesFileAnnoClass))
268 LLVM_DEBUG(llvm::dbgs()
269 <<
"Clock gate extraction config: " << anno.
getDict() <<
"\n");
270 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
271 auto groupAttr = anno.
getMember<StringAttr>(
"group");
272 auto instanceNameAttr = anno.
getMember<StringAttr>(
"instanceName");
274 circuit.emitError(
"missing `filename` attribute in `")
275 << anno.
getClass() <<
"` annotation";
280 if (!clkgateFileName.empty()) {
281 circuit.emitError(
"multiple `")
282 << anno.
getClass() <<
"` annotations on circuit";
287 clkgateFileName = filenameAttr.getValue();
289 clkgateWrapperModule = groupAttr.getValue();
290 if (instanceNameAttr)
291 clkgateWrapperInstanceName = instanceNameAttr.getValue();
296 StringRef memoryFileName;
297 StringRef memoryWrapperModule;
298 StringRef memoryWrapperInstanceName;
300 if (!anno.
isClass(extractSeqMemsFileAnnoClass))
302 LLVM_DEBUG(llvm::dbgs()
303 <<
"Memory extraction config: " << anno.
getDict() <<
"\n");
304 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
305 auto groupAttr = anno.
getMember<StringAttr>(
"group");
306 auto instanceNameAttr = anno.
getMember<StringAttr>(
"instanceName");
308 circuit.emitError(
"missing `filename` attribute in `")
309 << anno.
getClass() <<
"` annotation";
314 if (!memoryFileName.empty()) {
315 circuit.emitError(
"multiple `")
316 << anno.
getClass() <<
"` annotations on circuit";
321 memoryFileName = filenameAttr.getValue();
323 memoryWrapperModule = groupAttr.getValue();
324 if (instanceNameAttr)
325 memoryWrapperInstanceName = instanceNameAttr.getValue();
331 for (
auto module : circuit.getOps<FModuleLike>()) {
335 LLVM_DEBUG(llvm::dbgs() <<
"Annotated module `" << module.getModuleName()
336 <<
"`:\n " << anno.
getDict() <<
"\n");
337 annotatedModules[module].push_back(anno);
343 circuit.walk([&](InstanceOp inst) {
344 SmallVector<Annotation, 1> instAnnos;
345 Operation *module = inst.getReferencedModule(*instanceGraph);
348 auto it = annotatedModules.find(module);
349 if (it != annotatedModules.end())
350 instAnnos.append(it->second);
356 LLVM_DEBUG(llvm::dbgs() <<
"Annotated instance `" << inst.getName()
357 <<
"`:\n " << anno.
getDict() <<
"\n");
358 instAnnos.push_back(anno);
363 if (instAnnos.empty())
367 if (instAnnos.size() > 1) {
368 auto d = inst.emitError(
"multiple extraction annotations on instance `")
369 << inst.getName() <<
"`";
370 d.attachNote(inst.getLoc()) <<
"instance has the following annotations, "
371 "but at most one is allowed:";
372 for (
auto anno : instAnnos)
373 d.attachNote(inst.getLoc()) << anno.
getDict();
379 collectAnno(inst, instAnnos[0]);
388 if (!clkgateFileName.empty()) {
389 for (
auto module : circuit.getOps<FExtModuleOp>()) {
390 if (!module.getDefnameAttr().getValue().ends_with(
"EICG_wrapper"))
392 LLVM_DEBUG(llvm::dbgs()
393 <<
"Clock gate `" << module.getModuleName() <<
"`\n");
395 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
400 info.traceFilename = clkgateFileName;
401 info.prefix =
"clock_gate";
402 info.wrapperModule = clkgateWrapperModule;
403 info.wrapperInstanceName = clkgateWrapperInstanceName;
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});
412 instRecord->getInstance()->emitError()
413 <<
"cannot extract clock gate instances through non-InstanceOp";
423 if (!memoryFileName.empty()) {
427 getOrCreateFile(memoryFileName);
429 for (
auto module : circuit.getOps<FMemModuleOp>()) {
430 LLVM_DEBUG(llvm::dbgs() <<
"Memory `" << module.getModuleName() <<
"`\n");
432 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
437 info.traceFilename = memoryFileName;
438 info.prefix =
"mem_wiring";
439 info.wrapperModule = memoryWrapperModule;
440 info.wrapperInstanceName = memoryWrapperInstanceName;
441 for (
auto *instRecord : instanceGraph->
lookup(module)->
uses()) {
442 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
443 LLVM_DEBUG(llvm::dbgs()
445 << inst->getParentOfType<FModuleLike>().getModuleName()
446 <<
"." << inst.getName() <<
"`\n");
447 extractionWorklist.push_back({inst,
info});
449 instRecord->getInstance()->emitError()
450 <<
"cannot extract memory instances through non-InstanceOp";
460void ExtractInstancesPass::collectAnno(InstanceOp inst, Annotation anno) {
461 LLVM_DEBUG(llvm::dbgs() <<
"Processing instance `" << inst.getName() <<
"` "
464 auto getStringOrError = [&](StringRef member) {
465 auto attr = anno.
getMember<StringAttr>(member);
467 inst.emitError(
"missing `")
468 << member <<
"` attribute in `" << anno.
getClass() <<
"` annotation";
474 if (anno.
isClass(extractBlackBoxAnnoClass)) {
475 auto filename = getStringOrError(
"filename");
476 auto prefix = getStringOrError(
"prefix");
477 auto dest = anno.
getMember<StringAttr>(
"dest");
482 info.traceFilename = filename;
483 info.prefix = prefix;
484 info.wrapperModule = (dest ? dest.getValue() :
"");
490 extractionWorklist.push_back({inst,
info});
500 unsigned nlaLen = nla.getNamepath().size();
502 auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
503 for (
unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
504 auto refPart = nla.refPart(nlaIdx);
505 if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
514void ExtractInstancesPass::extractInstances() {
517 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
521 DenseMap<std::pair<Operation *, StringRef>,
unsigned> prefixUniqueIDs;
523 SmallPtrSet<Operation *, 4> nlasToRemove;
525 auto &nlaTable = getAnalysis<NLATable>();
528 for (
auto &[inst, info] : extractionWorklist)
529 originalInstanceParents[inst] =
530 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
532 while (!extractionWorklist.empty()) {
535 std::tie(inst, info) = extractionWorklist.pop_back_val();
537 auto parent = inst->getParentOfType<FModuleOp>();
547 auto &instPrefixEntry = instPrefixNamesPair[inst];
548 instPrefixEntry.second = inst.getInstanceNameAttr();
549 if (!
info.prefix.empty()) {
550 auto &prefixSlot = instPrefixEntry.first;
552 auto idx = prefixUniqueIDs[{parent,
info.prefix}]++;
553 (Twine(
info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
559 bool stopAtDUT = !moveDut && !
info.wrapperModule.empty();
565 if (inst->getParentOfType<LayerBlockOp>() ||
568 (stopAtDUT && instanceInfo->
isDut(parent))) {
569 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
570 extractedInstances.push_back({inst,
info});
574 llvm::dbgs() <<
"\nMoving ";
576 llvm::dbgs() <<
"`" << prefix <<
"` ";
577 llvm::dbgs() << inst <<
"\n";
582 unsigned numParentPorts = parent.getNumPorts();
583 unsigned numInstPorts = inst.getNumResults();
585 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
588 auto name = inst.getPortName(portIdx);
589 auto nameAttr = StringAttr::get(
591 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
593 PortInfo newPort{nameAttr,
596 newPort.
loc = inst.getResult(portIdx).getLoc();
597 newPorts.push_back({numParentPorts, newPort});
598 LLVM_DEBUG(llvm::dbgs()
599 <<
"- Adding port " << newPort.
direction <<
" "
600 << newPort.
name.getValue() <<
": " << newPort.
type <<
"\n");
602 parent.insertPorts(newPorts);
603 anythingChanged =
true;
607 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
608 inst.getResult(portIdx).replaceAllUsesWith(
609 parent.getArgument(numParentPorts + portIdx));
611 assert(inst.use_empty() &&
"instance ports should have been detached");
612 DenseSet<hw::HierPathOp> instanceNLAs;
615 nlaTable.getInstanceNLAs(inst, instanceNLAs);
618 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
621 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
625 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
626 instNonlocalAnnos[nla].push_back(anno);
627 instanceNLAs.insert(nla);
634 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
636 llvm::sort(sortedInstanceNLAs,
637 [](
auto a,
auto b) {
return a.getName() < b.getName(); });
642 auto *instParentNode =
643 instanceGraph->
lookup(cast<igraph::ModuleOpInterface>(*parent));
644 for (
auto *instRecord : instParentNode->uses()) {
645 auto oldParentInst = dyn_cast<InstanceOp>(*instRecord->getInstance());
646 if (!oldParentInst) {
647 inst.emitError(
"cannot extract instance `")
648 << inst.getName() <<
"` through a non-InstanceOp parent";
652 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
653 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
654 auto newParentInst = cast<InstanceOp>(
655 oldParentInst.cloneWithInsertedPortsAndReplaceUses(newPorts));
656 if (newParentInst.getInnerSymAttr())
657 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
659 auto newInst = cast<InstanceOp>(inst->clone());
665 getModuleNamespace(newParent).newName(instSym.getValue());
666 if (newName != instSym.getValue())
667 newInst.setInnerSymAttr(
668 hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
672 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
673 builder.setInsertionPointAfter(newParentInst);
674 builder.insert(newInst);
675 if (newParentInst.getInnerSymAttr())
677 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
678 auto dst = newInst.getResult(portIdx);
679 auto src = newParentInst.getResult(numParentPorts + portIdx);
680 if (newPorts[portIdx].second.direction == Direction::In)
682 MatchingConnectOp::create(builder, dst, src);
690 auto oldPrefix = instPrefixNamesPair.find(inst);
691 if (oldPrefix != instPrefixNamesPair.end()) {
692 LLVM_DEBUG(llvm::dbgs() <<
" - Moving prefix `"
693 << oldPrefix->second.first <<
"`\n");
694 auto newPrefix = std::move(oldPrefix->second);
695 instPrefixNamesPair.erase(oldPrefix);
696 instPrefixNamesPair.insert({newInst, newPrefix});
700 extractionPaths.try_emplace(newInst);
701 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
703 innerRefToInstances[instInnerRef] = newParentInst;
704 extractionPath.push_back(instInnerRef);
705 originalInstanceParents.try_emplace(newInst);
706 originalInstanceParents[newInst] = originalInstanceParents[inst];
709 SmallVector<Annotation> newInstNonlocalAnnos;
712 for (
auto nla : sortedInstanceNLAs) {
713 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
717 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
718 nla.getNamepath().end());
727 if (nlaIdx >= nlaPath.size()) {
728 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
731 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
738 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
740 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
742 LLVM_DEBUG(llvm::dbgs()
743 <<
" - Ignored since NLA parent " << innerRef
744 <<
" does not pass through extraction parent\n");
762 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
763 assert(isa<InnerRefAttr>(nlaPath[0]) &&
764 "head of hierpath must be an InnerRefAttr");
765 nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
768 if (instParentNode->hasOneUse()) {
773 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
774 for (
auto anno : instNonlocalAnnos.lookup(nla))
775 newInstNonlocalAnnos.push_back(anno);
776 nlaTable.addNLA(nla);
777 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
781 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
782 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
784 FlatSymbolRefAttr::get(newNla.getNameAttr()));
785 newInstNonlocalAnnos.push_back(anno);
788 nlaTable.addNLA(newNla);
789 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
799 inst.emitWarning(
"extraction of instance `")
800 << inst.getInstanceName()
801 <<
"` could break non-local annotations rooted at `"
802 << parent.getModuleName() <<
"`";
813 if (nlaPath.size() == 2) {
814 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
816 newInstNonlocalAnnos.push_back(anno);
817 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
821 nlasToRemove.insert(nla);
829 StringAttr parentName =
830 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
832 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
835 newRef = FlatSymbolRefAttr::get(parentName);
836 LLVM_DEBUG(llvm::dbgs()
837 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
838 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
839 nlaPath[nlaIdx] = newRef;
840 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
842 if (isa<FlatSymbolRefAttr>(newRef)) {
847 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
848 nlaTable.addNLA(newNla);
849 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
850 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
852 FlatSymbolRefAttr::get(newNla.getNameAttr()));
853 newInstNonlocalAnnos.push_back(anno);
856 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
857 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
858 for (
auto anno : instNonlocalAnnos.lookup(nla))
859 newInstNonlocalAnnos.push_back(anno);
866 AnnotationSet newInstAnnos(newInst);
867 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
868 newInstAnnos.applyToOperation(newInst);
872 extractionWorklist.push_back({newInst,
info});
873 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
877 oldParentInst.erase();
882 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
890 for (Operation *op : nlasToRemove) {
891 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
899void ExtractInstancesPass::groupInstances() {
903 using WrapperKey = std::tuple<Operation *, StringRef, StringRef>;
904 llvm::MapVector<WrapperKey, SmallVector<InstanceOp>> instsByWrapper;
905 for (
auto &[inst, info] : extractedInstances) {
906 if (
info.wrapperModule.empty())
908 auto instanceName =
info.wrapperInstanceName.empty()
910 :
info.wrapperInstanceName;
911 instsByWrapper[{inst->getParentOfType<FModuleOp>(),
info.wrapperModule,
915 if (instsByWrapper.empty())
917 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
920 SmallVector<PortInfo> ports;
921 auto &nlaTable = getAnalysis<NLATable>();
923 for (
auto &[wrapperKey, insts] : instsByWrapper) {
924 auto [parentOp, wrapperName, requestedInstanceName] = wrapperKey;
925 auto parent = cast<FModuleOp>(parentOp);
926 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
927 << parent.getModuleName() <<
"` with "
928 << insts.size() <<
" instances\n");
929 OpBuilder builder(parentOp);
932 auto wrapperModuleName =
933 builder.getStringAttr(circuitNamespace.
newName(wrapperName));
934 auto wrapperInstName = builder.getStringAttr(
935 getModuleNamespace(parent).newName(requestedInstanceName));
942 for (
auto inst : insts) {
944 StringRef prefix(instPrefixNamesPair[inst].first);
945 unsigned portNum = inst.getNumResults();
946 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
947 auto name = inst.getPortName(portIdx);
948 auto nameAttr = builder.getStringAttr(
949 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
950 PortInfo port{nameAttr,
952 inst.getPortDirection(portIdx)};
953 port.
loc = inst.getResult(portIdx).getLoc();
954 ports.push_back(port);
958 DenseSet<hw::HierPathOp> instNlas;
960 nlaTable.getInstanceNLAs(inst, instNlas);
961 AnnotationSet instAnnos(inst);
964 for (
auto anno : instAnnos) {
965 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
968 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
970 instNlas.insert(nla);
972 for (
auto nla : instNlas) {
973 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
977 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
978 nla.getNamepath().end());
980 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
981 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
986 InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
988 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
989 ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
991 ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
992 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
993 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
994 nlaPath[nlaIdx] = ref1;
995 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
999 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
1000 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
1002 nlaTable.addNLAtoModule(nla, wrapperModuleName);
1007 auto wrapper = FModuleOp::create(
1008 builder, builder.getUnknownLoc(), wrapperModuleName,
1009 ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
1010 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
1015 builder.setInsertionPointToStart(parent.getBodyBlock());
1016 auto wrapperInst = InstanceOp::create(
1017 builder, wrapper.getLoc(), wrapper, requestedInstanceName,
1018 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
1019 ArrayRef<Attribute>{},
false,
1020 false, hw::InnerSymAttr::get(wrapperInstName));
1021 unsigned portIdx = 0;
1022 for (
auto inst : insts)
1023 for (
auto result : inst.getResults())
1024 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
1029 builder.setInsertionPointToStart(wrapper.getBodyBlock());
1030 for (
auto inst : insts) {
1032 builder.insert(inst);
1033 for (
auto result : inst.getResults()) {
1035 Value src = wrapper.getArgument(portIdx);
1036 if (ports[portIdx].direction == Direction::Out)
1037 std::swap(dst, src);
1038 MatchingConnectOp::create(builder, result.getLoc(), dst, src);
1048void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1049 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1052 llvm::MapVector<StringRef, SmallVector<InstanceOp>> instsByTraceFile;
1053 for (
auto &[inst, info] : extractedInstances)
1054 if (!
info.traceFilename.empty())
1055 instsByTraceFile[
info.traceFilename].push_back(inst);
1058 SmallVector<Attribute> symbols;
1059 SmallDenseMap<Attribute, unsigned> symbolIndices;
1060 if (sifiveMetadataClass && !extractMetadataClass)
1063 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1065 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1066 classOp.getLoc(), classOp.getBodyBlock());
1067 auto portIndex = classOp.getNumPorts();
1068 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1069 for (
auto [index, port] : enumerate(objFields)) {
1071 auto obj = port.first;
1072 newPorts.emplace_back(
1074 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1075 obj.getType(), Direction::Out));
1077 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1078 PropAssignOp::create(builderOM, blockarg, obj);
1080 classOp.insertPorts(newPorts);
1083 HierPathCache pathCache(circuitOp, *symbolTable);
1084 SmallVector<std::pair<Value, Twine>> classFields;
1085 for (
auto &[fileName, insts] : instsByTraceFile) {
1086 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1088 llvm::raw_string_ostream os(buffer);
1090 symbolIndices.clear();
1092 auto addSymbol = [&](Attribute symbol) {
1094 auto it = symbolIndices.find(symbol);
1095 if (it != symbolIndices.end()) {
1098 id = symbols.size();
1099 symbols.push_back(symbol);
1100 symbolIndices.insert({symbol,
id});
1102 os <<
"{{" <<
id <<
"}}";
1105 auto file = getOrCreateFile(fileName);
1106 auto builder = OpBuilder::atBlockEnd(file.getBody());
1107 for (
auto inst : insts) {
1108 StringRef prefix(instPrefixNamesPair[inst].first);
1109 StringAttr origInstName(instPrefixNamesPair[inst].second);
1110 if (prefix.empty()) {
1111 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1112 <<
"` since it has no extraction prefix\n");
1115 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1117 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1118 <<
"` since it has not been moved\n");
1121 LLVM_DEBUG(llvm::dbgs()
1122 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1123 os << prefix <<
" -> ";
1125 if (sifiveMetadataClass) {
1127 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1128 inst.getLoc(), extractMetadataClass.getBodyBlock());
1129 auto prefixName = StringConstantOp::create(builderOM, prefix);
1130 auto object = ObjectOp::create(builderOM, schemaClass, prefix);
1132 ObjectSubfieldOp::create(builderOM,
object, prefixNameFieldId);
1133 PropAssignOp::create(builderOM, fPrefix, prefixName);
1135 auto targetInstance = innerRefToInstances[path.front()];
1136 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1137 auto nla = pathCache.getOpFor(
1138 ArrayAttr::get(circuitOp->getContext(), pathOpAttr));
1140 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1141 auto fPath = ObjectSubfieldOp::create(builderOM,
object, pathFieldId);
1142 PropAssignOp::create(builderOM, fPath, pathOp);
1144 ObjectSubfieldOp::create(builderOM,
object, fileNameFieldId);
1145 auto fileNameOp = StringConstantOp::create(
1146 builderOM, builder.getStringAttr(fileName));
1147 PropAssignOp::create(builderOM, fFile, fileNameOp);
1150 ObjectSubfieldOp::create(builderOM,
object, instNameFieldId);
1151 auto instNameOp = StringConstantOp::create(builderOM, origInstName);
1152 PropAssignOp::create(builderOM, finstName, instNameOp);
1155 classFields.emplace_back(
object, prefix +
"_field");
1160 while (!path.empty() &&
1162 symbolTable->lookup(path.back().getModule())))) {
1163 LLVM_DEBUG(llvm::dbgs()
1164 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1165 path = path.drop_back();
1170 addSymbol(FlatSymbolRefAttr::get(path.empty()
1171 ? originalInstanceParents[inst]
1172 : path.back().getModule()));
1173 for (
auto sym : llvm::reverse(path)) {
1177 os <<
"." << origInstName.getValue();
1185 sv::VerbatimOp::create(builder, builder.getUnknownLoc(), buffer,
1186 ValueRange{}, builder.getArrayAttr(symbols));
1188 if (!classFields.empty()) {
1189 addPortsToClass(classFields, extractMetadataClass);
1193 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1194 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1195 SmallVector<std::pair<Value, Twine>> classFields = {
1197 builderOM, extractMetadataClass,
1198 builderOM.getStringAttr(
"extract_instances_metadata")),
1199 "extractedInstances_field"}};
1201 addPortsToClass(classFields, sifiveMetadataClass);
1202 auto *node = instanceGraph->
lookup(sifiveMetadataClass);
1203 assert(node && node->hasOneUse());
1204 ObjectOp metadataObj = (*node->usesBegin())->getInstance<ObjectOp>();
1206 "expected the class to be instantiated by an object op");
1207 builderOM.setInsertionPoint(metadataObj);
1209 ObjectOp::create(builderOM, sifiveMetadataClass, metadataObj.getName());
1210 metadataObj->replaceAllUsesWith(newObj);
1211 metadataObj->remove();
1215void ExtractInstancesPass::createSchema() {
1217 auto *
context = circuitOp->getContext();
1218 auto unknownLoc = mlir::UnknownLoc::get(
context);
1219 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1220 unknownLoc, circuitOp.getBodyBlock());
1221 mlir::Type portsType[] = {
1227 StringRef portFields[] = {
"name",
"path",
"filename",
"inst_name"};
1229 schemaClass = ClassOp::create(builderOM,
"ExtractInstancesSchema", portFields,
1233 SmallVector<PortInfo> mports;
1234 extractMetadataClass = ClassOp::create(
1235 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 Block * getBodyBlock(FModuleLike mod)
void clear()
Empty the namespace.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
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.
bool isDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test.
bool anyInstanceInDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the design.
llvm::iterator_range< UseIterator > uses()
bool noUses()
Return true if there are no more instances of this module.
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
Direction flip(Direction direction)
Flip a port direction.
BaseTy type_cast(Type type)
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.
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void add(CircuitOp circuit)
Populate the namespace from a circuit operation.