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_INLINEADVISOR_H_ 10 #define LLVM_INLINEADVISOR_H_ 11 12 #include <memory> 13 #include <unordered_set> 14 #include <vector> 15 16 #include "llvm/Analysis/InlineCost.h" 17 #include "llvm/Config/llvm-config.h" 18 #include "llvm/IR/PassManager.h" 19 20 namespace llvm { 21 class BasicBlock; 22 class CallBase; 23 class Function; 24 class Module; 25 class OptimizationRemarkEmitter; 26 27 /// There are 4 scenarios we can use the InlineAdvisor: 28 /// - Default - use manual heuristics. 29 /// 30 /// - MandatoryOnly - only mandatory inlinings (i.e. AlwaysInline). 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 enum class InliningAdvisorMode : int { 43 Default, 44 MandatoryOnly, 45 Release, 46 Development 47 }; 48 49 class InlineAdvisor; 50 /// Capture state between an inlining decision having had been made, and 51 /// its impact being observable. When collecting model training data, this 52 /// allows recording features/decisions/partial reward data sets. 53 /// 54 /// Derivations of this type are expected to be tightly coupled with their 55 /// InliningAdvisors. The base type implements the minimal contractual 56 /// obligations. 57 class InlineAdvice { 58 public: 59 InlineAdvice(InlineAdvisor *Advisor, CallBase &CB, 60 OptimizationRemarkEmitter &ORE, bool IsInliningRecommended); 61 62 InlineAdvice(InlineAdvice &&) = delete; 63 InlineAdvice(const InlineAdvice &) = delete; ~InlineAdvice()64 virtual ~InlineAdvice() { 65 assert(Recorded && "InlineAdvice should have been informed of the " 66 "inliner's decision in all cases"); 67 } 68 69 /// Exactly one of the record* APIs must be called. Implementers may extend 70 /// behavior by implementing the corresponding record*Impl. 71 /// 72 /// Call after inlining succeeded, and did not result in deleting the callee. recordInlining()73 void recordInlining() { 74 markRecorded(); 75 recordInliningImpl(); 76 } 77 78 /// Call after inlining succeeded, and resulted in deleting the callee. 79 void recordInliningWithCalleeDeleted(); 80 81 /// Call after the decision for a call site was to not inline. recordUnsuccessfulInlining(const InlineResult & Result)82 void recordUnsuccessfulInlining(const InlineResult &Result) { 83 markRecorded(); 84 recordUnsuccessfulInliningImpl(Result); 85 } 86 87 /// Call to indicate inlining was not attempted. recordUnattemptedInlining()88 void recordUnattemptedInlining() { 89 markRecorded(); 90 recordUnattemptedInliningImpl(); 91 } 92 93 /// Get the inlining recommendation. isInliningRecommended()94 bool isInliningRecommended() const { return IsInliningRecommended; } getOriginalCallSiteDebugLoc()95 const DebugLoc &getOriginalCallSiteDebugLoc() const { return DLoc; } getOriginalCallSiteBasicBlock()96 const BasicBlock *getOriginalCallSiteBasicBlock() const { return Block; } 97 98 protected: recordInliningImpl()99 virtual void recordInliningImpl() {} recordInliningWithCalleeDeletedImpl()100 virtual void recordInliningWithCalleeDeletedImpl() {} recordUnsuccessfulInliningImpl(const InlineResult & Result)101 virtual void recordUnsuccessfulInliningImpl(const InlineResult &Result) {} recordUnattemptedInliningImpl()102 virtual void recordUnattemptedInliningImpl() {} 103 104 InlineAdvisor *const Advisor; 105 /// Caller and Callee are pre-inlining. 106 Function *const Caller; 107 Function *const Callee; 108 109 // Capture the context of CB before inlining, as a successful inlining may 110 // change that context, and we want to report success or failure in the 111 // original context. 112 const DebugLoc DLoc; 113 const BasicBlock *const Block; 114 OptimizationRemarkEmitter &ORE; 115 const bool IsInliningRecommended; 116 117 private: markRecorded()118 void markRecorded() { 119 assert(!Recorded && "Recording should happen exactly once"); 120 Recorded = true; 121 } 122 123 bool Recorded = false; 124 }; 125 126 /// Interface for deciding whether to inline a call site or not. 127 class InlineAdvisor { 128 public: 129 InlineAdvisor(InlineAdvisor &&) = delete; ~InlineAdvisor()130 virtual ~InlineAdvisor() { freeDeletedFunctions(); } 131 132 /// Get an InlineAdvice containing a recommendation on whether to 133 /// inline or not. \p CB is assumed to be a direct call. \p FAM is assumed to 134 /// be up-to-date wrt previous inlining decisions. 135 /// Returns an InlineAdvice with the inlining recommendation. 136 virtual std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB) = 0; 137 138 /// This must be called when the Inliner pass is entered, to allow the 139 /// InlineAdvisor update internal state, as result of function passes run 140 /// between Inliner pass runs (for the same module). onPassEntry()141 virtual void onPassEntry() {} 142 143 /// This must be called when the Inliner pass is exited, as function passes 144 /// may be run subsequently. This allows an implementation of InlineAdvisor 145 /// to prepare for a partial update. onPassExit()146 virtual void onPassExit() {} 147 148 protected: InlineAdvisor(FunctionAnalysisManager & FAM)149 InlineAdvisor(FunctionAnalysisManager &FAM) : FAM(FAM) {} 150 151 FunctionAnalysisManager &FAM; 152 153 /// We may want to defer deleting functions to after the inlining for a whole 154 /// module has finished. This allows us to reliably use function pointers as 155 /// unique identifiers, as an efficient implementation detail of the 156 /// InlineAdvisor. Otherwise, it is possible the memory allocator 157 /// re-allocate Function objects at the same address of a deleted Function; 158 /// and Functions are potentially created during the function passes called 159 /// after each SCC inlining (e.g. argument promotion does that). 160 void freeDeletedFunctions(); 161 isFunctionDeleted(const Function * F)162 bool isFunctionDeleted(const Function *F) const { 163 return DeletedFunctions.count(F); 164 } 165 166 private: 167 friend class InlineAdvice; 168 void markFunctionAsDeleted(Function *F); 169 std::unordered_set<const Function *> DeletedFunctions; 170 }; 171 172 /// The default (manual heuristics) implementation of the InlineAdvisor. This 173 /// implementation does not need to keep state between inliner pass runs, and is 174 /// reusable as-is for inliner pass test scenarios, as well as for regular use. 175 class DefaultInlineAdvisor : public InlineAdvisor { 176 public: DefaultInlineAdvisor(FunctionAnalysisManager & FAM,InlineParams Params)177 DefaultInlineAdvisor(FunctionAnalysisManager &FAM, InlineParams Params) 178 : InlineAdvisor(FAM), Params(Params) {} 179 180 private: 181 std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB) override; 182 onPassExit()183 void onPassExit() override { freeDeletedFunctions(); } 184 185 InlineParams Params; 186 }; 187 188 /// Advisor recommending only mandatory (AlwaysInline) cases. 189 class MandatoryInlineAdvisor final : public InlineAdvisor { 190 std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB) override; 191 192 public: MandatoryInlineAdvisor(FunctionAnalysisManager & FAM)193 MandatoryInlineAdvisor(FunctionAnalysisManager &FAM) : InlineAdvisor(FAM) {} 194 195 enum class MandatoryInliningKind { NotMandatory, Always, Never }; 196 197 static MandatoryInliningKind getMandatoryKind(CallBase &CB, 198 FunctionAnalysisManager &FAM, 199 OptimizationRemarkEmitter &ORE); 200 }; 201 202 /// The InlineAdvisorAnalysis is a module pass because the InlineAdvisor 203 /// needs to capture state right before inlining commences over a module. 204 class InlineAdvisorAnalysis : public AnalysisInfoMixin<InlineAdvisorAnalysis> { 205 public: 206 static AnalysisKey Key; 207 InlineAdvisorAnalysis() = default; 208 struct Result { ResultResult209 Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {} invalidateResult210 bool invalidate(Module &, const PreservedAnalyses &, 211 ModuleAnalysisManager::Invalidator &) { 212 // InlineAdvisor must be preserved across analysis invalidations. 213 return false; 214 } 215 bool tryCreate(InlineParams Params, InliningAdvisorMode Mode); getAdvisorResult216 InlineAdvisor *getAdvisor() const { return Advisor.get(); } clearResult217 void clear() { Advisor.reset(); } 218 219 private: 220 Module &M; 221 ModuleAnalysisManager &MAM; 222 std::unique_ptr<InlineAdvisor> Advisor; 223 }; 224 run(Module & M,ModuleAnalysisManager & MAM)225 Result run(Module &M, ModuleAnalysisManager &MAM) { return Result(M, MAM); } 226 }; 227 228 #ifdef LLVM_HAVE_TF_AOT 229 std::unique_ptr<InlineAdvisor> 230 getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM); 231 #endif 232 233 #ifdef LLVM_HAVE_TF_API 234 std::unique_ptr<InlineAdvisor> 235 getDevelopmentModeAdvisor(Module &M, ModuleAnalysisManager &MAM, 236 std::function<bool(CallBase &)> GetDefaultAdvice); 237 #endif 238 239 // Default (manual policy) decision making helper APIs. Shared with the legacy 240 // pass manager inliner. 241 242 /// Return the cost only if the inliner should attempt to inline at the given 243 /// CallSite. If we return the cost, we will emit an optimisation remark later 244 /// using that cost, so we won't do so from this function. Return None if 245 /// inlining should not be attempted. 246 Optional<InlineCost> 247 shouldInline(CallBase &CB, function_ref<InlineCost(CallBase &CB)> GetInlineCost, 248 OptimizationRemarkEmitter &ORE, bool EnableDeferral = true); 249 250 /// Emit ORE message. 251 void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc, 252 const BasicBlock *Block, const Function &Callee, 253 const Function &Caller, const InlineCost &IC, 254 bool ForProfileContext = false, 255 const char *PassName = nullptr); 256 257 /// get call site location as string 258 std::string getCallSiteLocation(DebugLoc DLoc); 259 260 /// Add location info to ORE message. 261 void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc); 262 263 /// Set the inline-remark attribute. 264 void setInlineRemark(CallBase &CB, StringRef Message); 265 266 /// Utility for extracting the inline cost message to a string. 267 std::string inlineCostStr(const InlineCost &IC); 268 } // namespace llvm 269 #endif // LLVM_INLINEADVISOR_H_ 270