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/Support/Debug.h"
33#include "llvm/Support/FormatVariadic.h"
35#define DEBUG_TYPE "firrtl-inliner"
39#define GEN_PASS_DEF_INLINER
40#include "circt/Dialect/FIRRTL/Passes.h.inc"
45using namespace firrtl;
46using namespace chirrtl;
48using hw::InnerRefAttr;
70 DenseMap<Attribute, unsigned> symIdx;
75 BitVector inlinedSymbols;
86 bool moduleOnly =
false;
92 SmallVector<InnerRefAttr> newTops;
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() {
160 if (inlinedSymbols.all() && newTops.empty() && renames.empty())
166 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
167 SmallVector<Attribute> namepath;
173 if (inlinedSymbols.size() == 1 || !inlinedSymbols.test(1)) {
176 namepath.push_back(InnerRefAttr::get(root, lookupRename(root)));
180 for (
signed i = 1, e = inlinedSymbols.size() - 1; i < e; ++i) {
181 if (!inlinedSymbols.test(i + 1)) {
183 lastMod = nla.modPart(i);
188 auto modPart = lastMod ? lastMod : nla.modPart(i);
189 auto refPart = lookupRename(modPart, i);
190 namepath.push_back(InnerRefAttr::get(modPart, refPart));
195 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
196 auto refPart = lookupRename(modPart, size - 1);
199 namepath.push_back(InnerRefAttr::get(modPart, refPart));
201 namepath.push_back(FlatSymbolRefAttr::get(modPart));
203 auto hp = hw::HierPathOp::create(b, b.getUnknownLoc(), sym,
204 b.getArrayAttr(namepath));
205 hp.setVisibility(nla.getVisibility());
210 assert(!dead || !newTops.empty());
212 last = writeBack(nla.root(), nla.getNameAttr());
213 for (
auto root : newTops)
214 last = writeBack(root.getModule(), root.getName());
221 llvm::errs() <<
" - orig: " << nla <<
"\n"
222 <<
" new: " << *
this <<
"\n"
223 <<
" dead: " << dead <<
"\n"
224 <<
" isDead: " << isDead() <<
"\n"
225 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
226 <<
" isLocal: " << isLocal() <<
"\n"
227 <<
" inlinedSymbols: [";
228 llvm::interleaveComma(inlinedSymbols.getData(), llvm::errs(), [](
auto a) {
229 llvm::errs() << llvm::formatv(
"{0:x-}", a);
231 llvm::errs() <<
"]\n"
233 for (
auto rename : renames)
234 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
241 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
242 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
244 os <<
"#hw.innerNameRef<";
245 os <<
"@" << mod.getValue();
247 os <<
"::@" << sym.getValue() <<
">";
250 auto writeOne = [&](StringAttr root, StringAttr sym) {
251 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
254 bool needsComma =
false;
259 if (x.inlinedSymbols.size() == 1 || !x.inlinedSymbols.test(1)) {
262 writePathSegment(root, x.lookupRename(root));
267 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i < e; ++i) {
268 if (!x.inlinedSymbols.test(i + 1)) {
270 lastMod = x.nla.modPart(i);
276 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
277 auto refPart = x.nla.refPart(i);
278 if (x.renames.count(modPart))
279 refPart = x.renames[modPart];
280 writePathSegment(modPart, refPart);
288 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
289 auto refPart = x.nla.refPart(x.size - 1);
290 if (x.renames.count(modPart))
291 refPart = x.renames[modPart];
292 writePathSegment(modPart, refPart);
296 SmallVector<InnerRefAttr> tops;
298 tops.push_back(InnerRefAttr::get(x.nla.root(), x.nla.getNameAttr()));
299 tops.append(x.newTops.begin(), x.newTops.end());
301 bool multiary = !x.newTops.empty();
304 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
305 writeOne(a.getModule(), a.getName());
317 bool isDead() {
return dead && newTops.empty(); }
320 bool isModuleOnly() {
return moduleOnly; }
326 return inlinedSymbols.find_first_in(1, inlinedSymbols.size()) == -1;
330 bool hasRoot(FModuleLike mod) {
return hasRoot(mod.getModuleNameAttr()); }
333 bool hasRoot(StringAttr modName) {
334 return symIdx.lookup_or(modName, -1) == 0;
338 void inlineModule(FModuleOp module) {
339 auto sym =
module.getNameAttr();
340 assert(sym != nla.root() &&
"unable to inline the root module");
341 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
342 auto idx = symIdx[sym];
343 inlinedSymbols.reset(idx);
346 if (idx == size - 1 && moduleOnly)
353 void flattenModule(FModuleOp module) {
354 auto sym =
module.getNameAttr();
355 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
358 auto moduleIdx = symIdx[sym];
359 inlinedSymbols.reset(moduleIdx, size);
366 StringAttr reTop(FModuleOp module) {
367 StringAttr sym = nla.getSymNameAttr();
368 if (!newTops.empty())
369 sym = StringAttr::get(nla.getContext(),
370 circuitNamespace->
newName(sym.getValue()));
371 newTops.push_back(InnerRefAttr::get(module.getNameAttr(), sym));
372 symIdx.insert({
module.getNameAttr(), 0});
377 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
379 void setInnerSym(Attribute module, StringAttr innerSym) {
380 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
384 [[maybe_unused]]
auto [it, inserted] = renames.insert({module, innerSym});
385 assert((inserted || it->second == innerSym) &&
"Conflicting rename");
396 InstanceOp instance) {
397 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
398 auto result = instance.getResult(i);
399 auto wire = wires[i];
400 mapper.map(result, wire);
408 StringAttr istName) {
409 mlir::AttrTypeReplacer replacer;
410 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
411 auto it = map.find(innerRef);
415 return std::pair{hw::InnerRefAttr::get(istName, it->second),
418 llvm::for_each(newOps,
419 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
427 StringAttr istName) {
428 if (!old || old.empty())
431 bool anyChanged =
false;
433 SmallVector<hw::InnerSymPropertiesAttr> newProps;
434 auto *
context = old.getContext();
435 for (
auto &prop : old) {
436 auto newSym = ns.
newName(prop.getName().strref());
437 if (newSym == prop.getName()) {
438 newProps.push_back(prop);
441 auto newSymStrAttr = StringAttr::get(
context, newSym);
442 auto newProp = hw::InnerSymPropertiesAttr::get(
443 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
445 newProps.push_back(newProp);
448 auto newSymAttr = anyChanged ? hw::InnerSymAttr::get(
context, newProps) : old;
450 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
451 assert(oldProp.getFieldID() == newProp.getFieldID());
453 map[hw::InnerRefAttr::get(istName, oldProp.getName())] = newProp.getName();
485 Inliner(CircuitOp circuit, SymbolTable &symbolTable,
494 struct ModuleInliningContext {
495 ModuleInliningContext(FModuleOp module)
496 : module(module), modNamespace(module), b(module.getContext()) {}
508 struct InliningLevel {
509 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
510 : mic(mic), childModule(childModule) {}
513 ModuleInliningContext &mic;
517 SmallVector<Operation *> newOps;
519 SmallVector<Value> wires;
521 FModuleOp childModule;
527 mic.module.getNameAttr());
534 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
538 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
543 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
545 const DenseMap<Attribute, Attribute> &symbolRenames);
549 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
551 const DenseMap<Attribute, Attribute> &symbolRenames,
552 const DenseSet<Attribute> &localSymbols);
557 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
558 const DenseSet<Attribute> &localSymbols);
561 bool shouldFlatten(Operation *op);
564 bool shouldInline(Operation *op);
568 LogicalResult checkInstanceParents(InstanceOp instance);
574 inliningWalk(OpBuilder &builder, Block *block, IRMapping &mapper,
575 llvm::function_ref<LogicalResult(Operation *op)> process);
580 LogicalResult flattenInto(StringRef prefix, InliningLevel &il,
582 DenseSet<Attribute> localSymbols);
587 LogicalResult inlineInto(StringRef prefix, InliningLevel &il,
589 DenseMap<Attribute, Attribute> &symbolRenames);
592 LogicalResult flattenInstances(FModuleOp module);
595 LogicalResult inlineInstances(FModuleOp module);
599 void createDebugScope(InliningLevel &il, InstanceOp instance,
600 Value parentScope = {});
603 void identifyNLAsTargetingOnlyModules();
609 void markUnknownFInstanceLikeModulesLive(FInstanceLike instanceLike) {
610 for (
auto module : instanceLike.getReferencedModuleNamesAttr()
611 .getAsValueRange<StringAttr>()) {
612 auto *moduleOp = symbolTable.lookup(module);
613 liveModules.insert(moduleOp);
621 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
623 instOpHierPaths[InnerRefAttr::get(moduleName, instInnerSym)];
624 if (currentPath.empty()) {
625 activeHierpaths.insert(instPaths.begin(), instPaths.end());
628 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
631 llvm::set_intersect(activeHierpaths, hPaths);
634 for (
auto hPath : instPaths)
635 if (nlaMap[hPath].hasRoot(moduleName))
636 activeHierpaths.insert(hPath);
640 MLIRContext *context;
643 SymbolTable &symbolTable;
650 DenseSet<Operation *> liveModules;
653 DenseMap<Attribute, MutableNLA> nlaMap;
656 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
661 SmallVector<std::pair<Attribute, Attribute>> currentPath;
663 DenseSet<StringAttr> activeHierpaths;
668 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
672 SmallVector<debug::ScopeOp> debugScopes;
679bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
680 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
687bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
690 auto updateDebugScope = [&](
auto op) {
692 op.getScopeMutable().assign(il.debugScope);
694 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
695 return updateDebugScope(varOp),
false;
696 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
697 return updateDebugScope(scopeOp),
false;
700 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
701 op->setAttr(
"name", StringAttr::get(op->getContext(),
702 (prefix + nameAttr.getValue())));
706 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
709 auto oldSymAttr = symOp.getInnerSymAttr();
712 il.childModule.getNameAttr());
718 if (
auto newSymStrAttr = newSymAttr.getSymName();
719 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
721 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
729 auto &mnla = nlaMap[sym.getAttr()];
730 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
732 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
736 symOp.setInnerSymbolAttr(newSymAttr);
738 return newSymAttr != oldSymAttr;
741bool Inliner::renameInstance(
742 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
743 const DenseMap<Attribute, Attribute> &symbolRenames) {
748 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
753 auto parentActivePaths = activeHierpaths;
754 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
756 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
761 SmallVector<StringAttr> validHierPaths;
762 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
769 auto oldInnerRef = InnerRefAttr::get(oldParent, oldInstSym);
770 for (
auto old : instOpHierPaths[oldInnerRef]) {
774 if (activeHierpaths.find(old) != activeHierpaths.end())
775 validHierPaths.push_back(old);
779 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
780 if (activeHierpaths.find(additionalSym.
getName()) !=
781 activeHierpaths.
end()) {
782 validHierPaths.push_back(old);
791 auto symbolChanged = rename(prefix, newInst, il);
799 auto newInnerRef = InnerRefAttr::get(
800 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
801 instOpHierPaths[newInnerRef] = validHierPaths;
803 for (
auto nla : instOpHierPaths[newInnerRef]) {
804 if (!nlaMap.count(nla))
806 auto &mnla = nlaMap[nla];
807 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
812 auto innerRef = InnerRefAttr::get(
813 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
814 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
816 for (
const auto &
en :
llvm::enumerate(nlaList)) {
817 auto oldNLA =
en.value();
818 if (
auto newSym = symbolRenames.lookup(oldNLA))
819 nlaList[
en.index()] = cast<StringAttr>(newSym);
822 activeHierpaths = std::move(parentActivePaths);
823 return symbolChanged;
831void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
833 const DenseSet<Attribute> &localSymbols) {
834 auto target = il.childModule;
835 auto portInfo = target.getPorts();
836 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
837 auto arg = target.getArgument(i);
839 auto type = type_cast<FIRRTLType>(arg.getType());
842 auto oldSymAttr = portInfo[i].sym;
845 il.mic.modNamespace, target.getNameAttr());
847 StringAttr newRootSymName, oldRootSymName;
849 oldRootSymName = oldSymAttr.getSymName();
851 newRootSymName = newSymAttr.getSymName();
853 SmallVector<Attribute> newAnnotations;
856 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
857 auto &mnla = nlaMap[sym.getAttr()];
859 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
863 if (oldRootSymName != newRootSymName)
864 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
866 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
867 anno.removeMember(
"circt.nonlocal");
869 newAnnotations.push_back(anno.getAttr());
874 il.mic.b, target.getLoc(), type,
875 StringAttr::get(
context, (prefix + portInfo[i].getName())),
876 NameKindEnumAttr::get(
context, NameKindEnum::DroppableName),
877 ArrayAttr::get(
context, newAnnotations), newSymAttr,
880 il.wires.push_back(wire);
881 mapper.map(arg, wire);
888void 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);
935bool Inliner::shouldFlatten(Operation *op) {
939bool Inliner::shouldInline(Operation *op) {
943LogicalResult 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()),
1022LogicalResult 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();
1036LogicalResult 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 if (
auto instanceLike = dyn_cast<FInstanceLike>(op))
1050 markUnknownFInstanceLikeModulesLive(instanceLike);
1051 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1056 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1057 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1059 liveModules.insert(moduleOp);
1061 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1065 if (failed(checkInstanceParents(instance)))
1071 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
1073 auto parentActivePaths = activeHierpaths;
1074 setActiveHierPaths(moduleName, instInnerSym);
1075 currentPath.emplace_back(moduleName, instInnerSym);
1077 InliningLevel childIL(il.mic, childModule);
1078 createDebugScope(childIL, instance, il.debugScope);
1081 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1082 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
1086 if (failed(flattenInto(nestedPrefix, childIL, mapper, localSymbols)))
1088 currentPath.pop_back();
1089 activeHierpaths = parentActivePaths;
1092 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1095LogicalResult Inliner::flattenInstances(FModuleOp module) {
1096 auto moduleName =
module.getNameAttr();
1097 ModuleInliningContext mic(module);
1099 auto visit = [&](FInstanceLike instanceLike) {
1100 auto instance = dyn_cast<InstanceOp>(*instanceLike);
1102 markUnknownFInstanceLikeModulesLive(instanceLike);
1103 return WalkResult::advance();
1106 auto *targetModule = symbolTable.lookup(instance.getModuleName());
1107 auto target = dyn_cast<FModuleOp>(targetModule);
1109 liveModules.insert(targetModule);
1110 return WalkResult::advance();
1113 if (failed(checkInstanceParents(instance)))
1114 return WalkResult::interrupt();
1117 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1121 for (
auto targetNLA : instOpHierPaths[innerRef])
1122 nlaMap[targetNLA].flattenModule(target);
1128 DenseSet<Attribute> localSymbols;
1129 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1131 auto parentActivePaths = activeHierpaths;
1132 setActiveHierPaths(moduleName, instInnerSym);
1133 currentPath.emplace_back(moduleName, instInnerSym);
1138 mic.b.setInsertionPoint(instance);
1140 InliningLevel il(mic, target);
1141 createDebugScope(il, instance);
1143 auto nestedPrefix = (instance.getName() +
"_").str();
1144 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1145 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1146 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1149 if (failed(flattenInto(nestedPrefix, il, mapper, localSymbols)))
1150 return WalkResult::interrupt();
1151 currentPath.pop_back();
1152 activeHierpaths = parentActivePaths;
1156 return WalkResult::skip();
1158 return failure(module.getBodyBlock()
1159 ->walk<mlir::WalkOrder::PreOrder>(visit)
1165Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1166 DenseMap<Attribute, Attribute> &symbolRenames) {
1167 auto target = il.childModule;
1168 auto inlineToParent = il.mic.module;
1169 auto moduleName = target.getNameAttr();
1171 LLVM_DEBUG(llvm::dbgs() <<
"inlining " << target.getModuleName() <<
" into "
1172 << inlineToParent.getModuleName() <<
"\n");
1174 auto visit = [&](Operation *op) {
1176 auto instance = dyn_cast<InstanceOp>(op);
1178 if (
auto instanceLike = dyn_cast<FInstanceLike>(op))
1179 markUnknownFInstanceLikeModulesLive(instanceLike);
1181 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1186 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1187 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1189 liveModules.insert(moduleOp);
1190 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1195 if (!shouldInline(childModule)) {
1196 liveModules.insert(childModule);
1197 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1201 if (failed(checkInstanceParents(instance)))
1204 auto toBeFlattened = shouldFlatten(childModule);
1206 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1210 for (
auto sym : instOpHierPaths[innerRef]) {
1212 nlaMap[sym].flattenModule(childModule);
1214 nlaMap[sym].inlineModule(childModule);
1225 DenseMap<Attribute, Attribute> symbolRenames;
1226 if (!rootMap[childModule.getNameAttr()].empty()) {
1227 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1228 auto &mnla = nlaMap[sym];
1231 sym = mnla.reTop(inlineToParent);
1234 instSym = StringAttr::get(
1235 context, il.mic.modNamespace.newName(instance.getName()));
1236 instance.setInnerSymAttr(hw::InnerSymAttr::get(instSym));
1238 instOpHierPaths[InnerRefAttr::get(moduleName, instSym)].push_back(
1239 cast<StringAttr>(sym));
1243 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1247 auto parentActivePaths = activeHierpaths;
1248 setActiveHierPaths(moduleName, instInnerSym);
1250 currentPath.emplace_back(moduleName, instInnerSym);
1252 InliningLevel childIL(il.mic, childModule);
1253 createDebugScope(childIL, instance, il.debugScope);
1256 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1257 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1261 if (toBeFlattened) {
1262 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1265 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1268 currentPath.pop_back();
1269 activeHierpaths = parentActivePaths;
1273 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1276LogicalResult Inliner::inlineInstances(FModuleOp module) {
1279 auto moduleName =
module.getNameAttr();
1280 ModuleInliningContext mic(module);
1282 auto visit = [&](FInstanceLike instanceLike) {
1283 auto instance = dyn_cast<InstanceOp>(*instanceLike);
1285 markUnknownFInstanceLikeModulesLive(instanceLike);
1286 return WalkResult::advance();
1289 auto *childModule = symbolTable.lookup(instance.getModuleName());
1290 auto target = dyn_cast<FModuleOp>(childModule);
1292 liveModules.insert(childModule);
1293 return WalkResult::advance();
1297 if (!shouldInline(target)) {
1298 liveModules.insert(target);
1299 return WalkResult::advance();
1302 if (failed(checkInstanceParents(instance)))
1303 return WalkResult::interrupt();
1305 auto toBeFlattened = shouldFlatten(target);
1307 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1311 for (
auto sym : instOpHierPaths[innerRef]) {
1313 nlaMap[sym].flattenModule(target);
1315 nlaMap[sym].inlineModule(target);
1322 DenseMap<Attribute, Attribute> symbolRenames;
1323 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1324 for (
auto sym : rootMap[target.getNameAttr()]) {
1325 auto &mnla = nlaMap[sym];
1326 sym = mnla.reTop(module);
1329 return mic.modNamespace;
1331 instOpHierPaths[InnerRefAttr::get(moduleName, instSym)].push_back(
1332 cast<StringAttr>(sym));
1336 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1340 auto parentActivePaths = activeHierpaths;
1341 setActiveHierPaths(moduleName, instInnerSym);
1343 currentPath.emplace_back(moduleName, instInnerSym);
1347 mic.b.setInsertionPoint(instance);
1348 auto nestedPrefix = (instance.getName() +
"_").str();
1350 InliningLevel childIL(mic, target);
1351 createDebugScope(childIL, instance);
1353 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1354 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1355 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1358 if (toBeFlattened) {
1359 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1360 return WalkResult::interrupt();
1364 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1365 return WalkResult::interrupt();
1367 currentPath.pop_back();
1368 activeHierpaths = parentActivePaths;
1372 return WalkResult::skip();
1375 return failure(module.getBodyBlock()
1376 ->walk<mlir::WalkOrder::PreOrder>(visit)
1380void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1381 Value parentScope) {
1382 auto op = debug::ScopeOp::create(
1383 il.mic.b, instance.getLoc(), instance.getInstanceNameAttr(),
1384 instance.getModuleNameAttr().getAttr(), parentScope);
1385 debugScopes.push_back(op);
1389void Inliner::identifyNLAsTargetingOnlyModules() {
1390 DenseSet<Operation *> nlaTargetedModules;
1393 for (
auto &[sym, mnla] : nlaMap) {
1394 auto nla = mnla.getNLA();
1395 if (nla.isModule()) {
1396 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1398 "NLA ends in module reference but does not target FModuleLike?");
1399 nlaTargetedModules.insert(mod);
1404 auto scanForNLARefs = [&](FModuleLike mod) {
1405 DenseSet<StringAttr> referencedNLASyms;
1407 for (
auto anno : annos)
1408 if (auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1409 referencedNLASyms.insert(sym.getAttr());
1412 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1417 mod.walk([&](Operation *op) {
1418 if (op == mod.getOperation())
1423 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1424 for (
auto portAnnoAttr : op.getPortAnnotations())
1429 return referencedNLASyms;
1433 auto mergeSets = [](
auto &&a,
auto &&b) {
1434 a.insert(b.begin(), b.end());
1435 return std::move(a);
1440 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1441 nlaTargetedModules.end());
1442 auto nonModOnlyNLAs =
1444 mergeSets, scanForNLARefs);
1447 for (
auto &[_, mnla] : nlaMap) {
1448 auto nla = mnla.getNLA();
1449 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1450 mnla.markModuleOnly();
1454Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable,
1456 : circuit(circuit),
context(circuit.getContext()), symbolTable(symbolTable),
1457 instanceGraph(instanceGraph) {}
1459LogicalResult Inliner::run() {
1463 for (
auto nla : circuit.
getBodyBlock()->getOps<
hw::HierPathOp>()) {
1464 auto mnla = MutableNLA(nla, &circuitNamespace);
1465 nlaMap.insert({nla.getSymNameAttr(), mnla});
1466 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1467 for (
auto p : nla.getNamepath())
1468 if (auto ref = dyn_cast<InnerRefAttr>(p))
1469 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1473 identifyNLAsTargetingOnlyModules();
1475 for (
auto &op : circuit.getOps()) {
1477 if (
auto module = dyn_cast<FModuleLike>(op)) {
1478 if (module.canDiscardOnUseEmpty())
1480 liveModules.insert(module);
1485 if (isa<hw::HierPathOp>(op))
1489 auto symbolUses = SymbolTable::getSymbolUses(&op);
1492 for (
const auto &use : *symbolUses) {
1493 if (
auto flat = dyn_cast<FlatSymbolRefAttr>(use.getSymbolRef()))
1494 if (
auto moduleLike = symbolTable.lookup<FModuleLike>(flat.getAttr()))
1495 liveModules.insert(moduleLike);
1503 SmallVector<FModuleOp, 16> modules;
1505 if (
auto module = dyn_cast<FModuleOp>(*node.
getModule()))
1506 modules.push_back(module);
1511 for (
auto moduleOp : modules) {
1513 if (!liveModules.count(moduleOp))
1515 if (shouldFlatten(moduleOp)) {
1516 if (failed(flattenInstances(moduleOp)))
1523 if (failed(inlineInstances(moduleOp)))
1530 for (
auto scopeOp :
llvm::reverse(debugScopes))
1531 if (scopeOp.use_empty())
1533 debugScopes.clear();
1537 for (
auto mod :
llvm::make_early_inc_range(
1539 if (liveModules.count(mod))
1541 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1542 nlaMap[nla].markDead();
1548 for (
auto mod : circuit.
getBodyBlock()->getOps<FModuleLike>()) {
1549 if (shouldInline(mod)) {
1551 "non-public module with inline annotation still present");
1554 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1558 llvm::dbgs() <<
"NLA modifications:\n";
1559 for (
auto nla : circuit.
getBodyBlock()->getOps<
hw::HierPathOp>()) {
1560 auto &mnla = nlaMap[nla.getNameAttr()];
1566 for (
auto &nla : nlaMap)
1567 nla.getSecond().applyUpdates();
1571 for (
auto fmodule : circuit.
getBodyBlock()->getOps<FModuleOp>()) {
1572 SmallVector<Attribute> newAnnotations;
1573 auto processNLAs = [&](
Annotation anno) ->
bool {
1574 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1578 if (!nlaMap.count(sym.getAttr()))
1581 auto mnla = nlaMap[sym.getAttr()];
1593 if (mnla.isLocal()) {
1594 if (mnla.hasRoot(fmodule)) {
1595 anno.removeMember(
"circt.nonlocal");
1596 newAnnotations.push_back(anno.getAttr());
1605 auto newTops = mnla.getAdditionalSymbols();
1606 if (newTops.empty() || mnla.hasRoot(fmodule))
1612 for (
auto rootAndSym : newTops.drop_front()) {
1613 NamedAttrList newAnnotation;
1614 for (
auto pair : anno.getDict()) {
1615 if (pair.getName().getValue() !=
"circt.nonlocal") {
1616 newAnnotation.push_back(pair);
1619 newAnnotation.push_back(
1620 {pair.getName(), FlatSymbolRefAttr::get(rootAndSym.getName())});
1622 newAnnotations.push_back(DictionaryAttr::get(
context, newAnnotation));
1627 fmodule.walk([&](Operation *op) {
1631 if (annotations.empty())
1635 newAnnotations.clear();
1636 annotations.removeAnnotations(processNLAs);
1637 annotations.addAnnotations(newAnnotations);
1638 annotations.applyToOperation(op);
1642 SmallVector<Attribute> newPortAnnotations;
1643 for (
auto port : fmodule.getPorts()) {
1644 newAnnotations.clear();
1645 port.annotations.removeAnnotations(processNLAs);
1646 port.annotations.addAnnotations(newAnnotations);
1647 newPortAnnotations.push_back(
1648 ArrayAttr::get(
context, port.annotations.getArray()));
1650 fmodule->setAttr(
"portAnnotations",
1651 ArrayAttr::get(
context, newPortAnnotations));
1661class InlinerPass :
public circt::firrtl::impl::InlinerBase<InlinerPass> {
1662 void runOnOperation()
override {
1664 Inliner inliner(getOperation(), getAnalysis<SymbolTable>(),
1665 getAnalysis<InstanceGraph>());
1666 if (failed(inliner.run()))
1667 signalPassFailure();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static void dump(DIModule &module, raw_indented_ostream &os)
static AnnotationSet forPort(Operation *op, size_t portNo)
static Location getLoc(DefSlot slot)
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...
static Block * getBodyBlock(FModuleLike mod)
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
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 removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
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.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
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.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
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.
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...
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
The namespace of a CircuitOp, generally inhabited by modules.