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