MLIR  22.0.0git
InlinerPass.cpp
Go to the documentation of this file.
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.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "mlir/Transforms/Passes.h"
17 
19 #include "mlir/Pass/PassManager.h"
21 #include "llvm/Support/DebugLog.h"
22 
23 namespace mlir {
24 #define GEN_PASS_DEF_INLINER
25 #include "mlir/Transforms/Passes.h.inc"
26 } // namespace mlir
27 
28 #define DEBUG_TYPE "inliner-pass"
29 
30 using namespace mlir;
31 
32 /// This function implements the inliner optimization pipeline.
35 }
36 
37 //===----------------------------------------------------------------------===//
38 // InlinerPass
39 //===----------------------------------------------------------------------===//
40 
41 namespace {
42 class InlinerPass : public impl::InlinerBase<InlinerPass> {
43 public:
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 execute
52  /// the specified pass pipeline on the given operation
53  /// 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 foreign
57  /// inliner driver.
58  static LogicalResult runPipelineHelper(Pass &pass, OpPassManager &pipeline,
59  Operation *op) {
60  return mlir::cast<InlinerPass>(pass).runPipeline(pipeline, op);
61  }
62 
63 private:
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 which
66  /// options are initialized, but should generally always invoke this base
67  /// 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.
74 };
75 } // namespace
76 
77 InlinerPass::InlinerPass() : InlinerPass(defaultInlinerOptPipeline) {}
78 
79 InlinerPass::InlinerPass(
80  std::function<void(OpPassManager &)> defaultPipelineArg)
81  : InlinerPass(std::move(defaultPipelineArg),
82  llvm::StringMap<OpPassManager>{}) {}
83 
84 InlinerPass::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.
97 static 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: " << inliningThreshold
125  << "%): " << ratio << "%";
126  return ratio <= inliningThreshold;
127 }
128 
129 void 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 
155 LogicalResult 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 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);
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 
186 std::unique_ptr<Pass> mlir::createInlinerPass() {
187  return std::make_unique<InlinerPass>();
188 }
189 std::unique_ptr<Pass>
190 mlir::createInlinerPass(llvm::StringMap<OpPassManager> opPipelines) {
191  return std::make_unique<InlinerPass>(defaultInlinerOptPipeline,
192  std::move(opPipelines));
193 }
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));
199 }
static void defaultInlinerOptPipeline(OpPassManager &pm)
This function implements the inliner optimization pipeline.
Definition: InlinerPass.cpp:33
static bool isProfitableToInline(const Inliner::ResolvedCall &resolvedCall, unsigned inliningThreshold)
Definition: InlinerPass.cpp:97
static llvm::ManagedStatic< PassManagerOptions > options
Region * getCallableRegion() const
Returns the callable region this node represents.
Definition: CallGraph.cpp:36
This is an implementation of the inliner that operates bottom up over the Strongly Connected Componen...
Definition: Inliner.h:103
This class represents a pass manager that runs passes on either a specific operation type,...
Definition: PassManager.h:46
void addPass(std::unique_ptr< Pass > pass)
Add the given pass to this pass manager.
Definition: Pass.cpp:367
A trait used to provide symbol table functionalities to a region operation.
Definition: SymbolTable.h:452
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:749
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:672
The abstract base pass class.
Definition: Pass.h:51
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
const FrozenRewritePatternSet GreedyRewriteConfig config
std::unique_ptr< Pass > createInlinerPass()
Creates a pass which inlines calls and callable operations as defined by the CallGraph.
std::unique_ptr< Pass > createCanonicalizerPass()
Creates an instance of the Canonicalizer pass, configured with default settings (which can be overrid...
LogicalResult parsePassPipeline(StringRef pipeline, OpPassManager &pm, raw_ostream &errorStream=llvm::errs())
Parse the textual representation of a pass pipeline, adding the result to 'pm' on success.
This struct represents a resolved call to a given callgraph node.
Definition: Inliner.h:109
CallGraphNode * sourceNode
Definition: Inliner.h:114
CallGraphNode * targetNode
Definition: Inliner.h:114