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);
579 LogicalResult checkInstanceParents(InstanceOp instance);
585 inliningWalk(OpBuilder &builder, Block *block, IRMapping &mapper,
586 llvm::function_ref<LogicalResult(Operation *op)> process);
591 LogicalResult flattenInto(StringRef prefix, InliningLevel &il,
593 DenseSet<Attribute> localSymbols);
598 LogicalResult inlineInto(StringRef prefix, InliningLevel &il,
600 DenseMap<Attribute, Attribute> &symbolRenames);
603 LogicalResult flattenInstances(FModuleOp module);
606 LogicalResult inlineInstances(FModuleOp module);
610 void createDebugScope(InliningLevel &il, InstanceOp instance,
611 Value parentScope = {});
614 void identifyNLAsTargetingOnlyModules();
620 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
623 if (currentPath.empty()) {
624 activeHierpaths.insert(instPaths.begin(), instPaths.end());
627 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
630 llvm::set_intersect(activeHierpaths, hPaths);
633 for (
auto hPath : instPaths)
634 if (nlaMap[hPath].hasRoot(moduleName))
635 activeHierpaths.insert(hPath);
639 MLIRContext *context;
642 SymbolTable &symbolTable;
646 DenseSet<Operation *> liveModules;
649 SmallVector<FModuleOp, 16> worklist;
652 DenseMap<Attribute, MutableNLA> nlaMap;
655 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
660 SmallVector<std::pair<Attribute, Attribute>> currentPath;
662 DenseSet<StringAttr> activeHierpaths;
667 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
671 SmallVector<debug::ScopeOp> debugScopes;
678 bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
679 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
686 bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
689 auto updateDebugScope = [&](
auto op) {
691 op.getScopeMutable().assign(il.debugScope);
693 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
694 return updateDebugScope(varOp),
false;
695 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
696 return updateDebugScope(scopeOp),
false;
699 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
701 (prefix + nameAttr.getValue())));
705 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
708 auto oldSymAttr = symOp.getInnerSymAttr();
711 il.childModule.getNameAttr());
717 if (
auto newSymStrAttr = newSymAttr.getSymName();
718 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
720 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
728 auto &mnla = nlaMap[sym.getAttr()];
729 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
731 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
735 symOp.setInnerSymbolAttr(newSymAttr);
737 return newSymAttr != oldSymAttr;
740 bool Inliner::renameInstance(
741 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
742 const DenseMap<Attribute, Attribute> &symbolRenames) {
747 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
752 auto parentActivePaths = activeHierpaths;
753 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
755 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
760 SmallVector<StringAttr> validHierPaths;
761 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
769 for (
auto old : instOpHierPaths[oldInnerRef]) {
773 if (activeHierpaths.find(old) != activeHierpaths.end())
774 validHierPaths.push_back(old);
778 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
779 if (activeHierpaths.find(additionalSym.getName()) !=
780 activeHierpaths.end()) {
781 validHierPaths.push_back(old);
790 auto symbolChanged = rename(prefix, newInst, il);
799 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
800 instOpHierPaths[newInnerRef] = validHierPaths;
802 for (
auto nla : instOpHierPaths[newInnerRef]) {
803 if (!nlaMap.count(nla))
805 auto &mnla = nlaMap[nla];
806 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
812 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
813 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
815 for (
const auto &en : llvm::enumerate(nlaList)) {
816 auto oldNLA =
en.value();
817 if (
auto newSym = symbolRenames.lookup(oldNLA))
818 nlaList[
en.index()] = cast<StringAttr>(newSym);
821 activeHierpaths = std::move(parentActivePaths);
822 return symbolChanged;
830 void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
832 const DenseSet<Attribute> &localSymbols) {
833 auto target = il.childModule;
834 auto portInfo = target.getPorts();
835 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
836 auto arg = target.getArgument(i);
838 auto type = type_cast<FIRRTLType>(arg.getType());
841 auto oldSymAttr = portInfo[i].sym;
844 il.mic.modNamespace, target.getNameAttr());
846 StringAttr newRootSymName, oldRootSymName;
848 oldRootSymName = oldSymAttr.getSymName();
850 newRootSymName = newSymAttr.getSymName();
852 SmallVector<Attribute> newAnnotations;
855 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
856 auto &mnla = nlaMap[sym.getAttr()];
858 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
862 if (oldRootSymName != newRootSymName)
863 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
865 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
866 anno.removeMember(
"circt.nonlocal");
868 newAnnotations.push_back(anno.getAttr());
874 target.getLoc(), type,
880 il.wires.push_back(wire);
881 mapper.map(arg, wire);
888 void Inliner::cloneAndRename(
889 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
890 const DenseMap<Attribute, Attribute> &symbolRenames,
891 const DenseSet<Attribute> &localSymbols) {
894 SmallVector<Annotation> newAnnotations;
895 for (
auto anno : oldAnnotations) {
898 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
900 auto &mnla = nlaMap[sym.getAttr()];
902 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
905 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
906 anno.removeMember(
"circt.nonlocal");
909 newAnnotations.push_back(anno);
913 assert(op.getNumRegions() == 0 &&
914 "operation with regions should not reach cloneAndRename");
915 auto *newOp = il.mic.b.cloneWithoutRegions(op, mapper);
922 if (
auto oldInst = dyn_cast<InstanceOp>(op))
923 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOp), symbolRenames);
925 rename(prefix, newOp, il);
929 if (!newAnnotations.empty() || !oldAnnotations.empty())
932 il.newOps.push_back(newOp);
935 bool Inliner::shouldFlatten(Operation *op) {
939 bool Inliner::shouldInline(Operation *op) {
943 LogicalResult Inliner::inliningWalk(
944 OpBuilder &builder, Block *block, IRMapping &mapper,
945 llvm::function_ref<LogicalResult(Operation *op)> process) {
947 OpBuilder::InsertPoint target;
948 Block::iterator source;
951 SmallVector<IPs> inliningStack;
955 inliningStack.push_back(IPs{builder.saveInsertionPoint(), block->begin()});
956 OpBuilder::InsertionGuard guard(builder);
958 while (!inliningStack.empty()) {
959 auto target = inliningStack.back().target;
960 builder.restoreInsertionPoint(target);
964 auto &ips = inliningStack.back();
965 source = &*ips.source;
966 auto end = source->getBlock()->end();
967 if (++ips.source == end)
968 inliningStack.pop_back();
972 if (source->getNumRegions() == 0) {
973 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
975 if (failed(process(source)))
977 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
983 if (!isa<LayerBlockOp, WhenOp, MatchOp>(source))
984 return source->emitError(
"unsupported operation '")
985 << source->getName() <<
"' cannot be inlined";
991 auto *newOp = builder.cloneWithoutRegions(*source, mapper);
992 for (
auto [newRegion, oldRegion] : llvm::reverse(
993 llvm::zip_equal(newOp->getRegions(), source->getRegions()))) {
995 if (oldRegion.empty()) {
996 assert(newRegion.empty());
1000 assert(oldRegion.hasOneBlock());
1003 auto &oldBlock = oldRegion.getBlocks().front();
1004 auto &newBlock = newRegion.emplaceBlock();
1005 mapper.map(&oldBlock, &newBlock);
1008 for (
auto arg : oldBlock.getArguments())
1009 mapper.map(arg, newBlock.addArgument(arg.getType(), arg.getLoc()));
1011 if (oldBlock.empty())
1014 inliningStack.push_back(
1015 IPs{OpBuilder::InsertPoint(&newBlock, newBlock.begin()),
1022 LogicalResult Inliner::checkInstanceParents(InstanceOp instance) {
1023 auto *parent = instance->getParentOp();
1024 while (!isa<FModuleLike>(parent)) {
1025 if (!isa<LayerBlockOp>(parent))
1026 return instance->emitError(
"cannot inline instance")
1027 .attachNote(parent->getLoc())
1028 <<
"containing operation '" << parent->getName()
1029 <<
"' not safe to inline into";
1030 parent = parent->getParentOp();
1036 LogicalResult Inliner::flattenInto(StringRef prefix, InliningLevel &il,
1038 DenseSet<Attribute> localSymbols) {
1039 auto target = il.childModule;
1040 auto moduleName = target.getNameAttr();
1041 DenseMap<Attribute, Attribute> symbolRenames;
1043 LLVM_DEBUG(llvm::dbgs() <<
"flattening " << target.getModuleName() <<
" into "
1044 << il.mic.module.getModuleName() <<
"\n");
1045 auto visit = [&](Operation *op) {
1047 auto instance = dyn_cast<InstanceOp>(op);
1049 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1054 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1055 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1057 liveModules.insert(moduleOp);
1059 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1063 if (failed(checkInstanceParents(instance)))
1069 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
1071 auto parentActivePaths = activeHierpaths;
1072 setActiveHierPaths(moduleName, instInnerSym);
1073 currentPath.emplace_back(moduleName, instInnerSym);
1075 InliningLevel childIL(il.mic, childModule);
1076 createDebugScope(childIL, instance, il.debugScope);
1079 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1080 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
1084 if (failed(flattenInto(nestedPrefix, childIL, mapper, localSymbols)))
1086 currentPath.pop_back();
1087 activeHierpaths = parentActivePaths;
1090 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1093 LogicalResult Inliner::flattenInstances(FModuleOp module) {
1094 auto moduleName = module.getNameAttr();
1095 ModuleInliningContext mic(module);
1097 auto visit = [&](InstanceOp instance) {
1099 auto *targetModule = symbolTable.lookup(instance.getModuleName());
1100 auto target = dyn_cast<FModuleOp>(targetModule);
1102 liveModules.insert(targetModule);
1103 return WalkResult::advance();
1106 if (failed(checkInstanceParents(instance)))
1107 return WalkResult::interrupt();
1114 for (
auto targetNLA : instOpHierPaths[innerRef])
1115 nlaMap[targetNLA].flattenModule(target);
1121 DenseSet<Attribute> localSymbols;
1122 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1124 auto parentActivePaths = activeHierpaths;
1125 setActiveHierPaths(moduleName, instInnerSym);
1126 currentPath.emplace_back(moduleName, instInnerSym);
1131 mic.b.setInsertionPoint(instance);
1133 InliningLevel il(mic, target);
1134 createDebugScope(il, instance);
1136 auto nestedPrefix = (instance.getName() +
"_").str();
1137 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1138 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1139 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1142 if (failed(flattenInto(nestedPrefix, il, mapper, localSymbols)))
1143 return WalkResult::interrupt();
1144 currentPath.pop_back();
1145 activeHierpaths = parentActivePaths;
1149 return WalkResult::skip();
1151 return failure(module.getBodyBlock()
1152 ->walk<mlir::WalkOrder::PreOrder>(visit)
1158 Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1159 DenseMap<Attribute, Attribute> &symbolRenames) {
1160 auto target = il.childModule;
1161 auto inlineToParent = il.mic.module;
1162 auto moduleName = target.getNameAttr();
1164 LLVM_DEBUG(llvm::dbgs() <<
"inlining " << target.getModuleName() <<
" into "
1165 << inlineToParent.getModuleName() <<
"\n");
1167 auto visit = [&](Operation *op) {
1169 auto instance = dyn_cast<InstanceOp>(op);
1171 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1176 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1177 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1179 liveModules.insert(moduleOp);
1180 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1185 if (!shouldInline(childModule)) {
1186 if (liveModules.insert(childModule).second) {
1187 worklist.push_back(childModule);
1189 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1193 if (failed(checkInstanceParents(instance)))
1196 auto toBeFlattened = shouldFlatten(childModule);
1202 for (
auto sym : instOpHierPaths[innerRef]) {
1204 nlaMap[sym].flattenModule(childModule);
1206 nlaMap[sym].inlineModule(childModule);
1217 DenseMap<Attribute, Attribute> symbolRenames;
1218 if (!rootMap[childModule.getNameAttr()].empty()) {
1219 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1220 auto &mnla = nlaMap[sym];
1223 sym = mnla.reTop(inlineToParent);
1227 context, il.mic.modNamespace.newName(instance.getName()));
1231 cast<StringAttr>(sym));
1235 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1239 auto parentActivePaths = activeHierpaths;
1240 setActiveHierPaths(moduleName, instInnerSym);
1242 currentPath.emplace_back(moduleName, instInnerSym);
1244 InliningLevel childIL(il.mic, childModule);
1245 createDebugScope(childIL, instance, il.debugScope);
1248 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1249 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1253 if (toBeFlattened) {
1254 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1257 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1260 currentPath.pop_back();
1261 activeHierpaths = parentActivePaths;
1265 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1268 LogicalResult Inliner::inlineInstances(FModuleOp module) {
1270 auto moduleName = module.getNameAttr();
1271 ModuleInliningContext mic(module);
1273 auto visit = [&](InstanceOp instance) {
1275 auto *childModule = symbolTable.lookup(instance.getModuleName());
1276 auto target = dyn_cast<FModuleOp>(childModule);
1278 liveModules.insert(childModule);
1279 return WalkResult::advance();
1283 if (!shouldInline(target)) {
1284 if (liveModules.insert(target).second) {
1285 worklist.push_back(target);
1287 return WalkResult::advance();
1290 if (failed(checkInstanceParents(instance)))
1291 return WalkResult::interrupt();
1293 auto toBeFlattened = shouldFlatten(target);
1299 for (
auto sym : instOpHierPaths[innerRef]) {
1301 nlaMap[sym].flattenModule(target);
1303 nlaMap[sym].inlineModule(target);
1310 DenseMap<Attribute, Attribute> symbolRenames;
1311 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1312 for (
auto sym : rootMap[target.getNameAttr()]) {
1313 auto &mnla = nlaMap[sym];
1314 sym = mnla.reTop(module);
1316 instance, [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1317 return mic.modNamespace;
1320 cast<StringAttr>(sym));
1324 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1328 auto parentActivePaths = activeHierpaths;
1329 setActiveHierPaths(moduleName, instInnerSym);
1331 currentPath.emplace_back(moduleName, instInnerSym);
1335 mic.b.setInsertionPoint(instance);
1336 auto nestedPrefix = (instance.getName() +
"_").str();
1338 InliningLevel childIL(mic, target);
1339 createDebugScope(childIL, instance);
1341 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1342 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1343 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1346 if (toBeFlattened) {
1347 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1348 return WalkResult::interrupt();
1352 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1353 return WalkResult::interrupt();
1355 currentPath.pop_back();
1356 activeHierpaths = parentActivePaths;
1360 return WalkResult::skip();
1363 return failure(module.getBodyBlock()
1364 ->walk<mlir::WalkOrder::PreOrder>(visit)
1368 void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1369 Value parentScope) {
1370 auto op = il.mic.b.create<debug::ScopeOp>(
1371 instance.getLoc(), instance.getInstanceNameAttr(),
1372 instance.getModuleNameAttr().getAttr(), parentScope);
1373 debugScopes.push_back(op);
1377 void Inliner::identifyNLAsTargetingOnlyModules() {
1378 DenseSet<Operation *> nlaTargetedModules;
1381 for (
auto &[sym, mnla] : nlaMap) {
1382 auto nla = mnla.getNLA();
1383 if (nla.isModule()) {
1384 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1386 "NLA ends in module reference but does not target FModuleLike?");
1387 nlaTargetedModules.insert(mod);
1392 auto scanForNLARefs = [&](FModuleLike mod) {
1393 DenseSet<StringAttr> referencedNLASyms;
1395 for (
auto anno : annos)
1396 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1397 referencedNLASyms.insert(sym.getAttr());
1400 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1405 mod.walk([&](Operation *op) {
1406 if (op == mod.getOperation())
1411 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1412 for (
auto portAnnoAttr : op.getPortAnnotations())
1417 return referencedNLASyms;
1421 auto mergeSets = [](
auto &&a,
auto &&b) {
1422 a.insert(b.begin(), b.end());
1423 return std::move(a);
1428 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1429 nlaTargetedModules.end());
1430 auto nonModOnlyNLAs =
1432 mergeSets, scanForNLARefs);
1435 for (
auto &[_, mnla] : nlaMap) {
1436 auto nla = mnla.getNLA();
1437 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1438 mnla.markModuleOnly();
1442 Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1443 : circuit(circuit), context(circuit.getContext()),
1444 symbolTable(symbolTable) {}
1450 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1451 auto mnla = MutableNLA(nla, &circuitNamespace);
1452 nlaMap.insert({nla.getSymNameAttr(), mnla});
1453 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1454 for (
auto p : nla.getNamepath())
1455 if (
auto ref = dyn_cast<InnerRefAttr>(p))
1456 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1460 identifyNLAsTargetingOnlyModules();
1463 for (
auto module : circuit.getOps<FModuleLike>()) {
1464 if (module.canDiscardOnUseEmpty())
1466 liveModules.insert(module);
1467 if (isa<FModuleOp>(module))
1468 worklist.push_back(cast<FModuleOp>(module));
1473 while (!worklist.empty()) {
1474 auto moduleOp = worklist.pop_back_val();
1475 if (shouldFlatten(moduleOp)) {
1476 if (failed(flattenInstances(moduleOp)))
1483 if (failed(inlineInstances(moduleOp)))
1490 for (
auto scopeOp : llvm::reverse(debugScopes))
1491 if (scopeOp.use_empty())
1493 debugScopes.clear();
1497 for (
auto mod : llvm::make_early_inc_range(
1498 circuit.getBodyBlock()->getOps<FModuleLike>())) {
1499 if (liveModules.count(mod))
1501 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1502 nlaMap[nla].markDead();
1508 for (
auto mod : circuit.getBodyBlock()->getOps<FModuleLike>()) {
1509 if (shouldInline(mod)) {
1511 "non-public module with inline annotation still present");
1514 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1518 llvm::dbgs() <<
"NLA modifications:\n";
1519 for (
auto nla : circuit.getBodyBlock()->getOps<hw::HierPathOp>()) {
1520 auto &mnla = nlaMap[nla.getNameAttr()];
1526 for (
auto &nla : nlaMap)
1527 nla.getSecond().applyUpdates();
1531 for (
auto fmodule : circuit.getBodyBlock()->getOps<FModuleOp>()) {
1532 SmallVector<Attribute> newAnnotations;
1533 auto processNLAs = [&](
Annotation anno) ->
bool {
1534 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1538 if (!nlaMap.count(sym.getAttr()))
1541 auto mnla = nlaMap[sym.getAttr()];
1552 auto newTops = mnla.getAdditionalSymbols();
1553 if (newTops.empty() || mnla.hasRoot(fmodule))
1559 NamedAttrList newAnnotation;
1560 for (
auto rootAndSym : newTops.drop_front()) {
1561 for (
auto pair : anno.getDict()) {
1562 if (pair.getName().getValue() !=
"circt.nonlocal") {
1563 newAnnotation.push_back(pair);
1566 newAnnotation.push_back(
1574 fmodule.walk([&](Operation *op) {
1578 if (annotations.empty())
1582 newAnnotations.clear();
1583 annotations.removeAnnotations(processNLAs);
1584 annotations.addAnnotations(newAnnotations);
1585 annotations.applyToOperation(op);
1589 SmallVector<Attribute> newPortAnnotations;
1590 for (
auto port : fmodule.getPorts()) {
1591 newAnnotations.clear();
1592 port.annotations.removeAnnotations(processNLAs);
1593 port.annotations.addAnnotations(newAnnotations);
1594 newPortAnnotations.push_back(
1597 fmodule->setAttr(
"portAnnotations",
1608 class InlinerPass :
public circt::firrtl::impl::InlinerBase<InlinerPass> {
1609 void runOnOperation()
override {
1611 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1612 if (failed(inliner.run()))
1613 signalPassFailure();
1620 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.