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