1 //===-- CSPreInliner.h - Profile guided preinliner ---------------- 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_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H 10 #define LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H 11 12 #include "ProfiledBinary.h" 13 #include "llvm/ADT/PriorityQueue.h" 14 #include "llvm/ProfileData/ProfileCommon.h" 15 #include "llvm/ProfileData/SampleProf.h" 16 #include "llvm/Transforms/IPO/ProfiledCallGraph.h" 17 #include "llvm/Transforms/IPO/SampleContextTracker.h" 18 19 using namespace llvm; 20 using namespace sampleprof; 21 22 namespace llvm { 23 namespace sampleprof { 24 25 // Inline candidate seen from profile 26 struct ProfiledInlineCandidate { ProfiledInlineCandidateProfiledInlineCandidate27 ProfiledInlineCandidate(const FunctionSamples *Samples, uint64_t Count, 28 uint32_t Size) 29 : CalleeSamples(Samples), CallsiteCount(Count), SizeCost(Size) {} 30 // Context-sensitive function profile for inline candidate 31 const FunctionSamples *CalleeSamples; 32 // Call site count for an inline candidate 33 // TODO: make sure entry count for context profile and call site 34 // target count for corresponding call are consistent. 35 uint64_t CallsiteCount; 36 // Size proxy for function under particular call context. 37 uint64_t SizeCost; 38 }; 39 40 // Inline candidate comparer using call site weight 41 struct ProfiledCandidateComparer { operatorProfiledCandidateComparer42 bool operator()(const ProfiledInlineCandidate &LHS, 43 const ProfiledInlineCandidate &RHS) { 44 // Always prioritize inlining zero-sized functions as they do not affect the 45 // size budget. This could happen when all of the callee's code is gone and 46 // only pseudo probes are left. 47 if ((LHS.SizeCost == 0 || RHS.SizeCost == 0) && 48 (LHS.SizeCost != RHS.SizeCost)) 49 return RHS.SizeCost == 0; 50 51 if (LHS.CallsiteCount != RHS.CallsiteCount) 52 return LHS.CallsiteCount < RHS.CallsiteCount; 53 54 if (LHS.SizeCost != RHS.SizeCost) 55 return LHS.SizeCost > RHS.SizeCost; 56 57 // Tie breaker using GUID so we have stable/deterministic inlining order 58 assert(LHS.CalleeSamples && RHS.CalleeSamples && 59 "Expect non-null FunctionSamples"); 60 return LHS.CalleeSamples->getGUID() < RHS.CalleeSamples->getGUID(); 61 } 62 }; 63 64 using ProfiledCandidateQueue = 65 PriorityQueue<ProfiledInlineCandidate, std::vector<ProfiledInlineCandidate>, 66 ProfiledCandidateComparer>; 67 68 // Pre-compilation inliner based on context-sensitive profile. 69 // The PreInliner estimates inline decision using hotness from profile 70 // and cost estimation from machine code size. It helps merges context 71 // profile globally and achieves better post-inine profile quality, which 72 // otherwise won't be possible for ThinLTO. It also reduce context profile 73 // size by only keep context that is estimated to be inlined. 74 class CSPreInliner { 75 public: 76 CSPreInliner(SampleContextTracker &Tracker, ProfiledBinary &Binary, 77 ProfileSummary *Summary); 78 void run(); 79 80 private: 81 bool getInlineCandidates(ProfiledCandidateQueue &CQueue, 82 const FunctionSamples *FCallerContextSamples); 83 std::vector<FunctionId> buildTopDownOrder(); 84 void processFunction(FunctionId Name); 85 bool shouldInline(ProfiledInlineCandidate &Candidate); 86 uint32_t getFuncSize(const ContextTrieNode *ContextNode); 87 bool UseContextCost; 88 SampleContextTracker &ContextTracker; 89 ProfiledBinary &Binary; 90 ProfileSummary *Summary; 91 }; 92 93 } // end namespace sampleprof 94 } // end namespace llvm 95 96 #endif 97