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;
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.
100   void recordUnsuccessfulInlining(const InlineResult &Result) {
101     markRecorded();
102     recordUnsuccessfulInliningImpl(Result);
103   }
104 
105   /// Call to indicate inlining was not attempted.
106   void recordUnattemptedInlining() {
107     markRecorded();
108     recordUnattemptedInliningImpl();
109   }
110 
111   /// Get the inlining recommendation.
112   bool isInliningRecommended() const { return IsInliningRecommended; }
113   const DebugLoc &getOriginalCallSiteDebugLoc() const { return DLoc; }
114   const BasicBlock *getOriginalCallSiteBasicBlock() const { return Block; }
115 
116 protected:
117   virtual void recordInliningImpl() {}
118   virtual void recordInliningWithCalleeDeletedImpl() {}
119   virtual void recordUnsuccessfulInliningImpl(const InlineResult &Result) {}
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:
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
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.
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:
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 
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 
309   Result run(Module &M, ModuleAnalysisManager &MAM) { return {Factory}; }
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 {
323     Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {}
324     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);
334     InlineAdvisor *getAdvisor() const { return Advisor.get(); }
335 
336   private:
337     Module &M;
338     ModuleAnalysisManager &MAM;
339     std::unique_ptr<InlineAdvisor> Advisor;
340   };
341 
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:
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                       std::function<bool(CallBase &)> GetDefaultAdvice);
362 
363 std::unique_ptr<InlineAdvisor>
364 getDevelopmentModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
365                           std::function<bool(CallBase &)> GetDefaultAdvice);
366 
367 // Default (manual policy) decision making helper APIs. Shared with the legacy
368 // pass manager inliner.
369 
370 /// Return the cost only if the inliner should attempt to inline at the given
371 /// CallSite. If we return the cost, we will emit an optimisation remark later
372 /// using that cost, so we won't do so from this function. Return std::nullopt
373 /// if inlining should not be attempted.
374 std::optional<InlineCost>
375 shouldInline(CallBase &CB, function_ref<InlineCost(CallBase &CB)> GetInlineCost,
376              OptimizationRemarkEmitter &ORE, bool EnableDeferral = true);
377 
378 /// Emit ORE message.
379 void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
380                      const BasicBlock *Block, const Function &Callee,
381                      const Function &Caller, bool IsMandatory,
382                      function_ref<void(OptimizationRemark &)> ExtraContext = {},
383                      const char *PassName = nullptr);
384 
385 /// Emit ORE message based in cost (default heuristic).
386 void emitInlinedIntoBasedOnCost(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
387                                 const BasicBlock *Block, const Function &Callee,
388                                 const Function &Caller, const InlineCost &IC,
389                                 bool ForProfileContext = false,
390                                 const char *PassName = nullptr);
391 
392 /// Add location info to ORE message.
393 void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc);
394 
395 /// Set the inline-remark attribute.
396 void setInlineRemark(CallBase &CB, StringRef Message);
397 
398 /// Utility for extracting the inline cost message to a string.
399 std::string inlineCostStr(const InlineCost &IC);
400 } // namespace llvm
401 #endif // LLVM_ANALYSIS_INLINEADVISOR_H
402