26 #include "mlir/IR/IRMapping.h"
27 #include "mlir/Pass/Pass.h"
28 #include "llvm/ADT/BitVector.h"
29 #include "llvm/ADT/SetOperations.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/FormatVariadic.h"
33 #define DEBUG_TYPE "firrtl-inliner"
37 #define GEN_PASS_DEF_INLINER
38 #include "circt/Dialect/FIRRTL/Passes.h.inc"
42 using namespace circt;
43 using namespace firrtl;
44 using namespace chirrtl;
46 using hw::InnerRefAttr;
47 using llvm::BitVector;
68 DenseMap<Attribute, unsigned> symIdx;
71 BitVector inlinedSymbols;
75 signed flattenPoint = -1;
86 bool moduleOnly =
false;
92 SmallVector<InnerRefAttr> newTops;
96 DenseSet<StringAttr> rootSet;
105 DenseMap<Attribute, StringAttr> renames;
111 StringAttr lookupRename(Attribute lastMod,
unsigned idx = 0) {
112 if (renames.count(lastMod))
113 return renames[lastMod];
114 return nla.refPart(idx);
119 : nla(nla), circuitNamespace(circuitNamespace),
120 inlinedSymbols(BitVector(nla.getNamepath().size(),
true)),
121 size(nla.getNamepath().size()) {
122 for (
size_t i = 0, e = size; i != e; ++i)
123 symIdx.insert({nla.modPart(i), i});
136 "the default constructor for MutableNLA should never be used");
141 void markDead() { dead =
true; }
144 void markModuleOnly() { moduleOnly =
true; }
147 hw::HierPathOp getNLA() {
return nla; }
155 hw::HierPathOp applyUpdates() {
157 if (isLocal() || isDead()) {
164 if (inlinedSymbols.all() && newTops.empty() && flattenPoint == -1 &&
171 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
172 SmallVector<Attribute> namepath;
176 if (!inlinedSymbols.test(1))
182 for (
signed i = 1, e = inlinedSymbols.size() - 1; i != e; ++i) {
183 if (i == flattenPoint) {
184 lastMod = nla.modPart(i);
188 if (!inlinedSymbols.test(i + 1)) {
190 lastMod = nla.modPart(i);
195 auto modPart = lastMod ? lastMod : nla.modPart(i);
196 auto refPart = lookupRename(modPart, i);
202 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
203 auto refPart = lookupRename(modPart, size - 1);
210 auto hp = b.create<hw::HierPathOp>(b.getUnknownLoc(), sym,
211 b.getArrayAttr(namepath));
212 hp.setVisibility(nla.getVisibility());
217 assert(!dead || !newTops.empty());
219 last = writeBack(nla.root(), nla.getNameAttr());
220 for (
auto root : newTops)
221 last = writeBack(root.getModule(), root.getName());
228 llvm::errs() <<
" - orig: " << nla <<
"\n"
229 <<
" new: " << *
this <<
"\n"
230 <<
" dead: " << dead <<
"\n"
231 <<
" isDead: " << isDead() <<
"\n"
232 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
233 <<
" isLocal: " << isLocal() <<
"\n"
234 <<
" inlinedSymbols: [";
235 llvm::interleaveComma(inlinedSymbols.getData(), llvm::errs(), [](
auto a) {
236 llvm::errs() << llvm::formatv(
"{0:x-}", a);
238 llvm::errs() <<
"]\n"
239 <<
" flattenPoint: " << flattenPoint <<
"\n"
241 for (
auto rename : renames)
242 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
249 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
250 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
252 os <<
"#hw.innerNameRef<";
253 os <<
"@" << mod.getValue();
255 os <<
"::@" << sym.getValue() <<
">";
258 auto writeOne = [&](StringAttr root, StringAttr sym) {
259 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
263 if (!x.inlinedSymbols.test(1))
266 writePathSegment(root, x.lookupRename(root));
269 bool needsComma =
false;
270 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i != e; ++i) {
271 if (i == x.flattenPoint) {
272 lastMod = x.nla.modPart(i);
276 if (!x.inlinedSymbols.test(i + 1)) {
278 lastMod = x.nla.modPart(i);
284 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
285 auto refPart = x.nla.refPart(i);
286 if (x.renames.count(modPart))
287 refPart = x.renames[modPart];
288 writePathSegment(modPart, refPart);
295 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
296 auto refPart = x.nla.refPart(x.size - 1);
297 if (x.renames.count(modPart))
298 refPart = x.renames[modPart];
299 writePathSegment(modPart, refPart);
303 SmallVector<InnerRefAttr> tops;
306 tops.append(x.newTops.begin(), x.newTops.end());
308 bool multiary = !x.newTops.empty();
311 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
312 writeOne(a.getModule(), a.getName());
324 bool isDead() {
return dead && newTops.empty(); }
327 bool isModuleOnly() {
return moduleOnly; }
333 unsigned end = flattenPoint > -1 ? flattenPoint + 1 : inlinedSymbols.size();
334 return inlinedSymbols.find_first_in(1, end) == -1;
338 bool hasRoot(FModuleLike mod) {
339 return (isDead() && nla.root() == mod.getModuleNameAttr()) ||
340 rootSet.contains(mod.getModuleNameAttr());
344 bool hasRoot(StringAttr modName) {
345 return (nla.root() == modName) || rootSet.contains(modName);
349 void inlineModule(FModuleOp module) {
350 auto sym = module.getNameAttr();
351 assert(sym != nla.root() &&
"unable to inline the root module");
352 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
353 auto idx = symIdx[sym];
354 inlinedSymbols.reset(idx);
357 if (idx == size - 1 && moduleOnly)
364 void flattenModule(FModuleOp module) {
365 auto sym = module.getNameAttr();
366 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
367 auto idx = symIdx[sym] - 1;
375 StringAttr reTop(FModuleOp module) {
376 StringAttr sym = nla.getSymNameAttr();
377 if (!newTops.empty())
379 circuitNamespace->
newName(sym.getValue()));
381 rootSet.insert(module.getNameAttr());
382 symIdx.insert({module.getNameAttr(), 0});
387 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
389 void setInnerSym(Attribute module, StringAttr innerSym) {
390 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
391 assert(!renames.count(module) &&
"Module already renamed");
392 renames.insert({module, innerSym});
403 InstanceOp instance) {
404 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
405 auto result = instance.getResult(i);
406 auto wire = wires[i];
407 mapper.map(result, wire);
415 StringAttr istName) {
416 mlir::AttrTypeReplacer replacer;
417 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
418 auto it = map.find(innerRef);
425 llvm::for_each(newOps,
426 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
433 hw::InnerSymbolNamespace &ns,
434 StringAttr istName) {
435 if (!old || old.empty())
438 bool anyChanged =
false;
440 SmallVector<hw::InnerSymPropertiesAttr> newProps;
441 auto *context = old.getContext();
442 for (
auto &prop : old) {
443 auto newSym = ns.newName(prop.getName().strref());
444 if (newSym == prop.getName()) {
445 newProps.push_back(prop);
450 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
452 newProps.push_back(newProp);
457 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
458 assert(oldProp.getFieldID() == newProp.getFieldID());
492 Inliner(CircuitOp circuit, SymbolTable &symbolTable);
500 struct ModuleInliningContext {
501 ModuleInliningContext(FModuleOp module)
502 : module(module), modNamespace(module), b(module.getContext()) {}
506 hw::InnerSymbolNamespace modNamespace;
514 struct InliningLevel {
515 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
516 : mic(mic), childModule(childModule) {}
519 ModuleInliningContext &mic;
523 SmallVector<Operation *> newOps;
525 SmallVector<Value> wires;
527 FModuleOp childModule;
533 mic.module.getNameAttr());
540 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
544 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
549 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
551 const DenseMap<Attribute, Attribute> &symbolRenames);
555 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
557 const DenseMap<Attribute, Attribute> &symbolRenames,
558 const DenseSet<Attribute> &localSymbols);
563 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
564 const DenseSet<Attribute> &localSymbols);
567 bool shouldFlatten(Operation *op);
570 bool shouldInline(Operation *op);
574 LogicalResult checkInstanceParents(InstanceOp instance);
580 inliningWalk(OpBuilder &builder, Block *block, IRMapping &mapper,
581 llvm::function_ref<LogicalResult(Operation *op)> process);
586 LogicalResult flattenInto(StringRef prefix, InliningLevel &il,
588 DenseSet<Attribute> localSymbols);
593 LogicalResult inlineInto(StringRef prefix, InliningLevel &il,
595 DenseMap<Attribute, Attribute> &symbolRenames);
598 LogicalResult flattenInstances(FModuleOp module);
601 LogicalResult inlineInstances(FModuleOp module);
605 void createDebugScope(InliningLevel &il, InstanceOp instance,
606 Value parentScope = {});
609 void identifyNLAsTargetingOnlyModules();
615 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
618 if (currentPath.empty()) {
619 activeHierpaths.insert(instPaths.begin(), instPaths.end());
622 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
625 llvm::set_intersect(activeHierpaths, hPaths);
628 for (
auto hPath : instPaths)
629 if (nlaMap[hPath].hasRoot(moduleName))
630 activeHierpaths.insert(hPath);
634 MLIRContext *context;
637 SymbolTable &symbolTable;
641 DenseSet<Operation *> liveModules;
644 SmallVector<FModuleOp, 16> worklist;
647 DenseMap<Attribute, MutableNLA> nlaMap;
650 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
655 SmallVector<std::pair<Attribute, Attribute>> currentPath;
657 DenseSet<StringAttr> activeHierpaths;
662 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
666 SmallVector<debug::ScopeOp> debugScopes;
673 bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
674 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
681 bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
684 auto updateDebugScope = [&](
auto op) {
686 op.getScopeMutable().assign(il.debugScope);
688 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
689 return updateDebugScope(varOp),
false;
690 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
691 return updateDebugScope(scopeOp),
false;
694 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
696 (prefix + nameAttr.getValue())));
700 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
703 auto oldSymAttr = symOp.getInnerSymAttr();
706 il.childModule.getNameAttr());
712 if (
auto newSymStrAttr = newSymAttr.getSymName();
713 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
715 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
723 auto &mnla = nlaMap[sym.getAttr()];
724 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
726 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
730 symOp.setInnerSymbolAttr(newSymAttr);
732 return newSymAttr != oldSymAttr;
735 bool Inliner::renameInstance(
736 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
737 const DenseMap<Attribute, Attribute> &symbolRenames) {
742 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
747 auto parentActivePaths = activeHierpaths;
748 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
750 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
755 SmallVector<StringAttr> validHierPaths;
756 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
764 for (
auto old : instOpHierPaths[oldInnerRef]) {
768 if (activeHierpaths.find(old) != activeHierpaths.end())
769 validHierPaths.push_back(old);
773 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
774 if (activeHierpaths.find(additionalSym.getName()) !=
775 activeHierpaths.end()) {
776 validHierPaths.push_back(old);
785 auto symbolChanged = rename(prefix, newInst, il);
794 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
795 instOpHierPaths[newInnerRef] = validHierPaths;
797 for (
auto nla : instOpHierPaths[newInnerRef]) {
798 if (!nlaMap.count(nla))
800 auto &mnla = nlaMap[nla];
801 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
807 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
808 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
810 for (
const auto &en : llvm::enumerate(nlaList)) {
811 auto oldNLA =
en.value();
812 if (
auto newSym = symbolRenames.lookup(oldNLA))
813 nlaList[
en.index()] = cast<StringAttr>(newSym);
816 activeHierpaths = std::move(parentActivePaths);
817 return symbolChanged;
825 void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
827 const DenseSet<Attribute> &localSymbols) {
828 auto target = il.childModule;
829 auto portInfo = target.getPorts();
830 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
831 auto arg = target.getArgument(i);
833 auto type = type_cast<FIRRTLType>(arg.getType());
836 auto oldSymAttr = portInfo[i].sym;
839 il.mic.modNamespace, target.getNameAttr());
841 StringAttr newRootSymName, oldRootSymName;
843 oldRootSymName = oldSymAttr.getSymName();
845 newRootSymName = newSymAttr.getSymName();
847 SmallVector<Attribute> newAnnotations;
850 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
851 auto &mnla = nlaMap[sym.getAttr()];
853 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
857 if (oldRootSymName != newRootSymName)
858 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
860 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
861 anno.removeMember(
"circt.nonlocal");
863 newAnnotations.push_back(anno.getAttr());
869 target.getLoc(), type,
875 il.wires.push_back(wire);
876 mapper.map(arg, wire);
883 void Inliner::cloneAndRename(
884 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
885 const DenseMap<Attribute, Attribute> &symbolRenames,
886 const DenseSet<Attribute> &localSymbols) {
889 SmallVector<Annotation> newAnnotations;
890 for (
auto anno : oldAnnotations) {
893 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
895 auto &mnla = nlaMap[sym.getAttr()];
897 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
900 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
901 anno.removeMember(
"circt.nonlocal");
904 newAnnotations.push_back(anno);
908 assert(op.getNumRegions() == 0 &&
909 "operation with regions should not reach cloneAndRename");
910 auto *newOp = il.mic.b.cloneWithoutRegions(op, mapper);
917 if (
auto oldInst = dyn_cast<InstanceOp>(op))
918 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOp), symbolRenames);
920 rename(prefix, newOp, il);
924 if (!newAnnotations.empty() || !oldAnnotations.empty())
927 il.newOps.push_back(newOp);
930 bool Inliner::shouldFlatten(Operation *op) {
934 bool Inliner::shouldInline(Operation *op) {
938 LogicalResult Inliner::inliningWalk(
939 OpBuilder &builder, Block *block, IRMapping &mapper,
940 llvm::function_ref<LogicalResult(Operation *op)> process) {
942 OpBuilder::InsertPoint target;
943 Block::iterator source;
946 SmallVector<IPs> inliningStack;
950 inliningStack.push_back(IPs{builder.saveInsertionPoint(), block->begin()});
951 OpBuilder::InsertionGuard guard(builder);
953 while (!inliningStack.empty()) {
954 auto target = inliningStack.back().target;
955 builder.restoreInsertionPoint(target);
959 auto &ips = inliningStack.back();
960 source = &*ips.source;
961 auto end = source->getBlock()->end();
962 if (++ips.source == end)
963 inliningStack.pop_back();
967 if (source->getNumRegions() == 0) {
968 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
970 if (failed(process(source)))
972 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
978 if (!isa<LayerBlockOp, WhenOp, MatchOp>(source))
979 return source->emitError(
"unsupported operation '")
980 << source->getName() <<
"' cannot be inlined";
986 auto *newOp = builder.cloneWithoutRegions(*source, mapper);
987 for (
auto [newRegion, oldRegion] : llvm::reverse(
988 llvm::zip_equal(newOp->getRegions(), source->getRegions()))) {
990 if (oldRegion.empty()) {
991 assert(newRegion.empty());
995 assert(oldRegion.hasOneBlock());
998 auto &oldBlock = oldRegion.getBlocks().front();
999 auto &newBlock = newRegion.emplaceBlock();
1000 mapper.map(&oldBlock, &newBlock);
1003 for (
auto arg : oldBlock.getArguments())
1004 mapper.map(arg, newBlock.addArgument(arg.getType(), arg.getLoc()));
1006 if (oldBlock.empty())
1009 inliningStack.push_back(
1010 IPs{OpBuilder::InsertPoint(&newBlock, newBlock.begin()),
1017 LogicalResult Inliner::checkInstanceParents(InstanceOp instance) {
1018 auto *parent = instance->getParentOp();
1019 while (!isa<FModuleLike>(parent)) {
1020 if (!isa<LayerBlockOp>(parent))
1021 return instance->emitError(
"cannot inline instance")
1022 .attachNote(parent->getLoc())
1023 <<
"containing operation '" << parent->getName()
1024 <<
"' not safe to inline into";
1025 parent = parent->getParentOp();
1031 LogicalResult Inliner::flattenInto(StringRef prefix, InliningLevel &il,
1033 DenseSet<Attribute> localSymbols) {
1034 auto target = il.childModule;
1035 auto moduleName = target.getNameAttr();
1036 DenseMap<Attribute, Attribute> symbolRenames;
1038 LLVM_DEBUG(llvm::dbgs() <<
"flattening " << target.getModuleName() <<
" into "
1039 << il.mic.module.getModuleName() <<
"\n");
1040 auto visit = [&](Operation *op) {
1042 auto instance = dyn_cast<InstanceOp>(op);
1044 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1049 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1050 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1052 liveModules.insert(moduleOp);
1054 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1058 if (failed(checkInstanceParents(instance)))
1064 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
1066 auto parentActivePaths = activeHierpaths;
1067 setActiveHierPaths(moduleName, instInnerSym);
1068 currentPath.emplace_back(moduleName, instInnerSym);
1070 InliningLevel childIL(il.mic, childModule);
1071 createDebugScope(childIL, instance, il.debugScope);
1074 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1075 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
1079 if (failed(flattenInto(nestedPrefix, childIL, mapper, localSymbols)))
1081 currentPath.pop_back();
1082 activeHierpaths = parentActivePaths;
1085 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1088 LogicalResult Inliner::flattenInstances(FModuleOp module) {
1089 auto moduleName = module.getNameAttr();
1090 ModuleInliningContext mic(module);
1092 auto visit = [&](InstanceOp instance) {
1094 auto *targetModule = symbolTable.lookup(instance.getModuleName());
1095 auto target = dyn_cast<FModuleOp>(targetModule);
1097 liveModules.insert(targetModule);
1098 return WalkResult::advance();
1101 if (failed(checkInstanceParents(instance)))
1102 return WalkResult::interrupt();
1109 for (
auto targetNLA : instOpHierPaths[innerRef])
1110 nlaMap[targetNLA].flattenModule(target);
1116 DenseSet<Attribute> localSymbols;
1117 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1119 auto parentActivePaths = activeHierpaths;
1120 setActiveHierPaths(moduleName, instInnerSym);
1121 currentPath.emplace_back(moduleName, instInnerSym);
1126 mic.b.setInsertionPoint(instance);
1128 InliningLevel il(mic, target);
1129 createDebugScope(il, instance);
1131 auto nestedPrefix = (instance.getName() +
"_").str();
1132 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1133 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1134 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1137 if (failed(flattenInto(nestedPrefix, il, mapper, localSymbols)))
1138 return WalkResult::interrupt();
1139 currentPath.pop_back();
1140 activeHierpaths = parentActivePaths;
1144 return WalkResult::skip();
1146 return failure(module.getBodyBlock()
1147 ->walk<mlir::WalkOrder::PreOrder>(visit)
1153 Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1154 DenseMap<Attribute, Attribute> &symbolRenames) {
1155 auto target = il.childModule;
1156 auto inlineToParent = il.mic.module;
1157 auto moduleName = target.getNameAttr();
1159 LLVM_DEBUG(llvm::dbgs() <<
"inlining " << target.getModuleName() <<
" into "
1160 << inlineToParent.getModuleName() <<
"\n");
1162 auto visit = [&](Operation *op) {
1164 auto instance = dyn_cast<InstanceOp>(op);
1166 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1171 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1172 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1174 liveModules.insert(moduleOp);
1175 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1180 if (!shouldInline(childModule)) {
1181 if (liveModules.insert(childModule).second) {
1182 worklist.push_back(childModule);
1184 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1188 if (failed(checkInstanceParents(instance)))
1191 auto toBeFlattened = shouldFlatten(childModule);
1197 for (
auto sym : instOpHierPaths[innerRef]) {
1199 nlaMap[sym].flattenModule(childModule);
1201 nlaMap[sym].inlineModule(childModule);
1212 DenseMap<Attribute, Attribute> symbolRenames;
1213 if (!rootMap[childModule.getNameAttr()].empty()) {
1214 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1215 auto &mnla = nlaMap[sym];
1218 sym = mnla.reTop(inlineToParent);
1222 context, il.mic.modNamespace.newName(instance.getName()));
1226 cast<StringAttr>(sym));
1230 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1234 auto parentActivePaths = activeHierpaths;
1235 setActiveHierPaths(moduleName, instInnerSym);
1237 currentPath.emplace_back(moduleName, instInnerSym);
1239 InliningLevel childIL(il.mic, childModule);
1240 createDebugScope(childIL, instance, il.debugScope);
1243 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1244 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1248 if (toBeFlattened) {
1249 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1252 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1255 currentPath.pop_back();
1256 activeHierpaths = parentActivePaths;
1260 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1263 LogicalResult Inliner::inlineInstances(FModuleOp module) {
1265 auto moduleName = module.getNameAttr();
1266 ModuleInliningContext mic(module);
1268 auto visit = [&](InstanceOp instance) {
1270 auto *childModule = symbolTable.lookup(instance.getModuleName());
1271 auto target = dyn_cast<FModuleOp>(childModule);
1273 liveModules.insert(childModule);
1274 return WalkResult::advance();
1278 if (!shouldInline(target)) {
1279 if (liveModules.insert(target).second) {
1280 worklist.push_back(target);
1282 return WalkResult::advance();
1285 if (failed(checkInstanceParents(instance)))
1286 return WalkResult::interrupt();
1288 auto toBeFlattened = shouldFlatten(target);
1294 for (
auto sym : instOpHierPaths[innerRef]) {
1296 nlaMap[sym].flattenModule(target);
1298 nlaMap[sym].inlineModule(target);
1305 DenseMap<Attribute, Attribute> symbolRenames;
1306 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1307 for (
auto sym : rootMap[target.getNameAttr()]) {
1308 auto &mnla = nlaMap[sym];
1309 sym = mnla.reTop(module);
1311 instance, [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1312 return mic.modNamespace;
1315 cast<StringAttr>(sym));
1319 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1323 auto parentActivePaths = activeHierpaths;
1324 setActiveHierPaths(moduleName, instInnerSym);
1326 currentPath.emplace_back(moduleName, instInnerSym);
1330 mic.b.setInsertionPoint(instance);
1331 auto nestedPrefix = (instance.getName() +
"_").str();
1333 InliningLevel childIL(mic, target);
1334 createDebugScope(childIL, instance);
1336 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1337 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1338 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1341 if (toBeFlattened) {
1342 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1343 return WalkResult::interrupt();
1347 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1348 return WalkResult::interrupt();
1350 currentPath.pop_back();
1351 activeHierpaths = parentActivePaths;
1355 return WalkResult::skip();
1358 return failure(module.getBodyBlock()
1359 ->walk<mlir::WalkOrder::PreOrder>(visit)
1363 void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1364 Value parentScope) {
1365 auto op = il.mic.b.create<debug::ScopeOp>(
1366 instance.getLoc(), instance.getInstanceNameAttr(),
1367 instance.getModuleNameAttr().getAttr(), parentScope);
1368 debugScopes.push_back(op);
1372 void Inliner::identifyNLAsTargetingOnlyModules() {
1373 DenseSet<Operation *> nlaTargetedModules;
1376 for (
auto &[sym, mnla] : nlaMap) {
1377 auto nla = mnla.getNLA();
1378 if (nla.isModule()) {
1379 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1381 "NLA ends in module reference but does not target FModuleLike?");
1382 nlaTargetedModules.insert(mod);
1387 auto scanForNLARefs = [&](FModuleLike mod) {
1388 DenseSet<StringAttr> referencedNLASyms;
1390 for (
auto anno : annos)
1391 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1392 referencedNLASyms.insert(sym.getAttr());
1395 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1400 mod.walk([&](Operation *op) {
1401 if (op == mod.getOperation())
1406 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1407 for (
auto portAnnoAttr : op.getPortAnnotations())
1412 return referencedNLASyms;
1416 auto mergeSets = [](
auto &&a,
auto &&b) {
1417 a.insert(b.begin(), b.end());
1418 return std::move(a);
1423 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1424 nlaTargetedModules.end());
1425 auto nonModOnlyNLAs =
1427 mergeSets, scanForNLARefs);
1430 for (
auto &[_, mnla] : nlaMap) {
1431 auto nla = mnla.getNLA();
1432 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1433 mnla.markModuleOnly();
1437 Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1438 : circuit(circuit), context(circuit.getContext()),
1439 symbolTable(symbolTable) {}
1445 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1446 auto mnla = MutableNLA(nla, &circuitNamespace);
1447 nlaMap.insert({nla.getSymNameAttr(), mnla});
1448 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1449 for (
auto p : nla.getNamepath())
1450 if (
auto ref = dyn_cast<InnerRefAttr>(p))
1451 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1455 identifyNLAsTargetingOnlyModules();
1458 for (
auto module : circuit.getOps<FModuleLike>()) {
1459 if (module.canDiscardOnUseEmpty())
1461 liveModules.insert(module);
1462 if (isa<FModuleOp>(module))
1463 worklist.push_back(cast<FModuleOp>(module));
1468 while (!worklist.empty()) {
1469 auto moduleOp = worklist.pop_back_val();
1470 if (shouldFlatten(moduleOp)) {
1471 if (failed(flattenInstances(moduleOp)))
1478 if (failed(inlineInstances(moduleOp)))
1485 for (
auto scopeOp : llvm::reverse(debugScopes))
1486 if (scopeOp.use_empty())
1488 debugScopes.clear();
1492 for (
auto mod : llvm::make_early_inc_range(
1493 circuit.getBodyBlock()->getOps<FModuleLike>())) {
1494 if (liveModules.count(mod))
1496 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1497 nlaMap[nla].markDead();
1503 for (
auto mod : circuit.getBodyBlock()->getOps<FModuleLike>()) {
1504 if (shouldInline(mod)) {
1506 "non-public module with inline annotation still present");
1509 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1513 llvm::dbgs() <<
"NLA modifications:\n";
1514 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1515 auto &mnla = nlaMap[nla.getNameAttr()];
1521 for (
auto &nla : nlaMap)
1522 nla.getSecond().applyUpdates();
1526 for (
auto fmodule : circuit.getBodyBlock()->getOps<FModuleOp>()) {
1527 SmallVector<Attribute> newAnnotations;
1528 auto processNLAs = [&](
Annotation anno) ->
bool {
1529 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1533 if (!nlaMap.count(sym.getAttr()))
1536 auto mnla = nlaMap[sym.getAttr()];
1547 auto newTops = mnla.getAdditionalSymbols();
1548 if (newTops.empty() || mnla.hasRoot(fmodule))
1554 NamedAttrList newAnnotation;
1555 for (
auto rootAndSym : newTops.drop_front()) {
1556 for (
auto pair : anno.getDict()) {
1557 if (pair.getName().getValue() !=
"circt.nonlocal") {
1558 newAnnotation.push_back(pair);
1561 newAnnotation.push_back(
1569 fmodule.walk([&](Operation *op) {
1573 if (annotations.empty())
1577 newAnnotations.clear();
1578 annotations.removeAnnotations(processNLAs);
1579 annotations.addAnnotations(newAnnotations);
1580 annotations.applyToOperation(op);
1584 SmallVector<Attribute> newPortAnnotations;
1585 for (
auto port : fmodule.getPorts()) {
1586 newAnnotations.clear();
1587 port.annotations.removeAnnotations(processNLAs);
1588 port.annotations.addAnnotations(newAnnotations);
1589 newPortAnnotations.push_back(
1592 fmodule->setAttr(
"portAnnotations",
1603 class InlinerPass :
public circt::firrtl::impl::InlinerBase<InlinerPass> {
1604 void runOnOperation()
override {
1606 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1607 if (failed(inliner.run()))
1608 signalPassFailure();
1615 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()
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...
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
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.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
The namespace of a CircuitOp, generally inhabited by modules.