Loading [MathJax]/jax/output/HTML-CSS/config.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FlattenModules.cpp
Go to the documentation of this file.
1//===- FlattenModules.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13#include "mlir/IR/IRMapping.h"
14#include "mlir/Pass/Pass.h"
15#include "mlir/Transforms/Inliner.h"
16#include "mlir/Transforms/InliningUtils.h"
17#include "llvm/ADT/PostOrderIterator.h"
18#include "llvm/Support/Debug.h"
19
20#define DEBUG_TYPE "hw-flatten-modules"
21
22namespace circt {
23namespace hw {
24#define GEN_PASS_DEF_FLATTENMODULES
25#include "circt/Dialect/HW/Passes.h.inc"
26} // namespace hw
27} // namespace circt
28
29using namespace circt;
30using namespace hw;
31using namespace igraph;
32using mlir::InlinerConfig;
33using mlir::InlinerInterface;
34
35namespace {
36struct FlattenModulesPass
37 : public circt::hw::impl::FlattenModulesBase<FlattenModulesPass> {
38 void runOnOperation() override;
39};
40
41/// A simple implementation of the `InlinerInterface` that marks all inlining as
42/// legal since we know that we only ever attempt to inline `HWModuleOp` bodies
43/// at `InstanceOp` sites.
44struct PrefixingInliner : public InlinerInterface {
45 StringRef prefix;
46 PrefixingInliner(MLIRContext *context, StringRef prefix)
47 : InlinerInterface(context), prefix(prefix) {}
48
49 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
50 IRMapping &valueMapping) const override {
51 return true;
52 }
53 bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
54 IRMapping &valueMapping) const override {
55 return true;
56 }
57 void handleTerminator(Operation *op,
58 mlir::ValueRange valuesToRepl) const override {
59 assert(isa<hw::OutputOp>(op));
60 for (auto [from, to] : llvm::zip(valuesToRepl, op->getOperands()))
61 from.replaceAllUsesWith(to);
62 }
63
64 void processInlinedBlocks(
65 iterator_range<Region::iterator> inlinedBlocks) override {
66 for (Block &block : inlinedBlocks)
67 block.walk([&](Operation *op) { updateNames(op); });
68 }
69
70 StringAttr updateName(StringAttr attr) const {
71 if (attr.getValue().empty())
72 return attr;
73 return StringAttr::get(attr.getContext(), prefix + "/" + attr.getValue());
74 }
75
76 void updateNames(Operation *op) const {
77 if (auto name = op->getAttrOfType<StringAttr>("name"))
78 op->setAttr("name", updateName(name));
79 if (auto name = op->getAttrOfType<StringAttr>("instanceName"))
80 op->setAttr("instanceName", updateName(name));
81 if (auto namesAttr = op->getAttrOfType<ArrayAttr>("names")) {
82 SmallVector<Attribute> names(namesAttr.getValue().begin(),
83 namesAttr.getValue().end());
84 for (auto &name : names)
85 if (auto nameStr = dyn_cast<StringAttr>(name))
86 name = updateName(nameStr);
87 op->setAttr("names", ArrayAttr::get(namesAttr.getContext(), names));
88 }
89 }
90
91 bool allowSingleBlockOptimization(
92 iterator_range<Region::iterator> inlinedBlocks) const final {
93 return true;
94 }
95};
96} // namespace
97
98void FlattenModulesPass::runOnOperation() {
99 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
100 DenseSet<Operation *> handled;
101
102 InlinerConfig config;
103
104 // Iterate over all instances in the instance graph. This ensures we visit
105 // every module, even private top modules (private and never instantiated).
106 for (auto *startNode : instanceGraph) {
107 if (handled.count(startNode->getModule().getOperation()))
108 continue;
109
110 // Visit the instance subhierarchy starting at the current module, in a
111 // depth-first manner. This allows us to inline child modules into parents
112 // before we attempt to inline parents into their parents.
113 for (InstanceGraphNode *node : llvm::post_order(startNode)) {
114 if (!handled.insert(node->getModule().getOperation()).second)
115 continue;
116
117 unsigned numUsesLeft = node->getNumUses();
118 if (numUsesLeft == 0)
119 continue;
120
121 for (auto *instRecord : node->uses()) {
122 // Only inline private `HWModuleOp`s (no extern or generated modules).
123 auto module =
124 dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
125 if (!module || !module.isPrivate())
126 continue;
127
128 // Only inline at plain old HW `InstanceOp`s.
129 auto inst = dyn_cast_or_null<InstanceOp>(
130 instRecord->getInstance().getOperation());
131 if (!inst)
132 continue;
133
134 bool isLastModuleUse = --numUsesLeft == 0;
135
136 PrefixingInliner inliner(&getContext(), inst.getInstanceName());
137 if (failed(mlir::inlineRegion(inliner, config.getCloneCallback(),
138 &module.getBody(), inst,
139 inst.getOperands(), inst.getResults(),
140 std::nullopt, !isLastModuleUse))) {
141 inst.emitError("failed to inline '")
142 << module.getModuleName() << "' into instance '"
143 << inst.getInstanceName() << "'";
144 return signalPassFailure();
145 }
146
147 inst.erase();
148 if (isLastModuleUse)
149 module->erase();
150 }
151 }
152 }
153}
154
155std::unique_ptr<Pass> circt::hw::createFlattenModulesPass() {
156 return std::make_unique<FlattenModulesPass>();
157}
assert(baseType &&"element must be base type")
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.
This is a Node in the InstanceGraph.
std::unique_ptr< mlir::Pass > createFlattenModulesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1