CIRCT  19.0.0git
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 
10 #include "circt/Dialect/HW/HWOps.h"
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 
21 namespace circt {
22 namespace hw {
23 #define GEN_PASS_DEF_FLATTENMODULES
24 #include "circt/Dialect/HW/Passes.h.inc"
25 } // namespace hw
26 } // namespace circt
27 
28 using namespace circt;
29 using namespace hw;
30 using namespace igraph;
31 using mlir::InlinerInterface;
32 
33 namespace {
34 struct 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.
42 struct 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 } // namespace
90 
91 void FlattenModulesPass::runOnOperation() {
92  auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
93  DenseSet<Operation *> handled;
94 
95  // Iterate over all instances in the instance graph. This ensures we visit
96  // every module, even private top modules (private and never instantiated).
97  for (auto *startNode : instanceGraph) {
98  if (handled.count(startNode->getModule().getOperation()))
99  continue;
100 
101  // Visit the instance subhierarchy starting at the current module, in a
102  // depth-first manner. This allows us to inline child modules into parents
103  // before we attempt to inline parents into their parents.
104  for (InstanceGraphNode *node : llvm::post_order(startNode)) {
105  if (!handled.insert(node->getModule().getOperation()).second)
106  continue;
107 
108  unsigned numUsesLeft = node->getNumUses();
109  if (numUsesLeft == 0)
110  continue;
111 
112  for (auto *instRecord : node->uses()) {
113  // Only inline private `HWModuleOp`s (no extern or generated modules).
114  auto module =
115  dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
116  if (!module || !module.isPrivate())
117  continue;
118 
119  // Only inline at plain old HW `InstanceOp`s.
120  auto inst = dyn_cast_or_null<InstanceOp>(
121  instRecord->getInstance().getOperation());
122  if (!inst)
123  continue;
124 
125  bool isLastModuleUse = --numUsesLeft == 0;
126 
127  PrefixingInliner inliner(&getContext(), inst.getInstanceName());
128  if (failed(mlir::inlineRegion(inliner, &module.getBody(), inst,
129  inst.getOperands(), inst.getResults(),
130  std::nullopt, !isLastModuleUse))) {
131  inst.emitError("failed to inline '")
132  << module.getModuleName() << "' into instance '"
133  << inst.getInstanceName() << "'";
134  return signalPassFailure();
135  }
136 
137  inst.erase();
138  if (isLastModuleUse)
139  module->erase();
140  }
141  }
142  }
143 }
144 
145 std::unique_ptr<Pass> circt::hw::createFlattenModulesPass() {
146  return std::make_unique<FlattenModulesPass>();
147 }
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.
Definition: FIRRTLFolds.cpp:91
This is a Node in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< mlir::Pass > createFlattenModulesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1