Commit 10b7602f by Nathan Sidwell Committed by Nathan Sidwell

gcov.c (struct arc_info): Replace local_span with cycle.

.	* gcov.c (struct arc_info): Replace local_span with cycle.
	(struct block_info): Replace u.span with u.cycle. Add is_call_return.
	(solve_flow_graph): Set is_call_return.
	(add_line_counts): Adjust. In block mode, blocks attach to last line.
	(accumulate_line_counts): Find graph cycles, not spanning tree.
	(output_branch_count): Adjust.
	(output_lines): Adjust.
	* doc/gcov.texi: Update.
testsuite:
	* gcc.misc-test/gcov-9.c: New test.
	* gcc.misc-test/gcov-10.c: New test
	* gcc.misc-test/gcov-11.c: New test.

From-SVN: r65299
parent 910c46b5
2003-04-05 Nathan Sidwell <nathan@codesourcery.com>
* gcov.c (struct arc_info): Replace local_span with cycle.
(struct block_info): Replace u.span with u.cycle. Add is_call_return.
(solve_flow_graph): Set is_call_return.
(add_line_counts): Adjust. In block mode, blocks attach to last line.
(accumulate_line_counts): Find graph cycles, not spanning tree.
(output_branch_count): Adjust.
(output_lines): Adjust.
* doc/gcov.texi: Update.
2003-04-06 Kazu Hirata <kazu@cs.umass.edu> 2003-04-06 Kazu Hirata <kazu@cs.umass.edu>
* config/h8300/h8300.md (*zero_extendqisi2_h8300hs): Change * config/h8300/h8300.md (*zero_extendqisi2_h8300hs): Change
......
...@@ -152,11 +152,7 @@ and exit without doing any further processing. ...@@ -152,11 +152,7 @@ and exit without doing any further processing.
Write individual execution counts for every basic block. Normally gcov Write individual execution counts for every basic block. Normally gcov
outputs execution counts only for the main blocks of a line. With this outputs execution counts only for the main blocks of a line. With this
option you can determine if blocks within a single line are not being option you can determine if blocks within a single line are not being
executed. In this mode each block is shown, and contributes to the executed.
occupancy and execution count of, the first line of source that it
contains. A multi-line block will only contribute to that first line,
and other lines will not be show to contain code, unless a subsequent
block begins on those lines.
@item -b @item -b
@itemx --branch-probabilities @itemx --branch-probabilities
...@@ -327,6 +323,17 @@ function main called 1 returned 1 blocks executed 75% ...@@ -327,6 +323,17 @@ function main called 1 returned 1 blocks executed 75%
-: 17:@} -: 17:@}
@end smallexample @end smallexample
In this mode, each basic block is only shown on one line -- the last
line of the block. A multi-line block will only contribute to the
execution count of that last line, and other lines will not be shown
to contain code, unless previous blocks end on those lines.
The total execution count of a line is shown and subsequent lines show
the execution counts for individual blocks that end on that line. After each
block, the branch and call counts of the block will be shown, if the
@option{-b} option is given.
Because of the way gcc instruments calls, a call count can be shown
after a line with no individual blocks.
As you can see, line 13 contains a basic block that was not executed. As you can see, line 13 contains a basic block that was not executed.
@need 450 @need 450
......
...@@ -96,9 +96,9 @@ typedef struct arc_info ...@@ -96,9 +96,9 @@ typedef struct arc_info
/* Is an unconditional branch. */ /* Is an unconditional branch. */
unsigned int is_unconditional : 1; unsigned int is_unconditional : 1;
/* Arc on the local block spanning tree. */ /* Loop making arc. */
unsigned int local_span : 1; unsigned int cycle : 1;
/* Next branch on line. */ /* Next branch on line. */
struct arc_info *line_next; struct arc_info *line_next;
...@@ -128,7 +128,8 @@ typedef struct block_info ...@@ -128,7 +128,8 @@ typedef struct block_info
unsigned invalid_chain : 1; unsigned invalid_chain : 1;
/* Block is a call instrumenting site. */ /* Block is a call instrumenting site. */
unsigned is_call_site : 1; unsigned is_call_site : 1; /* Does the call. */
unsigned is_call_return : 1; /* Is the return. */
/* Block is a landing pad for longjmp or throw. */ /* Block is a landing pad for longjmp or throw. */
unsigned is_nonlocal_return : 1; unsigned is_nonlocal_return : 1;
...@@ -146,10 +147,11 @@ typedef struct block_info ...@@ -146,10 +147,11 @@ typedef struct block_info
} line; /* Valid until blocks are linked onto lines */ } line; /* Valid until blocks are linked onto lines */
struct struct
{ {
/* Single line spanning tree workspace. Used for all-blocks mode. */ /* Single line graph cycle workspace. Used for all-blocks
struct block_info *root; mode. */
unsigned siblings; arc_t *arc;
} span; /* Used in all-blocks mode, after blocks are linked onto unsigned ident;
} cycle; /* Used in all-blocks mode, after blocks are linked onto
lines. */ lines. */
} u; } u;
...@@ -1185,17 +1187,15 @@ solve_flow_graph (fn) ...@@ -1185,17 +1187,15 @@ solve_flow_graph (fn)
arc->is_unconditional = 1; arc->is_unconditional = 1;
/* If this block is instrumenting a call, it might be /* If this block is instrumenting a call, it might be
an artifical block. It is not artificial if it has an artifical block. It is not artificial if it has
a non-fallthrough exit, or the destination of the a non-fallthrough exit, or the destination of this
exit has more than one entry. */ arc has more than one entry. Mark the destination
if (!arc->fall_through block as a return site, if none of those conditions
|| arc->dst->pred != arc || arc->pred_next) hold. */
blk->is_call_site = 0; if (blk->is_call_site && arc->fall_through
&& arc->dst->pred == arc && !arc->pred_next)
arc->dst->is_call_return = 1;
} }
} }
else
/* If there is more than one exit, it cannot be an artificial
call instrumenting site. */
blk->is_call_site = 0;
/* Sort the successor arcs into ascending dst order. profile.c /* Sort the successor arcs into ascending dst order. profile.c
normally produces arcs in the right order, but sometimes with normally produces arcs in the right order, but sometimes with
...@@ -1558,7 +1558,6 @@ add_line_counts (coverage, fn) ...@@ -1558,7 +1558,6 @@ add_line_counts (coverage, fn)
unsigned *encoding; unsigned *encoding;
const source_t *src = NULL; const source_t *src = NULL;
unsigned jx; unsigned jx;
line_t *first_line = NULL;
if (block->count && ix && ix + 1 != fn->num_blocks) if (block->count && ix && ix + 1 != fn->num_blocks)
fn->blocks_executed++; fn->blocks_executed++;
...@@ -1585,23 +1584,19 @@ add_line_counts (coverage, fn) ...@@ -1585,23 +1584,19 @@ add_line_counts (coverage, fn)
} }
line->exists = 1; line->exists = 1;
line->count += block->count; line->count += block->count;
if (!first_line)
first_line = line;
} }
free (block->u.line.encoding); free (block->u.line.encoding);
block->u.span.root = NULL; block->u.cycle.arc = NULL;
if (!first_line) block->u.cycle.ident = ~0U;
first_line = line;
if (!ix || ix + 1 == fn->num_blocks) if (!ix || ix + 1 == fn->num_blocks)
/* Entry or exit block */; /* Entry or exit block */;
else if (flag_all_blocks) else if (flag_all_blocks)
{ {
if (!first_line) line_t *block_line = line ? line : &fn->src->lines[fn->line];
first_line = &fn->src->lines[fn->line];
block->chain = first_line->u.blocks; block->chain = block_line->u.blocks;
first_line->u.blocks = block; block_line->u.blocks = block;
} }
else if (flag_branches) else if (flag_branches)
{ {
...@@ -1661,10 +1656,10 @@ accumulate_line_counts (src) ...@@ -1661,10 +1656,10 @@ accumulate_line_counts (src)
/* The user expects the line count to be the number of times /* The user expects the line count to be the number of times
a line has been executed. Simply summing the block count a line has been executed. Simply summing the block count
will give an artificially high number. The Right Thing will give an artificially high number. The Right Thing
is to generate the spanning tree of the blocks on this is to sum the entry counts to the graph of blocks on this
line, and the sum the entry arcs to that tree. */ line, then find the elementary cycles of the local graph
and add the transition counts of those cycles. */
block_t *block, *block_p, *block_n; block_t *block, *block_p, *block_n;
int changes = 1;
gcov_type count = 0; gcov_type count = 0;
/* Reverse the block information */ /* Reverse the block information */
...@@ -1673,77 +1668,110 @@ accumulate_line_counts (src) ...@@ -1673,77 +1668,110 @@ accumulate_line_counts (src)
{ {
block_n = block->chain; block_n = block->chain;
block->chain = block_p; block->chain = block_p;
/* Each block is it's own spanning tree, with no siblings */ block->u.cycle.ident = ix;
block->u.span.root = block;
block->u.span.siblings = 0;
} }
line->u.blocks = block_p; line->u.blocks = block_p;
/* Sum the entry arcs. */
for (block = line->u.blocks; block; block = block->chain)
{
arc_t *arc;
while (changes) for (arc = block->pred; arc; arc = arc->pred_next)
{
if (arc->src->u.cycle.ident != ix)
count += arc->count;
if (flag_branches)
add_branch_counts (&src->coverage, arc);
}
}
/* Find the loops. This uses the algorithm described in
Tiernan 'An Efficient Search Algorithm to Find the
Elementary Circuits of a Graph', CACM Dec 1970. We hold
the P array by having each block point to the arc that
connects to the previous block. The H array is implicitly
held because of the arc ordering, and the block's
previous arc pointer.
Although the algorithm is O(N^3) for highly connected
graphs, at worst we'll have O(N^2), as most blocks have
only one or two exits. Most graphs will be small.
For each loop we find, locate the arc with the smallest
transition count, and add that to the cumulative
count. Remove the arc from consideration. */
for (block = line->u.blocks; block; block = block->chain)
{ {
changes = 0; block_t *head = block;
arc_t *arc;
for (block = line->u.blocks; block; block = block->chain) next_vertex:;
arc = head->succ;
current_vertex:;
while (arc)
{ {
arc_t *arc; block_t *dst = arc->dst;
if (/* Already used that arc. */
arc->cycle
/* Not to same graph, or before first vertex. */
|| dst->u.cycle.ident != ix
/* Already in path. */
|| dst->u.cycle.arc)
{
arc = arc->succ_next;
continue;
}
for (arc = block->succ; arc; arc = arc->succ_next) if (dst == block)
{ {
block_t *dst = arc->dst; /* Found a closing arc. */
gcov_type cycle_count = arc->count;
arc_t *cycle_arc = arc;
arc_t *probe_arc;
if (!dst->u.span.root) /* Locate the smallest arc count of the loop. */
/* Not on this line. */; for (dst = head; (probe_arc = dst->u.cycle.arc);
else if (dst->u.span.root == block->u.span.root) dst = probe_arc->src)
/* Same spanning tree. */; if (cycle_count > probe_arc->count)
else {
cycle_count = probe_arc->count;
cycle_arc = probe_arc;
}
count += cycle_count;
cycle_arc->cycle = 1;
/* Unwind to the cyclic arc. */
while (head != cycle_arc->src)
{ {
block_t *root = block->u.span.root; arc = head->u.cycle.arc;
block_t *dst_root = dst->u.span.root; head = arc->src;
/* Join spanning trees */
if (root->u.span.siblings
&& !dst_root->u.span.siblings)
{
root = dst->u.span.root;
dst_root = block->u.span.root;
}
dst_root->u.span.root = root;
root->u.span.siblings
+= 1 + dst_root->u.span.siblings;
if (dst_root->u.span.siblings)
{
block_t *dst_sib;
dst_root->u.span.siblings = 0;
for (dst_sib = line->u.blocks; dst_sib;
dst_sib = dst_sib->chain)
if (dst_sib->u.span.root == dst_root)
dst_sib->u.span.root = root;
}
arc->local_span = 1;
changes = 1;
} }
/* Move on. */
arc = arc->succ_next;
continue;
} }
/* Add new block to chain. */
dst->u.cycle.arc = arc;
head = dst;
goto next_vertex;
} }
} /* We could not add another vertex to the path. Remove
the last vertex from the list. */
/* Now sum the entry counts */ arc = head->u.cycle.arc;
for (block = line->u.blocks; block; block = block->chain) if (arc)
{
arc_t *arc;
for (arc = block->succ; arc; arc = arc->succ_next)
{ {
if (!arc->local_span) /* It was not the first vertex. Move onto next arc. */
count += arc->count; head->u.cycle.arc = NULL;
if (flag_branches) head = arc->src;
add_branch_counts (&src->coverage, arc); arc = arc->succ_next;
goto current_vertex;
} }
block->u.span.root = NULL; /* Mark this block as unusable. */
block->u.cycle.ident = ~0U;
} }
line->count = count; line->count = count;
} }
...@@ -1786,7 +1814,7 @@ output_branch_count (gcov_file, ix, arc) ...@@ -1786,7 +1814,7 @@ output_branch_count (gcov_file, ix, arc)
else else
fnotice (gcov_file, "branch %2d never executed\n", ix); fnotice (gcov_file, "branch %2d never executed\n", ix);
} }
else if (flag_unconditional && !arc->src->is_call_site) else if (flag_unconditional && !arc->dst->is_call_return)
{ {
if (arc->src->count) if (arc->src->count)
fnotice (gcov_file, "unconditional %2d taken %s\n", ix, fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
...@@ -1895,17 +1923,17 @@ output_lines (gcov_file, src) ...@@ -1895,17 +1923,17 @@ output_lines (gcov_file, src)
if (flag_all_blocks) if (flag_all_blocks)
{ {
block_t *block; block_t *block;
arc_t *arc;
int ix, jx; int ix, jx;
for (ix = jx = 0, block = line->u.blocks; block; for (ix = jx = 0, block = line->u.blocks; block;
block = block->chain) block = block->chain)
{ {
arc_t *arc; if (!block->is_call_return)
if (!block->is_call_site)
fprintf (gcov_file, "%9s:%5u-block %2d\n", fprintf (gcov_file, "%9s:%5u-block %2d\n",
!line->exists ? "-" : !block->count ? "$$$$$" !line->exists ? "-" : !block->count ? "$$$$$"
: format_gcov (block->count, 0, -1), line_num, ix++); : format_gcov (block->count, 0, -1),
line_num, ix++);
if (flag_branches) if (flag_branches)
for (arc = block->succ; arc; arc = arc->succ_next) for (arc = block->succ; arc; arc = arc->succ_next)
jx += output_branch_count (gcov_file, jx, arc); jx += output_branch_count (gcov_file, jx, arc);
......
2003-04-06 Nathan Sidwell <nathan@codesourcery.com>
* gcc.misc-test/gcov-9.c: New test.
* gcc.misc-test/gcov-10.c: New test
* gcc.misc-test/gcov-11.c: New test.
2003-04-05 Zack Weinberg <zack@codesourcery.com> 2003-04-05 Zack Weinberg <zack@codesourcery.com>
PR optimization/10024 PR optimization/10024
......
/* Test gcov block mode. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-do run { target native } } */
int main ()
{
unsigned ix, jx = 0;
for (ix = 10; ix--;) if (ix & 1) jx++; /* count(11) */
return jx != 5;
}
/* { dg-final { run-gcov { -a gcov-10.c } } } */
/* Test gcov block mode. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-do run { target native } } */
int one = 1; /* subvert constant folder. */
int zero = 0;
int foo (int ix)
{
return ix & 1 ? one : zero; /* count(10) */
}
int main ()
{
unsigned ix, jx = 0;
for (ix = 10; ix--;) jx += foo (ix); /* count(11) */
return jx != 5;
}
/* { dg-final { run-gcov { -a gcov-11.c } } } */
/* Test gcov block mode. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-do run { target native } } */
int main ()
{
unsigned ix;
for (ix = 10; ix--;); /* count(11) */
return 0;
}
/* { dg-final { run-gcov { -a gcov-9.c } } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment