200 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a basic inlining algorithm that operates bottom up over10// the Strongly Connect Components(SCCs) of the CallGraph. This enables a more11// incremental propagation of inlining decisions from the leafs to the roots of12// the callgraph.13//14//===----------------------------------------------------------------------===//15 16#include "mlir/Transforms/Passes.h"17 18#include "mlir/Analysis/CallGraph.h"19#include "mlir/Pass/PassManager.h"20#include "mlir/Transforms/Inliner.h"21#include "llvm/Support/DebugLog.h"22 23namespace mlir {24#define GEN_PASS_DEF_INLINER25#include "mlir/Transforms/Passes.h.inc"26} // namespace mlir27 28#define DEBUG_TYPE "inliner-pass"29 30using namespace mlir;31 32/// This function implements the inliner optimization pipeline.33static void defaultInlinerOptPipeline(OpPassManager &pm) {34 pm.addPass(createCanonicalizerPass());35}36 37//===----------------------------------------------------------------------===//38// InlinerPass39//===----------------------------------------------------------------------===//40 41namespace {42class InlinerPass : public impl::InlinerBase<InlinerPass> {43public:44 InlinerPass();45 InlinerPass(const InlinerPass &) = default;46 InlinerPass(std::function<void(OpPassManager &)> defaultPipeline);47 InlinerPass(std::function<void(OpPassManager &)> defaultPipeline,48 llvm::StringMap<OpPassManager> opPipelines);49 void runOnOperation() override;50 51 /// A callback provided to the inliner driver to execute52 /// the specified pass pipeline on the given operation53 /// within the context of the current inliner pass,54 /// which is passed as the first argument.55 /// runPipeline API is protected within the Pass class,56 /// so this helper is required to call it from the foreign57 /// inliner driver.58 static LogicalResult runPipelineHelper(Pass &pass, OpPassManager &pipeline,59 Operation *op) {60 return mlir::cast<InlinerPass>(pass).runPipeline(pipeline, op);61 }62 63private:64 /// Attempt to initialize the options of this pass from the given string.65 /// Derived classes may override this method to hook into the point at which66 /// options are initialized, but should generally always invoke this base67 /// class variant.68 LogicalResult initializeOptions(69 StringRef options,70 function_ref<LogicalResult(const Twine &)> errorHandler) override;71 72 /// Inliner configuration parameters created from the pass options.73 InlinerConfig config;74};75} // namespace76 77InlinerPass::InlinerPass() : InlinerPass(defaultInlinerOptPipeline) {}78 79InlinerPass::InlinerPass(80 std::function<void(OpPassManager &)> defaultPipelineArg)81 : InlinerPass(std::move(defaultPipelineArg),82 llvm::StringMap<OpPassManager>{}) {}83 84InlinerPass::InlinerPass(std::function<void(OpPassManager &)> defaultPipeline,85 llvm::StringMap<OpPassManager> opPipelines)86 : config(std::move(defaultPipeline), maxInliningIterations) {87 if (opPipelines.empty())88 return;89 90 // Update the option for the op specific optimization pipelines.91 for (auto &it : opPipelines)92 opPipelineList.addValue(it.second);93 config.setOpPipelines(std::move(opPipelines));94}95 96// Return true if the inlining ratio does not exceed the threshold.97static bool isProfitableToInline(const Inliner::ResolvedCall &resolvedCall,98 unsigned inliningThreshold) {99 // Return early, ratio <= 0U will always be false.100 if (inliningThreshold == 0U)101 return false;102 // Return early, ratio <= -1U will always be true.103 if (inliningThreshold == -1U)104 return true;105 106 Region *callerRegion = resolvedCall.sourceNode->getCallableRegion();107 Region *calleeRegion = resolvedCall.targetNode->getCallableRegion();108 109 assert(calleeRegion && callerRegion && "unexpected external node");110 111 auto countOps = [](Region *region) {112 unsigned count = 0;113 region->walk([&](Operation *) { ++count; });114 return count;115 };116 117 unsigned callerOps = countOps(callerRegion);118 119 // Always inline empty callees (if it is possible at all).120 if (callerOps == 0)121 return true;122 123 unsigned ratio = countOps(calleeRegion) * 100 / callerOps;124 LDBG() << "Callee / caller operation ratio (max: " << inliningThreshold125 << "%): " << ratio << "%";126 return ratio <= inliningThreshold;127}128 129void InlinerPass::runOnOperation() {130 CallGraph &cg = getAnalysis<CallGraph>();131 132 // The inliner should only be run on operations that define a symbol table,133 // as the callgraph will need to resolve references.134 Operation *op = getOperation();135 if (!op->hasTrait<OpTrait::SymbolTable>()) {136 op->emitOpError() << " was scheduled to run under the inliner, but does "137 "not define a symbol table";138 return signalPassFailure();139 }140 141 // By default, assume that any inlining is profitable.142 auto profitabilityCb = [this](const Inliner::ResolvedCall &call) {143 return isProfitableToInline(call, inliningThreshold);144 };145 146 // Get an instance of the inliner.147 Inliner inliner(op, cg, *this, getAnalysisManager(), runPipelineHelper,148 config, profitabilityCb);149 150 // Run the inlining.151 if (failed(inliner.doInlining()))152 signalPassFailure();153}154 155LogicalResult InlinerPass::initializeOptions(156 StringRef options,157 function_ref<LogicalResult(const Twine &)> errorHandler) {158 if (failed(Pass::initializeOptions(options, errorHandler)))159 return failure();160 161 // Initialize the pipeline builder for operations without the dedicated162 // optimization pipeline in opPipelineList to use the option string.163 // TODO: Use a generic pass manager for the pre-inline pipeline, and remove164 // this.165 if (!defaultPipelineStr.empty()) {166 std::string defaultPipelineCopy = defaultPipelineStr;167 config.setDefaultPipeline([=](OpPassManager &pm) {168 (void)parsePassPipeline(defaultPipelineCopy, pm);169 });170 } else if (defaultPipelineStr.getNumOccurrences()) {171 config.setDefaultPipeline(nullptr);172 }173 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));180 181 config.setMaxInliningIterations(maxInliningIterations);182 183 return success();184}185 186std::unique_ptr<Pass> mlir::createInlinerPass() {187 return std::make_unique<InlinerPass>();188}189std::unique_ptr<Pass>190mlir::createInlinerPass(llvm::StringMap<OpPassManager> opPipelines) {191 return std::make_unique<InlinerPass>(defaultInlinerOptPipeline,192 std::move(opPipelines));193}194std::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));199}200