| //===-- CSPreInliner.h - Profile guided preinliner ---------------- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H |
| #define LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H |
| |
| #include "ProfiledBinary.h" |
| #include "llvm/ADT/PriorityQueue.h" |
| #include "llvm/ProfileData/ProfileCommon.h" |
| #include "llvm/ProfileData/SampleProf.h" |
| #include "llvm/Transforms/IPO/ProfiledCallGraph.h" |
| #include "llvm/Transforms/IPO/SampleContextTracker.h" |
| |
| using namespace llvm; |
| using namespace sampleprof; |
| |
| namespace llvm { |
| namespace sampleprof { |
| |
| // Inline candidate seen from profile |
| struct ProfiledInlineCandidate { |
| ProfiledInlineCandidate(const FunctionSamples *Samples, uint64_t Count, |
| uint32_t Size) |
| : CalleeSamples(Samples), CallsiteCount(Count), SizeCost(Size) {} |
| // Context-sensitive function profile for inline candidate |
| const FunctionSamples *CalleeSamples; |
| // Call site count for an inline candidate |
| // TODO: make sure entry count for context profile and call site |
| // target count for corresponding call are consistent. |
| uint64_t CallsiteCount; |
| // Size proxy for function under particular call context. |
| uint64_t SizeCost; |
| }; |
| |
| // Inline candidate comparer using call site weight |
| struct ProfiledCandidateComparer { |
| bool operator()(const ProfiledInlineCandidate &LHS, |
| const ProfiledInlineCandidate &RHS) { |
| // Always prioritize inlining zero-sized functions as they do not affect the |
| // size budget. This could happen when all of the callee's code is gone and |
| // only pseudo probes are left. |
| if ((LHS.SizeCost == 0 || RHS.SizeCost == 0) && |
| (LHS.SizeCost != RHS.SizeCost)) |
| return RHS.SizeCost == 0; |
| |
| if (LHS.CallsiteCount != RHS.CallsiteCount) |
| return LHS.CallsiteCount < RHS.CallsiteCount; |
| |
| if (LHS.SizeCost != RHS.SizeCost) |
| return LHS.SizeCost > RHS.SizeCost; |
| |
| // Tie breaker using GUID so we have stable/deterministic inlining order |
| assert(LHS.CalleeSamples && RHS.CalleeSamples && |
| "Expect non-null FunctionSamples"); |
| return LHS.CalleeSamples->getGUID() < RHS.CalleeSamples->getGUID(); |
| } |
| }; |
| |
| using ProfiledCandidateQueue = |
| PriorityQueue<ProfiledInlineCandidate, std::vector<ProfiledInlineCandidate>, |
| ProfiledCandidateComparer>; |
| |
| // Pre-compilation inliner based on context-sensitive profile. |
| // The PreInliner estimates inline decision using hotness from profile |
| // and cost estimation from machine code size. It helps merges context |
| // profile globally and achieves better post-inine profile quality, which |
| // otherwise won't be possible for ThinLTO. It also reduce context profile |
| // size by only keep context that is estimated to be inlined. |
| class CSPreInliner { |
| public: |
| CSPreInliner(SampleContextTracker &Tracker, ProfiledBinary &Binary, |
| ProfileSummary *Summary); |
| void run(); |
| |
| private: |
| bool getInlineCandidates(ProfiledCandidateQueue &CQueue, |
| const FunctionSamples *FCallerContextSamples); |
| std::vector<FunctionId> buildTopDownOrder(); |
| void processFunction(FunctionId Name); |
| bool shouldInline(ProfiledInlineCandidate &Candidate); |
| uint32_t getFuncSize(const ContextTrieNode *ContextNode); |
| bool UseContextCost; |
| SampleContextTracker &ContextTracker; |
| ProfiledBinary &Binary; |
| ProfileSummary *Summary; |
| }; |
| |
| } // end namespace sampleprof |
| } // end namespace llvm |
| |
| #endif |