[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