28 #include "mlir/IR/IRMapping.h"
29 #include "mlir/Pass/Pass.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"
42 #define GEN_PASS_DEF_INLINER
43 #include "circt/Dialect/FIRRTL/Passes.h.inc"
47 using namespace circt;
48 using namespace firrtl;
49 using namespace chirrtl;
51 using hw::InnerRefAttr;
52 using llvm::BitVector;
73 DenseMap<Attribute, unsigned> symIdx;
76 BitVector inlinedSymbols;
80 signed flattenPoint = -1;
91 bool moduleOnly =
false;
97 SmallVector<InnerRefAttr> newTops;
101 DenseSet<StringAttr> rootSet;
110 DenseMap<Attribute, StringAttr> renames;
116 StringAttr lookupRename(Attribute lastMod,
unsigned idx = 0) {
117 if (renames.count(lastMod))
118 return renames[lastMod];
119 return nla.refPart(idx);
124 : nla(nla), circuitNamespace(circuitNamespace),
125 inlinedSymbols(BitVector(nla.getNamepath().size(),
true)),
126 size(nla.getNamepath().size()) {
127 for (
size_t i = 0, e = size; i != e; ++i)
128 symIdx.insert({nla.modPart(i), i});
141 "the default constructor for MutableNLA should never be used");
146 void markDead() { dead =
true; }
149 void markModuleOnly() { moduleOnly =
true; }
152 hw::HierPathOp getNLA() {
return nla; }
160 hw::HierPathOp applyUpdates() {
162 if (isLocal() || isDead()) {
169 if (inlinedSymbols.all() && newTops.empty() && flattenPoint == -1 &&
176 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
177 SmallVector<Attribute> namepath;
181 if (!inlinedSymbols.test(1))
187 for (
signed i = 1, e = inlinedSymbols.size() - 1; i != e; ++i) {
188 if (i == flattenPoint) {
189 lastMod = nla.modPart(i);
193 if (!inlinedSymbols.test(i + 1)) {
195 lastMod = nla.modPart(i);
200 auto modPart = lastMod ? lastMod : nla.modPart(i);
201 auto refPart = lookupRename(modPart, i);
207 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
208 auto refPart = lookupRename(modPart, size - 1);
215 auto hp = b.create<hw::HierPathOp>(b.getUnknownLoc(), sym,
216 b.getArrayAttr(namepath));
217 hp.setVisibility(nla.getVisibility());
222 assert(!dead || !newTops.empty());
224 last = writeBack(nla.root(), nla.getNameAttr());
225 for (
auto root : newTops)
226 last = writeBack(root.getModule(), root.getName());
233 llvm::errs() <<
" - orig: " << nla <<
"\n"
234 <<
" new: " << *
this <<
"\n"
235 <<
" dead: " << dead <<
"\n"
236 <<
" isDead: " << isDead() <<
"\n"
237 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
238 <<
" isLocal: " << isLocal() <<
"\n"
239 <<
" inlinedSymbols: [";
240 llvm::interleaveComma(inlinedSymbols.getData(), llvm::errs(), [](
auto a) {
241 llvm::errs() << llvm::formatv(
"{0:x-}", a);
243 llvm::errs() <<
"]\n"
244 <<
" flattenPoint: " << flattenPoint <<
"\n"
246 for (
auto rename : renames)
247 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
254 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
255 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
257 os <<
"#hw.innerNameRef<";
258 os <<
"@" << mod.getValue();
260 os <<
"::@" << sym.getValue() <<
">";
263 auto writeOne = [&](StringAttr root, StringAttr sym) {
264 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
268 if (!x.inlinedSymbols.test(1))
271 writePathSegment(root, x.lookupRename(root));
274 bool needsComma =
false;
275 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i != e; ++i) {
276 if (i == x.flattenPoint) {
277 lastMod = x.nla.modPart(i);
281 if (!x.inlinedSymbols.test(i + 1)) {
283 lastMod = x.nla.modPart(i);
289 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
290 auto refPart = x.nla.refPart(i);
291 if (x.renames.count(modPart))
292 refPart = x.renames[modPart];
293 writePathSegment(modPart, refPart);
300 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
301 auto refPart = x.nla.refPart(x.size - 1);
302 if (x.renames.count(modPart))
303 refPart = x.renames[modPart];
304 writePathSegment(modPart, refPart);
308 SmallVector<InnerRefAttr> tops;
311 tops.append(x.newTops.begin(), x.newTops.end());
313 bool multiary = !x.newTops.empty();
316 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
317 writeOne(a.getModule(), a.getName());
329 bool isDead() {
return dead && newTops.empty(); }
332 bool isModuleOnly() {
return moduleOnly; }
338 unsigned end = flattenPoint > -1 ? flattenPoint + 1 : inlinedSymbols.size();
339 return inlinedSymbols.find_first_in(1, end) == -1;
343 bool hasRoot(FModuleLike mod) {
344 return (isDead() && nla.root() == mod.getModuleNameAttr()) ||
345 rootSet.contains(mod.getModuleNameAttr());
349 bool hasRoot(StringAttr modName) {
350 return (nla.root() == modName) || rootSet.contains(modName);
354 void inlineModule(FModuleOp module) {
355 auto sym = module.getNameAttr();
356 assert(sym != nla.root() &&
"unable to inline the root module");
357 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
358 auto idx = symIdx[sym];
359 inlinedSymbols.reset(idx);
362 if (idx == size - 1 && moduleOnly)
369 void flattenModule(FModuleOp module) {
370 auto sym = module.getNameAttr();
371 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
372 auto idx = symIdx[sym] - 1;
380 StringAttr reTop(FModuleOp module) {
381 StringAttr sym = nla.getSymNameAttr();
382 if (!newTops.empty())
384 circuitNamespace->
newName(sym.getValue()));
386 rootSet.insert(module.getNameAttr());
387 symIdx.insert({module.getNameAttr(), 0});
392 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
394 void setInnerSym(Attribute module, StringAttr innerSym) {
395 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
396 assert(!renames.count(module) &&
"Module already renamed");
397 renames.insert({module, innerSym});
408 InstanceOp instance) {
409 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
410 auto result = instance.getResult(i);
411 auto wire = wires[i];
412 mapper.map(result, wire);
420 StringAttr istName) {
421 mlir::AttrTypeReplacer replacer;
422 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
423 auto it = map.find(innerRef);
430 llvm::for_each(newOps,
431 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
438 hw::InnerSymbolNamespace &ns,
439 StringAttr istName) {
440 if (!old || old.empty())
443 bool anyChanged =
false;
445 SmallVector<hw::InnerSymPropertiesAttr> newProps;
446 auto *context = old.getContext();
447 for (
auto &prop : old) {
448 auto newSym = ns.newName(prop.getName().strref());
449 if (newSym == prop.getName()) {
450 newProps.push_back(prop);
455 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
457 newProps.push_back(newProp);
462 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
463 assert(oldProp.getFieldID() == newProp.getFieldID());
497 Inliner(CircuitOp circuit, SymbolTable &symbolTable);
505 struct ModuleInliningContext {
506 ModuleInliningContext(FModuleOp module)
507 : module(module), modNamespace(module), b(module.getContext()) {}
511 hw::InnerSymbolNamespace modNamespace;
519 struct InliningLevel {
520 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
521 : mic(mic), childModule(childModule) {}
524 ModuleInliningContext &mic;
528 SmallVector<Operation *> newOps;
530 SmallVector<Value> wires;
532 FModuleOp childModule;
538 mic.module.getNameAttr());
545 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
549 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
554 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
556 const DenseMap<Attribute, Attribute> &symbolRenames);
560 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
562 const DenseMap<Attribute, Attribute> &symbolRenames,
563 const DenseSet<Attribute> &localSymbols);
568 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
569 const DenseSet<Attribute> &localSymbols);
572 bool shouldFlatten(Operation *op);
575 bool shouldInline(Operation *op);
580 void flattenInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
581 DenseSet<Attribute> localSymbols);
586 void inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
587 DenseMap<Attribute, Attribute> &symbolRenames);
590 void flattenInstances(FModuleOp module);
593 void inlineInstances(FModuleOp module);
597 void createDebugScope(InliningLevel &il, InstanceOp instance,
598 Value parentScope = {});
601 void identifyNLAsTargetingOnlyModules();
607 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
610 if (currentPath.empty()) {
611 activeHierpaths.insert(instPaths.begin(), instPaths.end());
614 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
617 llvm::set_intersect(activeHierpaths, hPaths);
620 for (
auto hPath : instPaths)
621 if (nlaMap[hPath].hasRoot(moduleName))
622 activeHierpaths.insert(hPath);
626 MLIRContext *context;
629 SymbolTable &symbolTable;
633 DenseSet<Operation *> liveModules;
636 SmallVector<FModuleOp, 16> worklist;
639 DenseMap<Attribute, MutableNLA> nlaMap;
642 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
647 SmallVector<std::pair<Attribute, Attribute>> currentPath;
649 DenseSet<StringAttr> activeHierpaths;
654 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
658 SmallVector<debug::ScopeOp> debugScopes;
665 bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
666 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
673 bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
676 auto updateDebugScope = [&](
auto op) {
678 op.getScopeMutable().assign(il.debugScope);
680 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
681 return updateDebugScope(varOp),
false;
682 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
683 return updateDebugScope(scopeOp),
false;
686 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
688 (prefix + nameAttr.getValue())));
692 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
695 auto oldSymAttr = symOp.getInnerSymAttr();
698 il.childModule.getNameAttr());
704 if (
auto newSymStrAttr = newSymAttr.getSymName();
705 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
707 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
715 auto &mnla = nlaMap[sym.getAttr()];
716 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
718 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
722 symOp.setInnerSymbolAttr(newSymAttr);
724 return newSymAttr != oldSymAttr;
727 bool Inliner::renameInstance(
728 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
729 const DenseMap<Attribute, Attribute> &symbolRenames) {
734 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
739 auto parentActivePaths = activeHierpaths;
740 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
742 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
747 SmallVector<StringAttr> validHierPaths;
748 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
756 for (
auto old : instOpHierPaths[oldInnerRef]) {
760 if (activeHierpaths.find(old) != activeHierpaths.end())
761 validHierPaths.push_back(old);
765 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
766 if (activeHierpaths.find(additionalSym.getName()) !=
767 activeHierpaths.end()) {
768 validHierPaths.push_back(old);
777 auto symbolChanged = rename(prefix, newInst, il);
786 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
787 instOpHierPaths[newInnerRef] = validHierPaths;
789 for (
auto nla : instOpHierPaths[newInnerRef]) {
790 if (!nlaMap.count(nla))
792 auto &mnla = nlaMap[nla];
793 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
799 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
800 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
802 for (
const auto &en : llvm::enumerate(nlaList)) {
803 auto oldNLA =
en.value();
804 if (
auto newSym = symbolRenames.lookup(oldNLA))
805 nlaList[
en.index()] = cast<StringAttr>(newSym);
808 activeHierpaths = std::move(parentActivePaths);
809 return symbolChanged;
817 void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
819 const DenseSet<Attribute> &localSymbols) {
820 auto target = il.childModule;
821 auto portInfo = target.getPorts();
822 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
823 auto arg = target.getArgument(i);
825 auto type = type_cast<FIRRTLType>(arg.getType());
828 auto oldSymAttr = portInfo[i].sym;
831 il.mic.modNamespace, target.getNameAttr());
833 StringAttr newRootSymName, oldRootSymName;
835 oldRootSymName = oldSymAttr.getSymName();
837 newRootSymName = newSymAttr.getSymName();
839 SmallVector<Attribute> newAnnotations;
842 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
843 auto &mnla = nlaMap[sym.getAttr()];
845 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
849 if (oldRootSymName != newRootSymName)
850 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
852 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
853 anno.removeMember(
"circt.nonlocal");
855 newAnnotations.push_back(anno.getAttr());
861 target.getLoc(), type,
867 il.wires.push_back(wire);
868 mapper.map(arg, wire);
875 void Inliner::cloneAndRename(
876 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
877 const DenseMap<Attribute, Attribute> &symbolRenames,
878 const DenseSet<Attribute> &localSymbols) {
881 SmallVector<Annotation> newAnnotations;
882 for (
auto anno : oldAnnotations) {
885 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
887 auto &mnla = nlaMap[sym.getAttr()];
889 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
892 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
893 anno.removeMember(
"circt.nonlocal");
896 newAnnotations.push_back(anno);
900 auto *newOp = il.mic.b.clone(op, mapper);
904 op.walk<mlir::WalkOrder::PreOrder>([&](Operation *origOp) {
905 auto *newOpToRename = mapper.lookup(origOp);
909 assert((origOp == &op || !isa<InstanceOp>(origOp)) &&
910 "Cannot handle instances not at top-level");
914 if (
auto oldInst = dyn_cast<InstanceOp>(origOp))
915 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOpToRename),
918 rename(prefix, newOpToRename, il);
923 if (!newAnnotations.empty() || !oldAnnotations.empty())
926 il.newOps.push_back(newOp);
929 bool Inliner::shouldFlatten(Operation *op) {
933 bool Inliner::shouldInline(Operation *op) {
938 void Inliner::flattenInto(StringRef prefix, InliningLevel &il,
939 IRMapping &mapper, DenseSet<Attribute> localSymbols) {
940 auto target = il.childModule;
941 auto moduleName = target.getNameAttr();
942 DenseMap<Attribute, Attribute> symbolRenames;
943 for (
auto &op : *target.getBodyBlock()) {
945 auto instance = dyn_cast<InstanceOp>(op);
947 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
952 auto *module = symbolTable.lookup(instance.getModuleName());
953 auto childModule = dyn_cast<FModuleOp>(module);
955 liveModules.insert(module);
957 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
964 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
966 auto parentActivePaths = activeHierpaths;
967 setActiveHierPaths(moduleName, instInnerSym);
968 currentPath.emplace_back(moduleName, instInnerSym);
970 InliningLevel childIL(il.mic, childModule);
971 createDebugScope(childIL, instance, il.debugScope);
974 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
975 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
979 flattenInto(nestedPrefix, childIL, mapper, localSymbols);
980 currentPath.pop_back();
981 activeHierpaths = parentActivePaths;
985 void Inliner::flattenInstances(FModuleOp module) {
986 auto moduleName = module.getNameAttr();
987 ModuleInliningContext mic(module);
989 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
991 auto instance = dyn_cast<InstanceOp>(op);
996 auto *targetModule = symbolTable.lookup(instance.getModuleName());
997 auto target = dyn_cast<FModuleOp>(targetModule);
999 liveModules.insert(targetModule);
1007 for (
auto targetNLA : instOpHierPaths[innerRef]) {
1008 nlaMap[targetNLA].flattenModule(target);
1015 DenseSet<Attribute> localSymbols;
1016 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1018 auto parentActivePaths = activeHierpaths;
1019 setActiveHierPaths(moduleName, instInnerSym);
1020 currentPath.emplace_back(moduleName, instInnerSym);
1025 mic.b.setInsertionPoint(instance);
1027 InliningLevel il(mic, target);
1028 createDebugScope(il, instance);
1030 auto nestedPrefix = (instance.getName() +
"_").str();
1031 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1032 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1033 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1036 flattenInto(nestedPrefix, il, mapper, localSymbols);
1037 currentPath.pop_back();
1038 activeHierpaths = parentActivePaths;
1046 void Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1047 DenseMap<Attribute, Attribute> &symbolRenames) {
1048 auto target = il.childModule;
1049 auto inlineToParent = il.mic.module;
1050 auto moduleName = target.getNameAttr();
1052 for (
auto &op : *target.getBodyBlock()) {
1054 auto instance = dyn_cast<InstanceOp>(op);
1056 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1061 auto *module = symbolTable.lookup(instance.getModuleName());
1062 auto childModule = dyn_cast<FModuleOp>(module);
1064 liveModules.insert(module);
1065 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1070 if (!shouldInline(childModule)) {
1071 if (liveModules.insert(childModule).second) {
1072 worklist.push_back(childModule);
1074 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1078 auto toBeFlattened = shouldFlatten(childModule);
1084 for (
auto sym : instOpHierPaths[innerRef]) {
1086 nlaMap[sym].flattenModule(childModule);
1088 nlaMap[sym].inlineModule(childModule);
1099 DenseMap<Attribute, Attribute> symbolRenames;
1100 if (!rootMap[childModule.getNameAttr()].empty()) {
1101 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1102 auto &mnla = nlaMap[sym];
1105 sym = mnla.reTop(inlineToParent);
1109 context, il.mic.modNamespace.newName(instance.getName()));
1113 cast<StringAttr>(sym));
1117 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1121 auto parentActivePaths = activeHierpaths;
1122 setActiveHierPaths(moduleName, instInnerSym);
1124 currentPath.emplace_back(moduleName, instInnerSym);
1126 InliningLevel childIL(il.mic, childModule);
1127 createDebugScope(childIL, instance, il.debugScope);
1130 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1131 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1135 if (toBeFlattened) {
1136 flattenInto(nestedPrefix, childIL, mapper, {});
1138 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1140 currentPath.pop_back();
1141 activeHierpaths = parentActivePaths;
1145 void Inliner::inlineInstances(FModuleOp module) {
1147 auto moduleName = module.getNameAttr();
1149 SmallVector<Value> wires;
1150 ModuleInliningContext mic(module);
1152 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
1154 auto instance = dyn_cast<InstanceOp>(op);
1159 auto *childModule = symbolTable.lookup(instance.getModuleName());
1160 auto target = dyn_cast<FModuleOp>(childModule);
1162 liveModules.insert(childModule);
1167 if (!shouldInline(target)) {
1168 if (liveModules.insert(target).second) {
1169 worklist.push_back(target);
1174 auto toBeFlattened = shouldFlatten(target);
1180 for (
auto sym : instOpHierPaths[innerRef]) {
1182 nlaMap[sym].flattenModule(target);
1184 nlaMap[sym].inlineModule(target);
1191 DenseMap<Attribute, Attribute> symbolRenames;
1192 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1193 for (
auto sym : rootMap[target.getNameAttr()]) {
1194 auto &mnla = nlaMap[sym];
1195 sym = mnla.reTop(module);
1197 instance, [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1198 return mic.modNamespace;
1201 cast<StringAttr>(sym));
1205 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1209 auto parentActivePaths = activeHierpaths;
1210 setActiveHierPaths(moduleName, instInnerSym);
1212 currentPath.emplace_back(moduleName, instInnerSym);
1216 mic.b.setInsertionPoint(instance);
1217 auto nestedPrefix = (instance.getName() +
"_").str();
1219 InliningLevel childIL(mic, target);
1220 createDebugScope(childIL, instance);
1222 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1223 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1224 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1227 if (toBeFlattened) {
1228 flattenInto(nestedPrefix, childIL, mapper, {});
1232 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1234 currentPath.pop_back();
1235 activeHierpaths = parentActivePaths;
1242 void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1243 Value parentScope) {
1244 auto op = il.mic.b.create<debug::ScopeOp>(
1245 instance.getLoc(), instance.getInstanceNameAttr(),
1246 instance.getModuleNameAttr().getAttr(), parentScope);
1247 debugScopes.push_back(op);
1251 void Inliner::identifyNLAsTargetingOnlyModules() {
1252 DenseSet<Operation *> nlaTargetedModules;
1255 for (
auto &[sym, mnla] : nlaMap) {
1256 auto nla = mnla.getNLA();
1257 if (nla.isModule()) {
1258 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1260 "NLA ends in module reference but does not target FModuleLike?");
1261 nlaTargetedModules.insert(mod);
1266 auto scanForNLARefs = [&](FModuleLike mod) {
1267 DenseSet<StringAttr> referencedNLASyms;
1269 for (
auto anno : annos)
1270 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1271 referencedNLASyms.insert(sym.getAttr());
1274 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1279 mod.walk([&](Operation *op) {
1280 if (op == mod.getOperation())
1285 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1286 for (
auto portAnnoAttr : op.getPortAnnotations())
1291 return referencedNLASyms;
1295 auto mergeSets = [](
auto &&a,
auto &&b) {
1296 a.insert(b.begin(), b.end());
1297 return std::move(a);
1302 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1303 nlaTargetedModules.end());
1304 auto nonModOnlyNLAs =
1306 mergeSets, scanForNLARefs);
1309 for (
auto &[_, mnla] : nlaMap) {
1310 auto nla = mnla.getNLA();
1311 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1312 mnla.markModuleOnly();
1316 Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1317 : circuit(circuit), context(circuit.getContext()),
1318 symbolTable(symbolTable) {}
1320 void Inliner::run() {
1324 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1325 auto mnla = MutableNLA(nla, &circuitNamespace);
1326 nlaMap.insert({nla.getSymNameAttr(), mnla});
1327 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1328 for (
auto p : nla.getNamepath())
1329 if (
auto ref = dyn_cast<InnerRefAttr>(p))
1330 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1334 identifyNLAsTargetingOnlyModules();
1337 for (
auto module : circuit.getOps<FModuleLike>()) {
1338 if (module.canDiscardOnUseEmpty())
1340 liveModules.insert(module);
1341 if (isa<FModuleOp>(module))
1342 worklist.push_back(cast<FModuleOp>(module));
1347 while (!worklist.empty()) {
1348 auto module = worklist.pop_back_val();
1349 if (shouldFlatten(module)) {
1350 flattenInstances(module);
1356 inlineInstances(module);
1362 for (
auto scopeOp : llvm::reverse(debugScopes))
1363 if (scopeOp.use_empty())
1365 debugScopes.clear();
1369 for (
auto mod : llvm::make_early_inc_range(
1370 circuit.getBodyBlock()->getOps<FModuleLike>())) {
1371 if (liveModules.count(mod))
1373 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1374 nlaMap[nla].markDead();
1380 for (
auto mod : circuit.getBodyBlock()->getOps<FModuleLike>()) {
1381 if (shouldInline(mod)) {
1383 "non-public module with inline annotation still present");
1386 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1390 llvm::dbgs() <<
"NLA modifications:\n";
1391 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1392 auto &mnla = nlaMap[nla.getNameAttr()];
1398 for (
auto &nla : nlaMap)
1399 nla.getSecond().applyUpdates();
1403 for (
auto fmodule : circuit.getBodyBlock()->getOps<FModuleOp>()) {
1404 SmallVector<Attribute> newAnnotations;
1405 auto processNLAs = [&](
Annotation anno) ->
bool {
1406 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1410 if (!nlaMap.count(sym.getAttr()))
1413 auto mnla = nlaMap[sym.getAttr()];
1424 auto newTops = mnla.getAdditionalSymbols();
1425 if (newTops.empty() || mnla.hasRoot(fmodule))
1431 NamedAttrList newAnnotation;
1432 for (
auto rootAndSym : newTops.drop_front()) {
1433 for (
auto pair : anno.getDict()) {
1434 if (pair.getName().getValue() !=
"circt.nonlocal") {
1435 newAnnotation.push_back(pair);
1438 newAnnotation.push_back(
1446 fmodule.walk([&](Operation *op) {
1450 if (annotations.empty())
1454 newAnnotations.clear();
1455 annotations.removeAnnotations(processNLAs);
1456 annotations.addAnnotations(newAnnotations);
1457 annotations.applyToOperation(op);
1461 SmallVector<Attribute> newPortAnnotations;
1462 for (
auto port : fmodule.getPorts()) {
1463 newAnnotations.clear();
1464 port.annotations.removeAnnotations(processNLAs);
1465 port.annotations.addAnnotations(newAnnotations);
1466 newPortAnnotations.push_back(
1469 fmodule->setAttr(
"portAnnotations",
1479 class InlinerPass :
public circt::firrtl::impl::InlinerBase<InlinerPass> {
1480 void runOnOperation()
override {
1482 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1490 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.