1 //===- InlineAdvisor.h - Inlining decision making abstraction -*- C++ ---*-===//
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 #ifndef LLVM_ANALYSIS_INLINEADVISOR_H
10 #define LLVM_ANALYSIS_INLINEADVISOR_H
11 
12 #include "llvm/Analysis/CGSCCPassManager.h"
13 #include "llvm/Analysis/InlineCost.h"
14 #include "llvm/Analysis/LazyCallGraph.h"
15 #include "llvm/Config/llvm-config.h"
16 #include "llvm/IR/PassManager.h"
17 #include <memory>
18 
19 namespace llvm {
20 class BasicBlock;
21 class CallBase;
22 class Function;
23 class Module;
24 class OptimizationRemark;
25 class ImportedFunctionsInliningStatistics;
26 class OptimizationRemarkEmitter;
27 struct ReplayInlinerSettings;
28 
29 /// There are 4 scenarios we can use the InlineAdvisor:
30 /// - Default - use manual heuristics.
31 ///
32 /// - Release mode, the expected mode for production, day to day deployments.
33 /// In this mode, when building the compiler, we also compile a pre-trained ML
34 /// model to native code, and link it as a static library. This mode has low
35 /// overhead and no additional dependencies for the compiler runtime.
36 ///
37 /// - Development mode, for training new models.
38 /// In this mode, we trade off runtime performance for flexibility. This mode
39 /// requires the full C Tensorflow API library, and evaluates models
40 /// dynamically. This mode also permits generating training logs, for offline
41 /// training.
42 ///
43 /// - Dynamically load an advisor via a plugin (PluginInlineAdvisorAnalysis)
44 enum class InliningAdvisorMode : int { Default, Release, Development };
45 
46 // Each entry represents an inline driver.
47 enum class InlinePass : int {
48   AlwaysInliner,
49   CGSCCInliner,
50   EarlyInliner,
51   ModuleInliner,
52   MLInliner,
53   ReplayCGSCCInliner,
54   ReplaySampleProfileInliner,
55   SampleProfileInliner,
56 };
57 
58 /// Provides context on when an inline advisor is constructed in the pipeline
59 /// (e.g., link phase, inline driver).
60 struct InlineContext {
61   ThinOrFullLTOPhase LTOPhase;
62 
63   InlinePass Pass;
64 };
65 
66 std::string AnnotateInlinePassName(InlineContext IC);
67 
68 class InlineAdvisor;
69 /// Capture state between an inlining decision having had been made, and
70 /// its impact being observable. When collecting model training data, this
71 /// allows recording features/decisions/partial reward data sets.
72 ///
73 /// Derivations of this type are expected to be tightly coupled with their
74 /// InliningAdvisors. The base type implements the minimal contractual
75 /// obligations.
76 class InlineAdvice {
77 public:
78   InlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
79                OptimizationRemarkEmitter &ORE, bool IsInliningRecommended);
80 
81   InlineAdvice(InlineAdvice &&) = delete;
82   InlineAdvice(const InlineAdvice &) = delete;
~InlineAdvice()83   virtual ~InlineAdvice() {
84     assert(Recorded && "InlineAdvice should have been informed of the "
85                        "inliner's decision in all cases");
86   }
87 
88   /// Exactly one of the record* APIs must be called. Implementers may extend
89   /// behavior by implementing the corresponding record*Impl.
90   ///
91   /// Call after inlining succeeded, and did not result in deleting the callee.
92   void recordInlining();
93 
94   /// Call after inlining succeeded, and results in the callee being
95   /// delete-able, meaning, it has no more users, and will be cleaned up
96   /// subsequently.
97   void recordInliningWithCalleeDeleted();
98 
99   /// Call after the decision for a call site was to not inline.
recordUnsuccessfulInlining(const InlineResult & Result)100   void recordUnsuccessfulInlining(const InlineResult &Result) {
101     markRecorded();
102     recordUnsuccessfulInliningImpl(Result);
103   }
104 
105   /// Call to indicate inlining was not attempted.
recordUnattemptedInlining()106   void recordUnattemptedInlining() {
107     markRecorded();
108     recordUnattemptedInliningImpl();
109   }
110 
111   /// Get the inlining recommendation.
isInliningRecommended()112   bool isInliningRecommended() const { return IsInliningRecommended; }
getOriginalCallSiteDebugLoc()113   const DebugLoc &getOriginalCallSiteDebugLoc() const { return DLoc; }
getOriginalCallSiteBasicBlock()114   const BasicBlock *getOriginalCallSiteBasicBlock() const { return Block; }
115 
116 protected:
recordInliningImpl()117   virtual void recordInliningImpl() {}
recordInliningWithCalleeDeletedImpl()118   virtual void recordInliningWithCalleeDeletedImpl() {}
recordUnsuccessfulInliningImpl(const InlineResult & Result)119   virtual void recordUnsuccessfulInliningImpl(const InlineResult &Result) {}
recordUnattemptedInliningImpl()120   virtual void recordUnattemptedInliningImpl() {}
121 
122   InlineAdvisor *const Advisor;
123   /// Caller and Callee are pre-inlining.
124   Function *const Caller;
125   Function *const Callee;
126 
127   // Capture the context of CB before inlining, as a successful inlining may
128   // change that context, and we want to report success or failure in the
129   // original context.
130   const DebugLoc DLoc;
131   const BasicBlock *const Block;
132   OptimizationRemarkEmitter &ORE;
133   const bool IsInliningRecommended;
134 
135 private:
markRecorded()136   void markRecorded() {
137     assert(!Recorded && "Recording should happen exactly once");
138     Recorded = true;
139   }
140   void recordInlineStatsIfNeeded();
141 
142   bool Recorded = false;
143 };
144 
145 class DefaultInlineAdvice : public InlineAdvice {
146 public:
147   DefaultInlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
148                       std::optional<InlineCost> OIC,
149                       OptimizationRemarkEmitter &ORE, bool EmitRemarks = true)
150       : InlineAdvice(Advisor, CB, ORE, OIC.has_value()), OriginalCB(&CB),
151         OIC(OIC), EmitRemarks(EmitRemarks) {}
152 
153 private:
154   void recordUnsuccessfulInliningImpl(const InlineResult &Result) override;
155   void recordInliningWithCalleeDeletedImpl() override;
156   void recordInliningImpl() override;
157 
158 private:
159   CallBase *const OriginalCB;
160   std::optional<InlineCost> OIC;
161   bool EmitRemarks;
162 };
163 
164 /// Interface for deciding whether to inline a call site or not.
165 class InlineAdvisor {
166 public:
167   InlineAdvisor(InlineAdvisor &&) = delete;
168   virtual ~InlineAdvisor();
169 
170   /// Get an InlineAdvice containing a recommendation on whether to
171   /// inline or not. \p CB is assumed to be a direct call. \p FAM is assumed to
172   /// be up-to-date wrt previous inlining decisions. \p MandatoryOnly indicates
173   /// only mandatory (always-inline) call sites should be recommended - this
174   /// allows the InlineAdvisor track such inlininings.
175   /// Returns:
176   /// - An InlineAdvice with the inlining recommendation.
177   /// - Null when no recommendation is made (https://reviews.llvm.org/D110658).
178   /// TODO: Consider removing the Null return scenario by incorporating the
179   /// SampleProfile inliner into an InlineAdvisor
180   std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB,
181                                           bool MandatoryOnly = false);
182 
183   /// This must be called when the Inliner pass is entered, to allow the
184   /// InlineAdvisor update internal state, as result of function passes run
185   /// between Inliner pass runs (for the same module).
186   virtual void onPassEntry(LazyCallGraph::SCC *SCC = nullptr) {}
187 
188   /// This must be called when the Inliner pass is exited, as function passes
189   /// may be run subsequently. This allows an implementation of InlineAdvisor
190   /// to prepare for a partial update, based on the optional SCC.
191   virtual void onPassExit(LazyCallGraph::SCC *SCC = nullptr) {}
192 
193   /// Support for printer pass
print(raw_ostream & OS)194   virtual void print(raw_ostream &OS) const {
195     OS << "Unimplemented InlineAdvisor print\n";
196   }
197 
198   /// NOTE pass name is annotated only when inline advisor constructor provides InlineContext.
getAnnotatedInlinePassName()199   const char *getAnnotatedInlinePassName() const {
200     return AnnotatedInlinePassName.c_str();
201   }
202 
203 protected:
204   InlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
205                 std::optional<InlineContext> IC = std::nullopt);
206   virtual std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) = 0;
207   virtual std::unique_ptr<InlineAdvice> getMandatoryAdvice(CallBase &CB,
208                                                            bool Advice);
209 
210   Module &M;
211   FunctionAnalysisManager &FAM;
212   const std::optional<InlineContext> IC;
213   const std::string AnnotatedInlinePassName;
214   std::unique_ptr<ImportedFunctionsInliningStatistics> ImportedFunctionsStats;
215 
216   enum class MandatoryInliningKind { NotMandatory, Always, Never };
217 
218   static MandatoryInliningKind getMandatoryKind(CallBase &CB,
219                                                 FunctionAnalysisManager &FAM,
220                                                 OptimizationRemarkEmitter &ORE);
221 
222   OptimizationRemarkEmitter &getCallerORE(CallBase &CB);
223 
224 private:
225   friend class InlineAdvice;
226 };
227 
228 /// The default (manual heuristics) implementation of the InlineAdvisor. This
229 /// implementation does not need to keep state between inliner pass runs, and is
230 /// reusable as-is for inliner pass test scenarios, as well as for regular use.
231 class DefaultInlineAdvisor : public InlineAdvisor {
232 public:
DefaultInlineAdvisor(Module & M,FunctionAnalysisManager & FAM,InlineParams Params,InlineContext IC)233   DefaultInlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
234                        InlineParams Params, InlineContext IC)
235       : InlineAdvisor(M, FAM, IC), Params(Params) {}
236 
237 private:
238   std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
239 
240   InlineParams Params;
241 };
242 
243 /// Used for dynamically registering InlineAdvisors as plugins
244 ///
245 /// An advisor plugin adds a new advisor at runtime by registering an instance
246 /// of PluginInlineAdvisorAnalysis in the current ModuleAnalysisManager.
247 /// For example, the following code dynamically registers a
248 /// DefaultInlineAdvisor:
249 ///
250 /// namespace {
251 ///
252 /// InlineAdvisor *defaultAdvisorFactory(Module &M, FunctionAnalysisManager
253 /// &FAM,
254 ///                                      InlineParams Params, InlineContext IC)
255 ///                                      {
256 ///   return new DefaultInlineAdvisor(M, FAM, Params, IC);
257 /// }
258 ///
259 /// struct DefaultDynamicAdvisor : PassInfoMixin<DefaultDynamicAdvisor> {
260 ///   PreservedAnalyses run(Module &, ModuleAnalysisManager &MAM) {
261 ///     PluginInlineAdvisorAnalysis PA(defaultAdvisorFactory);
262 ///     MAM.registerPass([&] { return PA; });
263 ///     return PreservedAnalyses::all();
264 ///   }
265 /// };
266 ///
267 /// } // namespace
268 ///
269 /// extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
270 /// llvmGetPassPluginInfo() {
271 ///   return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultAdvisor",
272 ///   LLVM_VERSION_STRING,
273 ///           [](PassBuilder &PB) {
274 ///             PB.registerPipelineStartEPCallback(
275 ///                 [](ModulePassManager &MPM, OptimizationLevel Level) {
276 ///                   MPM.addPass(DefaultDynamicAdvisor());
277 ///                 });
278 ///           }};
279 /// }
280 ///
281 /// A plugin must implement an AdvisorFactory and register it with a
282 /// PluginInlineAdvisorAnlysis to the provided ModuleanAlysisManager.
283 ///
284 /// If such a plugin has been registered
285 /// InlineAdvisorAnalysis::Result::tryCreate will return the dynamically loaded
286 /// advisor.
287 ///
288 class PluginInlineAdvisorAnalysis
289     : public AnalysisInfoMixin<PluginInlineAdvisorAnalysis> {
290 public:
291   static AnalysisKey Key;
292   static bool HasBeenRegistered;
293 
294   typedef InlineAdvisor *(*AdvisorFactory)(Module &M,
295                                            FunctionAnalysisManager &FAM,
296                                            InlineParams Params,
297                                            InlineContext IC);
298 
PluginInlineAdvisorAnalysis(AdvisorFactory Factory)299   PluginInlineAdvisorAnalysis(AdvisorFactory Factory) : Factory(Factory) {
300     HasBeenRegistered = true;
301     assert(Factory != nullptr &&
302            "The plugin advisor factory should not be a null pointer.");
303   }
304 
305   struct Result {
306     AdvisorFactory Factory;
307   };
308 
run(Module & M,ModuleAnalysisManager & MAM)309   Result run(Module &M, ModuleAnalysisManager &MAM) { return {Factory}; }
getResult()310   Result getResult() { return {Factory}; }
311 
312 private:
313   AdvisorFactory Factory;
314 };
315 
316 /// The InlineAdvisorAnalysis is a module pass because the InlineAdvisor
317 /// needs to capture state right before inlining commences over a module.
318 class InlineAdvisorAnalysis : public AnalysisInfoMixin<InlineAdvisorAnalysis> {
319 public:
320   static AnalysisKey Key;
321   InlineAdvisorAnalysis() = default;
322   struct Result {
ResultResult323     Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {}
invalidateResult324     bool invalidate(Module &, const PreservedAnalyses &PA,
325                     ModuleAnalysisManager::Invalidator &) {
326       // Check whether the analysis has been explicitly invalidated. Otherwise,
327       // it's stateless and remains preserved.
328       auto PAC = PA.getChecker<InlineAdvisorAnalysis>();
329       return !PAC.preservedWhenStateless();
330     }
331     bool tryCreate(InlineParams Params, InliningAdvisorMode Mode,
332                    const ReplayInlinerSettings &ReplaySettings,
333                    InlineContext IC);
getAdvisorResult334     InlineAdvisor *getAdvisor() const { return Advisor.get(); }
335 
336   private:
337     Module &M;
338     ModuleAnalysisManager &MAM;
339     std::unique_ptr<InlineAdvisor> Advisor;
340   };
341 
run(Module & M,ModuleAnalysisManager & MAM)342   Result run(Module &M, ModuleAnalysisManager &MAM) { return Result(M, MAM); }
343 };
344 
345 /// Printer pass for the FunctionPropertiesAnalysis results.
346 class InlineAdvisorAnalysisPrinterPass
347     : public PassInfoMixin<InlineAdvisorAnalysisPrinterPass> {
348   raw_ostream &OS;
349 
350 public:
InlineAdvisorAnalysisPrinterPass(raw_ostream & OS)351   explicit InlineAdvisorAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
352 
353   PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
354 
355   PreservedAnalyses run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM,
356                         LazyCallGraph &CG, CGSCCUpdateResult &UR);
357 };
358 
359 std::unique_ptr<InlineAdvisor>
360 getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM);
361 
362 std::unique_ptr<InlineAdvisor>
363 getDevelopmentModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
364                           std::function<bool(CallBase &)> GetDefaultAdvice);
365 
366 // Default (manual policy) decision making helper APIs. Shared with the legacy
367 // pass manager inliner.
368 
369 /// Return the cost only if the inliner should attempt to inline at the given
370 /// CallSite. If we return the cost, we will emit an optimisation remark later
371 /// using that cost, so we won't do so from this function. Return std::nullopt
372 /// if inlining should not be attempted.
373 std::optional<InlineCost>
374 shouldInline(CallBase &CB, function_ref<InlineCost(CallBase &CB)> GetInlineCost,
375              OptimizationRemarkEmitter &ORE, bool EnableDeferral = true);
376 
377 /// Emit ORE message.
378 void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
379                      const BasicBlock *Block, const Function &Callee,
380                      const Function &Caller, bool IsMandatory,
381                      function_ref<void(OptimizationRemark &)> ExtraContext = {},
382                      const char *PassName = nullptr);
383 
384 /// Emit ORE message based in cost (default heuristic).
385 void emitInlinedIntoBasedOnCost(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
386                                 const BasicBlock *Block, const Function &Callee,
387                                 const Function &Caller, const InlineCost &IC,
388                                 bool ForProfileContext = false,
389                                 const char *PassName = nullptr);
390 
391 /// Add location info to ORE message.
392 void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc);
393 
394 /// Set the inline-remark attribute.
395 void setInlineRemark(CallBase &CB, StringRef Message);
396 
397 /// Utility for extracting the inline cost message to a string.
398 std::string inlineCostStr(const InlineCost &IC);
399 } // namespace llvm
400 #endif // LLVM_ANALYSIS_INLINEADVISOR_H
401