diff --git a/compiler-rt/test/profile/gcov-complex-line.c b/compiler-rt/test/profile/gcov-complex-line.c new file mode 100644 index 00000000000000..86286c4a93a582 --- /dev/null +++ b/compiler-rt/test/profile/gcov-complex-line.c @@ -0,0 +1,55 @@ +// This test checks that the cycle detection algorithm in llvm-cov is able to +// handle complex block graphs by skipping zero count cycles. +// +// RUN: mkdir -p %t.dir && cd %t.dir +// RUN: %clang --coverage %s -o %t +// RUN: rm -f gcov-complex-line.gcda && %run %t +// RUN: llvm-cov gcov -t gcov-complex-line.c | FileCheck %s + +#define M_0 \ + do { \ + if (x == 0) \ + x = 0; \ + else \ + x = 1; \ + } while (0) +#define M_1 \ + do { \ + M_0; \ + M_0; \ + M_0; \ + M_0; \ + } while (0) +#define M_2 \ + do { \ + M_1; \ + M_1; \ + M_1; \ + M_1; \ + } while (0) +#define M_3 \ + do { \ + M_2; \ + M_2; \ + M_2; \ + M_2; \ + } while (0) +#define M_4 \ + do { \ + M_3; \ + M_3; \ + M_3; \ + M_3; \ + } while (0) +#define COMPLEX_LINE \ + do { \ + for (int i = 0; i < 100; ++i) \ + M_4; \ + } while (0) + +int main() { + volatile int x = 0; + // In the following line, the number of cycles in the block graph is at least + // 2^256, where 256 is the number of expansions of M_0. + COMPLEX_LINE; // CHECK: 101: [[#@LINE]]: COMPLEX_LINE; +} diff --git a/llvm/include/llvm/ProfileData/GCOV.h b/llvm/include/llvm/ProfileData/GCOV.h index 2766ff52e4a09e..b15c5a6a6c651c 100644 --- a/llvm/include/llvm/ProfileData/GCOV.h +++ b/llvm/include/llvm/ProfileData/GCOV.h @@ -295,6 +295,7 @@ class GCOVBlock { static uint64_t getCycleCount(const Edges &Path); static void unblock(const GCOVBlock *U, BlockVector &Blocked, BlockVectorLists &BlockLists); + static void trimZeroCountSuffix(Edges &Path); static bool lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, Edges &Path, BlockVector &Blocked, BlockVectorLists &BlockLists, diff --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp index c0735250cfd8f7..b8bc0cc4837658 100644 --- a/llvm/lib/ProfileData/GCOV.cpp +++ b/llvm/lib/ProfileData/GCOV.cpp @@ -427,6 +427,11 @@ LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } // The algorithm in GCC is based on the algorithm by Hawick & James: // "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs" // http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf. +// +// An optimization is to skip any arc with zero count and to backtrack if the +// current path has such arcs: any cycle with such arc makes no contribution to +// the final cycle count. This reduces the complexity from exponential to +// polynomial of the arcs. /// Get the count for the detected cycle. uint64_t GCOVBlock::getCycleCount(const Edges &Path) { @@ -458,6 +463,15 @@ void GCOVBlock::unblock(const GCOVBlock *U, BlockVector &Blocked, } } +void GCOVBlock::trimZeroCountSuffix(Edges &Path) { + for (size_t index = 0; index < Path.size(); ++index) { + if (Path[index]->cycleCount == 0) { + Path.resize(index); + return; + } + } +} + bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, Edges &Path, BlockVector &Blocked, BlockVectorLists &BlockLists, @@ -465,10 +479,11 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, Blocked.push_back(V); BlockLists.emplace_back(BlockVector()); bool FoundCircuit = false; + const size_t PrefixLength = Path.size(); for (auto E : V->dsts()) { const GCOVBlock *W = &E->dst; - if (W < Start || find(Blocks, W) == Blocks.end()) { + if (E->cycleCount == 0 || W < Start || find(Blocks, W) == Blocks.end()) { continue; } @@ -477,6 +492,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, if (W == Start) { // We've a cycle. Count += GCOVBlock::getCycleCount(Path); + trimZeroCountSuffix(Path); FoundCircuit = true; } else if (find(Blocked, W) == Blocked.end() && // W is not blocked. GCOVBlock::lookForCircuit(W, Start, Path, Blocked, BlockLists, @@ -484,7 +500,10 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, FoundCircuit = true; } - Path.pop_back(); + if (Path.size() > PrefixLength) + Path.pop_back(); + else if (Path.size() < PrefixLength) + break; } if (FoundCircuit) { @@ -492,7 +511,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, } else { for (auto E : V->dsts()) { const GCOVBlock *W = &E->dst; - if (W < Start || find(Blocks, W) == Blocks.end()) { + if (E->cycleCount == 0 || W < Start || find(Blocks, W) == Blocks.end()) { continue; } const size_t index = find(Blocked, W) - Blocked.begin();