1 //===- InlinerPass.cpp - Pass to inline function calls --------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements a basic inlining algorithm that operates bottom up over
10 // the Strongly Connect Components(SCCs) of the CallGraph. This enables a more
11 // incremental propagation of inlining decisions from the leafs to the roots of
14 //===----------------------------------------------------------------------===//
16 #include "mlir/Transforms/Passes.h"
18 #include "mlir/Analysis/CallGraph.h"
19 #include "mlir/Pass/PassManager.h"
20 #include "mlir/Transforms/Inliner.h"
23 #define GEN_PASS_DEF_INLINER
24 #include "mlir/Transforms/Passes.h.inc"
27 #define DEBUG_TYPE "inliner-pass"
31 /// This function implements the inliner optimization pipeline.
32 static void defaultInlinerOptPipeline(OpPassManager
&pm
) {
33 pm
.addPass(createCanonicalizerPass());
36 //===----------------------------------------------------------------------===//
38 //===----------------------------------------------------------------------===//
41 class InlinerPass
: public impl::InlinerBase
<InlinerPass
> {
44 InlinerPass(const InlinerPass
&) = default;
45 InlinerPass(std::function
<void(OpPassManager
&)> defaultPipeline
);
46 InlinerPass(std::function
<void(OpPassManager
&)> defaultPipeline
,
47 llvm::StringMap
<OpPassManager
> opPipelines
);
48 void runOnOperation() override
;
50 /// A callback provided to the inliner driver to execute
51 /// the specified pass pipeline on the given operation
52 /// within the context of the current inliner pass,
53 /// which is passed as the first argument.
54 /// runPipeline API is protected within the Pass class,
55 /// so this helper is required to call it from the foreign
57 static LogicalResult
runPipelineHelper(Pass
&pass
, OpPassManager
&pipeline
,
59 return mlir::cast
<InlinerPass
>(pass
).runPipeline(pipeline
, op
);
63 /// Attempt to initialize the options of this pass from the given string.
64 /// Derived classes may override this method to hook into the point at which
65 /// options are initialized, but should generally always invoke this base
67 LogicalResult
initializeOptions(
69 function_ref
<LogicalResult(const Twine
&)> errorHandler
) override
;
71 /// Inliner configuration parameters created from the pass options.
76 InlinerPass::InlinerPass() : InlinerPass(defaultInlinerOptPipeline
) {}
78 InlinerPass::InlinerPass(
79 std::function
<void(OpPassManager
&)> defaultPipelineArg
)
80 : InlinerPass(std::move(defaultPipelineArg
),
81 llvm::StringMap
<OpPassManager
>{}) {}
83 InlinerPass::InlinerPass(std::function
<void(OpPassManager
&)> defaultPipeline
,
84 llvm::StringMap
<OpPassManager
> opPipelines
)
85 : config(std::move(defaultPipeline
), maxInliningIterations
) {
86 if (opPipelines
.empty())
89 // Update the option for the op specific optimization pipelines.
90 for (auto &it
: opPipelines
)
91 opPipelineList
.addValue(it
.second
);
92 config
.setOpPipelines(std::move(opPipelines
));
95 // Return true if the inlining ratio does not exceed the threshold.
96 static bool isProfitableToInline(const Inliner::ResolvedCall
&resolvedCall
,
97 unsigned inliningThreshold
) {
98 // Return early, ratio <= 0U will always be false.
99 if (inliningThreshold
== 0U)
101 // Return early, ratio <= -1U will always be true.
102 if (inliningThreshold
== -1U)
105 Region
*callerRegion
= resolvedCall
.sourceNode
->getCallableRegion();
106 Region
*calleeRegion
= resolvedCall
.targetNode
->getCallableRegion();
108 assert(calleeRegion
&& callerRegion
&& "unexpected external node");
110 auto countOps
= [](Region
*region
) {
112 region
->walk([&](Operation
*) { ++count
; });
116 unsigned callerOps
= countOps(callerRegion
);
118 // Always inline empty callees (if it is possible at all).
122 unsigned ratio
= countOps(calleeRegion
) * 100 / callerOps
;
123 LLVM_DEBUG(llvm::dbgs() << "Callee / caller operation ratio (max: "
124 << inliningThreshold
<< "%): " << ratio
<< "%\n");
125 return ratio
<= inliningThreshold
;
128 void InlinerPass::runOnOperation() {
129 CallGraph
&cg
= getAnalysis
<CallGraph
>();
131 // The inliner should only be run on operations that define a symbol table,
132 // as the callgraph will need to resolve references.
133 Operation
*op
= getOperation();
134 if (!op
->hasTrait
<OpTrait::SymbolTable
>()) {
135 op
->emitOpError() << " was scheduled to run under the inliner, but does "
136 "not define a symbol table";
137 return signalPassFailure();
140 // By default, assume that any inlining is profitable.
141 auto profitabilityCb
= [=](const Inliner::ResolvedCall
&call
) {
142 return isProfitableToInline(call
, inliningThreshold
);
145 // Get an instance of the inliner.
146 Inliner
inliner(op
, cg
, *this, getAnalysisManager(), runPipelineHelper
,
147 config
, profitabilityCb
);
150 if (failed(inliner
.doInlining()))
155 LogicalResult
InlinerPass::initializeOptions(
157 function_ref
<LogicalResult(const Twine
&)> errorHandler
) {
158 if (failed(Pass::initializeOptions(options
, errorHandler
)))
161 // Initialize the pipeline builder for operations without the dedicated
162 // optimization pipeline in opPipelineList to use the option string.
163 // TODO: Use a generic pass manager for the pre-inline pipeline, and remove
165 if (!defaultPipelineStr
.empty()) {
166 std::string defaultPipelineCopy
= defaultPipelineStr
;
167 config
.setDefaultPipeline([=](OpPassManager
&pm
) {
168 (void)parsePassPipeline(defaultPipelineCopy
, pm
);
170 } else if (defaultPipelineStr
.getNumOccurrences()) {
171 config
.setDefaultPipeline(nullptr);
174 // Initialize the op specific pass pipelines.
175 llvm::StringMap
<OpPassManager
> pipelines
;
176 for (OpPassManager pipeline
: opPipelineList
)
177 if (!pipeline
.empty())
178 pipelines
.try_emplace(pipeline
.getOpAnchorName(), pipeline
);
179 config
.setOpPipelines(std::move(pipelines
));
181 config
.setMaxInliningIterations(maxInliningIterations
);
186 std::unique_ptr
<Pass
> mlir::createInlinerPass() {
187 return std::make_unique
<InlinerPass
>();
189 std::unique_ptr
<Pass
>
190 mlir::createInlinerPass(llvm::StringMap
<OpPassManager
> opPipelines
) {
191 return std::make_unique
<InlinerPass
>(defaultInlinerOptPipeline
,
192 std::move(opPipelines
));
194 std::unique_ptr
<Pass
> mlir::createInlinerPass(
195 llvm::StringMap
<OpPassManager
> opPipelines
,
196 std::function
<void(OpPassManager
&)> defaultPipelineBuilder
) {
197 return std::make_unique
<InlinerPass
>(std::move(defaultPipelineBuilder
),
198 std::move(opPipelines
));