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