12542d345SSlava Zakharin //===- InlinerPass.cpp - Pass to inline function calls --------------------===// 22542d345SSlava Zakharin // 32542d345SSlava Zakharin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42542d345SSlava Zakharin // See https://llvm.org/LICENSE.txt for license information. 52542d345SSlava Zakharin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 62542d345SSlava Zakharin // 72542d345SSlava Zakharin //===----------------------------------------------------------------------===// 82542d345SSlava Zakharin // 92542d345SSlava Zakharin // This file implements a basic inlining algorithm that operates bottom up over 102542d345SSlava Zakharin // the Strongly Connect Components(SCCs) of the CallGraph. This enables a more 112542d345SSlava Zakharin // incremental propagation of inlining decisions from the leafs to the roots of 122542d345SSlava Zakharin // the callgraph. 132542d345SSlava Zakharin // 142542d345SSlava Zakharin //===----------------------------------------------------------------------===// 152542d345SSlava Zakharin 162542d345SSlava Zakharin #include "mlir/Transforms/Passes.h" 172542d345SSlava Zakharin 182542d345SSlava Zakharin #include "mlir/Analysis/CallGraph.h" 192542d345SSlava Zakharin #include "mlir/Pass/PassManager.h" 202542d345SSlava Zakharin #include "mlir/Transforms/Inliner.h" 212542d345SSlava Zakharin 222542d345SSlava Zakharin namespace mlir { 232542d345SSlava Zakharin #define GEN_PASS_DEF_INLINER 242542d345SSlava Zakharin #include "mlir/Transforms/Passes.h.inc" 252542d345SSlava Zakharin } // namespace mlir 262542d345SSlava Zakharin 27732f5368SSlava Zakharin #define DEBUG_TYPE "inliner-pass" 28732f5368SSlava Zakharin 292542d345SSlava Zakharin using namespace mlir; 302542d345SSlava Zakharin 312542d345SSlava Zakharin /// This function implements the inliner optimization pipeline. 322542d345SSlava Zakharin static void defaultInlinerOptPipeline(OpPassManager &pm) { 332542d345SSlava Zakharin pm.addPass(createCanonicalizerPass()); 342542d345SSlava Zakharin } 352542d345SSlava Zakharin 362542d345SSlava Zakharin //===----------------------------------------------------------------------===// 372542d345SSlava Zakharin // InlinerPass 382542d345SSlava Zakharin //===----------------------------------------------------------------------===// 392542d345SSlava Zakharin 402542d345SSlava Zakharin namespace { 412542d345SSlava Zakharin class InlinerPass : public impl::InlinerBase<InlinerPass> { 422542d345SSlava Zakharin public: 432542d345SSlava Zakharin InlinerPass(); 442542d345SSlava Zakharin InlinerPass(const InlinerPass &) = default; 452542d345SSlava Zakharin InlinerPass(std::function<void(OpPassManager &)> defaultPipeline); 462542d345SSlava Zakharin InlinerPass(std::function<void(OpPassManager &)> defaultPipeline, 472542d345SSlava Zakharin llvm::StringMap<OpPassManager> opPipelines); 482542d345SSlava Zakharin void runOnOperation() override; 492542d345SSlava Zakharin 502542d345SSlava Zakharin /// A callback provided to the inliner driver to execute 512542d345SSlava Zakharin /// the specified pass pipeline on the given operation 522542d345SSlava Zakharin /// within the context of the current inliner pass, 532542d345SSlava Zakharin /// which is passed as the first argument. 542542d345SSlava Zakharin /// runPipeline API is protected within the Pass class, 552542d345SSlava Zakharin /// so this helper is required to call it from the foreign 562542d345SSlava Zakharin /// inliner driver. 572542d345SSlava Zakharin static LogicalResult runPipelineHelper(Pass &pass, OpPassManager &pipeline, 582542d345SSlava Zakharin Operation *op) { 592542d345SSlava Zakharin return mlir::cast<InlinerPass>(pass).runPipeline(pipeline, op); 602542d345SSlava Zakharin } 612542d345SSlava Zakharin 622542d345SSlava Zakharin private: 632542d345SSlava Zakharin /// Attempt to initialize the options of this pass from the given string. 642542d345SSlava Zakharin /// Derived classes may override this method to hook into the point at which 652542d345SSlava Zakharin /// options are initialized, but should generally always invoke this base 662542d345SSlava Zakharin /// class variant. 671079fc4fSIvan Butygin LogicalResult initializeOptions( 681079fc4fSIvan Butygin StringRef options, 691079fc4fSIvan Butygin function_ref<LogicalResult(const Twine &)> errorHandler) override; 702542d345SSlava Zakharin 712542d345SSlava Zakharin /// Inliner configuration parameters created from the pass options. 722542d345SSlava Zakharin InlinerConfig config; 732542d345SSlava Zakharin }; 742542d345SSlava Zakharin } // namespace 752542d345SSlava Zakharin 762542d345SSlava Zakharin InlinerPass::InlinerPass() : InlinerPass(defaultInlinerOptPipeline) {} 772542d345SSlava Zakharin 782542d345SSlava Zakharin InlinerPass::InlinerPass( 792542d345SSlava Zakharin std::function<void(OpPassManager &)> defaultPipelineArg) 802542d345SSlava Zakharin : InlinerPass(std::move(defaultPipelineArg), 812542d345SSlava Zakharin llvm::StringMap<OpPassManager>{}) {} 822542d345SSlava Zakharin 832542d345SSlava Zakharin InlinerPass::InlinerPass(std::function<void(OpPassManager &)> defaultPipeline, 842542d345SSlava Zakharin llvm::StringMap<OpPassManager> opPipelines) 852542d345SSlava Zakharin : config(std::move(defaultPipeline), maxInliningIterations) { 862542d345SSlava Zakharin if (opPipelines.empty()) 872542d345SSlava Zakharin return; 882542d345SSlava Zakharin 892542d345SSlava Zakharin // Update the option for the op specific optimization pipelines. 902542d345SSlava Zakharin for (auto &it : opPipelines) 912542d345SSlava Zakharin opPipelineList.addValue(it.second); 922542d345SSlava Zakharin config.setOpPipelines(std::move(opPipelines)); 932542d345SSlava Zakharin } 942542d345SSlava Zakharin 95732f5368SSlava Zakharin // Return true if the inlining ratio does not exceed the threshold. 96732f5368SSlava Zakharin static bool isProfitableToInline(const Inliner::ResolvedCall &resolvedCall, 97732f5368SSlava Zakharin unsigned inliningThreshold) { 985d187898SFabian Tschopp // Return early, ratio <= 0U will always be false. 995d187898SFabian Tschopp if (inliningThreshold == 0U) 1005d187898SFabian Tschopp return false; 1015d187898SFabian Tschopp // Return early, ratio <= -1U will always be true. 1025d187898SFabian Tschopp if (inliningThreshold == -1U) 1035d187898SFabian Tschopp return true; 1045d187898SFabian Tschopp 105732f5368SSlava Zakharin Region *callerRegion = resolvedCall.sourceNode->getCallableRegion(); 106732f5368SSlava Zakharin Region *calleeRegion = resolvedCall.targetNode->getCallableRegion(); 107732f5368SSlava Zakharin 108*133156c1SSlava Zakharin assert(calleeRegion && callerRegion && "unexpected external node"); 109732f5368SSlava Zakharin 110732f5368SSlava Zakharin auto countOps = [](Region *region) { 111732f5368SSlava Zakharin unsigned count = 0; 112732f5368SSlava Zakharin region->walk([&](Operation *) { ++count; }); 113732f5368SSlava Zakharin return count; 114732f5368SSlava Zakharin }; 115732f5368SSlava Zakharin 116732f5368SSlava Zakharin unsigned callerOps = countOps(callerRegion); 117732f5368SSlava Zakharin 118732f5368SSlava Zakharin // Always inline empty callees (if it is possible at all). 119732f5368SSlava Zakharin if (callerOps == 0) 120732f5368SSlava Zakharin return true; 121732f5368SSlava Zakharin 122732f5368SSlava Zakharin unsigned ratio = countOps(calleeRegion) * 100 / callerOps; 123732f5368SSlava Zakharin LLVM_DEBUG(llvm::dbgs() << "Callee / caller operation ratio (max: " 124732f5368SSlava Zakharin << inliningThreshold << "%): " << ratio << "%\n"); 125732f5368SSlava Zakharin return ratio <= inliningThreshold; 126732f5368SSlava Zakharin } 127732f5368SSlava Zakharin 1282542d345SSlava Zakharin void InlinerPass::runOnOperation() { 1292542d345SSlava Zakharin CallGraph &cg = getAnalysis<CallGraph>(); 1302542d345SSlava Zakharin 1312542d345SSlava Zakharin // The inliner should only be run on operations that define a symbol table, 1322542d345SSlava Zakharin // as the callgraph will need to resolve references. 1332542d345SSlava Zakharin Operation *op = getOperation(); 1342542d345SSlava Zakharin if (!op->hasTrait<OpTrait::SymbolTable>()) { 1352542d345SSlava Zakharin op->emitOpError() << " was scheduled to run under the inliner, but does " 1362542d345SSlava Zakharin "not define a symbol table"; 1372542d345SSlava Zakharin return signalPassFailure(); 1382542d345SSlava Zakharin } 1392542d345SSlava Zakharin 140732f5368SSlava Zakharin // By default, assume that any inlining is profitable. 141732f5368SSlava Zakharin auto profitabilityCb = [=](const Inliner::ResolvedCall &call) { 142732f5368SSlava Zakharin return isProfitableToInline(call, inliningThreshold); 143732f5368SSlava Zakharin }; 144732f5368SSlava Zakharin 1452542d345SSlava Zakharin // Get an instance of the inliner. 1462542d345SSlava Zakharin Inliner inliner(op, cg, *this, getAnalysisManager(), runPipelineHelper, 147732f5368SSlava Zakharin config, profitabilityCb); 1482542d345SSlava Zakharin 1492542d345SSlava Zakharin // Run the inlining. 1502542d345SSlava Zakharin if (failed(inliner.doInlining())) 1512542d345SSlava Zakharin signalPassFailure(); 1522542d345SSlava Zakharin } 1532542d345SSlava Zakharin 1541079fc4fSIvan Butygin LogicalResult InlinerPass::initializeOptions( 1551079fc4fSIvan Butygin StringRef options, 1561079fc4fSIvan Butygin function_ref<LogicalResult(const Twine &)> errorHandler) { 1571079fc4fSIvan Butygin if (failed(Pass::initializeOptions(options, errorHandler))) 1582542d345SSlava Zakharin return failure(); 1592542d345SSlava Zakharin 1602542d345SSlava Zakharin // Initialize the pipeline builder for operations without the dedicated 1612542d345SSlava Zakharin // optimization pipeline in opPipelineList to use the option string. 1622542d345SSlava Zakharin // TODO: Use a generic pass manager for the pre-inline pipeline, and remove 1632542d345SSlava Zakharin // this. 1642542d345SSlava Zakharin if (!defaultPipelineStr.empty()) { 1652542d345SSlava Zakharin std::string defaultPipelineCopy = defaultPipelineStr; 1662542d345SSlava Zakharin config.setDefaultPipeline([=](OpPassManager &pm) { 1672542d345SSlava Zakharin (void)parsePassPipeline(defaultPipelineCopy, pm); 1682542d345SSlava Zakharin }); 1692542d345SSlava Zakharin } else if (defaultPipelineStr.getNumOccurrences()) { 1702542d345SSlava Zakharin config.setDefaultPipeline(nullptr); 1712542d345SSlava Zakharin } 1722542d345SSlava Zakharin 1732542d345SSlava Zakharin // Initialize the op specific pass pipelines. 1742542d345SSlava Zakharin llvm::StringMap<OpPassManager> pipelines; 1752542d345SSlava Zakharin for (OpPassManager pipeline : opPipelineList) 1762542d345SSlava Zakharin if (!pipeline.empty()) 1772542d345SSlava Zakharin pipelines.try_emplace(pipeline.getOpAnchorName(), pipeline); 1782542d345SSlava Zakharin config.setOpPipelines(std::move(pipelines)); 1792542d345SSlava Zakharin 1802542d345SSlava Zakharin config.setMaxInliningIterations(maxInliningIterations); 1812542d345SSlava Zakharin 1822542d345SSlava Zakharin return success(); 1832542d345SSlava Zakharin } 1842542d345SSlava Zakharin 1852542d345SSlava Zakharin std::unique_ptr<Pass> mlir::createInlinerPass() { 1862542d345SSlava Zakharin return std::make_unique<InlinerPass>(); 1872542d345SSlava Zakharin } 1882542d345SSlava Zakharin std::unique_ptr<Pass> 1892542d345SSlava Zakharin mlir::createInlinerPass(llvm::StringMap<OpPassManager> opPipelines) { 1902542d345SSlava Zakharin return std::make_unique<InlinerPass>(defaultInlinerOptPipeline, 1912542d345SSlava Zakharin std::move(opPipelines)); 1922542d345SSlava Zakharin } 1932542d345SSlava Zakharin std::unique_ptr<Pass> mlir::createInlinerPass( 1942542d345SSlava Zakharin llvm::StringMap<OpPassManager> opPipelines, 1952542d345SSlava Zakharin std::function<void(OpPassManager &)> defaultPipelineBuilder) { 1962542d345SSlava Zakharin return std::make_unique<InlinerPass>(std::move(defaultPipelineBuilder), 1972542d345SSlava Zakharin std::move(opPipelines)); 1982542d345SSlava Zakharin } 199