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