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