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;
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() {
164 if (inlinedSymbols.all() && newTops.empty() && renames.empty())
170 auto writeBack = [&](StringAttr root, StringAttr sym) -> hw::HierPathOp {
171 SmallVector<Attribute> namepath;
177 if (!inlinedSymbols.test(1)) {
180 namepath.push_back(InnerRefAttr::get(root, lookupRename(root)));
184 for (
signed i = 1, e = inlinedSymbols.size() - 1; i != e; ++i) {
185 if (!inlinedSymbols.test(i + 1)) {
187 lastMod = nla.modPart(i);
192 auto modPart = lastMod ? lastMod : nla.modPart(i);
193 auto refPart = lookupRename(modPart, i);
194 namepath.push_back(InnerRefAttr::get(modPart, refPart));
199 auto modPart = lastMod ? lastMod : nla.modPart(size - 1);
200 auto refPart = lookupRename(modPart, size - 1);
203 namepath.push_back(InnerRefAttr::get(modPart, refPart));
205 namepath.push_back(FlatSymbolRefAttr::get(modPart));
207 auto hp = hw::HierPathOp::create(b, b.getUnknownLoc(), sym,
208 b.getArrayAttr(namepath));
209 hp.setVisibility(nla.getVisibility());
214 assert(!dead || !newTops.empty());
216 last = writeBack(nla.root(), nla.getNameAttr());
217 for (
auto root : newTops)
218 last = writeBack(root.getModule(), root.getName());
225 llvm::errs() <<
" - orig: " << nla <<
"\n"
226 <<
" new: " << *
this <<
"\n"
227 <<
" dead: " << dead <<
"\n"
228 <<
" isDead: " << isDead() <<
"\n"
229 <<
" isModuleOnly: " << isModuleOnly() <<
"\n"
230 <<
" isLocal: " << isLocal() <<
"\n"
231 <<
" inlinedSymbols: [";
232 llvm::interleaveComma(inlinedSymbols.getData(), llvm::errs(), [](
auto a) {
233 llvm::errs() << llvm::formatv(
"{0:x-}", a);
235 llvm::errs() <<
"]\n"
237 for (
auto rename : renames)
238 llvm::errs() <<
" - " << rename.first <<
" -> " << rename.second
245 friend llvm::raw_ostream &
operator<<(llvm::raw_ostream &os, MutableNLA &x) {
246 auto writePathSegment = [&](StringAttr mod, StringAttr sym = {}) {
248 os <<
"#hw.innerNameRef<";
249 os <<
"@" << mod.getValue();
251 os <<
"::@" << sym.getValue() <<
">";
254 auto writeOne = [&](StringAttr root, StringAttr sym) {
255 os <<
"firrtl.nla @" << sym.getValue() <<
" [";
258 bool needsComma =
false;
263 if (!x.inlinedSymbols.test(1)) {
266 writePathSegment(root, x.lookupRename(root));
271 for (
signed i = 1, e = x.inlinedSymbols.size() - 1; i != e; ++i) {
272 if (!x.inlinedSymbols.test(i + 1)) {
274 lastMod = x.nla.modPart(i);
280 auto modPart = lastMod ? lastMod : x.nla.modPart(i);
281 auto refPart = x.nla.refPart(i);
282 if (x.renames.count(modPart))
283 refPart = x.renames[modPart];
284 writePathSegment(modPart, refPart);
292 auto modPart = lastMod ? lastMod : x.nla.modPart(x.size - 1);
293 auto refPart = x.nla.refPart(x.size - 1);
294 if (x.renames.count(modPart))
295 refPart = x.renames[modPart];
296 writePathSegment(modPart, refPart);
300 SmallVector<InnerRefAttr> tops;
302 tops.push_back(InnerRefAttr::get(x.nla.root(), x.nla.getNameAttr()));
303 tops.append(x.newTops.begin(), x.newTops.end());
305 bool multiary = !x.newTops.empty();
308 llvm::interleaveComma(tops, os, [&](InnerRefAttr a) {
309 writeOne(a.getModule(), a.getName());
321 bool isDead() {
return dead && newTops.empty(); }
324 bool isModuleOnly() {
return moduleOnly; }
330 return inlinedSymbols.find_first_in(1, inlinedSymbols.size()) == -1;
334 bool hasRoot(FModuleLike mod) {
335 return (isDead() && nla.root() == mod.getModuleNameAttr()) ||
336 rootSet.contains(mod.getModuleNameAttr());
340 bool hasRoot(StringAttr modName) {
341 return (nla.root() == modName) || rootSet.contains(modName);
345 void inlineModule(FModuleOp module) {
346 auto sym =
module.getNameAttr();
347 assert(sym != nla.root() &&
"unable to inline the root module");
348 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
349 auto idx = symIdx[sym];
350 inlinedSymbols.reset(idx);
353 if (idx == size - 1 && moduleOnly)
360 void flattenModule(FModuleOp module) {
361 auto sym =
module.getNameAttr();
362 assert(symIdx.count(sym) &&
"module is not in the symIdx map");
365 auto moduleIdx = symIdx[sym];
366 inlinedSymbols.reset(moduleIdx, size);
373 StringAttr reTop(FModuleOp module) {
374 StringAttr sym = nla.getSymNameAttr();
375 if (!newTops.empty())
376 sym = StringAttr::get(nla.getContext(),
377 circuitNamespace->
newName(sym.getValue()));
378 newTops.push_back(InnerRefAttr::get(module.getNameAttr(), sym));
379 rootSet.insert(module.getNameAttr());
380 symIdx.insert({
module.getNameAttr(), 0});
385 ArrayRef<InnerRefAttr> getAdditionalSymbols() {
return ArrayRef(newTops); }
387 void setInnerSym(Attribute module, StringAttr innerSym) {
388 assert(symIdx.count(module) &&
"Mutable NLA did not contain symbol");
389 assert(!renames.count(module) &&
"Module already renamed");
390 renames.insert({module, innerSym});
401 InstanceOp instance) {
402 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i) {
403 auto result = instance.getResult(i);
404 auto wire = wires[i];
405 mapper.map(result, wire);
413 StringAttr istName) {
414 mlir::AttrTypeReplacer replacer;
415 replacer.addReplacement([&](hw::InnerRefAttr innerRef) {
416 auto it = map.find(innerRef);
420 return std::pair{hw::InnerRefAttr::get(istName, it->second),
423 llvm::for_each(newOps,
424 [&](
auto *op) { replacer.recursivelyReplaceElementsIn(op); });
432 StringAttr istName) {
433 if (!old || old.empty())
436 bool anyChanged =
false;
438 SmallVector<hw::InnerSymPropertiesAttr> newProps;
439 auto *
context = old.getContext();
440 for (
auto &prop : old) {
441 auto newSym = ns.
newName(prop.getName().strref());
442 if (newSym == prop.getName()) {
443 newProps.push_back(prop);
446 auto newSymStrAttr = StringAttr::get(
context, newSym);
447 auto newProp = hw::InnerSymPropertiesAttr::get(
448 context, newSymStrAttr, prop.getFieldID(), prop.getSymVisibility());
450 newProps.push_back(newProp);
453 auto newSymAttr = anyChanged ? hw::InnerSymAttr::get(
context, newProps) : old;
455 for (
auto [oldProp, newProp] : llvm::zip(old, newSymAttr)) {
456 assert(oldProp.getFieldID() == newProp.getFieldID());
458 map[hw::InnerRefAttr::get(istName, oldProp.getName())] = newProp.getName();
490 Inliner(CircuitOp circuit, SymbolTable &symbolTable);
498 struct ModuleInliningContext {
499 ModuleInliningContext(FModuleOp module)
500 : module(module), modNamespace(module),
b(module.getContext()) {}
512 struct InliningLevel {
513 InliningLevel(ModuleInliningContext &mic, FModuleOp childModule)
514 : mic(mic), childModule(childModule) {}
517 ModuleInliningContext &mic;
521 SmallVector<Operation *> newOps;
523 SmallVector<Value> wires;
525 FModuleOp childModule;
531 mic.module.getNameAttr());
538 bool doesNLAMatchCurrentPath(hw::HierPathOp nla);
542 bool rename(StringRef prefix, Operation *op, InliningLevel &il);
547 bool renameInstance(StringRef prefix, InliningLevel &il, InstanceOp oldInst,
549 const DenseMap<Attribute, Attribute> &symbolRenames);
553 void cloneAndRename(StringRef prefix, InliningLevel &il, IRMapping &mapper,
555 const DenseMap<Attribute, Attribute> &symbolRenames,
556 const DenseSet<Attribute> &localSymbols);
561 void mapPortsToWires(StringRef prefix, InliningLevel &il, IRMapping &mapper,
562 const DenseSet<Attribute> &localSymbols);
565 bool shouldFlatten(Operation *op);
568 bool shouldInline(Operation *op);
572 LogicalResult checkInstanceParents(InstanceOp instance);
578 inliningWalk(OpBuilder &builder, Block *block, IRMapping &mapper,
579 llvm::function_ref<LogicalResult(Operation *op)> process);
584 LogicalResult flattenInto(StringRef prefix, InliningLevel &il,
586 DenseSet<Attribute> localSymbols);
591 LogicalResult inlineInto(StringRef prefix, InliningLevel &il,
593 DenseMap<Attribute, Attribute> &symbolRenames);
596 LogicalResult flattenInstances(FModuleOp module);
599 LogicalResult inlineInstances(FModuleOp module);
603 void createDebugScope(InliningLevel &il, InstanceOp instance,
604 Value parentScope = {});
607 void identifyNLAsTargetingOnlyModules();
613 void markUnknownFInstanceLikeModulesLive(FInstanceLike instanceLike) {
614 for (
auto module : instanceLike.getReferencedModuleNamesAttr()
615 .getAsValueRange<StringAttr>()) {
616 auto *moduleOp = symbolTable.lookup(module);
617 if (liveModules.insert(moduleOp).second) {
618 if (
auto fmodule = dyn_cast<FModuleOp>(moduleOp))
619 worklist.push_back(fmodule);
628 void setActiveHierPaths(StringAttr moduleName, StringAttr instInnerSym) {
630 instOpHierPaths[InnerRefAttr::get(moduleName, instInnerSym)];
631 if (currentPath.empty()) {
632 activeHierpaths.insert(instPaths.begin(), instPaths.end());
635 DenseSet<StringAttr> hPaths(instPaths.begin(), instPaths.end());
638 llvm::set_intersect(activeHierpaths, hPaths);
641 for (
auto hPath : instPaths)
642 if (nlaMap[hPath].hasRoot(moduleName))
643 activeHierpaths.insert(hPath);
647 MLIRContext *context;
650 SymbolTable &symbolTable;
654 DenseSet<Operation *> liveModules;
657 SmallVector<FModuleOp, 16> worklist;
660 DenseMap<Attribute, MutableNLA> nlaMap;
663 DenseMap<Attribute, SmallVector<Attribute>> rootMap;
668 SmallVector<std::pair<Attribute, Attribute>> currentPath;
670 DenseSet<StringAttr> activeHierpaths;
675 DenseMap<InnerRefAttr, SmallVector<StringAttr>> instOpHierPaths;
679 SmallVector<debug::ScopeOp> debugScopes;
686bool Inliner::doesNLAMatchCurrentPath(hw::HierPathOp nla) {
687 return (activeHierpaths.find(nla.getSymNameAttr()) != activeHierpaths.end());
694bool Inliner::rename(StringRef prefix, Operation *op, InliningLevel &il) {
697 auto updateDebugScope = [&](
auto op) {
699 op.getScopeMutable().assign(il.debugScope);
701 if (
auto varOp = dyn_cast<debug::VariableOp>(op))
702 return updateDebugScope(varOp),
false;
703 if (
auto scopeOp = dyn_cast<debug::ScopeOp>(op))
704 return updateDebugScope(scopeOp),
false;
707 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
708 op->setAttr(
"name", StringAttr::get(op->getContext(),
709 (prefix + nameAttr.getValue())));
713 auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op);
716 auto oldSymAttr = symOp.getInnerSymAttr();
719 il.childModule.getNameAttr());
725 if (
auto newSymStrAttr = newSymAttr.getSymName();
726 newSymStrAttr && newSymStrAttr != oldSymAttr.getSymName()) {
728 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
736 auto &mnla = nlaMap[sym.getAttr()];
737 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
739 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newSymStrAttr);
743 symOp.setInnerSymbolAttr(newSymAttr);
745 return newSymAttr != oldSymAttr;
748bool Inliner::renameInstance(
749 StringRef prefix, InliningLevel &il, InstanceOp oldInst, InstanceOp newInst,
750 const DenseMap<Attribute, Attribute> &symbolRenames) {
755 llvm::dbgs() <<
"Discarding parent debug scope for " << oldInst <<
"\n";
760 auto parentActivePaths = activeHierpaths;
761 assert(oldInst->getParentOfType<FModuleOp>() == il.childModule);
763 setActiveHierPaths(oldInst->getParentOfType<FModuleOp>().getNameAttr(),
768 SmallVector<StringAttr> validHierPaths;
769 auto oldParent = oldInst->getParentOfType<FModuleOp>().getNameAttr();
776 auto oldInnerRef = InnerRefAttr::get(oldParent, oldInstSym);
777 for (
auto old : instOpHierPaths[oldInnerRef]) {
781 if (activeHierpaths.find(old) != activeHierpaths.end())
782 validHierPaths.push_back(old);
786 for (
auto additionalSym : nlaMap[old].getAdditionalSymbols())
787 if (activeHierpaths.
find(additionalSym.
getName()) !=
788 activeHierpaths.
end()) {
789 validHierPaths.push_back(old);
798 auto symbolChanged = rename(prefix, newInst, il);
806 auto newInnerRef = InnerRefAttr::get(
807 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
808 instOpHierPaths[newInnerRef] = validHierPaths;
810 for (
auto nla : instOpHierPaths[newInnerRef]) {
811 if (!nlaMap.count(nla))
813 auto &mnla = nlaMap[nla];
814 mnla.setInnerSym(newInnerRef.getModule(), newSymAttr);
819 auto innerRef = InnerRefAttr::get(
820 newInst->getParentOfType<FModuleOp>().getNameAttr(), newSymAttr);
821 SmallVector<StringAttr> &nlaList = instOpHierPaths[innerRef];
823 for (
const auto &
en :
llvm::enumerate(nlaList)) {
824 auto oldNLA =
en.value();
825 if (
auto newSym = symbolRenames.lookup(oldNLA))
826 nlaList[
en.index()] = cast<StringAttr>(newSym);
829 activeHierpaths = std::move(parentActivePaths);
830 return symbolChanged;
838void Inliner::mapPortsToWires(StringRef prefix, InliningLevel &il,
840 const DenseSet<Attribute> &localSymbols) {
841 auto target = il.childModule;
842 auto portInfo = target.getPorts();
843 for (
unsigned i = 0, e = target.getNumPorts(); i < e; ++i) {
844 auto arg = target.getArgument(i);
846 auto type = type_cast<FIRRTLType>(arg.getType());
849 auto oldSymAttr = portInfo[i].sym;
852 il.mic.modNamespace, target.getNameAttr());
854 StringAttr newRootSymName, oldRootSymName;
856 oldRootSymName = oldSymAttr.getSymName();
858 newRootSymName = newSymAttr.getSymName();
860 SmallVector<Attribute> newAnnotations;
863 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
864 auto &mnla = nlaMap[sym.getAttr()];
866 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
870 if (oldRootSymName != newRootSymName)
871 mnla.setInnerSym(il.mic.module.getModuleNameAttr(), newRootSymName);
873 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
874 anno.removeMember(
"circt.nonlocal");
876 newAnnotations.push_back(anno.getAttr());
881 il.mic.b, target.getLoc(), type,
882 StringAttr::get(
context, (prefix + portInfo[i].getName())),
883 NameKindEnumAttr::get(
context, NameKindEnum::DroppableName),
884 ArrayAttr::get(
context, newAnnotations), newSymAttr,
887 il.wires.push_back(wire);
888 mapper.map(arg, wire);
895void Inliner::cloneAndRename(
896 StringRef prefix, InliningLevel &il, IRMapping &mapper, Operation &op,
897 const DenseMap<Attribute, Attribute> &symbolRenames,
898 const DenseSet<Attribute> &localSymbols) {
901 SmallVector<Annotation> newAnnotations;
902 for (
auto anno : oldAnnotations) {
905 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
907 auto &mnla = nlaMap[sym.getAttr()];
909 if (!doesNLAMatchCurrentPath(mnla.getNLA()))
912 if (mnla.isLocal() || localSymbols.count(sym.getAttr()))
913 anno.removeMember(
"circt.nonlocal");
916 newAnnotations.push_back(anno);
920 assert(op.getNumRegions() == 0 &&
921 "operation with regions should not reach cloneAndRename");
922 auto *newOp = il.mic.b.cloneWithoutRegions(op, mapper);
929 if (
auto oldInst = dyn_cast<InstanceOp>(op))
930 renameInstance(prefix, il, oldInst, cast<InstanceOp>(newOp), symbolRenames);
932 rename(prefix, newOp, il);
936 if (!newAnnotations.empty() || !oldAnnotations.empty())
939 il.newOps.push_back(newOp);
942bool Inliner::shouldFlatten(Operation *op) {
946bool Inliner::shouldInline(Operation *op) {
950LogicalResult Inliner::inliningWalk(
951 OpBuilder &builder, Block *block, IRMapping &mapper,
952 llvm::function_ref<LogicalResult(Operation *op)> process) {
954 OpBuilder::InsertPoint target;
955 Block::iterator source;
958 SmallVector<IPs> inliningStack;
962 inliningStack.push_back(IPs{builder.saveInsertionPoint(), block->begin()});
963 OpBuilder::InsertionGuard guard(builder);
965 while (!inliningStack.empty()) {
966 auto target = inliningStack.back().target;
967 builder.restoreInsertionPoint(target);
971 auto &ips = inliningStack.back();
972 source = &*ips.source;
973 auto end = source->getBlock()->end();
974 if (++ips.source == end)
975 inliningStack.pop_back();
979 if (source->getNumRegions() == 0) {
980 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
982 if (failed(process(source)))
984 assert(builder.saveInsertionPoint().getPoint() == target.getPoint());
990 if (!isa<LayerBlockOp, WhenOp, MatchOp>(source))
991 return source->emitError(
"unsupported operation '")
992 << source->getName() <<
"' cannot be inlined";
998 auto *newOp = builder.cloneWithoutRegions(*source, mapper);
999 for (
auto [newRegion, oldRegion] :
llvm::reverse(
1000 llvm::zip_equal(newOp->getRegions(), source->getRegions()))) {
1002 if (oldRegion.empty()) {
1003 assert(newRegion.empty());
1007 assert(oldRegion.hasOneBlock());
1010 auto &oldBlock = oldRegion.getBlocks().front();
1011 auto &newBlock = newRegion.emplaceBlock();
1012 mapper.map(&oldBlock, &newBlock);
1015 for (
auto arg : oldBlock.getArguments())
1016 mapper.map(arg, newBlock.addArgument(arg.getType(), arg.
getLoc()));
1018 if (oldBlock.empty())
1021 inliningStack.push_back(
1022 IPs{OpBuilder::InsertPoint(&newBlock, newBlock.begin()),
1029LogicalResult Inliner::checkInstanceParents(InstanceOp instance) {
1030 auto *parent = instance->getParentOp();
1031 while (!isa<FModuleLike>(parent)) {
1032 if (!isa<LayerBlockOp>(parent))
1033 return instance->emitError(
"cannot inline instance")
1034 .attachNote(parent->getLoc())
1035 <<
"containing operation '" << parent->getName()
1036 <<
"' not safe to inline into";
1037 parent = parent->getParentOp();
1043LogicalResult Inliner::flattenInto(StringRef prefix, InliningLevel &il,
1045 DenseSet<Attribute> localSymbols) {
1046 auto target = il.childModule;
1047 auto moduleName = target.getNameAttr();
1048 DenseMap<Attribute, Attribute> symbolRenames;
1050 LLVM_DEBUG(llvm::dbgs() <<
"flattening " << target.getModuleName() <<
" into "
1051 << il.mic.module.getModuleName() <<
"\n");
1052 auto visit = [&](Operation *op) {
1054 auto instance = dyn_cast<InstanceOp>(op);
1056 if (
auto instanceLike = dyn_cast<FInstanceLike>(op))
1057 markUnknownFInstanceLikeModulesLive(instanceLike);
1058 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1063 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1064 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1066 liveModules.insert(moduleOp);
1068 cloneAndRename(prefix, il, mapper, *op, symbolRenames, localSymbols);
1072 if (failed(checkInstanceParents(instance)))
1078 llvm::set_union(localSymbols, rootMap[childModule.getNameAttr()]);
1080 auto parentActivePaths = activeHierpaths;
1081 setActiveHierPaths(moduleName, instInnerSym);
1082 currentPath.emplace_back(moduleName, instInnerSym);
1084 InliningLevel childIL(il.mic, childModule);
1085 createDebugScope(childIL, instance, il.debugScope);
1088 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1089 mapPortsToWires(nestedPrefix, childIL, mapper, localSymbols);
1093 if (failed(flattenInto(nestedPrefix, childIL, mapper, localSymbols)))
1095 currentPath.pop_back();
1096 activeHierpaths = parentActivePaths;
1099 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1102LogicalResult Inliner::flattenInstances(FModuleOp module) {
1103 auto moduleName =
module.getNameAttr();
1104 ModuleInliningContext mic(module);
1106 auto visit = [&](FInstanceLike instanceLike) {
1107 auto instance = dyn_cast<InstanceOp>(*instanceLike);
1109 markUnknownFInstanceLikeModulesLive(instanceLike);
1110 return WalkResult::advance();
1113 auto *targetModule = symbolTable.lookup(instance.getModuleName());
1114 auto target = dyn_cast<FModuleOp>(targetModule);
1116 liveModules.insert(targetModule);
1117 return WalkResult::advance();
1120 if (failed(checkInstanceParents(instance)))
1121 return WalkResult::interrupt();
1124 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1128 for (
auto targetNLA : instOpHierPaths[innerRef])
1129 nlaMap[targetNLA].flattenModule(target);
1135 DenseSet<Attribute> localSymbols;
1136 llvm::set_union(localSymbols, rootMap[target.getNameAttr()]);
1138 auto parentActivePaths = activeHierpaths;
1139 setActiveHierPaths(moduleName, instInnerSym);
1140 currentPath.emplace_back(moduleName, instInnerSym);
1145 mic.b.setInsertionPoint(instance);
1147 InliningLevel il(mic, target);
1148 createDebugScope(il, instance);
1150 auto nestedPrefix = (instance.getName() +
"_").str();
1151 mapPortsToWires(nestedPrefix, il, mapper, localSymbols);
1152 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1153 instance.getResult(i).replaceAllUsesWith(il.wires[i]);
1156 if (failed(flattenInto(nestedPrefix, il, mapper, localSymbols)))
1157 return WalkResult::interrupt();
1158 currentPath.pop_back();
1159 activeHierpaths = parentActivePaths;
1163 return WalkResult::skip();
1165 return failure(module.getBodyBlock()
1166 ->walk<mlir::WalkOrder::PreOrder>(visit)
1172Inliner::inlineInto(StringRef prefix, InliningLevel &il, IRMapping &mapper,
1173 DenseMap<Attribute, Attribute> &symbolRenames) {
1174 auto target = il.childModule;
1175 auto inlineToParent = il.mic.module;
1176 auto moduleName = target.getNameAttr();
1178 LLVM_DEBUG(llvm::dbgs() <<
"inlining " << target.getModuleName() <<
" into "
1179 << inlineToParent.getModuleName() <<
"\n");
1181 auto visit = [&](Operation *op) {
1183 auto instance = dyn_cast<InstanceOp>(op);
1185 if (
auto instanceLike = dyn_cast<FInstanceLike>(op))
1186 markUnknownFInstanceLikeModulesLive(instanceLike);
1188 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1193 auto *moduleOp = symbolTable.lookup(instance.getModuleName());
1194 auto childModule = dyn_cast<FModuleOp>(moduleOp);
1196 liveModules.insert(moduleOp);
1197 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1202 if (!shouldInline(childModule)) {
1203 if (liveModules.insert(childModule).second) {
1204 worklist.push_back(childModule);
1206 cloneAndRename(prefix, il, mapper, *op, symbolRenames, {});
1210 if (failed(checkInstanceParents(instance)))
1213 auto toBeFlattened = shouldFlatten(childModule);
1215 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1219 for (
auto sym : instOpHierPaths[innerRef]) {
1221 nlaMap[sym].flattenModule(childModule);
1223 nlaMap[sym].inlineModule(childModule);
1234 DenseMap<Attribute, Attribute> symbolRenames;
1235 if (!rootMap[childModule.getNameAttr()].empty()) {
1236 for (
auto sym : rootMap[childModule.getNameAttr()]) {
1237 auto &mnla = nlaMap[sym];
1240 sym = mnla.reTop(inlineToParent);
1243 instSym = StringAttr::get(
1244 context, il.mic.modNamespace.newName(instance.getName()));
1245 instance.setInnerSymAttr(hw::InnerSymAttr::get(instSym));
1247 instOpHierPaths[InnerRefAttr::get(moduleName, instSym)].push_back(
1248 cast<StringAttr>(sym));
1252 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1256 auto parentActivePaths = activeHierpaths;
1257 setActiveHierPaths(moduleName, instInnerSym);
1259 currentPath.emplace_back(moduleName, instInnerSym);
1261 InliningLevel childIL(il.mic, childModule);
1262 createDebugScope(childIL, instance, il.debugScope);
1265 auto nestedPrefix = (prefix + instance.getName() +
"_").str();
1266 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1270 if (toBeFlattened) {
1271 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1274 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1277 currentPath.pop_back();
1278 activeHierpaths = parentActivePaths;
1282 return inliningWalk(il.mic.b, target.getBodyBlock(), mapper, visit);
1285LogicalResult Inliner::inlineInstances(FModuleOp module) {
1288 auto moduleName =
module.getNameAttr();
1289 ModuleInliningContext mic(module);
1291 auto visit = [&](FInstanceLike instanceLike) {
1292 auto instance = dyn_cast<InstanceOp>(*instanceLike);
1294 markUnknownFInstanceLikeModulesLive(instanceLike);
1295 return WalkResult::advance();
1298 auto *childModule = symbolTable.lookup(instance.getModuleName());
1299 auto target = dyn_cast<FModuleOp>(childModule);
1301 liveModules.insert(childModule);
1302 return WalkResult::advance();
1306 if (!shouldInline(target)) {
1307 if (liveModules.insert(target).second) {
1308 worklist.push_back(target);
1310 return WalkResult::advance();
1313 if (failed(checkInstanceParents(instance)))
1314 return WalkResult::interrupt();
1316 auto toBeFlattened = shouldFlatten(target);
1318 auto innerRef = InnerRefAttr::get(moduleName, instSym);
1322 for (
auto sym : instOpHierPaths[innerRef]) {
1324 nlaMap[sym].flattenModule(target);
1326 nlaMap[sym].inlineModule(target);
1333 DenseMap<Attribute, Attribute> symbolRenames;
1334 if (!rootMap[target.getNameAttr()].empty() && !toBeFlattened) {
1335 for (
auto sym : rootMap[target.getNameAttr()]) {
1336 auto &mnla = nlaMap[sym];
1337 sym = mnla.reTop(module);
1340 return mic.modNamespace;
1342 instOpHierPaths[InnerRefAttr::get(moduleName, instSym)].push_back(
1343 cast<StringAttr>(sym));
1347 symbolRenames.insert({mnla.getNLA().getNameAttr(), sym});
1351 auto parentActivePaths = activeHierpaths;
1352 setActiveHierPaths(moduleName, instInnerSym);
1354 currentPath.emplace_back(moduleName, instInnerSym);
1358 mic.b.setInsertionPoint(instance);
1359 auto nestedPrefix = (instance.getName() +
"_").str();
1361 InliningLevel childIL(mic, target);
1362 createDebugScope(childIL, instance);
1364 mapPortsToWires(nestedPrefix, childIL, mapper, {});
1365 for (
unsigned i = 0, e = instance.getNumResults(); i < e; ++i)
1366 instance.getResult(i).replaceAllUsesWith(childIL.wires[i]);
1369 if (toBeFlattened) {
1370 if (failed(flattenInto(nestedPrefix, childIL, mapper, {})))
1371 return WalkResult::interrupt();
1375 if (failed(inlineInto(nestedPrefix, childIL, mapper, symbolRenames)))
1376 return WalkResult::interrupt();
1378 currentPath.pop_back();
1379 activeHierpaths = parentActivePaths;
1383 return WalkResult::skip();
1386 return failure(module.getBodyBlock()
1387 ->walk<mlir::WalkOrder::PreOrder>(visit)
1391void Inliner::createDebugScope(InliningLevel &il, InstanceOp instance,
1392 Value parentScope) {
1393 auto op = debug::ScopeOp::create(
1394 il.mic.b, instance.getLoc(), instance.getInstanceNameAttr(),
1395 instance.getModuleNameAttr().getAttr(), parentScope);
1396 debugScopes.push_back(op);
1400void Inliner::identifyNLAsTargetingOnlyModules() {
1401 DenseSet<Operation *> nlaTargetedModules;
1404 for (
auto &[sym, mnla] : nlaMap) {
1405 auto nla = mnla.getNLA();
1406 if (nla.isModule()) {
1407 auto mod = symbolTable.lookup<FModuleLike>(nla.leafMod());
1409 "NLA ends in module reference but does not target FModuleLike?");
1410 nlaTargetedModules.insert(mod);
1415 auto scanForNLARefs = [&](FModuleLike mod) {
1416 DenseSet<StringAttr> referencedNLASyms;
1418 for (
auto anno : annos)
1419 if (auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1420 referencedNLASyms.insert(sym.getAttr());
1423 for (
unsigned i = 0, e = mod.getNumPorts(); i != e; ++i)
1428 mod.walk([&](Operation *op) {
1429 if (op == mod.getOperation())
1434 TypeSwitch<Operation *>(op).Case<MemOp, InstanceOp>([&](
auto op) {
1435 for (
auto portAnnoAttr : op.getPortAnnotations())
1440 return referencedNLASyms;
1444 auto mergeSets = [](
auto &&
a,
auto &&
b) {
1445 a.insert(
b.begin(),
b.end());
1446 return std::move(a);
1451 SmallVector<FModuleLike, 0> mods(nlaTargetedModules.begin(),
1452 nlaTargetedModules.end());
1453 auto nonModOnlyNLAs =
1455 mergeSets, scanForNLARefs);
1458 for (
auto &[_, mnla] : nlaMap) {
1459 auto nla = mnla.getNLA();
1460 if (nla.isModule() && !nonModOnlyNLAs.count(nla.getSymNameAttr()))
1461 mnla.markModuleOnly();
1465Inliner::Inliner(CircuitOp circuit, SymbolTable &symbolTable)
1466 : circuit(circuit),
context(circuit.getContext()),
1467 symbolTable(symbolTable) {}
1469LogicalResult Inliner::run() {
1473 for (
auto nla : circuit.
getBodyBlock()->getOps<
hw::HierPathOp>()) {
1474 auto mnla = MutableNLA(nla, &circuitNamespace);
1475 nlaMap.insert({nla.getSymNameAttr(), mnla});
1476 rootMap[mnla.getNLA().root()].push_back(nla.getSymNameAttr());
1477 for (
auto p : nla.getNamepath())
1478 if (auto ref = dyn_cast<InnerRefAttr>(p))
1479 instOpHierPaths[ref].push_back(nla.getSymNameAttr());
1483 identifyNLAsTargetingOnlyModules();
1486 for (
auto &op : circuit.getOps()) {
1488 if (
auto module = dyn_cast<FModuleLike>(op)) {
1489 if (module.canDiscardOnUseEmpty())
1491 liveModules.insert(module);
1492 if (isa<FModuleOp>(module))
1493 worklist.push_back(cast<FModuleOp>(module));
1498 if (isa<hw::HierPathOp>(op))
1502 auto symbolUses = SymbolTable::getSymbolUses(&op);
1505 for (
const auto &use : *symbolUses) {
1506 if (
auto flat = dyn_cast<FlatSymbolRefAttr>(use.getSymbolRef()))
1507 if (
auto moduleLike = symbolTable.lookup<FModuleLike>(flat.getAttr()))
1508 if (liveModules.insert(moduleLike).second)
1509 if (
auto module = dyn_cast<FModuleOp>(*moduleLike))
1510 worklist.push_back(module);
1516 while (!worklist.empty()) {
1517 auto moduleOp = worklist.pop_back_val();
1518 if (shouldFlatten(moduleOp)) {
1519 if (failed(flattenInstances(moduleOp)))
1526 if (failed(inlineInstances(moduleOp)))
1533 for (
auto scopeOp :
llvm::reverse(debugScopes))
1534 if (scopeOp.use_empty())
1536 debugScopes.clear();
1540 for (
auto mod :
llvm::make_early_inc_range(
1542 if (liveModules.count(mod))
1544 for (
auto nla : rootMap[mod.getModuleNameAttr()])
1545 nlaMap[nla].markDead();
1551 for (
auto mod : circuit.
getBodyBlock()->getOps<FModuleLike>()) {
1552 if (shouldInline(mod)) {
1554 "non-public module with inline annotation still present");
1557 assert(!shouldFlatten(mod) &&
"flatten annotation found on live module");
1561 llvm::dbgs() <<
"NLA modifications:\n";
1562 for (
auto nla : circuit.
getBodyBlock()->getOps<
hw::HierPathOp>()) {
1563 auto &mnla = nlaMap[nla.getNameAttr()];
1569 for (
auto &nla : nlaMap)
1570 nla.getSecond().applyUpdates();
1574 for (
auto fmodule : circuit.
getBodyBlock()->getOps<FModuleOp>()) {
1575 SmallVector<Attribute> newAnnotations;
1576 auto processNLAs = [&](
Annotation anno) ->
bool {
1577 if (
auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1581 if (!nlaMap.count(sym.getAttr()))
1584 auto mnla = nlaMap[sym.getAttr()];
1594 if (mnla.isLocal()) {
1595 anno.removeMember(
"circt.nonlocal");
1596 newAnnotations.push_back(anno.getAttr());
1604 auto newTops = mnla.getAdditionalSymbols();
1605 if (newTops.empty() || mnla.hasRoot(fmodule))
1611 NamedAttrList newAnnotation;
1612 for (
auto rootAndSym : newTops.drop_front()) {
1613 for (
auto pair : anno.getDict()) {
1614 if (pair.getName().getValue() !=
"circt.nonlocal") {
1615 newAnnotation.push_back(pair);
1618 newAnnotation.push_back(
1619 {pair.getName(), FlatSymbolRefAttr::get(rootAndSym.getName())});
1621 newAnnotations.push_back(DictionaryAttr::get(
context, newAnnotation));
1626 fmodule.walk([&](Operation *op) {
1630 if (annotations.empty())
1634 newAnnotations.clear();
1635 annotations.removeAnnotations(processNLAs);
1636 annotations.addAnnotations(newAnnotations);
1637 annotations.applyToOperation(op);
1641 SmallVector<Attribute> newPortAnnotations;
1642 for (
auto port : fmodule.getPorts()) {
1643 newAnnotations.clear();
1644 port.annotations.removeAnnotations(processNLAs);
1645 port.annotations.addAnnotations(newAnnotations);
1646 newPortAnnotations.push_back(
1647 ArrayAttr::get(
context, port.annotations.getArray()));
1649 fmodule->setAttr(
"portAnnotations",
1650 ArrayAttr::get(
context, newPortAnnotations));
1660class InlinerPass :
public circt::firrtl::impl::InlinerBase<InlinerPass> {
1661 void runOnOperation()
override {
1663 Inliner inliner(getOperation(), getAnalysis<SymbolTable>());
1664 if (failed(inliner.run()))
1665 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 Term * find(Term *x)
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.
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, cmdline_args=sys.argv)
The namespace of a CircuitOp, generally inhabited by modules.