Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:13

0001 //===- InlineAdvisor.h - Inlining decision making abstraction -*- C++ ---*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 #ifndef LLVM_ANALYSIS_INLINEADVISOR_H
0010 #define LLVM_ANALYSIS_INLINEADVISOR_H
0011 
0012 #include "llvm/Analysis/CGSCCPassManager.h"
0013 #include "llvm/Analysis/InlineCost.h"
0014 #include "llvm/Analysis/LazyCallGraph.h"
0015 #include "llvm/IR/PassManager.h"
0016 #include <memory>
0017 
0018 namespace llvm {
0019 class BasicBlock;
0020 class CallBase;
0021 class Function;
0022 class Module;
0023 class OptimizationRemark;
0024 class ImportedFunctionsInliningStatistics;
0025 class OptimizationRemarkEmitter;
0026 struct ReplayInlinerSettings;
0027 
0028 /// There are 4 scenarios we can use the InlineAdvisor:
0029 /// - Default - use manual heuristics.
0030 ///
0031 /// - Release mode, the expected mode for production, day to day deployments.
0032 /// In this mode, when building the compiler, we also compile a pre-trained ML
0033 /// model to native code, and link it as a static library. This mode has low
0034 /// overhead and no additional dependencies for the compiler runtime.
0035 ///
0036 /// - Development mode, for training new models.
0037 /// In this mode, we trade off runtime performance for flexibility. This mode
0038 /// requires the TFLite library, and evaluates models dynamically. This mode
0039 /// also permits generating training logs, for offline training.
0040 ///
0041 /// - Dynamically load an advisor via a plugin (PluginInlineAdvisorAnalysis)
0042 enum class InliningAdvisorMode : int { Default, Release, Development };
0043 
0044 // Each entry represents an inline driver.
0045 enum class InlinePass : int {
0046   AlwaysInliner,
0047   CGSCCInliner,
0048   EarlyInliner,
0049   ModuleInliner,
0050   MLInliner,
0051   ReplayCGSCCInliner,
0052   ReplaySampleProfileInliner,
0053   SampleProfileInliner,
0054 };
0055 
0056 /// Provides context on when an inline advisor is constructed in the pipeline
0057 /// (e.g., link phase, inline driver).
0058 struct InlineContext {
0059   ThinOrFullLTOPhase LTOPhase;
0060 
0061   InlinePass Pass;
0062 };
0063 
0064 std::string AnnotateInlinePassName(InlineContext IC);
0065 
0066 class InlineAdvisor;
0067 /// Capture state between an inlining decision having had been made, and
0068 /// its impact being observable. When collecting model training data, this
0069 /// allows recording features/decisions/partial reward data sets.
0070 ///
0071 /// Derivations of this type are expected to be tightly coupled with their
0072 /// InliningAdvisors. The base type implements the minimal contractual
0073 /// obligations.
0074 class InlineAdvice {
0075 public:
0076   InlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
0077                OptimizationRemarkEmitter &ORE, bool IsInliningRecommended);
0078 
0079   InlineAdvice(InlineAdvice &&) = delete;
0080   InlineAdvice(const InlineAdvice &) = delete;
0081   virtual ~InlineAdvice() {
0082     assert(Recorded && "InlineAdvice should have been informed of the "
0083                        "inliner's decision in all cases");
0084   }
0085 
0086   /// Exactly one of the record* APIs must be called. Implementers may extend
0087   /// behavior by implementing the corresponding record*Impl.
0088   ///
0089   /// Call after inlining succeeded, and did not result in deleting the callee.
0090   void recordInlining();
0091 
0092   /// Call after inlining succeeded, and results in the callee being
0093   /// delete-able, meaning, it has no more users, and will be cleaned up
0094   /// subsequently.
0095   void recordInliningWithCalleeDeleted();
0096 
0097   /// Call after the decision for a call site was to not inline.
0098   void recordUnsuccessfulInlining(const InlineResult &Result) {
0099     markRecorded();
0100     recordUnsuccessfulInliningImpl(Result);
0101   }
0102 
0103   /// Call to indicate inlining was not attempted.
0104   void recordUnattemptedInlining() {
0105     markRecorded();
0106     recordUnattemptedInliningImpl();
0107   }
0108 
0109   /// Get the inlining recommendation.
0110   bool isInliningRecommended() const { return IsInliningRecommended; }
0111   const DebugLoc &getOriginalCallSiteDebugLoc() const { return DLoc; }
0112   const BasicBlock *getOriginalCallSiteBasicBlock() const { return Block; }
0113 
0114 protected:
0115   virtual void recordInliningImpl() {}
0116   virtual void recordInliningWithCalleeDeletedImpl() {}
0117   virtual void recordUnsuccessfulInliningImpl(const InlineResult &Result) {}
0118   virtual void recordUnattemptedInliningImpl() {}
0119 
0120   InlineAdvisor *const Advisor;
0121   /// Caller and Callee are pre-inlining.
0122   Function *const Caller;
0123   Function *const Callee;
0124 
0125   // Capture the context of CB before inlining, as a successful inlining may
0126   // change that context, and we want to report success or failure in the
0127   // original context.
0128   const DebugLoc DLoc;
0129   const BasicBlock *const Block;
0130   OptimizationRemarkEmitter &ORE;
0131   const bool IsInliningRecommended;
0132 
0133 private:
0134   void markRecorded() {
0135     assert(!Recorded && "Recording should happen exactly once");
0136     Recorded = true;
0137   }
0138   void recordInlineStatsIfNeeded();
0139 
0140   bool Recorded = false;
0141 };
0142 
0143 class DefaultInlineAdvice : public InlineAdvice {
0144 public:
0145   DefaultInlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
0146                       std::optional<InlineCost> OIC,
0147                       OptimizationRemarkEmitter &ORE, bool EmitRemarks = true)
0148       : InlineAdvice(Advisor, CB, ORE, OIC.has_value()), OriginalCB(&CB),
0149         OIC(OIC), EmitRemarks(EmitRemarks) {}
0150 
0151 private:
0152   void recordUnsuccessfulInliningImpl(const InlineResult &Result) override;
0153   void recordInliningWithCalleeDeletedImpl() override;
0154   void recordInliningImpl() override;
0155 
0156 private:
0157   CallBase *const OriginalCB;
0158   std::optional<InlineCost> OIC;
0159   bool EmitRemarks;
0160 };
0161 
0162 /// Interface for deciding whether to inline a call site or not.
0163 class InlineAdvisor {
0164 public:
0165   InlineAdvisor(InlineAdvisor &&) = delete;
0166   virtual ~InlineAdvisor();
0167 
0168   /// Get an InlineAdvice containing a recommendation on whether to
0169   /// inline or not. \p CB is assumed to be a direct call. \p FAM is assumed to
0170   /// be up-to-date wrt previous inlining decisions. \p MandatoryOnly indicates
0171   /// only mandatory (always-inline) call sites should be recommended - this
0172   /// allows the InlineAdvisor track such inlininings.
0173   /// Returns:
0174   /// - An InlineAdvice with the inlining recommendation.
0175   /// - Null when no recommendation is made (https://reviews.llvm.org/D110658).
0176   /// TODO: Consider removing the Null return scenario by incorporating the
0177   /// SampleProfile inliner into an InlineAdvisor
0178   std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB,
0179                                           bool MandatoryOnly = false);
0180 
0181   /// This must be called when the Inliner pass is entered, to allow the
0182   /// InlineAdvisor update internal state, as result of function passes run
0183   /// between Inliner pass runs (for the same module).
0184   virtual void onPassEntry(LazyCallGraph::SCC *SCC = nullptr) {}
0185 
0186   /// This must be called when the Inliner pass is exited, as function passes
0187   /// may be run subsequently. This allows an implementation of InlineAdvisor
0188   /// to prepare for a partial update, based on the optional SCC.
0189   virtual void onPassExit(LazyCallGraph::SCC *SCC = nullptr) {}
0190 
0191   /// Support for printer pass
0192   virtual void print(raw_ostream &OS) const {
0193     OS << "Unimplemented InlineAdvisor print\n";
0194   }
0195 
0196   /// NOTE pass name is annotated only when inline advisor constructor provides InlineContext.
0197   const char *getAnnotatedInlinePassName() const {
0198     return AnnotatedInlinePassName.c_str();
0199   }
0200 
0201 protected:
0202   InlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
0203                 std::optional<InlineContext> IC = std::nullopt);
0204   virtual std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) = 0;
0205   virtual std::unique_ptr<InlineAdvice> getMandatoryAdvice(CallBase &CB,
0206                                                            bool Advice);
0207 
0208   Module &M;
0209   FunctionAnalysisManager &FAM;
0210   const std::optional<InlineContext> IC;
0211   const std::string AnnotatedInlinePassName;
0212   std::unique_ptr<ImportedFunctionsInliningStatistics> ImportedFunctionsStats;
0213 
0214   enum class MandatoryInliningKind { NotMandatory, Always, Never };
0215 
0216   static MandatoryInliningKind getMandatoryKind(CallBase &CB,
0217                                                 FunctionAnalysisManager &FAM,
0218                                                 OptimizationRemarkEmitter &ORE);
0219 
0220   OptimizationRemarkEmitter &getCallerORE(CallBase &CB);
0221 
0222 private:
0223   friend class InlineAdvice;
0224 };
0225 
0226 /// The default (manual heuristics) implementation of the InlineAdvisor. This
0227 /// implementation does not need to keep state between inliner pass runs, and is
0228 /// reusable as-is for inliner pass test scenarios, as well as for regular use.
0229 class DefaultInlineAdvisor : public InlineAdvisor {
0230 public:
0231   DefaultInlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
0232                        InlineParams Params, InlineContext IC)
0233       : InlineAdvisor(M, FAM, IC), Params(Params) {}
0234 
0235 private:
0236   std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
0237 
0238   InlineParams Params;
0239 };
0240 
0241 /// Used for dynamically registering InlineAdvisors as plugins
0242 ///
0243 /// An advisor plugin adds a new advisor at runtime by registering an instance
0244 /// of PluginInlineAdvisorAnalysis in the current ModuleAnalysisManager.
0245 /// For example, the following code dynamically registers a
0246 /// DefaultInlineAdvisor:
0247 ///
0248 /// namespace {
0249 ///
0250 /// InlineAdvisor *defaultAdvisorFactory(Module &M,
0251 ///                                      FunctionAnalysisManager &FAM,
0252 ///                                      InlineParams Params,
0253 ///                                      InlineContext IC) {
0254 ///   return new DefaultInlineAdvisor(M, FAM, Params, IC);
0255 /// }
0256 ///
0257 /// } // namespace
0258 ///
0259 /// extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
0260 /// llvmGetPassPluginInfo() {
0261 ///   return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultAdvisor",
0262 ///           LLVM_VERSION_STRING,
0263 ///           [](PassBuilder &PB) {
0264 ///             PB.registerAnalysisRegistrationCallback(
0265 ///                 [](ModuleAnalysisManager &MAM) {
0266 ///                   PluginInlineAdvisorAnalysis PA(defaultAdvisorFactory);
0267 ///                   MAM.registerPass([&] { return PA; });
0268 ///                 });
0269 ///           }};
0270 /// }
0271 ///
0272 /// A plugin must implement an AdvisorFactory and register it with a
0273 /// PluginInlineAdvisorAnlysis to the provided ModuleAnalysisManager.
0274 ///
0275 /// If such a plugin has been registered
0276 /// InlineAdvisorAnalysis::Result::tryCreate will return the dynamically loaded
0277 /// advisor.
0278 ///
0279 class PluginInlineAdvisorAnalysis
0280     : public AnalysisInfoMixin<PluginInlineAdvisorAnalysis> {
0281 public:
0282   static AnalysisKey Key;
0283 
0284   typedef InlineAdvisor *(*AdvisorFactory)(Module &M,
0285                                            FunctionAnalysisManager &FAM,
0286                                            InlineParams Params,
0287                                            InlineContext IC);
0288 
0289   PluginInlineAdvisorAnalysis(AdvisorFactory Factory) : Factory(Factory) {
0290     assert(Factory != nullptr &&
0291            "The plugin advisor factory should not be a null pointer.");
0292   }
0293 
0294   struct Result {
0295     AdvisorFactory Factory;
0296   };
0297 
0298   Result run(Module &M, ModuleAnalysisManager &MAM) { return {Factory}; }
0299   Result getResult() { return {Factory}; }
0300 
0301 private:
0302   AdvisorFactory Factory;
0303 };
0304 
0305 /// The InlineAdvisorAnalysis is a module pass because the InlineAdvisor
0306 /// needs to capture state right before inlining commences over a module.
0307 class InlineAdvisorAnalysis : public AnalysisInfoMixin<InlineAdvisorAnalysis> {
0308 public:
0309   static AnalysisKey Key;
0310   InlineAdvisorAnalysis() = default;
0311   struct Result {
0312     Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {}
0313     bool invalidate(Module &, const PreservedAnalyses &PA,
0314                     ModuleAnalysisManager::Invalidator &) {
0315       // Check whether the analysis has been explicitly invalidated. Otherwise,
0316       // it's stateless and remains preserved.
0317       auto PAC = PA.getChecker<InlineAdvisorAnalysis>();
0318       return !PAC.preservedWhenStateless();
0319     }
0320     bool tryCreate(InlineParams Params, InliningAdvisorMode Mode,
0321                    const ReplayInlinerSettings &ReplaySettings,
0322                    InlineContext IC);
0323     InlineAdvisor *getAdvisor() const { return Advisor.get(); }
0324 
0325   private:
0326     Module &M;
0327     ModuleAnalysisManager &MAM;
0328     std::unique_ptr<InlineAdvisor> Advisor;
0329   };
0330 
0331   Result run(Module &M, ModuleAnalysisManager &MAM) { return Result(M, MAM); }
0332 };
0333 
0334 /// Printer pass for the InlineAdvisorAnalysis results.
0335 class InlineAdvisorAnalysisPrinterPass
0336     : public PassInfoMixin<InlineAdvisorAnalysisPrinterPass> {
0337   raw_ostream &OS;
0338 
0339 public:
0340   explicit InlineAdvisorAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
0341 
0342   PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
0343 
0344   PreservedAnalyses run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM,
0345                         LazyCallGraph &CG, CGSCCUpdateResult &UR);
0346   static bool isRequired() { return true; }
0347 };
0348 
0349 std::unique_ptr<InlineAdvisor>
0350 getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
0351                       std::function<bool(CallBase &)> GetDefaultAdvice);
0352 
0353 std::unique_ptr<InlineAdvisor>
0354 getDevelopmentModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
0355                           std::function<bool(CallBase &)> GetDefaultAdvice);
0356 
0357 // Default (manual policy) decision making helper APIs. Shared with the legacy
0358 // pass manager inliner.
0359 
0360 /// Return the cost only if the inliner should attempt to inline at the given
0361 /// CallSite. If we return the cost, we will emit an optimisation remark later
0362 /// using that cost, so we won't do so from this function. Return std::nullopt
0363 /// if inlining should not be attempted.
0364 std::optional<InlineCost>
0365 shouldInline(CallBase &CB, TargetTransformInfo &CalleeTTI,
0366              function_ref<InlineCost(CallBase &CB)> GetInlineCost,
0367              OptimizationRemarkEmitter &ORE, bool EnableDeferral = true);
0368 
0369 /// Emit ORE message.
0370 void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
0371                      const BasicBlock *Block, const Function &Callee,
0372                      const Function &Caller, bool IsMandatory,
0373                      function_ref<void(OptimizationRemark &)> ExtraContext = {},
0374                      const char *PassName = nullptr);
0375 
0376 /// Emit ORE message based in cost (default heuristic).
0377 void emitInlinedIntoBasedOnCost(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
0378                                 const BasicBlock *Block, const Function &Callee,
0379                                 const Function &Caller, const InlineCost &IC,
0380                                 bool ForProfileContext = false,
0381                                 const char *PassName = nullptr);
0382 
0383 /// Add location info to ORE message.
0384 void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc);
0385 
0386 /// Set the inline-remark attribute.
0387 void setInlineRemark(CallBase &CB, StringRef Message);
0388 
0389 /// Utility for extracting the inline cost message to a string.
0390 std::string inlineCostStr(const InlineCost &IC);
0391 } // namespace llvm
0392 #endif // LLVM_ANALYSIS_INLINEADVISOR_H