12#include "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/Dialect/UB/IR/UBOps.h"
14#include "mlir/IR/SymbolTable.h"
15#include "mlir/IR/Threading.h"
16#include "mlir/IR/Visitors.h"
17#include "mlir/Interfaces/CallInterfaces.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Transforms/Inliner.h"
20#include "mlir/Transforms/InliningUtils.h"
21#include "llvm/Support/Debug.h"
23#define DEBUG_TYPE "llhd-inline-calls"
27#define GEN_PASS_DEF_INLINECALLSPASS
28#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
41struct FunctionInliner :
public InlinerInterface {
42 using InlinerInterface::InlinerInterface;
44 bool isLegalToInline(Operation *call, Operation *callable,
45 bool wouldBeCloned)
const override {
47 if (!isa<func::FuncOp>(callable))
51 if (!mayHaveSSADominance(*call->getParentRegion()))
56 bool isLegalToInline(Region *dest, Region *src,
bool wouldBeCloned,
57 IRMapping &valueMapping)
const override {
61 bool isLegalToInline(Operation *op, Region *dest,
bool wouldBeCloned,
62 IRMapping &valueMapping)
const override {
66 bool shouldAnalyzeRecursively(Operation *op)
const override {
return false; }
72 void handleTerminator(Operation *op, Block *newDest)
const override {
73 if (isa<mlir::ub::UnreachableOp>(op))
75 InlinerInterface::handleTerminator(op, newDest);
81 :
public llhd::impl::InlineCallsPassBase<InlineCallsPass> {
83 void runOnOperation()
override;
84 LogicalResult runOnRegion(Region ®ion,
const SymbolTable &symbolTable,
85 CallStack &callStack);
89void InlineCallsPass::runOnOperation() {
90 auto &symbolTable = getAnalysis<SymbolTable>();
91 if (failed(failableParallelForEach(
92 &getContext(), getOperation().getOps<hw::HWModuleOp>(),
95 return runOnRegion(module.getBody(), symbolTable, callStack);
100LogicalResult InlineCallsPass::runOnRegion(Region ®ion,
101 const SymbolTable &symbolTable,
102 CallStack &callStack) {
103 FunctionInliner inliner(&getContext());
104 InlinerConfig config;
105 SmallVector<Operation *> callsToErase;
106 SmallVector<std::pair<Operation *, func::FuncOp>> inlineEndMarkers;
112 for (
auto &block : region) {
113 for (
auto &op : block) {
115 while (!inlineEndMarkers.empty() &&
116 inlineEndMarkers.back().first == &op) {
117 assert(inlineEndMarkers.back().second == callStack.back());
118 LLVM_DEBUG(llvm::dbgs()
120 << inlineEndMarkers.back().second.getSymName() <<
"\n");
121 inlineEndMarkers.pop_back();
122 callStack.pop_back();
126 for (
auto &nestedRegion : op.getRegions())
127 if (failed(runOnRegion(nestedRegion, symbolTable, callStack)))
131 auto callOp = dyn_cast<func::CallOp>(op);
136 auto symbol = callOp.getCalleeAttr();
137 auto calledOp = symbolTable.lookup(symbol.getAttr());
138 auto funcOp = dyn_cast<func::FuncOp>(calledOp);
140 auto d = callOp.emitError(
"function call cannot be inlined: call "
141 "target is not a regular function");
142 d.attachNote(calledOp->getLoc()) <<
"call target defined here";
147 if (funcOp.isDeclaration())
152 if (!callStack.insert(funcOp))
153 return callOp.emitError(
"recursive function call cannot be inlined");
154 inlineEndMarkers.push_back({op.getNextNode(), funcOp});
160 LLVM_DEBUG(llvm::dbgs() <<
"- Inlining " << callOp <<
"\n");
161 if (failed(inlineCall(inliner, config.getCloneCallback(), callOp, funcOp,
162 funcOp.getCallableRegion())))
163 return callOp.emitError(
"function call cannot be inlined");
164 callsToErase.push_back(callOp);
170 for (
auto *callOp : callsToErase)
assert(baseType &&"element must be base type")
Signals that an operation's regions are procedural.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.