diff options
-rw-r--r-- | llvm/lib/Transforms/IPO/SampleProfile.cpp | 86 | ||||
-rw-r--r-- | llvm/test/Transforms/SampleProfile/Inputs/coverage-warning.prof | 5 | ||||
-rw-r--r-- | llvm/test/Transforms/SampleProfile/coverage-warning.ll | 45 |
3 files changed, 133 insertions, 3 deletions
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp index 8fd81d62dd8..47250cccc4d 100644 --- a/llvm/lib/Transforms/IPO/SampleProfile.cpp +++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp @@ -63,6 +63,10 @@ static cl::opt<unsigned> SampleProfileMaxPropagateIterations( "sample-profile-max-propagate-iterations", cl::init(100), cl::desc("Maximum number of iterations to go through when propagating " "sample block/edge weights through the CFG.")); +static cl::opt<unsigned> SampleProfileCoverage( + "sample-profile-check-coverage", cl::init(0), cl::value_desc("N"), + cl::desc("Emit a warning if less than N% of samples in the input profile " + "are matched to the IR.")); namespace { typedef DenseMap<const BasicBlock *, uint64_t> BlockWeightMap; @@ -174,6 +178,64 @@ protected: /// \brief Flag indicating whether the profile input loaded successfully. bool ProfileIsValid; }; + +class SampleCoverageTracker { +public: + SampleCoverageTracker() : SampleCoverage() {} + + void markSamplesUsed(const FunctionSamples *Samples, uint32_t LineOffset, + uint32_t Discriminator); + unsigned computeCoverage(const FunctionSamples *Samples) const; + unsigned getNumUsedSamples(const FunctionSamples *Samples) const; + +private: + typedef DenseMap<LineLocation, unsigned> BodySampleCoverageMap; + typedef DenseMap<const FunctionSamples *, BodySampleCoverageMap> + FunctionSamplesCoverageMap; + + /// Coverage map for sampling records. + /// + /// This map keeps a record of sampling records that have been matched to + /// an IR instruction. This is used to detect some form of staleness in + /// profiles (see flag -sample-profile-check-coverage). + /// + /// Each entry in the map corresponds to a FunctionSamples instance. This is + /// another map that counts how many times the sample record at the + /// given location has been used. + FunctionSamplesCoverageMap SampleCoverage; +}; + +SampleCoverageTracker CoverageTracker; +} + +/// Mark as used the sample record for the given function samples at +/// (LineOffset, Discriminator). +void SampleCoverageTracker::markSamplesUsed(const FunctionSamples *Samples, + uint32_t LineOffset, + uint32_t Discriminator) { + BodySampleCoverageMap &Coverage = SampleCoverage[Samples]; + Coverage[LineLocation(LineOffset, Discriminator)]++; +} + +/// Return the number of sample records that were applied from this profile. +unsigned +SampleCoverageTracker::getNumUsedSamples(const FunctionSamples *Samples) const { + auto I = SampleCoverage.find(Samples); + return (I != SampleCoverage.end()) ? I->second.size() : 0; +} + +/// Return the fraction of sample records used in this profile. +/// +/// The returned value is an unsigned integer in the range 0-100 indicating +/// the percentage of sample records that were used while applying this +/// profile to the associated function. +unsigned +SampleCoverageTracker::computeCoverage(const FunctionSamples *Samples) const { + uint32_t NumTotalRecords = Samples->getBodySamples().size(); + uint32_t NumUsedRecords = getNumUsedSamples(Samples); + assert(NumUsedRecords <= NumTotalRecords && + "number of used records cannot exceed the total number of records"); + return NumTotalRecords > 0 ? NumUsedRecords * 100 / NumTotalRecords : 100; } /// Clear all the per-function data used to load samples and propagate weights. @@ -257,13 +319,17 @@ SampleProfileLoader::getInstWeight(const Instruction &Inst) const { unsigned Lineno = DLoc.getLine(); unsigned HeaderLineno = DIL->getScope()->getSubprogram()->getLine(); - ErrorOr<uint64_t> R = FS->findSamplesAt(getOffset(Lineno, HeaderLineno), - DIL->getDiscriminator()); - if (R) + uint32_t LineOffset = getOffset(Lineno, HeaderLineno); + uint32_t Discriminator = DIL->getDiscriminator(); + ErrorOr<uint64_t> R = FS->findSamplesAt(LineOffset, Discriminator); + if (R) { + if (SampleProfileCoverage) + CoverageTracker.markSamplesUsed(FS, LineOffset, Discriminator); DEBUG(dbgs() << " " << Lineno << "." << DIL->getDiscriminator() << ":" << Inst << " (line offset: " << Lineno - HeaderLineno << "." << DIL->getDiscriminator() << " - weight: " << R.get() << ")\n"); + } return R; } @@ -311,6 +377,20 @@ bool SampleProfileLoader::computeBlockWeights(Function &F) { DEBUG(printBlockWeight(dbgs(), &BB)); } + if (SampleProfileCoverage) { + unsigned Coverage = CoverageTracker.computeCoverage(Samples); + if (Coverage < SampleProfileCoverage) { + const char *Filename = getDISubprogram(&F)->getFilename().str().c_str(); + F.getContext().diagnose(DiagnosticInfoSampleProfile( + Filename, getFunctionLoc(F), + Twine(CoverageTracker.getNumUsedSamples(Samples)) + " of " + + Twine(Samples->getBodySamples().size()) + + " available profile records (" + Twine(Coverage) + + "%) were applied", + DS_Warning)); + } + } + return Changed; } diff --git a/llvm/test/Transforms/SampleProfile/Inputs/coverage-warning.prof b/llvm/test/Transforms/SampleProfile/Inputs/coverage-warning.prof new file mode 100644 index 00000000000..57989b837a0 --- /dev/null +++ b/llvm/test/Transforms/SampleProfile/Inputs/coverage-warning.prof @@ -0,0 +1,5 @@ +foo:30000:100 + 2: 28000 + 3: 1000 +# This profile is stale. Function foo() does not have a line 8 anymore. + 8: 1700 diff --git a/llvm/test/Transforms/SampleProfile/coverage-warning.ll b/llvm/test/Transforms/SampleProfile/coverage-warning.ll new file mode 100644 index 00000000000..f96068002dd --- /dev/null +++ b/llvm/test/Transforms/SampleProfile/coverage-warning.ll @@ -0,0 +1,45 @@ +; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/coverage-warning.prof -sample-profile-check-coverage=90 2>& 1 | FileCheck %s +define i32 @foo(i32 %i) { +; The profile has samples for line locations that are no longer present. +; Coverage does not reach 90%, so we should get this warning: +; +; CHECK: warning: coverage-warning.c:1: 2 of 3 available profile records (66%) were applied +entry: + %retval = alloca i32, align 4 + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* %i.addr, align 4, !dbg !9 + %cmp = icmp sgt i32 %0, 1000, !dbg !10 + br i1 %cmp, label %if.then, label %if.end, !dbg !9 + +if.then: ; preds = %entry + store i32 30, i32* %retval, align 4, !dbg !11 + br label %return, !dbg !11 + +if.end: ; preds = %entry + store i32 3, i32* %retval, align 4, !dbg !12 + br label %return, !dbg !12 + +return: ; preds = %if.end, %if.then + %1 = load i32, i32* %retval, align 4, !dbg !13 + ret i32 %1, !dbg !13 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 251524) (llvm/trunk 251531)", isOptimized: false, runtimeVersion: 0, emissionKind: 2, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "coverage-warning.c", directory: ".") +!2 = !{} +!3 = !{!4} +!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @foo, variables: !2) +!5 = !DISubroutineType(types: !2) +!6 = !{i32 2, !"Dwarf Version", i32 4} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{!"clang version 3.8.0 (trunk 251524) (llvm/trunk 251531)"} +!9 = !DILocation(line: 2, column: 7, scope: !4) +!10 = !DILocation(line: 2, column: 9, scope: !4) +!11 = !DILocation(line: 3, column: 5, scope: !4) +!12 = !DILocation(line: 4, column: 3, scope: !4) +!13 = !DILocation(line: 5, column: 1, scope: !4) |