29 #include "mlir/IR/IRMapping.h"
30 #include "llvm/ADT/BitVector.h"
31 #include "llvm/ADT/SetOperations.h"
32 #include "llvm/ADT/SetVector.h"
33 #include "llvm/ADT/SmallPtrSet.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/FormatVariadic.h"
38 #define DEBUG_TYPE "firrtl-inliner"
40 using namespace circt;
41 using namespace firrtl;
42 using namespace chirrtl;
44 using hw::InnerRefAttr;
45 using llvm::BitVector;
66 DenseMap<Attribute, unsigned> symIdx;
69 BitVector inlinedSymbols;
73 signed flattenPoint = -1;
84 bool moduleOnly =
false;
90 SmallVector<InnerRefAttr> newTops;
94 DenseSet<StringAttr> rootSet;
103 DenseMap<Attribute, StringAttr> renames;
109 StringAttr lookupRename(Attribute lastMod,
unsigned idx = 0) {
110 if (renames.count(lastMod))
111 return renames[lastMod];
112 return nla.refPart(idx);
117 : nla(nla), circuitNamespace(circuitNamespace),
118 inlinedSymbols(BitVector(nla.getNamepath().size(),
true)),
119 size(nla.getNamepath().size()) {
120 for (
size_t i = 0, e = size; i != e; ++i)
121 symIdx.insert({nla.modPart(i), i});
134 "the default constructor for MutableNLA should never be used");
139 void markDead() { dead =
true; }
142 void markModuleOnly() { moduleOnly =
true; }
145 hw::HierPathOp getNLA() {
return nla; }
153 hw::HierPathOp applyUpdates() {
155 if (isLocal() || isDead()) {
162 if (inlinedSymbols.all() && newTops.empty() && flattenPoint == -1 &&
169 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
170 SmallVector<Attribute> namepath;
174 if (!inlinedSymbols.test(1))
180 for (
signed i = 1, e = inlinedSymbols.size() - 1; i != e; ++i) {
181 if (i == flattenPoint) {
182 lastMod = nla.modPart(i);
186 if (!inlinedSymbols.test(i + 1)) {
188 lastMod = nla.modPart(i);
193 auto modPart = lastMod ? lastMod : nla.modPart(i);
194 auto refPart = lookupRename(modPart, i);
200 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
201 auto refPart = lookupRename(modPart, size - 1);
208 auto hp = b.create<hw::HierPathOp>(b.getUnknownLoc(), sym,
209 b.getArrayAttr(namepath));
210 hp.setVisibility(nla.getVisibility());
215 assert(!dead || !newTops.empty());
217 last = writeBack(nla.root(), nla.getNameAttr());
218 for (
auto root : newTops)
219 last = writeBack(root.getModule(), root.getName());
226 llvm::errs() <<
" - orig: " << nla <<
"\n"
227 <<
" new: " << *
this <<
"\n"
228 <<
" dead: " << dead <<
"\n"
229 <<
" isDead: " << isDead() <<
"\n"
230 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
231 <<
" isLocal: " << isLocal() <<
"\n"
232 <<
" inlinedSymbols: [";
233 llvm::interleaveComma(inlinedSymbols.getData(), llvm::errs(), [](
auto a) {
234 llvm::errs() << llvm::formatv(
"{0:x-}", a);
236 llvm::errs() <<
"]\n"
237 <<
" flattenPoint: " << flattenPoint <<
"\n"
239 for (
auto rename : renames)
240 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
247 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
248 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
250 os <<
"#hw.innerNameRef<";
251 os <<
"@" << mod.getValue();
253 os <<
"::@" << sym.getValue() <<
">";
256 auto writeOne = [&](StringAttr root, StringAttr sym) {
257 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
261 if (!x.inlinedSymbols.test(1))
264 writePathSegment(root, x.lookupRename(root));
267 bool needsComma =
false;
268 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i != e; ++i) {
269 if (i == x.flattenPoint) {
270 lastMod = x.nla.modPart(i);
274 if (!x.inlinedSymbols.test(i + 1)) {
276 lastMod = x.nla.modPart(i);
282 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
283 auto refPart = x.nla.refPart(i);
284 if (x.renames.count(modPart))
285 refPart = x.renames[modPart];
286 writePathSegment(modPart, refPart);
293 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
294 auto refPart = x.nla.refPart(x.size - 1);
295 if (x.renames.count(modPart))
296 refPart = x.renames[modPart];
297 writePathSegment(modPart, refPart);
301 SmallVector<InnerRefAttr> tops;
304 tops.append(x.newTops.begin(), x.newTops.end());
306 bool multiary = !x.newTops.empty();
309 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
310 writeOne(a.getModule(), a.getName());
322 bool isDead() {
return dead && newTops.empty(); }
325 bool isModuleOnly() {
return moduleOnly; }
331 unsigned end = flattenPoint > -1 ? flattenPoint + 1 : inlinedSymbols.size();
332 return inlinedSymbols.find_first_in(1, end) == -1;
336 bool hasRoot(FModuleLike mod) {
337 return (isDead() && nla.root() == mod.getModuleNameAttr()) ||
338 rootSet.contains(mod.getModuleNameAttr());
342 bool hasRoot(StringAttr modName) {
343 return (nla.root() == modName) || rootSet.contains(modName);
347 void inlineModule(FModuleOp module) {
348 auto sym = module.getNameAttr();
349 assert(sym != nla.root() &&
"unable to inline the root module");
350 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
351 auto idx = symIdx[sym];
352 inlinedSymbols.reset(idx);
355 if (idx == size - 1 && moduleOnly)
362 void flattenModule(FModuleOp module) {
363 auto sym = module.getNameAttr();
364 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
365 auto idx = symIdx[sym] - 1;
373 StringAttr reTop(FModuleOp module) {
374 StringAttr sym = nla.getSymNameAttr();
375 if (!newTops.empty())
377 circuitNamespace->
newName(sym.getValue()));
379 rootSet.insert(module.getNameAttr());
380 symIdx.insert({module.getNameAttr(), 0});
385 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
387 void setInnerSym(Attribute module, StringAttr innerSym) {
388 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
389 assert(!renames.count(module) &&
"Module already renamed");
390 renames.insert({module, innerSym});
401 InstanceOp instance) {
402 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
403 auto result = instance.getResult(i);
404 auto wire = wires[i];
405 mapper.map(result, wire);
413 StringAttr istName) {
414 mlir::AttrTypeReplacer replacer;
415 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
416 auto it = map.find(innerRef);
423 llvm::for_each(newOps,
424 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
431 hw::InnerSymbolNamespace &ns,
432 StringAttr istName) {
433 if (!old || old.empty())
436 bool anyChanged =
false;
438 SmallVector<hw::InnerSymPropertiesAttr> newProps;
439 auto *context = old.getContext();
440 for (
auto &prop : old) {
441 auto newSym = ns.newName(prop.getName().strref());
442 if (newSym == prop.getName()) {
443 newProps.push_back(prop);
448 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
450 newProps.push_back(newProp);
455 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
456 assert(oldProp.getFieldID() == newProp.getFieldID());
490 Inliner(CircuitOp circuit, SymbolTable &symbolTable);
498 struct ModuleInliningContext {
499 ModuleInliningContext(FModuleOp module)
500 : module(module), modNamespace(module), b(module.getContext()) {}
504 hw::InnerSymbolNamespace modNamespace;
512 struct InliningLevel {
513 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
514 : mic(mic), childModule(childModule) {}
517 ModuleInliningContext &mic;
521 SmallVector<Operation *> newOps;
523 SmallVector<Value> wires;
525 FModuleOp childModule;
531 mic.module.getNameAttr());
538 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
542 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
547 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
549 const DenseMap<Attribute, Attribute> &symbolRenames);
553 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
555 const DenseMap<Attribute, Attribute> &symbolRenames,
556 const DenseSet<Attribute> &localSymbols);
561 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
562 const DenseSet<Attribute> &localSymbols);
565 bool shouldFlatten(Operation *op);
568 bool shouldInline(Operation *op);
573 void flattenInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
574 DenseSet<Attribute> localSymbols);
579 void inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
580 DenseMap<Attribute, Attribute> &symbolRenames);
583 void flattenInstances(FModuleOp module);
586 void inlineInstances(FModuleOp module);
590 void createDebugScope(InliningLevel &il, InstanceOp instance,
591 Value parentScope = {});
594 void identifyNLAsTargetingOnlyModules();
600 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
603 if (currentPath.empty()) {
604 activeHierpaths.insert(instPaths.begin(), instPaths.end());
607 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
610 llvm::set_intersect(activeHierpaths, hPaths);
613 for (
auto hPath : instPaths)
614 if (nlaMap[hPath].hasRoot(moduleName))
615 activeHierpaths.insert(hPath);
619 MLIRContext *context;
622 SymbolTable &symbolTable;
626 DenseSet<Operation *> liveModules;
629 SmallVector<FModuleOp, 16> worklist;
632 DenseMap<Attribute, MutableNLA> nlaMap;
635 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
640 SmallVector<std::pair<Attribute, Attribute>> currentPath;
642 DenseSet<StringAttr> activeHierpaths;
647 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
651 SmallVector<debug::ScopeOp> debugScopes;
658 bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
659 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
666 bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
669 auto updateDebugScope = [&](
auto op) {
671 op.getScopeMutable().assign(il.debugScope);
673 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
674 return updateDebugScope(varOp),
false;
675 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
676 return updateDebugScope(scopeOp),
false;
681 if (!isa<MemOp, SeqMemOp, CombMemOp, MemoryPortOp>(op)) {
682 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
684 (prefix + nameAttr.getValue())));
689 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
692 auto oldSymAttr = symOp.getInnerSymAttr();
695 il.childModule.getNameAttr());
701 if (
auto newSymStrAttr = newSymAttr.getSymName();
702 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
704 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
712 auto &mnla = nlaMap[sym.getAttr()];
713 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
715 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
719 symOp.setInnerSymbolAttr(newSymAttr);
721 return newSymAttr != oldSymAttr;
724 bool Inliner::renameInstance(
725 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
726 const DenseMap<Attribute, Attribute> &symbolRenames) {
731 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
736 auto parentActivePaths = activeHierpaths;
737 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
739 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
744 SmallVector<StringAttr> validHierPaths;
745 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
753 for (
auto old : instOpHierPaths[oldInnerRef]) {
757 if (activeHierpaths.find(old) != activeHierpaths.end())
758 validHierPaths.push_back(old);
762 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
763 if (activeHierpaths.find(additionalSym.getName()) !=
764 activeHierpaths.end()) {
765 validHierPaths.push_back(old);
774 auto symbolChanged = rename(prefix, newInst, il);
783 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
784 instOpHierPaths[newInnerRef] = validHierPaths;
786 for (
auto nla : instOpHierPaths[newInnerRef]) {
787 if (!nlaMap.count(nla))
789 auto &mnla = nlaMap[nla];
790 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
796 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
797 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
799 for (
const auto &en : llvm::enumerate(nlaList)) {
800 auto oldNLA =
en.value();
801 if (
auto newSym = symbolRenames.lookup(oldNLA))
802 nlaList[
en.index()] = cast<StringAttr>(newSym);
805 activeHierpaths = std::move(parentActivePaths);
806 return symbolChanged;
814 void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
816 const DenseSet<Attribute> &localSymbols) {
817 auto target = il.childModule;
818 auto portInfo = target.getPorts();
819 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
820 auto arg = target.getArgument(i);
822 auto type = type_cast<FIRRTLType>(arg.getType());
825 auto oldSymAttr = portInfo[i].sym;
828 il.mic.modNamespace, target.getNameAttr());
830 StringAttr newRootSymName, oldRootSymName;
832 oldRootSymName = oldSymAttr.getSymName();
834 newRootSymName = newSymAttr.getSymName();
836 SmallVector<Attribute> newAnnotations;
839 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
840 auto &mnla = nlaMap[sym.getAttr()];
842 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
846 if (oldRootSymName != newRootSymName)
847 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
849 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
850 anno.removeMember(
"circt.nonlocal");
852 newAnnotations.push_back(anno.getAttr());
858 target.getLoc(), type,
864 il.wires.push_back(wire);
865 mapper.map(arg, wire);
872 void Inliner::cloneAndRename(
873 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
874 const DenseMap<Attribute, Attribute> &symbolRenames,
875 const DenseSet<Attribute> &localSymbols) {
878 SmallVector<Annotation> newAnnotations;
879 for (
auto anno : oldAnnotations) {
882 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
884 auto &mnla = nlaMap[sym.getAttr()];
886 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
889 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
890 anno.removeMember(
"circt.nonlocal");
893 newAnnotations.push_back(anno);
897 auto *newOp = il.mic.b.clone(op, mapper);
901 op.walk<mlir::WalkOrder::PreOrder>([&](Operation *origOp) {
902 auto *newOpToRename = mapper.lookup(origOp);
906 assert((origOp == &op || !isa<InstanceOp>(origOp)) &&
907 "Cannot handle instances not at top-level");
911 if (
auto oldInst = dyn_cast<InstanceOp>(origOp))
912 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOpToRename),
915 rename(prefix, newOpToRename, il);
920 if (!newAnnotations.empty() || !oldAnnotations.empty())
923 il.newOps.push_back(newOp);
926 bool Inliner::shouldFlatten(Operation *op) {
930 bool Inliner::shouldInline(Operation *op) {
935 void Inliner::flattenInto(StringRef prefix, InliningLevel &il,
936 IRMapping &mapper, DenseSet<Attribute> localSymbols) {
937 auto target = il.childModule;
938 auto moduleName = target.getNameAttr();
939 DenseMap<Attribute, Attribute> symbolRenames;
940 for (
auto &op : *target.getBodyBlock()) {
942 auto instance = dyn_cast<InstanceOp>(op);
944 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
949 auto *module = symbolTable.lookup(instance.getModuleName());
950 auto childModule = dyn_cast<FModuleOp>(module);
952 liveModules.insert(module);
954 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
961 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
963 auto parentActivePaths = activeHierpaths;
964 setActiveHierPaths(moduleName, instInnerSym);
965 currentPath.emplace_back(moduleName, instInnerSym);
967 InliningLevel childIL(il.mic, childModule);
968 createDebugScope(childIL, instance, il.debugScope);
971 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
972 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
976 flattenInto(nestedPrefix, childIL, mapper, localSymbols);
977 currentPath.pop_back();
978 activeHierpaths = parentActivePaths;
982 void Inliner::flattenInstances(FModuleOp module) {
983 auto moduleName = module.getNameAttr();
984 ModuleInliningContext mic(module);
986 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
988 auto instance = dyn_cast<InstanceOp>(op);
993 auto *targetModule = symbolTable.lookup(instance.getModuleName());
994 auto target = dyn_cast<FModuleOp>(targetModule);
996 liveModules.insert(targetModule);
1004 for (
auto targetNLA : instOpHierPaths[innerRef]) {
1005 nlaMap[targetNLA].flattenModule(target);
1012 DenseSet<Attribute> localSymbols;
1013 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1015 auto parentActivePaths = activeHierpaths;
1016 setActiveHierPaths(moduleName, instInnerSym);
1017 currentPath.emplace_back(moduleName, instInnerSym);
1022 mic.b.setInsertionPoint(instance);
1024 InliningLevel il(mic, target);
1025 createDebugScope(il, instance);
1027 auto nestedPrefix = (instance.getName() +
"_").str();
1028 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1029 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1030 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1033 flattenInto(nestedPrefix, il, mapper, localSymbols);
1034 currentPath.pop_back();
1035 activeHierpaths = parentActivePaths;
1043 void Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1044 DenseMap<Attribute, Attribute> &symbolRenames) {
1045 auto target = il.childModule;
1046 auto inlineToParent = il.mic.module;
1047 auto moduleName = target.getNameAttr();
1049 for (
auto &op : *target.getBodyBlock()) {
1051 auto instance = dyn_cast<InstanceOp>(op);
1053 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1058 auto *module = symbolTable.lookup(instance.getModuleName());
1059 auto childModule = dyn_cast<FModuleOp>(module);
1061 liveModules.insert(module);
1062 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1067 if (!shouldInline(childModule)) {
1068 if (liveModules.insert(childModule).second) {
1069 worklist.push_back(childModule);
1071 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1075 auto toBeFlattened = shouldFlatten(childModule);
1081 for (
auto sym : instOpHierPaths[innerRef]) {
1083 nlaMap[sym].flattenModule(childModule);
1085 nlaMap[sym].inlineModule(childModule);
1096 DenseMap<Attribute, Attribute> symbolRenames;
1097 if (!rootMap[childModule.getNameAttr()].empty()) {
1098 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1099 auto &mnla = nlaMap[sym];
1102 sym = mnla.reTop(inlineToParent);
1106 context, il.mic.modNamespace.newName(instance.getName()));
1110 cast<StringAttr>(sym));
1114 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1118 auto parentActivePaths = activeHierpaths;
1119 setActiveHierPaths(moduleName, instInnerSym);
1121 currentPath.emplace_back(moduleName, instInnerSym);
1123 InliningLevel childIL(il.mic, childModule);
1124 createDebugScope(childIL, instance, il.debugScope);
1127 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1128 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1132 if (toBeFlattened) {
1133 flattenInto(nestedPrefix, childIL, mapper, {});
1135 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1137 currentPath.pop_back();
1138 activeHierpaths = parentActivePaths;
1142 void Inliner::inlineInstances(FModuleOp module) {
1144 auto moduleName = module.getNameAttr();
1146 SmallVector<Value> wires;
1147 ModuleInliningContext mic(module);
1149 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
1151 auto instance = dyn_cast<InstanceOp>(op);
1156 auto *childModule = symbolTable.lookup(instance.getModuleName());
1157 auto target = dyn_cast<FModuleOp>(childModule);
1159 liveModules.insert(childModule);
1164 if (!shouldInline(target)) {
1165 if (liveModules.insert(target).second) {
1166 worklist.push_back(target);
1171 auto toBeFlattened = shouldFlatten(target);
1177 for (
auto sym : instOpHierPaths[innerRef]) {
1179 nlaMap[sym].flattenModule(target);
1181 nlaMap[sym].inlineModule(target);
1188 DenseMap<Attribute, Attribute> symbolRenames;
1189 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1190 for (
auto sym : rootMap[target.getNameAttr()]) {
1191 auto &mnla = nlaMap[sym];
1192 sym = mnla.reTop(module);
1194 instance, [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1195 return mic.modNamespace;
1198 cast<StringAttr>(sym));
1202 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1206 auto parentActivePaths = activeHierpaths;
1207 setActiveHierPaths(moduleName, instInnerSym);
1209 currentPath.emplace_back(moduleName, instInnerSym);
1213 mic.b.setInsertionPoint(instance);
1214 auto nestedPrefix = (instance.getName() +
"_").str();
1216 InliningLevel childIL(mic, target);
1217 createDebugScope(childIL, instance);
1219 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1220 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1221 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1224 if (toBeFlattened) {
1225 flattenInto(nestedPrefix, childIL, mapper, {});
1229 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1231 currentPath.pop_back();
1232 activeHierpaths = parentActivePaths;
1239 void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1240 Value parentScope) {
1241 auto op = il.mic.b.create<debug::ScopeOp>(
1242 instance.getLoc(), instance.getInstanceNameAttr(),
1243 instance.getModuleNameAttr().getAttr(), parentScope);
1244 debugScopes.push_back(op);
1248 void Inliner::identifyNLAsTargetingOnlyModules() {
1249 DenseSet<Operation *> nlaTargetedModules;
1252 for (
auto &[sym, mnla] : nlaMap) {
1253 auto nla = mnla.getNLA();
1254 if (nla.isModule()) {
1255 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1257 "NLA ends in module reference but does not target FModuleLike?");
1258 nlaTargetedModules.insert(mod);
1263 auto scanForNLARefs = [&](FModuleLike mod) {
1264 DenseSet<StringAttr> referencedNLASyms;
1266 for (
auto anno : annos)
1267 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1268 referencedNLASyms.insert(sym.getAttr());
1271 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1276 mod.walk([&](Operation *op) {
1277 if (op == mod.getOperation())
1282 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1283 for (
auto portAnnoAttr : op.getPortAnnotations())
1288 return referencedNLASyms;
1292 auto mergeSets = [](
auto &&a,
auto &&b) {
1293 a.insert(b.begin(), b.end());
1294 return std::move(a);
1299 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1300 nlaTargetedModules.end());
1301 auto nonModOnlyNLAs =
1303 mergeSets, scanForNLARefs);
1306 for (
auto &[_, mnla] : nlaMap) {
1307 auto nla = mnla.getNLA();
1308 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1309 mnla.markModuleOnly();
1313 Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1314 : circuit(circuit), context(circuit.getContext()),
1315 symbolTable(symbolTable) {}
1317 void Inliner::run() {
1321 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1322 auto mnla = MutableNLA(nla, &circuitNamespace);
1323 nlaMap.insert({nla.getSymNameAttr(), mnla});
1324 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1325 for (
auto p : nla.getNamepath())
1326 if (
auto ref = dyn_cast<InnerRefAttr>(p))
1327 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1331 identifyNLAsTargetingOnlyModules();
1334 for (
auto module : circuit.getOps<FModuleLike>()) {
1335 if (module.canDiscardOnUseEmpty())
1337 liveModules.insert(module);
1338 if (isa<FModuleOp>(module))
1339 worklist.push_back(cast<FModuleOp>(module));
1344 while (!worklist.empty()) {
1345 auto module = worklist.pop_back_val();
1346 if (shouldFlatten(module)) {
1347 flattenInstances(module);
1353 inlineInstances(module);
1359 for (
auto scopeOp : llvm::reverse(debugScopes))
1360 if (scopeOp.use_empty())
1362 debugScopes.clear();
1366 for (
auto mod : llvm::make_early_inc_range(
1367 circuit.getBodyBlock()->getOps<FModuleLike>())) {
1368 if (liveModules.count(mod))
1370 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1371 nlaMap[nla].markDead();
1377 for (
auto mod : circuit.getBodyBlock()->getOps<FModuleLike>()) {
1378 if (shouldInline(mod)) {
1380 "non-public module with inline annotation still present");
1383 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1387 llvm::dbgs() <<
"NLA modifications:\n";
1388 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1389 auto &mnla = nlaMap[nla.getNameAttr()];
1395 for (
auto &nla : nlaMap)
1396 nla.getSecond().applyUpdates();
1400 for (
auto fmodule : circuit.getBodyBlock()->getOps<FModuleOp>()) {
1401 SmallVector<Attribute> newAnnotations;
1402 auto processNLAs = [&](
Annotation anno) ->
bool {
1403 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1407 if (!nlaMap.count(sym.getAttr()))
1410 auto mnla = nlaMap[sym.getAttr()];
1421 auto newTops = mnla.getAdditionalSymbols();
1422 if (newTops.empty() || mnla.hasRoot(fmodule))
1428 NamedAttrList newAnnotation;
1429 for (
auto rootAndSym : newTops.drop_front()) {
1430 for (
auto pair : anno.getDict()) {
1431 if (pair.getName().getValue() !=
"circt.nonlocal") {
1432 newAnnotation.push_back(pair);
1435 newAnnotation.push_back(
1443 fmodule.walk([&](Operation *op) {
1447 if (annotations.empty())
1451 newAnnotations.clear();
1452 annotations.removeAnnotations(processNLAs);
1453 annotations.addAnnotations(newAnnotations);
1454 annotations.applyToOperation(op);
1458 SmallVector<Attribute> newPortAnnotations;
1459 for (
auto port : fmodule.getPorts()) {
1460 newAnnotations.clear();
1461 port.annotations.removeAnnotations(processNLAs);
1462 port.annotations.addAnnotations(newAnnotations);
1463 newPortAnnotations.push_back(
1466 fmodule->setAttr(
"portAnnotations",
1476 class InlinerPass :
public InlinerBase<InlinerPass> {
1477 void runOnOperation()
override {
1479 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1487 return std::make_unique<InlinerPass>();
assert(baseType &&"element must be base type")
static void dump(DIModule &module, raw_indented_ostream &os)
DenseMap< hw::InnerRefAttr, StringAttr > InnerRefToNewNameMap
static hw::InnerSymAttr uniqueInNamespace(hw::InnerSymAttr old, InnerRefToNewNameMap &map, hw::InnerSymbolNamespace &ns, StringAttr istName)
Generate and creating map entries for new inner symbol based on old one and an appropriate namespace ...
static void mapResultsToWires(IRMapping &mapper, SmallVectorImpl< Value > &wires, InstanceOp instance)
This function is used after inlining a module, to handle the conversion between module ports and inst...
static void replaceInnerRefUsers(ArrayRef< Operation * > newOps, const InnerRefToNewNameMap &map, StringAttr istName)
Process each operation, updating InnerRefAttr's using the specified map and the given name as the con...
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
This class provides a read-only projection of an annotation.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
std::unique_ptr< mlir::Pass > createInlinerPass()
T & operator<<(T &os, FIRVersion version)
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
constexpr const char * inlineAnnoClass
constexpr const char * flattenAnnoClass
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
llvm::raw_ostream & debugFooter(int width=80)
Write a boilerplate footer to the debug stream to indicate that a pass has ended.
The namespace of a CircuitOp, generally inhabited by modules.