[compiler-rt] b9d0866 - [llvm-cov gcov] Compute unmeasured arc counts by Kirchhoff's circuit law

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 18:48:09 PDT 2020


Author: Fangrui Song
Date: 2020-09-08T18:45:11-07:00
New Revision: b9d086693b5baebc477793af0d86a447bae01b6f

URL: https://github.com/llvm/llvm-project/commit/b9d086693b5baebc477793af0d86a447bae01b6f
DIFF: https://github.com/llvm/llvm-project/commit/b9d086693b5baebc477793af0d86a447bae01b6f.diff

LOG: [llvm-cov gcov] Compute unmeasured arc counts by Kirchhoff's circuit law

For a CFG G=(V,E), Knuth describes that by Kirchoff's circuit law, the minimum
number of counters necessary is |E|-(|V|-1). The emitted edges form a spanning
tree. libgcov emitted .gcda files leverages this optimization while clang
--coverage's doesn't.

Propagate counts by Kirchhoff's circuit law so that llvm-cov gcov can
correctly print line counts of gcc --coverage emitted files and enable
the future improvement of clang --coverage.

Added: 
    

Modified: 
    compiler-rt/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
    llvm/include/llvm/ProfileData/GCOV.h
    llvm/lib/ProfileData/GCOV.cpp
    llvm/test/tools/llvm-cov/gcov-4.7.c
    llvm/test/tools/llvm-cov/gcov-8.c
    llvm/test/tools/llvm-cov/gcov-9.c

Removed: 
    


################################################################################
diff  --git a/compiler-rt/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov b/compiler-rt/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
index d1104b7f5bbf..4debf8fc1b68 100644
--- a/compiler-rt/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
+++ b/compiler-rt/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
@@ -3,7 +3,7 @@
 // CHECK-NEXT:        -:    0:Data:instrprof-gcov-multiple-bbs-single-line.gcda
 // CHECK-NEXT:        -:    0:Runs:1
 // CHECK-NEXT:        -:    0:Programs:1
-// CHECK-NEXT:function main called 1 returned 100% blocks executed 80%
+// CHECK-NEXT:function main called 1 returned 100% blocks executed 77%
 // CHECK-NEXT:        1:    1:int main(void)
 // CHECK-NEXT:        -:    2:{
 // CHECK-NEXT:        -:    3:  int var;

diff  --git a/llvm/include/llvm/ProfileData/GCOV.h b/llvm/include/llvm/ProfileData/GCOV.h
index 7b9ba4410b65..f87eab6d3ead 100644
--- a/llvm/include/llvm/ProfileData/GCOV.h
+++ b/llvm/include/llvm/ProfileData/GCOV.h
@@ -212,12 +212,13 @@ class GCOVFile {
 };
 
 struct GCOVArc {
-  GCOVArc(GCOVBlock &src, GCOVBlock &dst, bool fallthrough)
-      : src(src), dst(dst), fallthrough(fallthrough) {}
+  GCOVArc(GCOVBlock &src, GCOVBlock &dst, uint32_t flags)
+      : src(src), dst(dst), flags(flags) {}
+  bool onTree() const;
 
   GCOVBlock &src;
   GCOVBlock &dst;
-  bool fallthrough;
+  uint32_t flags;
   uint64_t Count = 0;
   uint64_t CyclesCount = 0;
 };
@@ -234,7 +235,7 @@ class GCOVFunction {
   StringRef getFilename() const;
   size_t getNumBlocks() const { return Blocks.size(); }
   uint64_t getEntryCount() const;
-  uint64_t getExitCount() const;
+  GCOVBlock &getExitBlock() const;
 
   BlockIterator block_begin() const { return Blocks.begin(); }
   BlockIterator block_end() const { return Blocks.end(); }
@@ -242,6 +243,7 @@ class GCOVFunction {
     return make_range(block_begin(), block_end());
   }
 
+  uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *arc);
   void print(raw_ostream &OS) const;
   void dump() const;
   void collectLineCounts(FileInfo &FI);

diff  --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp
index 7b97723da60c..0292e2a09d17 100644
--- a/llvm/lib/ProfileData/GCOV.cpp
+++ b/llvm/lib/ProfileData/GCOV.cpp
@@ -108,11 +108,10 @@ bool GCOVFile::readGCNO(GCOVBuffer &buf) {
       for (uint32_t i = 0, e = (length - 1) / 2; i != e; ++i) {
         uint32_t dstNo = buf.getWord(), flags = buf.getWord();
         GCOVBlock *dst = fn->Blocks[dstNo].get();
-        auto arc =
-            std::make_unique<GCOVArc>(*src, *dst, flags & GCOV_ARC_FALLTHROUGH);
+        auto arc = std::make_unique<GCOVArc>(*src, *dst, flags);
         src->addDstEdge(arc.get());
         dst->addSrcEdge(arc.get());
-        if (flags & GCOV_ARC_ON_TREE)
+        if (arc->onTree())
           fn->treeArcs.push_back(std::move(arc));
         else
           fn->arcs.push_back(std::move(arc));
@@ -226,6 +225,17 @@ bool GCOVFile::readGCDA(GCOVBuffer &buf) {
         if (arc->dst.succ.empty())
           arc->dst.Counter += arc->Count;
       }
+
+      if (fn->Blocks.size() >= 2) {
+        GCOVBlock &src = *fn->Blocks[0];
+        GCOVBlock &sink =
+            Version < GCOV::V408 ? *fn->Blocks.back() : *fn->Blocks[1];
+        auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE);
+        sink.addDstEdge(arc.get());
+        src.addSrcEdge(arc.get());
+        fn->treeArcs.push_back(std::move(arc));
+        fn->propagateCounts(src, nullptr);
+      }
     }
     pos += 4 * length;
     if (pos < buf.cursor.tell())
@@ -260,6 +270,8 @@ void GCOVFile::collectLineCounts(FileInfo &fi) {
   fi.setProgramCount(ProgramCount);
 }
 
+bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; }
+
 //===----------------------------------------------------------------------===//
 // GCOVFunction implementation.
 
@@ -271,10 +283,27 @@ uint64_t GCOVFunction::getEntryCount() const {
   return Blocks.front()->getCount();
 }
 
-/// getExitCount - Get the number of times the function returned by retrieving
-/// the exit block's count.
-uint64_t GCOVFunction::getExitCount() const {
-  return Blocks.back()->getCount();
+GCOVBlock &GCOVFunction::getExitBlock() const {
+  return file.getVersion() < GCOV::V408 ? *Blocks.back() : *Blocks[1];
+}
+
+// For each basic block, the sum of incoming edge counts equals the sum of
+// outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a
+// spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be
+// uniquely identified.
+uint64_t GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) {
+  uint64_t excess = 0;
+  for (GCOVArc *e : v.srcs())
+    if (e != pred)
+      excess += e->onTree() ? propagateCounts(e->src, e) : e->Count;
+  for (GCOVArc *e : v.dsts())
+    if (e != pred)
+      excess -= e->onTree() ? propagateCounts(e->dst, e) : e->Count;
+  if (int64_t(excess) < 0)
+    excess = -excess;
+  if (pred)
+    pred->Count = excess;
+  return excess;
 }
 
 void GCOVFunction::print(raw_ostream &OS) const {
@@ -322,8 +351,11 @@ void GCOVBlock::print(raw_ostream &OS) const {
   }
   if (!succ.empty()) {
     OS << "\tDestination Edges : ";
-    for (const GCOVArc *Edge : succ)
+    for (const GCOVArc *Edge : succ) {
+      if (Edge->flags & GCOV_ARC_ON_TREE)
+        OS << '*';
       OS << Edge->dst.Number << " (" << Edge->Count << "), ";
+    }
     OS << "\n";
   }
   if (!Lines.empty()) {
@@ -441,7 +473,7 @@ uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) {
   uint64_t Count = 0;
 
   for (auto Block : Blocks) {
-    if (Block->getNumSrcEdges() == 0) {
+    if (Block->getNumSrcEdges() == 0 || Block->Number == 0) {
       // The block has no predecessors and a non-null counter
       // (can be the case with entry block in functions).
       Count += Block->getCount();
@@ -467,11 +499,13 @@ uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) {
 //===----------------------------------------------------------------------===//
 // FileInfo implementation.
 
-// Safe integer division, returns 0 if numerator is 0.
-static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
-  if (!Numerator)
+// Format dividend/divisor as a percentage. Return 1 if the result is greater
+// than 0% and less than 1%.
+static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) {
+  if (!dividend || !divisor)
     return 0;
-  return Numerator / Divisor;
+  dividend *= 100;
+  return dividend < divisor ? 1 : dividend / divisor;
 }
 
 // This custom division function mimics gcov's branch ouputs:
@@ -794,14 +828,15 @@ void FileInfo::printFunctionSummary(raw_ostream &OS,
   for (const GCOVFunction *Func : Funcs) {
     uint64_t EntryCount = Func->getEntryCount();
     uint32_t BlocksExec = 0;
+    const GCOVBlock &ExitBlock = Func->getExitBlock();
     for (const GCOVBlock &Block : Func->blocks())
-      if (Block.getNumDstEdges() && Block.getCount())
+      if (Block.Number != 0 && &Block != &ExitBlock && Block.getCount())
         ++BlocksExec;
 
     OS << "function " << Func->getName() << " called " << EntryCount
-       << " returned " << safeDiv(Func->getExitCount() * 100, EntryCount)
+       << " returned " << formatPercentage(ExitBlock.getCount(), EntryCount)
        << "% blocks executed "
-       << safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n";
+       << formatPercentage(BlocksExec, Func->getNumBlocks() - 2) << "%\n";
   }
 }
 

diff  --git a/llvm/test/tools/llvm-cov/gcov-4.7.c b/llvm/test/tools/llvm-cov/gcov-4.7.c
index d92953a6b0b6..211c635f5128 100644
--- a/llvm/test/tools/llvm-cov/gcov-4.7.c
+++ b/llvm/test/tools/llvm-cov/gcov-4.7.c
@@ -1,27 +1,25 @@
 /// Test that llvm-cov supports gcov [4.7,8) compatible format.
 #include <math.h>
 #include <stdio.h>
-int main() {                                      // GCOV:      #####: [[@LINE]]:int main
-  double a[11], result;                           // GCOV-NEXT: -: [[@LINE]]:
-  for (int i = 0; i < 11; i++)                    // GCOV-NEXT: #####: [[@LINE]]:
+int main() {                                      // GCOV:       1: [[@LINE]]:int main
+  double a[11], result;                           // GCOV-NEXT:  -: [[@LINE]]:
+  for (int i = 0; i < 11; i++)                    // GCOV-NEXT: 12: [[@LINE]]:
     scanf("%lf", &a[i]);                          // GCOV-NEXT: 11: [[@LINE]]:
-  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 4: [[@LINE]]:
+  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 12: [[@LINE]]:
     result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
     printf("\nf(%lf) = ");                        // GCOV-NEXT: 11: [[@LINE]]:
-    if (result > 400) printf("Overflow!");        // GCOV-NEXT: #####: [[@LINE]]:
-    else printf("%lf", result);                   // GCOV-NEXT: 4: [[@LINE]]:
-  }                                               // GCOV-NEXT: -: [[@LINE]]:
-  return 0;                                       // GCOV-NEXT: #####: [[@LINE]]:
-}                                                 // GCOV-NEXT: -: [[@LINE]]:
-/// FIXME several lines do not match gcov 7
+    if (result > 400) printf("Overflow!");        // GCOV-NEXT: 11: [[@LINE]]:
+    else printf("%lf", result);                   // GCOV-NEXT:  4: [[@LINE]]:
+  }                                               // GCOV-NEXT:  -: [[@LINE]]:
+  return 0;                                       // GCOV-NEXT:  1: [[@LINE]]:
+}                                                 // GCOV-NEXT:  -: [[@LINE]]:
 
 // RUN: rm -rf %t && mkdir %t && cd %t
 // RUN: cp %s %p/Inputs/gcov-4.7.gc* .
 
-/// FIXME Lines executed:100.00% of 12
 // RUN: llvm-cov gcov gcov-4.7.c | FileCheck %s
 // CHECK:      File 'gcov-4.7.c'
-// CHECK-NEXT: Lines executed:55.56% of 9
+// CHECK-NEXT: Lines executed:100.00% of 9
 // CHECK-NEXT: Creating 'gcov-4.7.c.gcov'
 
 // RUN: FileCheck --input-file=%t/gcov-4.7.c.gcov --check-prefix=HEADER %s

diff  --git a/llvm/test/tools/llvm-cov/gcov-8.c b/llvm/test/tools/llvm-cov/gcov-8.c
index eef3511e93a7..996e4cbe71b3 100644
--- a/llvm/test/tools/llvm-cov/gcov-8.c
+++ b/llvm/test/tools/llvm-cov/gcov-8.c
@@ -1,29 +1,27 @@
 /// Test that llvm-cov supports gcov 8 compatible format.
 #include <math.h>
 #include <stdio.h>
-int main() {                                      // GCOV:      1: [[@LINE]]:int main
-  double a[11], result;                           // GCOV-NEXT: -: [[@LINE]]:
+int main() {                                      // GCOV:       1: [[@LINE]]:int main
+  double a[11], result;                           // GCOV-NEXT:  -: [[@LINE]]:
   for (int i = 0; i < 11; i++)                    // GCOV-NEXT: 12: [[@LINE]]:
     scanf("%lf", &a[i]);                          // GCOV-NEXT: 11: [[@LINE]]:
-  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 7: [[@LINE]]:
+  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 12: [[@LINE]]:
     result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
     printf("\nf(%lf) = ");                        // GCOV-NEXT: 11: [[@LINE]]:
     if (result > 400) printf("Overflow!");        // GCOV-NEXT: 11: [[@LINE]]:
-    else printf("%lf", result);                   // GCOV-NEXT: #####: [[@LINE]]:
-  }                                               // GCOV-NEXT: -: [[@LINE]]:
-  return 0;                                       // GCOV-NEXT: #####: [[@LINE]]:
-}                                                 // GCOV-NEXT: -: [[@LINE]]:
-/// FIXME several lines do not match gcov 8
+    else printf("%lf", result);                   // GCOV-NEXT:  4: [[@LINE]]:
+  }                                               // GCOV-NEXT:  -: [[@LINE]]:
+  return 0;                                       // GCOV-NEXT:  1: [[@LINE]]:
+}                                                 // GCOV-NEXT:  -: [[@LINE]]:
 
 // RUN: rm -rf %t && mkdir %t && cd %t
 // RUN: cp %s %p/Inputs/gcov-8.gc* .
 
-/// FIXME Lines executed:100.00% of 12
 // RUN: llvm-cov gcov gcov-8.c | FileCheck %s --check-prefixes=OUT,OUTFILE
 // OUT:          File 'gcov-8.c'
-// OUT-NEXT:     Lines executed:77.78% of 9
+// OUT-NEXT:     Lines executed:100.00% of 9
 // OUT-B-NEXT:   Branches executed:85.71% of 14
-// OUT-B-NEXT:   Taken at least once:42.86% of 14
+// OUT-B-NEXT:   Taken at least once:71.43% of 14
 // OUT-B-NEXT:   No calls
 // OUTFILE-NEXT: Creating 'gcov-8.c.gcov'
 // OUT-EMPTY:
@@ -51,23 +49,23 @@ int main() {                                      // GCOV:      1: [[@LINE]]:int
 //   I-NEXT:lcount:4,1
 //   I-NEXT:lcount:6,12
 // I-B-NEXT:branch:6,taken
-// I-B-NEXT:branch:6,nottaken
+// I-B-NEXT:branch:6,taken
 //   I-NEXT:lcount:7,11
 // I-B-NEXT:branch:7,taken
 // I-B-NEXT:branch:7,nottaken
-//   I-NEXT:lcount:8,7
+//   I-NEXT:lcount:8,12
+// I-B-NEXT:branch:8,taken
 // I-B-NEXT:branch:8,taken
-// I-B-NEXT:branch:8,nottaken
 //   I-NEXT:lcount:9,11
 //   I-NEXT:lcount:10,11
 // I-B-NEXT:branch:10,taken
 // I-B-NEXT:branch:10,nottaken
 //   I-NEXT:lcount:11,11
 // I-B-NEXT:branch:11,taken
-// I-B-NEXT:branch:11,nottaken
+// I-B-NEXT:branch:11,taken
 // I-B-NEXT:branch:11,taken
 // I-B-NEXT:branch:11,nottaken
-//   I-NEXT:lcount:12,0
+//   I-NEXT:lcount:12,4
 // I-B-NEXT:branch:12,notexec
 // I-B-NEXT:branch:12,notexec
-//   I-NEXT:lcount:14,0
+//   I-NEXT:lcount:14,1

diff  --git a/llvm/test/tools/llvm-cov/gcov-9.c b/llvm/test/tools/llvm-cov/gcov-9.c
index 335e6c0663db..a2e9cf474973 100644
--- a/llvm/test/tools/llvm-cov/gcov-9.c
+++ b/llvm/test/tools/llvm-cov/gcov-9.c
@@ -1,27 +1,25 @@
 /// Test that llvm-cov supports gcov 9 compatible format.
 #include <math.h>
 #include <stdio.h>
-int main() {                                      // GCOV:      1: [[@LINE]]:int main
-  double a[11], result;                           // GCOV-NEXT: -: [[@LINE]]:
+int main() {                                      // GCOV:       1: [[@LINE]]:int main
+  double a[11], result;                           // GCOV-NEXT:  -: [[@LINE]]:
   for (int i = 0; i < 11; i++)                    // GCOV-NEXT: 12: [[@LINE]]:
     scanf("%lf", &a[i]);                          // GCOV-NEXT: 11: [[@LINE]]:
-  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 7: [[@LINE]]:
+  for (int i = 10; i >= 0; i--) {                 // GCOV-NEXT: 12: [[@LINE]]:
     result = sqrt(fabs(a[i])) + 5 * pow(a[i], 3); // GCOV-NEXT: 11: [[@LINE]]:
     printf("\nf(%lf) = ");                        // GCOV-NEXT: 11: [[@LINE]]:
     if (result > 400) printf("Overflow!");        // GCOV-NEXT: 11: [[@LINE]]:
-    else printf("%lf", result);                   // GCOV-NEXT: #####: [[@LINE]]:
-  }                                               // GCOV-NEXT: -: [[@LINE]]:
-  return 0;                                       // GCOV-NEXT: #####: [[@LINE]]:
-}                                                 // GCOV-NEXT: -: [[@LINE]]:
-/// FIXME several lines do not match gcov 9
+    else printf("%lf", result);                   // GCOV-NEXT:  4: [[@LINE]]:
+  }                                               // GCOV-NEXT:  -: [[@LINE]]:
+  return 0;                                       // GCOV-NEXT:  1: [[@LINE]]:
+}                                                 // GCOV-NEXT:  -: [[@LINE]]:
 
 // RUN: rm -rf %t && mkdir %t && cd %t
 // RUN: cp %s %p/Inputs/gcov-9.gc* .
 
-/// FIXME Lines executed:100.00% of 12
 // RUN: llvm-cov gcov gcov-9.c | FileCheck %s
 // CHECK:      File 'gcov-9.c'
-// CHECK-NEXT: Lines executed:77.78% of 9
+// CHECK-NEXT: Lines executed:100.00% of 9
 // CHECK-NEXT: Creating 'gcov-9.c.gcov'
 
 // RUN: FileCheck --input-file=%t/gcov-9.c.gcov --check-prefix=HEADER %s


        


More information about the llvm-commits mailing list