xref: /llvm-project/mlir/lib/Transforms/InlinerPass.cpp (revision cd9ca423b7400000b4e0199450283439fcc1bbd9)
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