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