1 | //===- SampleProfileLoaderBaseUtil.cpp - Profile loader Util func ---------===// |
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 implements the SampleProfileLoader base utility functions. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/Transforms/Utils/SampleProfileLoaderBaseUtil.h" |
14 | #include "llvm/Analysis/ProfileSummaryInfo.h" |
15 | #include "llvm/IR/Constants.h" |
16 | #include "llvm/IR/Module.h" |
17 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
18 | |
19 | namespace llvm { |
20 | |
21 | cl::opt<unsigned> SampleProfileMaxPropagateIterations( |
22 | "sample-profile-max-propagate-iterations" , cl::init(Val: 100), |
23 | cl::desc("Maximum number of iterations to go through when propagating " |
24 | "sample block/edge weights through the CFG." )); |
25 | |
26 | cl::opt<unsigned> SampleProfileRecordCoverage( |
27 | "sample-profile-check-record-coverage" , cl::init(Val: 0), cl::value_desc("N" ), |
28 | cl::desc("Emit a warning if less than N% of records in the input profile " |
29 | "are matched to the IR." )); |
30 | |
31 | cl::opt<unsigned> SampleProfileSampleCoverage( |
32 | "sample-profile-check-sample-coverage" , cl::init(Val: 0), cl::value_desc("N" ), |
33 | cl::desc("Emit a warning if less than N% of samples in the input profile " |
34 | "are matched to the IR." )); |
35 | |
36 | cl::opt<bool> NoWarnSampleUnused( |
37 | "no-warn-sample-unused" , cl::init(Val: false), cl::Hidden, |
38 | cl::desc("Use this option to turn off/on warnings about function with " |
39 | "samples but without debug information to use those samples. " )); |
40 | |
41 | cl::opt<bool> SampleProfileUseProfi( |
42 | "sample-profile-use-profi" , cl::Hidden, |
43 | cl::desc("Use profi to infer block and edge counts." )); |
44 | |
45 | namespace sampleprofutil { |
46 | |
47 | /// Return true if the given callsite is hot wrt to hot cutoff threshold. |
48 | /// |
49 | /// Functions that were inlined in the original binary will be represented |
50 | /// in the inline stack in the sample profile. If the profile shows that |
51 | /// the original inline decision was "good" (i.e., the callsite is executed |
52 | /// frequently), then we will recreate the inline decision and apply the |
53 | /// profile from the inlined callsite. |
54 | /// |
55 | /// To decide whether an inlined callsite is hot, we compare the callsite |
56 | /// sample count with the hot cutoff computed by ProfileSummaryInfo, it is |
57 | /// regarded as hot if the count is above the cutoff value. |
58 | /// |
59 | /// When ProfileAccurateForSymsInList is enabled and profile symbol list |
60 | /// is present, functions in the profile symbol list but without profile will |
61 | /// be regarded as cold and much less inlining will happen in CGSCC inlining |
62 | /// pass, so we tend to lower the hot criteria here to allow more early |
63 | /// inlining to happen for warm callsites and it is helpful for performance. |
64 | bool callsiteIsHot(const FunctionSamples *CallsiteFS, ProfileSummaryInfo *PSI, |
65 | bool ProfAccForSymsInList) { |
66 | if (!CallsiteFS) |
67 | return false; // The callsite was not inlined in the original binary. |
68 | |
69 | assert(PSI && "PSI is expected to be non null" ); |
70 | uint64_t CallsiteTotalSamples = CallsiteFS->getTotalSamples(); |
71 | if (ProfAccForSymsInList) |
72 | return !PSI->isColdCount(C: CallsiteTotalSamples); |
73 | else |
74 | return PSI->isHotCount(C: CallsiteTotalSamples); |
75 | } |
76 | |
77 | /// Mark as used the sample record for the given function samples at |
78 | /// (LineOffset, Discriminator). |
79 | /// |
80 | /// \returns true if this is the first time we mark the given record. |
81 | bool SampleCoverageTracker::markSamplesUsed(const FunctionSamples *FS, |
82 | uint32_t LineOffset, |
83 | uint32_t Discriminator, |
84 | uint64_t Samples) { |
85 | LineLocation Loc(LineOffset, Discriminator); |
86 | unsigned &Count = SampleCoverage[FS][Loc]; |
87 | bool FirstTime = (++Count == 1); |
88 | if (FirstTime) |
89 | TotalUsedSamples += Samples; |
90 | return FirstTime; |
91 | } |
92 | |
93 | /// Return the number of sample records that were applied from this profile. |
94 | /// |
95 | /// This count does not include records from cold inlined callsites. |
96 | unsigned |
97 | SampleCoverageTracker::countUsedRecords(const FunctionSamples *FS, |
98 | ProfileSummaryInfo *PSI) const { |
99 | auto I = SampleCoverage.find(Val: FS); |
100 | |
101 | // The size of the coverage map for FS represents the number of records |
102 | // that were marked used at least once. |
103 | unsigned Count = (I != SampleCoverage.end()) ? I->second.size() : 0; |
104 | |
105 | // If there are inlined callsites in this function, count the samples found |
106 | // in the respective bodies. However, do not bother counting callees with 0 |
107 | // total samples, these are callees that were never invoked at runtime. |
108 | for (const auto &I : FS->getCallsiteSamples()) |
109 | for (const auto &J : I.second) { |
110 | const FunctionSamples *CalleeSamples = &J.second; |
111 | if (callsiteIsHot(CallsiteFS: CalleeSamples, PSI, ProfAccForSymsInList)) |
112 | Count += countUsedRecords(FS: CalleeSamples, PSI); |
113 | } |
114 | |
115 | return Count; |
116 | } |
117 | |
118 | /// Return the number of sample records in the body of this profile. |
119 | /// |
120 | /// This count does not include records from cold inlined callsites. |
121 | unsigned |
122 | SampleCoverageTracker::countBodyRecords(const FunctionSamples *FS, |
123 | ProfileSummaryInfo *PSI) const { |
124 | unsigned Count = FS->getBodySamples().size(); |
125 | |
126 | // Only count records in hot callsites. |
127 | for (const auto &I : FS->getCallsiteSamples()) |
128 | for (const auto &J : I.second) { |
129 | const FunctionSamples *CalleeSamples = &J.second; |
130 | if (callsiteIsHot(CallsiteFS: CalleeSamples, PSI, ProfAccForSymsInList)) |
131 | Count += countBodyRecords(FS: CalleeSamples, PSI); |
132 | } |
133 | |
134 | return Count; |
135 | } |
136 | |
137 | /// Return the number of samples collected in the body of this profile. |
138 | /// |
139 | /// This count does not include samples from cold inlined callsites. |
140 | uint64_t |
141 | SampleCoverageTracker::countBodySamples(const FunctionSamples *FS, |
142 | ProfileSummaryInfo *PSI) const { |
143 | uint64_t Total = 0; |
144 | for (const auto &I : FS->getBodySamples()) |
145 | Total += I.second.getSamples(); |
146 | |
147 | // Only count samples in hot callsites. |
148 | for (const auto &I : FS->getCallsiteSamples()) |
149 | for (const auto &J : I.second) { |
150 | const FunctionSamples *CalleeSamples = &J.second; |
151 | if (callsiteIsHot(CallsiteFS: CalleeSamples, PSI, ProfAccForSymsInList)) |
152 | Total += countBodySamples(FS: CalleeSamples, PSI); |
153 | } |
154 | |
155 | return Total; |
156 | } |
157 | |
158 | /// Return the fraction of sample records used in this profile. |
159 | /// |
160 | /// The returned value is an unsigned integer in the range 0-100 indicating |
161 | /// the percentage of sample records that were used while applying this |
162 | /// profile to the associated function. |
163 | unsigned SampleCoverageTracker::computeCoverage(unsigned Used, |
164 | unsigned Total) const { |
165 | assert(Used <= Total && |
166 | "number of used records cannot exceed the total number of records" ); |
167 | return Total > 0 ? Used * 100 / Total : 100; |
168 | } |
169 | |
170 | /// Create a global variable to flag FSDiscriminators are used. |
171 | void createFSDiscriminatorVariable(Module *M) { |
172 | const char *FSDiscriminatorVar = "__llvm_fs_discriminator__" ; |
173 | if (M->getGlobalVariable(Name: FSDiscriminatorVar)) |
174 | return; |
175 | |
176 | auto &Context = M->getContext(); |
177 | // Place this variable to llvm.used so it won't be GC'ed. |
178 | appendToUsed(M&: *M, Values: {new GlobalVariable(*M, Type::getInt1Ty(C&: Context), true, |
179 | GlobalValue::WeakODRLinkage, |
180 | ConstantInt::getTrue(Context), |
181 | FSDiscriminatorVar)}); |
182 | } |
183 | |
184 | } // end of namespace sampleprofutil |
185 | } // end of namespace llvm |
186 | |