xref: /llvm-project/llvm/include/llvm/Analysis/ProfileSummaryInfo.h (revision 0da2ba811ac8a01509bc533428941fb9519c0715)
1 //===- llvm/Analysis/ProfileSummaryInfo.h - profile summary ---*- 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 // This file contains a pass that provides access to profile summary
10 // information.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_ANALYSIS_PROFILESUMMARYINFO_H
15 #define LLVM_ANALYSIS_PROFILESUMMARYINFO_H
16 
17 #include "llvm/ADT/DenseMap.h"
18 #include "llvm/IR/Function.h"
19 #include "llvm/IR/Instructions.h"
20 #include "llvm/IR/PassManager.h"
21 #include "llvm/IR/ProfileSummary.h"
22 #include "llvm/Pass.h"
23 #include "llvm/Support/BlockFrequency.h"
24 #include <memory>
25 #include <optional>
26 
27 namespace llvm {
28 class BlockFrequencyInfo;
29 class MachineFunction;
30 
31 /// Analysis providing profile information.
32 ///
33 /// This is an immutable analysis pass that provides ability to query global
34 /// (program-level) profile information. The main APIs are isHotCount and
35 /// isColdCount that tells whether a given profile count is considered hot/cold
36 /// based on the profile summary. This also provides convenience methods to
37 /// check whether a function is hot or cold.
38 
39 // FIXME: Provide convenience methods to determine hotness/coldness of other IR
40 // units. This would require making this depend on BFI.
41 class ProfileSummaryInfo {
42 private:
43   const Module *M;
44   std::unique_ptr<ProfileSummary> Summary;
45   void computeThresholds();
46   // Count thresholds to answer isHotCount and isColdCount queries.
47   std::optional<uint64_t> HotCountThreshold, ColdCountThreshold;
48   // True if the working set size of the code is considered huge,
49   // because the number of profile counts required to reach the hot
50   // percentile is above a huge threshold.
51   std::optional<bool> HasHugeWorkingSetSize;
52   // True if the working set size of the code is considered large,
53   // because the number of profile counts required to reach the hot
54   // percentile is above a large threshold.
55   std::optional<bool> HasLargeWorkingSetSize;
56   // Compute the threshold for a given cutoff.
57   std::optional<uint64_t> computeThreshold(int PercentileCutoff) const;
58   // The map that caches the threshold values. The keys are the percentile
59   // cutoff values and the values are the corresponding threshold values.
60   mutable DenseMap<int, uint64_t> ThresholdCache;
61 
62 public:
63   ProfileSummaryInfo(const Module &M) : M(&M) { refresh(); }
64   ProfileSummaryInfo(ProfileSummaryInfo &&Arg) = default;
65 
66   /// If no summary is present, attempt to refresh.
67   void refresh();
68 
69   /// Returns true if profile summary is available.
70   bool hasProfileSummary() const { return Summary != nullptr; }
71 
72   /// Returns true if module \c M has sample profile.
73   bool hasSampleProfile() const {
74     return hasProfileSummary() &&
75            Summary->getKind() == ProfileSummary::PSK_Sample;
76   }
77 
78   /// Returns true if module \c M has instrumentation profile.
79   bool hasInstrumentationProfile() const {
80     return hasProfileSummary() &&
81            Summary->getKind() == ProfileSummary::PSK_Instr;
82   }
83 
84   /// Returns true if module \c M has context sensitive instrumentation profile.
85   bool hasCSInstrumentationProfile() const {
86     return hasProfileSummary() &&
87            Summary->getKind() == ProfileSummary::PSK_CSInstr;
88   }
89 
90   /// Handle the invalidation of this information.
91   ///
92   /// When used as a result of \c ProfileSummaryAnalysis this method will be
93   /// called when the module this was computed for changes. Since profile
94   /// summary is immutable after it is annotated on the module, we return false
95   /// here.
96   bool invalidate(Module &, const PreservedAnalyses &,
97                   ModuleAnalysisManager::Invalidator &) {
98     return false;
99   }
100 
101   /// Returns the profile count for \p CallInst.
102   std::optional<uint64_t> getProfileCount(const CallBase &CallInst,
103                                           BlockFrequencyInfo *BFI,
104                                           bool AllowSynthetic = false) const;
105   /// Returns true if module \c M has partial-profile sample profile.
106   bool hasPartialSampleProfile() const;
107   /// Returns true if the working set size of the code is considered huge.
108   bool hasHugeWorkingSetSize() const;
109   /// Returns true if the working set size of the code is considered large.
110   bool hasLargeWorkingSetSize() const;
111   /// Returns true if \p F has hot function entry. If it returns false, it
112   /// either means it is not hot or it is unknown whether it is hot or not (for
113   /// example, no profile data is available).
114   template <typename FuncT> bool isFunctionEntryHot(const FuncT *F) const {
115     if (!F || !hasProfileSummary())
116       return false;
117     std::optional<Function::ProfileCount> FunctionCount = getEntryCount(F);
118     // FIXME: The heuristic used below for determining hotness is based on
119     // preliminary SPEC tuning for inliner. This will eventually be a
120     // convenience method that calls isHotCount.
121     return FunctionCount && isHotCount(FunctionCount->getCount());
122   }
123 
124   /// Returns true if \p F contains hot code.
125   template <typename FuncT, typename BFIT>
126   bool isFunctionHotInCallGraph(const FuncT *F, BFIT &BFI) const {
127     if (!F || !hasProfileSummary())
128       return false;
129     if (auto FunctionCount = getEntryCount(F))
130       if (isHotCount(FunctionCount->getCount()))
131         return true;
132 
133     if (auto TotalCallCount = getTotalCallCount(F))
134       if (isHotCount(*TotalCallCount))
135         return true;
136 
137     for (const auto &BB : *F)
138       if (isHotBlock(&BB, &BFI))
139         return true;
140     return false;
141   }
142   /// Returns true if \p F has cold function entry.
143   bool isFunctionEntryCold(const Function *F) const;
144   /// Returns true if \p F contains only cold code.
145   template <typename FuncT, typename BFIT>
146   bool isFunctionColdInCallGraph(const FuncT *F, BFIT &BFI) const {
147     if (!F || !hasProfileSummary())
148       return false;
149     if (auto FunctionCount = getEntryCount(F))
150       if (!isColdCount(FunctionCount->getCount()))
151         return false;
152 
153     if (auto TotalCallCount = getTotalCallCount(F))
154       if (!isColdCount(*TotalCallCount))
155         return false;
156 
157     for (const auto &BB : *F)
158       if (!isColdBlock(&BB, &BFI))
159         return false;
160     return true;
161   }
162   /// Returns true if the hotness of \p F is unknown.
163   bool isFunctionHotnessUnknown(const Function &F) const;
164   /// Returns true if \p F contains hot code with regard to a given hot
165   /// percentile cutoff value.
166   template <typename FuncT, typename BFIT>
167   bool isFunctionHotInCallGraphNthPercentile(int PercentileCutoff,
168                                              const FuncT *F, BFIT &BFI) const {
169     return isFunctionHotOrColdInCallGraphNthPercentile<true, FuncT, BFIT>(
170         PercentileCutoff, F, BFI);
171   }
172   /// Returns true if \p F contains cold code with regard to a given cold
173   /// percentile cutoff value.
174   template <typename FuncT, typename BFIT>
175   bool isFunctionColdInCallGraphNthPercentile(int PercentileCutoff,
176                                               const FuncT *F, BFIT &BFI) const {
177     return isFunctionHotOrColdInCallGraphNthPercentile<false, FuncT, BFIT>(
178         PercentileCutoff, F, BFI);
179   }
180   /// Returns true if count \p C is considered hot.
181   bool isHotCount(uint64_t C) const;
182   /// Returns true if count \p C is considered cold.
183   bool isColdCount(uint64_t C) const;
184   /// Returns true if count \p C is considered hot with regard to a given
185   /// hot percentile cutoff value.
186   /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where
187   /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile.
188   bool isHotCountNthPercentile(int PercentileCutoff, uint64_t C) const;
189   /// Returns true if count \p C is considered cold with regard to a given
190   /// cold percentile cutoff value.
191   /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where
192   /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile.
193   bool isColdCountNthPercentile(int PercentileCutoff, uint64_t C) const;
194 
195   /// Returns true if BasicBlock \p BB is considered hot.
196   template <typename BBType, typename BFIT>
197   bool isHotBlock(const BBType *BB, BFIT *BFI) const {
198     auto Count = BFI->getBlockProfileCount(BB);
199     return Count && isHotCount(*Count);
200   }
201 
202   /// Returns true if BasicBlock \p BB is considered cold.
203   template <typename BBType, typename BFIT>
204   bool isColdBlock(const BBType *BB, BFIT *BFI) const {
205     auto Count = BFI->getBlockProfileCount(BB);
206     return Count && isColdCount(*Count);
207   }
208 
209   template <typename BFIT>
210   bool isColdBlock(BlockFrequency BlockFreq, const BFIT *BFI) const {
211     auto Count = BFI->getProfileCountFromFreq(BlockFreq);
212     return Count && isColdCount(*Count);
213   }
214 
215   template <typename BBType, typename BFIT>
216   bool isHotBlockNthPercentile(int PercentileCutoff, const BBType *BB,
217                                BFIT *BFI) const {
218     return isHotOrColdBlockNthPercentile<true, BBType, BFIT>(PercentileCutoff,
219                                                              BB, BFI);
220   }
221 
222   template <typename BFIT>
223   bool isHotBlockNthPercentile(int PercentileCutoff, BlockFrequency BlockFreq,
224                                BFIT *BFI) const {
225     return isHotOrColdBlockNthPercentile<true, BFIT>(PercentileCutoff,
226                                                      BlockFreq, BFI);
227   }
228 
229   /// Returns true if BasicBlock \p BB is considered cold with regard to a given
230   /// cold percentile cutoff value.
231   /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where
232   /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile.
233   template <typename BBType, typename BFIT>
234   bool isColdBlockNthPercentile(int PercentileCutoff, const BBType *BB,
235                                 BFIT *BFI) const {
236     return isHotOrColdBlockNthPercentile<false, BBType, BFIT>(PercentileCutoff,
237                                                               BB, BFI);
238   }
239   template <typename BFIT>
240   bool isColdBlockNthPercentile(int PercentileCutoff, BlockFrequency BlockFreq,
241                                 BFIT *BFI) const {
242     return isHotOrColdBlockNthPercentile<false, BFIT>(PercentileCutoff,
243                                                       BlockFreq, BFI);
244   }
245   /// Returns true if the call site \p CB is considered hot.
246   bool isHotCallSite(const CallBase &CB, BlockFrequencyInfo *BFI) const;
247   /// Returns true if call site \p CB is considered cold.
248   bool isColdCallSite(const CallBase &CB, BlockFrequencyInfo *BFI) const;
249   /// Returns HotCountThreshold if set. Recompute HotCountThreshold
250   /// if not set.
251   uint64_t getOrCompHotCountThreshold() const;
252   /// Returns ColdCountThreshold if set. Recompute HotCountThreshold
253   /// if not set.
254   uint64_t getOrCompColdCountThreshold() const;
255   /// Returns HotCountThreshold if set.
256   uint64_t getHotCountThreshold() const {
257     return HotCountThreshold.value_or(0);
258   }
259   /// Returns ColdCountThreshold if set.
260   uint64_t getColdCountThreshold() const {
261     return ColdCountThreshold.value_or(0);
262   }
263 
264 private:
265   template <typename FuncT>
266   std::optional<uint64_t> getTotalCallCount(const FuncT *F) const {
267     return std::nullopt;
268   }
269 
270   template <bool isHot, typename FuncT, typename BFIT>
271   bool isFunctionHotOrColdInCallGraphNthPercentile(int PercentileCutoff,
272                                                    const FuncT *F,
273                                                    BFIT &FI) const {
274     if (!F || !hasProfileSummary())
275       return false;
276     if (auto FunctionCount = getEntryCount(F)) {
277       if (isHot &&
278           isHotCountNthPercentile(PercentileCutoff, FunctionCount->getCount()))
279         return true;
280       if (!isHot && !isColdCountNthPercentile(PercentileCutoff,
281                                               FunctionCount->getCount()))
282         return false;
283     }
284     if (auto TotalCallCount = getTotalCallCount(F)) {
285       if (isHot && isHotCountNthPercentile(PercentileCutoff, *TotalCallCount))
286         return true;
287       if (!isHot &&
288           !isColdCountNthPercentile(PercentileCutoff, *TotalCallCount))
289         return false;
290     }
291     for (const auto &BB : *F) {
292       if (isHot && isHotBlockNthPercentile(PercentileCutoff, &BB, &FI))
293         return true;
294       if (!isHot && !isColdBlockNthPercentile(PercentileCutoff, &BB, &FI))
295         return false;
296     }
297     return !isHot;
298   }
299 
300   template <bool isHot>
301   bool isHotOrColdCountNthPercentile(int PercentileCutoff, uint64_t C) const;
302 
303   template <bool isHot, typename BBType, typename BFIT>
304   bool isHotOrColdBlockNthPercentile(int PercentileCutoff, const BBType *BB,
305                                      BFIT *BFI) const {
306     auto Count = BFI->getBlockProfileCount(BB);
307     if (isHot)
308       return Count && isHotCountNthPercentile(PercentileCutoff, *Count);
309     else
310       return Count && isColdCountNthPercentile(PercentileCutoff, *Count);
311   }
312 
313   template <bool isHot, typename BFIT>
314   bool isHotOrColdBlockNthPercentile(int PercentileCutoff,
315                                      BlockFrequency BlockFreq,
316                                      BFIT *BFI) const {
317     auto Count = BFI->getProfileCountFromFreq(BlockFreq);
318     if (isHot)
319       return Count && isHotCountNthPercentile(PercentileCutoff, *Count);
320     else
321       return Count && isColdCountNthPercentile(PercentileCutoff, *Count);
322   }
323 
324   template <typename FuncT>
325   std::optional<Function::ProfileCount> getEntryCount(const FuncT *F) const {
326     return F->getEntryCount();
327   }
328 };
329 
330 template <>
331 inline std::optional<uint64_t>
332 ProfileSummaryInfo::getTotalCallCount<Function>(const Function *F) const {
333   if (!hasSampleProfile())
334     return std::nullopt;
335   uint64_t TotalCallCount = 0;
336   for (const auto &BB : *F)
337     for (const auto &I : BB)
338       if (isa<CallInst>(I) || isa<InvokeInst>(I))
339         if (auto CallCount = getProfileCount(cast<CallBase>(I), nullptr))
340           TotalCallCount += *CallCount;
341   return TotalCallCount;
342 }
343 
344 // Declare template specialization for llvm::MachineFunction. Do not implement
345 // here, because we cannot include MachineFunction header here, that would break
346 // dependency rules.
347 template <>
348 std::optional<Function::ProfileCount>
349 ProfileSummaryInfo::getEntryCount<MachineFunction>(
350     const MachineFunction *F) const;
351 
352 /// An analysis pass based on legacy pass manager to deliver ProfileSummaryInfo.
353 class ProfileSummaryInfoWrapperPass : public ImmutablePass {
354   std::unique_ptr<ProfileSummaryInfo> PSI;
355 
356 public:
357   static char ID;
358   ProfileSummaryInfoWrapperPass();
359 
360   ProfileSummaryInfo &getPSI() { return *PSI; }
361   const ProfileSummaryInfo &getPSI() const { return *PSI; }
362 
363   bool doInitialization(Module &M) override;
364   bool doFinalization(Module &M) override;
365   void getAnalysisUsage(AnalysisUsage &AU) const override {
366     AU.setPreservesAll();
367   }
368 };
369 
370 /// An analysis pass based on the new PM to deliver ProfileSummaryInfo.
371 class ProfileSummaryAnalysis
372     : public AnalysisInfoMixin<ProfileSummaryAnalysis> {
373 public:
374   typedef ProfileSummaryInfo Result;
375 
376   Result run(Module &M, ModuleAnalysisManager &);
377 
378 private:
379   friend AnalysisInfoMixin<ProfileSummaryAnalysis>;
380   static AnalysisKey Key;
381 };
382 
383 /// Printer pass that uses \c ProfileSummaryAnalysis.
384 class ProfileSummaryPrinterPass
385     : public PassInfoMixin<ProfileSummaryPrinterPass> {
386   raw_ostream &OS;
387 
388 public:
389   explicit ProfileSummaryPrinterPass(raw_ostream &OS) : OS(OS) {}
390   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
391   static bool isRequired() { return true; }
392 };
393 
394 } // end namespace llvm
395 
396 #endif
397