15#include "mlir/IR/AttrTypeSubElements.h"
16#include "mlir/IR/Builders.h"
17#include "mlir/IR/IRMapping.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Transforms/Inliner.h"
20#include "mlir/Transforms/InliningUtils.h"
21#include "llvm/ADT/PostOrderIterator.h"
23#define DEBUG_TYPE "hw-flatten-modules"
27#define GEN_PASS_DEF_FLATTENMODULES
28#include "circt/Dialect/HW/Passes.h.inc"
34using namespace igraph;
35using mlir::InlinerConfig;
36using mlir::InlinerInterface;
38using HierPathTable = DenseMap<hw::InnerRefAttr, SmallVector<hw::HierPathOp>>;
43static const StringRef innerSymAttrName =
45struct FlattenModulesPass
46 :
public circt::hw::impl::FlattenModulesBase<FlattenModulesPass> {
49 void runOnOperation()
override;
60struct PrefixingInliner :
public InlinerInterface {
65 DenseMap<StringAttr, StringAttr> *symMapping;
66 mlir::AttrTypeReplacer *replacer;
68 hw::InnerRefAttr instanceRef;
70 PrefixingInliner(MLIRContext *
context, StringRef prefix,
73 DenseMap<StringAttr, StringAttr> *symMapping,
75 hw::InnerRefAttr instanceRef)
76 : InlinerInterface(
context), prefix(prefix), ns(ns),
77 parentModule(parentModule), sourceModule(sourceModule),
78 symMapping(symMapping), replacer(replacer), pathsTable(pathsTable),
79 instanceRef(instanceRef) {}
81 bool isLegalToInline(Region *dest, Region *src,
bool wouldBeCloned,
82 IRMapping &valueMapping)
const override {
85 bool isLegalToInline(Operation *op, Region *dest,
bool wouldBeCloned,
86 IRMapping &valueMapping)
const override {
89 void handleTerminator(Operation *op,
90 mlir::ValueRange valuesToRepl)
const override {
91 assert(isa<hw::OutputOp>(op));
92 for (
auto [from, to] : llvm::zip(valuesToRepl, op->getOperands()))
93 from.replaceAllUsesWith(to);
96 void processInlinedBlocks(
97 iterator_range<Region::iterator> inlinedBlocks)
override {
98 for (Block &block : inlinedBlocks)
99 block.walk([&](Operation *op) {
101 updateInnerSymbols(op);
108 void updateHierPaths()
const {
114 auto it = pathsTable->find(instanceRef);
115 if (it == pathsTable->end())
119 for (hw::HierPathOp path : it->second) {
120 SmallVector<Attribute, 4> newPath;
121 for (
auto elem : path.getNamepath()) {
123 if (elem != instanceRef)
124 newPath.push_back(replacer->replace(elem));
126 path.setNamepathAttr(ArrayAttr::get(path.getContext(), newPath));
130 StringAttr
updateName(StringAttr attr)
const {
131 if (attr.getValue().empty())
133 return StringAttr::get(attr.getContext(), prefix +
"/" + attr.getValue());
136 void updateNames(Operation *op)
const {
137 if (
auto name = op->getAttrOfType<StringAttr>(
"name"))
139 if (
auto name = op->getAttrOfType<StringAttr>(
"instanceName"))
140 op->setAttr(
"instanceName",
updateName(name));
141 if (
auto namesAttr = op->getAttrOfType<ArrayAttr>(
"names")) {
142 SmallVector<Attribute> names(namesAttr.getValue().begin(),
143 namesAttr.getValue().end());
144 for (
auto &name : names)
145 if (
auto nameStr = dyn_cast<StringAttr>(name))
147 op->setAttr(
"names", ArrayAttr::get(namesAttr.getContext(), names));
151 void updateInnerSymbols(Operation *op)
const {
153 if (
auto innerSymAttr =
154 op->getAttrOfType<hw::InnerSymAttr>(innerSymAttrName)) {
155 StringAttr symName = innerSymAttr.getSymName();
156 auto it = symMapping->find(symName);
157 if (it != symMapping->end())
158 op->setAttr(innerSymAttrName, hw::InnerSymAttr::get(it->second));
162 replacer->replaceElementsIn(op);
165 bool allowSingleBlockOptimization(
166 iterator_range<Region::iterator> inlinedBlocks)
const final {
172bool FlattenModulesPass::shouldInline(
HWModuleOp module,
180 bool isEmpty = bodySize == 1;
181 bool hasNoOutputs =
module.getNumOutputPorts() == 0;
182 bool hasOneUse = instanceNode->
getNumUses() == 1;
183 bool hasState =
false;
184 module.walk([&](Operation *op) {
186 if (isa<seq::FirRegOp, seq::CompRegOp, seq::CompRegClockEnabledOp,
187 seq::ShiftRegOp, seq::FirMemOp, seq::HLMemOp>(op)) {
189 return WalkResult::interrupt();
191 return WalkResult::advance();
195 if (hasState && !this->inlineWithState)
199 return (this->inlineEmpty && isEmpty) ||
200 (this->inlineNoOutputs && hasNoOutputs) ||
201 (this->inlineSingleUse && hasOneUse) ||
202 (this->inlineSmall && bodySize < this->smallThreshold);
205void FlattenModulesPass::runOnOperation() {
206 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
207 DenseSet<Operation *> handled;
209 InlinerConfig config;
212 DenseSet<StringAttr> leafModules;
214 for (
auto path : getOperation().getOps<
hw::HierPathOp>()) {
217 leafModules.insert(path.leafMod());
220 for (
auto name : path.getNamepath()) {
221 if (
auto ref = dyn_cast<hw::InnerRefAttr>(name))
222 pathsTable[ref].push_back(path);
228 DenseMap<HWModuleOp, std::unique_ptr<InnerSymbolNamespace>> nsCache;
232 for (
auto *startNode : instanceGraph) {
233 if (handled.count(startNode->getModule().getOperation()))
240 if (!handled.insert(node->getModule().getOperation()).second)
243 unsigned numUsesLeft = node->getNumUses();
244 if (numUsesLeft == 0)
249 dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
250 if (!module || !module.isPrivate())
254 if (leafModules.count(module.getNameAttr()))
258 auto *body =
module.getBodyBlock();
259 size_t bodySize = std::distance(body->begin(), body->end());
260 if (!shouldInline(module, node, bodySize))
264 DenseMap<StringAttr, StringAttr> inlineModuleInnerSyms;
265 mlir::AttrTypeReplacer innerRefReplacer;
268 module.walk([&](Operation *op) {
269 if (auto innerSymAttr =
270 op->getAttrOfType<hw::InnerSymAttr>(innerSymAttrName))
271 inlineModuleInnerSyms.insert(
272 {innerSymAttr.getSymName(), StringAttr()});
275 for (
auto *instRecord : node->uses()) {
277 auto inst = dyn_cast_or_null<InstanceOp>(
278 instRecord->getInstance().getOperation());
282 bool isLastModuleUse = --numUsesLeft == 0;
288 auto &nsPtr = nsCache[parentModule];
290 nsPtr = std::make_unique<InnerSymbolNamespace>(parentModule);
293 DenseMap<StringAttr, StringAttr> oldToNewInnerSyms;
294 for (
auto [oldSym, _] : inlineModuleInnerSyms)
295 oldToNewInnerSyms.insert(
296 {oldSym, StringAttr::get(&getContext(),
297 nsPtr->newName(oldSym.getValue()))});
300 mlir::AttrTypeReplacer instanceReplacer;
301 instanceReplacer.addReplacement(
302 [&](InnerRefAttr attr) -> std::pair<Attribute, WalkResult> {
303 if (attr.getModule() != module.getModuleNameAttr())
304 return {attr, WalkResult::skip()};
306 auto it = oldToNewInnerSyms.find(attr.getName());
307 if (it == oldToNewInnerSyms.end())
308 return {attr, WalkResult::skip()};
310 auto newAttr = InnerRefAttr::get(parentModule.getModuleNameAttr(),
312 return {newAttr, WalkResult::skip()};
316 hw::InnerRefAttr instanceRef;
317 if (
auto sym = inst.getInnerSymAttr())
318 instanceRef = inst.getInnerRef();
320 PrefixingInliner inliner(&getContext(), inst.getInstanceName(),
321 nsPtr.get(), parentModule, module,
322 &oldToNewInnerSyms, &instanceReplacer,
323 &pathsTable, instanceRef);
325 if (failed(mlir::inlineRegion(inliner, config.getCloneCallback(),
326 &module.getBody(), inst,
327 inst.getOperands(), inst.getResults(),
328 std::nullopt, !isLastModuleUse))) {
329 inst.emitError(
"failed to inline '")
330 <<
module.getModuleName() << "' into instance '"
331 << inst.getInstanceName() << "'";
332 return signalPassFailure();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static void updateName(PatternRewriter &rewriter, Operation *op, StringAttr name)
Set the name of an op based on the best of two names: The current name, and the name passed in.
DenseMap< hw::InnerRefAttr, SmallVector< hw::HierPathOp > > HierPathTable
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This is a Node in the InstanceGraph.
size_t getNumUses()
Get the number of direct instantiations of this module.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.