CIRCT  18.0.0git
InlineModules.cpp
Go to the documentation of this file.
1 //===- InlineModules.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 
12 #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 "arc-inline-modules"
21 
22 namespace circt {
23 namespace arc {
24 #define GEN_PASS_DEF_INLINEMODULES
25 #include "circt/Dialect/Arc/ArcPasses.h.inc"
26 } // namespace arc
27 } // namespace circt
28 
29 using namespace circt;
30 using namespace arc;
31 using namespace hw;
32 using namespace igraph;
33 using mlir::InlinerInterface;
34 
35 namespace {
36 struct InlineModulesPass
37  : public arc::impl::InlineModulesBase<InlineModulesPass> {
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.
44 struct 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  ArrayRef<Value> 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 = name.dyn_cast<StringAttr>())
86  name = updateName(nameStr);
87  op->setAttr("names", ArrayAttr::get(namesAttr.getContext(), names));
88  }
89  }
90 };
91 } // namespace
92 
93 void InlineModulesPass::runOnOperation() {
94  auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
95  DenseSet<Operation *> handled;
96 
97  // Iterate over all instances in the instance graph. This ensures we visit
98  // every module, even private top modules (private and never instantiated).
99  for (auto *startNode : instanceGraph) {
100  if (handled.count(startNode->getModule().getOperation()))
101  continue;
102 
103  // Visit the instance subhierarchy starting at the current module, in a
104  // depth-first manner. This allows us to inline child modules into parents
105  // before we attempt to inline parents into their parents.
106  for (InstanceGraphNode *node : llvm::post_order(startNode)) {
107  if (!handled.insert(node->getModule().getOperation()).second)
108  continue;
109 
110  unsigned numUsesLeft = node->getNumUses();
111  if (numUsesLeft == 0)
112  continue;
113 
114  for (auto *instRecord : node->uses()) {
115  // Only inline private `HWModuleOp`s (no extern or generated modules).
116  auto module =
117  dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
118  if (!module || !module.isPrivate())
119  continue;
120 
121  // Only inline at plain old HW `InstanceOp`s.
122  auto inst = dyn_cast_or_null<InstanceOp>(
123  instRecord->getInstance().getOperation());
124  if (!inst)
125  continue;
126 
127  bool isLastModuleUse = --numUsesLeft == 0;
128 
129  PrefixingInliner inliner(&getContext(), inst.getInstanceName());
130  if (failed(mlir::inlineRegion(inliner, &module.getBody(), inst,
131  inst.getOperands(), inst.getResults(),
132  std::nullopt, !isLastModuleUse))) {
133  inst.emitError("failed to inline '")
134  << module.getModuleName() << "' into instance '"
135  << inst.getInstanceName() << "'";
136  return signalPassFailure();
137  }
138 
139  inst.erase();
140  if (isLastModuleUse)
141  module->erase();
142  }
143  }
144  }
145 }
146 
147 std::unique_ptr<Pass> arc::createInlineModulesPass() {
148  return std::make_unique<InlineModulesPass>();
149 }
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 > createInlineModulesPass()
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
Definition: hw.py:1