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