CIRCT 22.0.0git
Loading...
Searching...
No Matches
InlineCalls.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/IR/SymbolTable.h"
14#include "mlir/IR/Threading.h"
15#include "mlir/IR/Visitors.h"
16#include "mlir/Interfaces/CallInterfaces.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Transforms/Inliner.h"
19#include "mlir/Transforms/InliningUtils.h"
20#include "llvm/Support/Debug.h"
21
22#define DEBUG_TYPE "llhd-inline-calls"
23
24namespace circt {
25namespace llhd {
26#define GEN_PASS_DEF_INLINECALLSPASS
27#include "circt/Dialect/LLHD/Transforms/LLHDPasses.h.inc"
28} // namespace llhd
29} // namespace circt
30
31using namespace mlir;
32using namespace circt;
33using namespace llhd;
34using llvm::SmallSetVector;
35
36namespace {
37/// Implementation of the `InlinerInterface` that allows calls in SSACFG regions
38/// nested within `llhd.process`, `llhd.final`, and `llhd.combinational` ops to
39/// be inlined.
40struct FunctionInliner : public InlinerInterface {
41 using InlinerInterface::InlinerInterface;
42
43 bool isLegalToInline(Operation *call, Operation *callable,
44 bool wouldBeCloned) const override {
45 // Only inline `func.func` ops.
46 if (!isa<func::FuncOp>(callable))
47 return false;
48
49 // Only inline into SSACFG regions embedded within LLHD processes.
50 if (!mayHaveSSADominance(*call->getParentRegion()))
51 return false;
52 return call->getParentWithTrait<ProceduralRegion>();
53 }
54
55 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
56 IRMapping &valueMapping) const override {
57 return true;
58 }
59
60 bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
61 IRMapping &valueMapping) const override {
62 return true;
63 }
64
65 bool shouldAnalyzeRecursively(Operation *op) const override { return false; }
66};
67
68/// Pass implementation.
69struct InlineCallsPass
70 : public llhd::impl::InlineCallsPassBase<InlineCallsPass> {
71 using CallStack = SmallSetVector<func::FuncOp, 8>;
72 void runOnOperation() override;
73 LogicalResult runOnRegion(Region &region, const SymbolTable &symbolTable,
74 CallStack &callStack);
75};
76} // namespace
77
78void InlineCallsPass::runOnOperation() {
79 auto &symbolTable = getAnalysis<SymbolTable>();
80 if (failed(failableParallelForEach(
81 &getContext(), getOperation().getOps<hw::HWModuleOp>(),
82 [&](auto module) {
83 CallStack callStack;
84 return runOnRegion(module.getBody(), symbolTable, callStack);
85 })))
86 signalPassFailure();
87}
88
89LogicalResult InlineCallsPass::runOnRegion(Region &region,
90 const SymbolTable &symbolTable,
91 CallStack &callStack) {
92 FunctionInliner inliner(&getContext());
93 InlinerConfig config;
94 SmallVector<Operation *> callsToErase;
95 SmallVector<std::pair<Operation *, func::FuncOp>> inlineEndMarkers;
96
97 // Walk all calls in the HW module and inline each. Emit a diagnostic if a
98 // call does not target a `func.func` op or the inliner fails for some reason.
99 // We use a custom version of `Operation::walk` here to ensure that we visit
100 // the inlined operations immediately after visiting the call.
101 for (auto &block : region) {
102 for (auto &op : block) {
103 // If this is an op after a call, pop that call off the call stack.
104 if (!inlineEndMarkers.empty() && inlineEndMarkers.back().first == &op) {
105 assert(inlineEndMarkers.back().second == callStack.back());
106 LLVM_DEBUG(llvm::dbgs()
107 << "- Finished @"
108 << inlineEndMarkers.back().second.getSymName() << "\n");
109 inlineEndMarkers.pop_back();
110 callStack.pop_back();
111 }
112
113 // Handle nested regions.
114 for (auto &nestedRegion : op.getRegions())
115 if (failed(runOnRegion(nestedRegion, symbolTable, callStack)))
116 return failure();
117
118 // We only care about calls.
119 auto callOp = dyn_cast<func::CallOp>(op);
120 if (!callOp)
121 continue;
122
123 // Make sure we're calling a `func.func`.
124 auto symbol = callOp.getCalleeAttr();
125 auto calledOp = symbolTable.lookup(symbol.getAttr());
126 auto funcOp = dyn_cast<func::FuncOp>(calledOp);
127 if (!funcOp) {
128 auto d = callOp.emitError("function call cannot be inlined: call "
129 "target is not a regular function");
130 d.attachNote(calledOp->getLoc()) << "call target defined here";
131 return failure();
132 }
133
134 // Ensure that we are not recursively inlining a function, which would
135 // just expand infinitely in the IR.
136 if (!callStack.insert(funcOp))
137 return callOp.emitError("recursive function call cannot be inlined");
138 inlineEndMarkers.push_back({op.getNextNode(), funcOp});
139
140 // Inline the function body and remember the call for later removal. The
141 // `inlineCall` function will inline the function body *after* the call
142 // op, which allows the loop to immediately visit the inlined ops and
143 // handling nested calls.
144 LLVM_DEBUG(llvm::dbgs() << "- Inlining " << callOp << "\n");
145 if (failed(inlineCall(inliner, config.getCloneCallback(), callOp, funcOp,
146 funcOp.getCallableRegion())))
147 return callOp.emitError("function call cannot be inlined");
148 callsToErase.push_back(callOp);
149 ++numInlined;
150 }
151 }
152
153 // Erase all call ops that were successfully inlined.
154 for (auto *callOp : callsToErase)
155 callOp->erase();
156
157 return success();
158}
assert(baseType &&"element must be base type")
Signals that an operations regions are procedural.
Definition LLHDOps.h:38
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.