brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · 77a9e6c Raw
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