| 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 | |