27 #include "mlir/IR/IRMapping.h"
28 #include "llvm/ADT/BitVector.h"
29 #include "llvm/ADT/SetOperations.h"
30 #include "llvm/ADT/SetVector.h"
31 #include "llvm/ADT/SmallPtrSet.h"
32 #include "llvm/ADT/TypeSwitch.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/FormatVariadic.h"
36 #define DEBUG_TYPE "firrtl-inliner"
38 using namespace circt;
39 using namespace firrtl;
40 using namespace chirrtl;
42 using hw::InnerRefAttr;
43 using llvm::BitVector;
64 DenseMap<Attribute, unsigned> symIdx;
67 BitVector inlinedSymbols;
71 signed flattenPoint = -1;
82 bool moduleOnly =
false;
88 SmallVector<InnerRefAttr> newTops;
92 DenseSet<StringAttr> rootSet;
101 DenseMap<Attribute, StringAttr> renames;
107 StringAttr lookupRename(Attribute lastMod,
unsigned idx = 0) {
108 if (renames.count(lastMod))
109 return renames[lastMod];
110 return nla.refPart(idx);
115 : nla(nla), circuitNamespace(circuitNamespace),
116 inlinedSymbols(BitVector(nla.getNamepath().size(),
true)),
117 size(nla.getNamepath().size()) {
118 for (
size_t i = 0, e = size; i != e; ++i)
119 symIdx.insert({nla.modPart(i), i});
132 "the default constructor for MutableNLA should never be used");
137 void markDead() { dead =
true; }
140 void markModuleOnly() { moduleOnly =
true; }
143 hw::HierPathOp getNLA() {
return nla; }
151 hw::HierPathOp applyUpdates() {
153 if (isLocal() || isDead()) {
160 if (inlinedSymbols.all() && newTops.empty() && flattenPoint == -1 &&
167 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
168 SmallVector<Attribute> namepath;
172 if (!inlinedSymbols.test(1))
178 for (
signed i = 1, e = inlinedSymbols.size() - 1; i != e; ++i) {
179 if (i == flattenPoint) {
180 lastMod = nla.modPart(i);
184 if (!inlinedSymbols.test(i + 1)) {
186 lastMod = nla.modPart(i);
191 auto modPart = lastMod ? lastMod : nla.modPart(i);
192 auto refPart = lookupRename(modPart, i);
198 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
199 auto refPart = lookupRename(modPart, size - 1);
206 auto hp = b.create<hw::HierPathOp>(b.getUnknownLoc(), sym,
207 b.getArrayAttr(namepath));
208 hp.setVisibility(nla.getVisibility());
213 assert(!dead || !newTops.empty());
215 last = writeBack(nla.root(), nla.getNameAttr());
216 for (
auto root : newTops)
217 last = writeBack(root.getModule(), root.getName());
225 <<
" new: " << *
this <<
"\n"
226 <<
" dead: " << dead <<
"\n"
227 <<
" isDead: " << isDead() <<
"\n"
228 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
229 <<
" isLocal: " << isLocal() <<
"\n"
230 <<
" inlinedSymbols: [";
231 llvm::interleaveComma(inlinedSymbols.getData(),
llvm::errs(), [](
auto a) {
232 llvm::errs() << llvm::formatv(
"{0:x-}", a);
235 <<
" flattenPoint: " << flattenPoint <<
"\n"
237 for (
auto rename : renames)
238 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
245 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
246 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
248 os <<
"#hw.innerNameRef<";
249 os <<
"@" << mod.getValue();
251 os <<
"::@" << sym.getValue() <<
">";
254 auto writeOne = [&](StringAttr root, StringAttr sym) {
255 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
259 if (!x.inlinedSymbols.test(1))
262 writePathSegment(root, x.lookupRename(root));
265 bool needsComma =
false;
266 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i != e; ++i) {
267 if (i == x.flattenPoint) {
268 lastMod = x.nla.modPart(i);
272 if (!x.inlinedSymbols.test(i + 1)) {
274 lastMod = x.nla.modPart(i);
280 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
281 auto refPart = x.nla.refPart(i);
282 if (x.renames.count(modPart))
283 refPart = x.renames[modPart];
284 writePathSegment(modPart, refPart);
291 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
292 auto refPart = x.nla.refPart(x.size - 1);
293 if (x.renames.count(modPart))
294 refPart = x.renames[modPart];
295 writePathSegment(modPart, refPart);
299 SmallVector<InnerRefAttr> tops;
302 tops.append(x.newTops.begin(), x.newTops.end());
304 bool multiary = !x.newTops.empty();
307 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
308 writeOne(a.getModule(), a.getName());
320 bool isDead() {
return dead && newTops.empty(); }
323 bool isModuleOnly() {
return moduleOnly; }
329 unsigned end = flattenPoint > -1 ? flattenPoint + 1 : inlinedSymbols.size();
330 return inlinedSymbols.find_first_in(1, end) == -1;
334 bool hasRoot(FModuleLike mod) {
335 return (isDead() && nla.root() == mod.getModuleNameAttr()) ||
336 rootSet.contains(mod.getModuleNameAttr());
340 bool hasRoot(StringAttr modName) {
341 return (nla.root() == modName) || rootSet.contains(modName);
345 void inlineModule(FModuleOp module) {
346 auto sym = module.getNameAttr();
347 assert(sym != nla.root() &&
"unable to inline the root module");
348 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
349 auto idx = symIdx[sym];
350 inlinedSymbols.reset(idx);
353 if (idx == size - 1 && moduleOnly)
360 void flattenModule(FModuleOp module) {
361 auto sym = module.getNameAttr();
362 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
363 auto idx = symIdx[sym] - 1;
371 StringAttr reTop(FModuleOp module) {
372 StringAttr sym = nla.getSymNameAttr();
373 if (!newTops.empty())
375 circuitNamespace->
newName(sym.getValue()));
377 rootSet.insert(module.getNameAttr());
378 symIdx.insert({module.getNameAttr(), 0});
383 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
385 void setInnerSym(Attribute module, StringAttr innerSym) {
386 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
387 assert(!renames.count(module) &&
"Module already renamed");
388 renames.insert({module, innerSym});
399 InstanceOp instance) {
400 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
401 auto result = instance.getResult(i);
402 auto wire = wires[i];
403 mapper.map(result, wire);
411 StringAttr istName) {
412 mlir::AttrTypeReplacer replacer;
413 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
414 auto it = map.find(innerRef);
421 llvm::for_each(newOps,
422 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
429 hw::InnerSymbolNamespace &ns,
430 StringAttr istName) {
431 if (!old || old.empty())
434 bool anyChanged =
false;
436 SmallVector<hw::InnerSymPropertiesAttr> newProps;
437 auto *context = old.getContext();
438 for (
auto &prop : old) {
439 auto newSym = ns.newName(prop.getName().strref());
440 if (newSym == prop.getName()) {
441 newProps.push_back(prop);
446 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
448 newProps.push_back(newProp);
453 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
454 assert(oldProp.getFieldID() == newProp.getFieldID());
488 Inliner(CircuitOp circuit, SymbolTable &symbolTable);
496 struct ModuleInliningContext {
497 ModuleInliningContext(FModuleOp module)
498 : module(module), modNamespace(module), b(module.getContext()) {}
502 hw::InnerSymbolNamespace modNamespace;
510 struct InliningLevel {
511 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
512 : mic(mic), childModule(childModule) {}
514 ModuleInliningContext &mic;
518 SmallVector<Operation *> newOps;
520 SmallVector<Value> wires;
522 FModuleOp childModule;
525 mic.module.getNameAttr());
532 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
536 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
541 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
543 const DenseMap<Attribute, Attribute> &symbolRenames);
547 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
549 const DenseMap<Attribute, Attribute> &symbolRenames,
550 const DenseSet<Attribute> &localSymbols);
555 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
556 const DenseSet<Attribute> &localSymbols);
559 bool shouldFlatten(Operation *op);
562 bool shouldInline(Operation *op);
567 void flattenInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
568 DenseSet<Attribute> localSymbols);
573 void inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
574 DenseMap<Attribute, Attribute> &symbolRenames);
577 void flattenInstances(FModuleOp module);
580 void inlineInstances(FModuleOp module);
583 void identifyNLAsTargetingOnlyModules();
589 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
592 if (currentPath.empty()) {
593 activeHierpaths.insert(instPaths.begin(), instPaths.end());
596 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
599 llvm::set_intersect(activeHierpaths, hPaths);
602 for (
auto hPath : instPaths)
603 if (nlaMap[hPath].hasRoot(moduleName))
604 activeHierpaths.insert(hPath);
608 MLIRContext *context;
611 SymbolTable &symbolTable;
615 DenseSet<Operation *> liveModules;
618 SmallVector<FModuleOp, 16> worklist;
621 DenseMap<Attribute, MutableNLA> nlaMap;
624 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
629 SmallVector<std::pair<Attribute, Attribute>> currentPath;
631 DenseSet<StringAttr> activeHierpaths;
636 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
643 bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
644 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
651 bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
655 if (!isa<MemOp, SeqMemOp, CombMemOp, MemoryPortOp>(op)) {
656 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
658 (prefix + nameAttr.getValue())));
663 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
666 auto oldSymAttr = symOp.getInnerSymAttr();
669 il.childModule.getNameAttr());
675 if (
auto newSymStrAttr = newSymAttr.getSymName();
676 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
678 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
686 auto &mnla = nlaMap[sym.getAttr()];
687 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
689 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
693 symOp.setInnerSymbolAttr(newSymAttr);
695 return newSymAttr != oldSymAttr;
698 bool Inliner::renameInstance(
699 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
700 const DenseMap<Attribute, Attribute> &symbolRenames) {
703 auto parentActivePaths = activeHierpaths;
704 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
706 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
711 SmallVector<StringAttr> validHierPaths;
712 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
720 for (
auto old : instOpHierPaths[oldInnerRef]) {
724 if (activeHierpaths.find(old) != activeHierpaths.end())
725 validHierPaths.push_back(old);
729 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
730 if (activeHierpaths.find(additionalSym.getName()) !=
731 activeHierpaths.end()) {
732 validHierPaths.push_back(old);
741 auto symbolChanged = rename(prefix, newInst, il);
750 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
751 instOpHierPaths[newInnerRef] = validHierPaths;
753 for (
auto nla : instOpHierPaths[newInnerRef]) {
754 if (!nlaMap.count(nla))
756 auto &mnla = nlaMap[nla];
757 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
763 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
764 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
766 for (
const auto &en : llvm::enumerate(nlaList)) {
767 auto oldNLA =
en.value();
768 if (
auto newSym = symbolRenames.lookup(oldNLA))
769 nlaList[
en.index()] = cast<StringAttr>(newSym);
772 activeHierpaths = std::move(parentActivePaths);
773 return symbolChanged;
781 void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
783 const DenseSet<Attribute> &localSymbols) {
784 auto target = il.childModule;
785 auto portInfo = target.getPorts();
786 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
787 auto arg = target.getArgument(i);
789 auto type = type_cast<FIRRTLType>(arg.getType());
792 auto oldSymAttr = portInfo[i].sym;
795 il.mic.modNamespace, target.getNameAttr());
797 StringAttr newRootSymName, oldRootSymName;
799 oldRootSymName = oldSymAttr.getSymName();
801 newRootSymName = newSymAttr.getSymName();
803 SmallVector<Attribute> newAnnotations;
806 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
807 auto &mnla = nlaMap[sym.getAttr()];
809 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
813 if (oldRootSymName != newRootSymName)
814 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
816 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
817 anno.removeMember(
"circt.nonlocal");
819 newAnnotations.push_back(anno.getAttr());
825 target.getLoc(), type,
831 il.wires.push_back(wire);
832 mapper.map(arg, wire);
839 void Inliner::cloneAndRename(
840 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
841 const DenseMap<Attribute, Attribute> &symbolRenames,
842 const DenseSet<Attribute> &localSymbols) {
845 SmallVector<Annotation> newAnnotations;
846 for (
auto anno : oldAnnotations) {
849 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
851 auto &mnla = nlaMap[sym.getAttr()];
853 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
856 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
857 anno.removeMember(
"circt.nonlocal");
860 newAnnotations.push_back(anno);
864 auto *newOp = il.mic.b.clone(op, mapper);
868 op.walk<mlir::WalkOrder::PreOrder>([&](Operation *origOp) {
869 auto *newOpToRename = mapper.lookup(origOp);
873 assert((origOp == &op || !isa<InstanceOp>(origOp)) &&
874 "Cannot handle instances not at top-level");
878 if (
auto oldInst = dyn_cast<InstanceOp>(origOp))
879 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOpToRename),
882 rename(prefix, newOpToRename, il);
887 if (!newAnnotations.empty() || !oldAnnotations.empty())
890 il.newOps.push_back(newOp);
893 bool Inliner::shouldFlatten(Operation *op) {
897 bool Inliner::shouldInline(Operation *op) {
902 void Inliner::flattenInto(StringRef prefix, InliningLevel &il,
903 IRMapping &mapper, DenseSet<Attribute> localSymbols) {
904 auto target = il.childModule;
905 auto moduleName = target.getNameAttr();
906 DenseMap<Attribute, Attribute> symbolRenames;
907 for (
auto &op : *target.getBodyBlock()) {
909 auto instance = dyn_cast<InstanceOp>(op);
911 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
916 auto *module = symbolTable.lookup(instance.getModuleName());
917 auto childModule = dyn_cast<FModuleOp>(module);
919 liveModules.insert(module);
921 cloneAndRename(prefix, il, mapper, op, symbolRenames, localSymbols);
928 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
930 auto parentActivePaths = activeHierpaths;
931 setActiveHierPaths(moduleName, instInnerSym);
932 currentPath.emplace_back(moduleName, instInnerSym);
934 InliningLevel childIL(il.mic, childModule);
937 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
938 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
942 flattenInto(nestedPrefix, childIL, mapper, localSymbols);
943 currentPath.pop_back();
944 activeHierpaths = parentActivePaths;
948 void Inliner::flattenInstances(FModuleOp module) {
949 auto moduleName = module.getNameAttr();
950 ModuleInliningContext mic(module);
952 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
954 auto instance = dyn_cast<InstanceOp>(op);
959 auto *targetModule = symbolTable.lookup(instance.getModuleName());
960 auto target = dyn_cast<FModuleOp>(targetModule);
962 liveModules.insert(targetModule);
970 for (
auto targetNLA : instOpHierPaths[innerRef]) {
971 nlaMap[targetNLA].flattenModule(target);
978 DenseSet<Attribute> localSymbols;
979 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
981 auto parentActivePaths = activeHierpaths;
982 setActiveHierPaths(moduleName, instInnerSym);
983 currentPath.emplace_back(moduleName, instInnerSym);
988 mic.b.setInsertionPoint(instance);
990 InliningLevel il(mic, target);
992 auto nestedPrefix = (instance.getName() +
"_").str();
993 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
994 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
995 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
998 flattenInto(nestedPrefix, il, mapper, localSymbols);
999 currentPath.pop_back();
1000 activeHierpaths = parentActivePaths;
1008 void Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1009 DenseMap<Attribute, Attribute> &symbolRenames) {
1010 auto target = il.childModule;
1011 auto inlineToParent = il.mic.module;
1012 auto moduleName = target.getNameAttr();
1014 for (
auto &op : *target.getBodyBlock()) {
1016 auto instance = dyn_cast<InstanceOp>(op);
1018 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1023 auto *module = symbolTable.lookup(instance.getModuleName());
1024 auto childModule = dyn_cast<FModuleOp>(module);
1026 liveModules.insert(module);
1027 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1032 if (!shouldInline(childModule)) {
1033 if (liveModules.insert(childModule).second) {
1034 worklist.push_back(childModule);
1036 cloneAndRename(prefix, il, mapper, op, symbolRenames, {});
1040 auto toBeFlattened = shouldFlatten(childModule);
1046 for (
auto sym : instOpHierPaths[innerRef]) {
1048 nlaMap[sym].flattenModule(childModule);
1050 nlaMap[sym].inlineModule(childModule);
1061 DenseMap<Attribute, Attribute> symbolRenames;
1062 if (!rootMap[childModule.getNameAttr()].empty()) {
1063 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1064 auto &mnla = nlaMap[sym];
1067 sym = mnla.reTop(inlineToParent);
1071 context, il.mic.modNamespace.newName(instance.getName()));
1075 cast<StringAttr>(sym));
1079 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1083 auto parentActivePaths = activeHierpaths;
1084 setActiveHierPaths(moduleName, instInnerSym);
1086 currentPath.emplace_back(moduleName, instInnerSym);
1088 InliningLevel childIL(il.mic, childModule);
1091 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1092 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1096 if (toBeFlattened) {
1097 flattenInto(nestedPrefix, childIL, mapper, {});
1099 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1101 currentPath.pop_back();
1102 activeHierpaths = parentActivePaths;
1106 void Inliner::inlineInstances(FModuleOp module) {
1108 auto moduleName = module.getNameAttr();
1110 SmallVector<Value> wires;
1111 ModuleInliningContext mic(module);
1113 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
1115 auto instance = dyn_cast<InstanceOp>(op);
1120 auto *childModule = symbolTable.lookup(instance.getModuleName());
1121 auto target = dyn_cast<FModuleOp>(childModule);
1123 liveModules.insert(childModule);
1128 if (!shouldInline(target)) {
1129 if (liveModules.insert(target).second) {
1130 worklist.push_back(target);
1135 auto toBeFlattened = shouldFlatten(target);
1141 for (
auto sym : instOpHierPaths[innerRef]) {
1143 nlaMap[sym].flattenModule(target);
1145 nlaMap[sym].inlineModule(target);
1152 DenseMap<Attribute, Attribute> symbolRenames;
1153 if (!rootMap[target.getNameAttr()].empty()) {
1154 for (
auto sym : rootMap[target.getNameAttr()]) {
1155 auto &mnla = nlaMap[sym];
1156 sym = mnla.reTop(module);
1158 instance, [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1159 return mic.modNamespace;
1162 cast<StringAttr>(sym));
1166 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1170 auto parentActivePaths = activeHierpaths;
1171 setActiveHierPaths(moduleName, instInnerSym);
1173 currentPath.emplace_back(moduleName, instInnerSym);
1177 mic.b.setInsertionPoint(instance);
1178 auto nestedPrefix = (instance.getName() +
"_").str();
1180 InliningLevel childIL(mic, target);
1182 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1183 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1184 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1187 if (toBeFlattened) {
1188 flattenInto(nestedPrefix, childIL, mapper, {});
1192 inlineInto(nestedPrefix, childIL, mapper, symbolRenames);
1194 currentPath.pop_back();
1195 activeHierpaths = parentActivePaths;
1202 void Inliner::identifyNLAsTargetingOnlyModules() {
1203 DenseSet<Operation *> nlaTargetedModules;
1206 for (
auto &[sym, mnla] : nlaMap) {
1207 auto nla = mnla.getNLA();
1208 if (nla.isModule()) {
1209 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1211 "NLA ends in module reference but does not target FModuleLike?");
1212 nlaTargetedModules.insert(mod);
1217 auto scanForNLARefs = [&](FModuleLike mod) {
1218 DenseSet<StringAttr> referencedNLASyms;
1220 for (
auto anno : annos)
1221 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1222 referencedNLASyms.insert(sym.getAttr());
1225 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1230 mod.walk([&](Operation *op) {
1231 if (op == mod.getOperation())
1236 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1237 for (
auto portAnnoAttr : op.getPortAnnotations())
1238 scanAnnos(
AnnotationSet(portAnnoAttr.template cast<ArrayAttr>()));
1242 return referencedNLASyms;
1246 auto mergeSets = [](
auto &&a,
auto &&b) {
1247 a.insert(b.begin(), b.end());
1248 return std::move(a);
1253 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1254 nlaTargetedModules.end());
1255 auto nonModOnlyNLAs =
1257 mergeSets, scanForNLARefs);
1260 for (
auto &[_, mnla] : nlaMap) {
1261 auto nla = mnla.getNLA();
1262 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1263 mnla.markModuleOnly();
1267 Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1268 : circuit(circuit), context(circuit.getContext()),
1269 symbolTable(symbolTable) {}
1271 void Inliner::run() {
1275 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1276 auto mnla = MutableNLA(nla, &circuitNamespace);
1277 nlaMap.insert({nla.getSymNameAttr(), mnla});
1278 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1279 for (
auto p : nla.getNamepath())
1280 if (
auto ref = dyn_cast<InnerRefAttr>(p))
1281 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1285 identifyNLAsTargetingOnlyModules();
1288 for (
auto module : circuit.getOps<FModuleLike>()) {
1289 if (module.canDiscardOnUseEmpty())
1291 liveModules.insert(module);
1292 if (isa<FModuleOp>(module))
1293 worklist.push_back(cast<FModuleOp>(module));
1298 while (!worklist.empty()) {
1299 auto module = worklist.pop_back_val();
1300 if (shouldFlatten(module)) {
1301 flattenInstances(module);
1307 inlineInstances(module);
1313 for (
auto mod : llvm::make_early_inc_range(
1314 circuit.getBodyBlock()->getOps<FModuleLike>())) {
1315 if (liveModules.count(mod))
1317 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1318 nlaMap[nla].markDead();
1324 for (
auto mod : circuit.getBodyBlock()->getOps<FModuleLike>()) {
1325 if (shouldInline(mod)) {
1327 "non-public module with inline annotation still present");
1330 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1335 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1336 auto &mnla = nlaMap[nla.getNameAttr()];
1342 for (
auto &nla : nlaMap)
1343 nla.getSecond().applyUpdates();
1347 for (
auto fmodule : circuit.getBodyBlock()->getOps<FModuleOp>()) {
1348 SmallVector<Attribute> newAnnotations;
1349 auto processNLAs = [&](
Annotation anno) ->
bool {
1350 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1354 if (!nlaMap.count(sym.getAttr()))
1357 auto mnla = nlaMap[sym.getAttr()];
1368 auto newTops = mnla.getAdditionalSymbols();
1369 if (newTops.empty() || mnla.hasRoot(fmodule))
1375 NamedAttrList newAnnotation;
1376 for (
auto rootAndSym : newTops.drop_front()) {
1377 for (
auto pair : anno.getDict()) {
1378 if (pair.getName().getValue() !=
"circt.nonlocal") {
1379 newAnnotation.push_back(pair);
1382 newAnnotation.push_back(
1390 fmodule.walk([&](Operation *op) {
1394 if (annotations.empty())
1398 newAnnotations.clear();
1399 annotations.removeAnnotations(processNLAs);
1400 annotations.addAnnotations(newAnnotations);
1401 annotations.applyToOperation(op);
1405 SmallVector<Attribute> newPortAnnotations;
1406 for (
auto port : fmodule.getPorts()) {
1407 newAnnotations.clear();
1408 port.annotations.removeAnnotations(processNLAs);
1409 port.annotations.addAnnotations(newAnnotations);
1410 newPortAnnotations.push_back(
1413 fmodule->setAttr(
"portAnnotations",
1423 class InlinerPass :
public InlinerBase<InlinerPass> {
1424 void runOnOperation()
override {
1426 <<
"===- Running Module Inliner Pass "
1427 "--------------------------------------------===\n");
1428 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1430 LLVM_DEBUG(
llvm::dbgs() <<
"===--------------------------------------------"
1431 "------------------------------===\n");
1437 return std::make_unique<InlinerPass>();
assert(baseType &&"element must be base type")
static void dump(DIVariable &variable, 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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
mlir::raw_indented_ostream & errs()
mlir::raw_indented_ostream & dbgs()
The namespace of a CircuitOp, generally inhabited by modules.