[rtsan] Remove mkfifoat interceptor (#116997)
[llvm-project.git] / mlir / lib / Transforms / InlinerPass.cpp
blob04b2f9e419187792713ee604e447fc818c9bb058
1 //===- InlinerPass.cpp - Pass to inline function calls --------------------===//
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 //
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
12 // the callgraph.
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"
22 namespace mlir {
23 #define GEN_PASS_DEF_INLINER
24 #include "mlir/Transforms/Passes.h.inc"
25 } // namespace mlir
27 #define DEBUG_TYPE "inliner-pass"
29 using namespace mlir;
31 /// This function implements the inliner optimization pipeline.
32 static void defaultInlinerOptPipeline(OpPassManager &pm) {
33 pm.addPass(createCanonicalizerPass());
36 //===----------------------------------------------------------------------===//
37 // InlinerPass
38 //===----------------------------------------------------------------------===//
40 namespace {
41 class InlinerPass : public impl::InlinerBase<InlinerPass> {
42 public:
43 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
56 /// inliner driver.
57 static LogicalResult runPipelineHelper(Pass &pass, OpPassManager &pipeline,
58 Operation *op) {
59 return mlir::cast<InlinerPass>(pass).runPipeline(pipeline, op);
62 private:
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
66 /// class variant.
67 LogicalResult initializeOptions(
68 StringRef options,
69 function_ref<LogicalResult(const Twine &)> errorHandler) override;
71 /// Inliner configuration parameters created from the pass options.
72 InlinerConfig config;
74 } // namespace
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())
87 return;
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)
100 return false;
101 // Return early, ratio <= -1U will always be true.
102 if (inliningThreshold == -1U)
103 return true;
105 Region *callerRegion = resolvedCall.sourceNode->getCallableRegion();
106 Region *calleeRegion = resolvedCall.targetNode->getCallableRegion();
108 assert(calleeRegion && callerRegion && "unexpected external node");
110 auto countOps = [](Region *region) {
111 unsigned count = 0;
112 region->walk([&](Operation *) { ++count; });
113 return count;
116 unsigned callerOps = countOps(callerRegion);
118 // Always inline empty callees (if it is possible at all).
119 if (callerOps == 0)
120 return true;
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);
149 // Run the inlining.
150 if (failed(inliner.doInlining()))
151 signalPassFailure();
152 return;
155 LogicalResult InlinerPass::initializeOptions(
156 StringRef options,
157 function_ref<LogicalResult(const Twine &)> errorHandler) {
158 if (failed(Pass::initializeOptions(options, errorHandler)))
159 return failure();
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
164 // this.
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);
183 return success();
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));