SamplePGO - Add coverage tracking for samples.
[oota-llvm.git] / lib / Transforms / IPO / SampleProfile.cpp
index de4170692fe0a098b775da6e4f21e969abd90972..843453775fb0c1f2176cb7905ced8671989fbdd6 100644 (file)
@@ -63,8 +63,12 @@ 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"),
+static cl::opt<unsigned> SampleProfileRecordCoverage(
+    "sample-profile-check-record-coverage", cl::init(0), cl::value_desc("N"),
+    cl::desc("Emit a warning if less than N% of records in the input profile "
+             "are matched to the IR."));
+static cl::opt<unsigned> SampleProfileSampleCoverage(
+    "sample-profile-check-sample-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."));
 
@@ -181,14 +185,19 @@ protected:
 
 class SampleCoverageTracker {
 public:
-  SampleCoverageTracker() : SampleCoverage() {}
+  SampleCoverageTracker() : SampleCoverage(), TotalUsedSamples(0) {}
 
-  bool markSamplesUsed(const FunctionSamples *Samples, uint32_t LineOffset,
-                       uint32_t Discriminator);
+  bool markSamplesUsed(const FunctionSamples *FS, uint32_t LineOffset,
+                       uint32_t Discriminator, uint64_t Samples);
   unsigned computeCoverage(unsigned Used, unsigned Total) const;
-  unsigned countUsedSamples(const FunctionSamples *Samples) const;
-  unsigned countBodySamples(const FunctionSamples *Samples) const;
-  void clear() { SampleCoverage.clear(); }
+  unsigned countUsedRecords(const FunctionSamples *FS) const;
+  unsigned countBodyRecords(const FunctionSamples *FS) const;
+  uint64_t getTotalUsedSamples() const { return TotalUsedSamples; }
+  uint64_t countBodySamples(const FunctionSamples *FS) const;
+  void clear() {
+    SampleCoverage.clear();
+    TotalUsedSamples = 0;
+  }
 
 private:
   typedef DenseMap<LineLocation, unsigned> BodySampleCoverageMap;
@@ -205,6 +214,19 @@ private:
   /// another map that counts how many times the sample record at the
   /// given location has been used.
   FunctionSamplesCoverageMap SampleCoverage;
+
+  /// Number of samples used from the profile.
+  ///
+  /// When a sampling record is used for the first time, the samples from
+  /// that record are added to this accumulator.  Coverage is later computed
+  /// based on the total number of samples available in this function and
+  /// its callsites.
+  ///
+  /// Note that this accumulator tracks samples used from a single function
+  /// and all the inlined callsites. Strictly, we should have a map of counters
+  /// keyed by FunctionSamples pointers, but these stats are cleared after
+  /// every function, so we just need to keep a single counter.
+  uint64_t TotalUsedSamples;
 };
 
 SampleCoverageTracker CoverageTracker;
@@ -214,30 +236,34 @@ SampleCoverageTracker CoverageTracker;
 /// (LineOffset, Discriminator).
 ///
 /// \returns true if this is the first time we mark the given record.
-bool SampleCoverageTracker::markSamplesUsed(const FunctionSamples *Samples,
+bool SampleCoverageTracker::markSamplesUsed(const FunctionSamples *FS,
                                             uint32_t LineOffset,
-                                            uint32_t Discriminator) {
+                                            uint32_t Discriminator,
+                                            uint64_t Samples) {
   LineLocation Loc(LineOffset, Discriminator);
-  unsigned &Count = SampleCoverage[Samples][Loc];
-  return ++Count == 1;
+  unsigned &Count = SampleCoverage[FS][Loc];
+  bool FirstTime = (++Count == 1);
+  if (FirstTime)
+    TotalUsedSamples += Samples;
+  return FirstTime;
 }
 
 /// Return the number of sample records that were applied from this profile.
 unsigned
-SampleCoverageTracker::countUsedSamples(const FunctionSamples *Samples) const {
-  auto I = SampleCoverage.find(Samples);
+SampleCoverageTracker::countUsedRecords(const FunctionSamples *FS) const {
+  auto I = SampleCoverage.find(FS);
 
-  // The size of the coverage map for Samples represents the number of records
+  // The size of the coverage map for FS represents the number of records
   // that were marked used at least once.
   unsigned Count = (I != SampleCoverage.end()) ? I->second.size() : 0;
 
   // If there are inlined callsites in this function, count the samples found
   // in the respective bodies. However, do not bother counting callees with 0
   // total samples, these are callees that were never invoked at runtime.
-  for (const auto &I : Samples->getCallsiteSamples()) {
+  for (const auto &I : FS->getCallsiteSamples()) {
     const FunctionSamples *CalleeSamples = &I.second;
     if (CalleeSamples->getTotalSamples() > 0)
-      Count += countUsedSamples(CalleeSamples);
+      Count += countUsedRecords(CalleeSamples);
   }
 
   return Count;
@@ -249,19 +275,40 @@ SampleCoverageTracker::countUsedSamples(const FunctionSamples *Samples) const {
 /// with 0 samples indicate inlined function calls that were never actually
 /// invoked at runtime. Ignore these callsites for coverage purposes.
 unsigned
-SampleCoverageTracker::countBodySamples(const FunctionSamples *Samples) const {
-  unsigned Count = Samples->getBodySamples().size();
+SampleCoverageTracker::countBodyRecords(const FunctionSamples *FS) const {
+  unsigned Count = FS->getBodySamples().size();
 
   // Count all the callsites with non-zero samples.
-  for (const auto &I : Samples->getCallsiteSamples()) {
+  for (const auto &I : FS->getCallsiteSamples()) {
     const FunctionSamples *CalleeSamples = &I.second;
     if (CalleeSamples->getTotalSamples() > 0)
-      Count += countBodySamples(CalleeSamples);
+      Count += countBodyRecords(CalleeSamples);
   }
 
   return Count;
 }
 
+/// Return the number of samples collected in the body of this profile.
+///
+/// The count includes all the samples in inlined callees. However, callsites
+/// with 0 samples indicate inlined function calls that were never actually
+/// invoked at runtime. Ignore these callsites for coverage purposes.
+uint64_t
+SampleCoverageTracker::countBodySamples(const FunctionSamples *FS) const {
+  uint64_t Total = 0;
+  for (const auto &I : FS->getBodySamples())
+    Total += I.second.getSamples();
+
+  // Count all the callsites with non-zero samples.
+  for (const auto &I : FS->getCallsiteSamples()) {
+    const FunctionSamples *CalleeSamples = &I.second;
+    if (CalleeSamples->getTotalSamples() > 0)
+      Total += countBodySamples(CalleeSamples);
+  }
+
+  return Total;
+}
+
 /// Return the fraction of sample records used in this profile.
 ///
 /// The returned value is an unsigned integer in the range 0-100 indicating
@@ -361,7 +408,7 @@ SampleProfileLoader::getInstWeight(const Instruction &Inst) const {
   ErrorOr<uint64_t> R = FS->findSamplesAt(LineOffset, Discriminator);
   if (R) {
     bool FirstMark =
-        CoverageTracker.markSamplesUsed(FS, LineOffset, Discriminator);
+        CoverageTracker.markSamplesUsed(FS, LineOffset, Discriminator, R.get());
     if (FirstMark) {
       const Function *F = Inst.getParent()->getParent();
       LLVMContext &Ctx = F->getContext();
@@ -1027,11 +1074,11 @@ bool SampleProfileLoader::emitAnnotations(Function &F) {
   }
 
   // If coverage checking was requested, compute it now.
-  if (SampleProfileCoverage) {
-    unsigned Used = CoverageTracker.countUsedSamples(Samples);
-    unsigned Total = CoverageTracker.countBodySamples(Samples);
+  if (SampleProfileRecordCoverage) {
+    unsigned Used = CoverageTracker.countUsedRecords(Samples);
+    unsigned Total = CoverageTracker.countBodyRecords(Samples);
     unsigned Coverage = CoverageTracker.computeCoverage(Used, Total);
-    if (Coverage < SampleProfileCoverage) {
+    if (Coverage < SampleProfileRecordCoverage) {
       F.getContext().diagnose(DiagnosticInfoSampleProfile(
           getDISubprogram(&F)->getFilename(), getFunctionLoc(F),
           Twine(Used) + " of " + Twine(Total) + " available profile records (" +
@@ -1040,6 +1087,18 @@ bool SampleProfileLoader::emitAnnotations(Function &F) {
     }
   }
 
+  if (SampleProfileSampleCoverage) {
+    uint64_t Used = CoverageTracker.getTotalUsedSamples();
+    uint64_t Total = CoverageTracker.countBodySamples(Samples);
+    unsigned Coverage = CoverageTracker.computeCoverage(Used, Total);
+    if (Coverage < SampleProfileSampleCoverage) {
+      F.getContext().diagnose(DiagnosticInfoSampleProfile(
+          getDISubprogram(&F)->getFilename(), getFunctionLoc(F),
+          Twine(Used) + " of " + Twine(Total) + " available profile samples (" +
+              Twine(Coverage) + "%) were applied",
+          DS_Warning));
+    }
+  }
   return Changed;
 }