On Mon, Mar 6, 2023 at 8:22 AM Xionghu Luo <[email protected]> wrote:
>
>
>
> On 2023/3/2 18:45, Richard Biener wrote:
> >>
> >>
> >> small.gcno: 648: block 2:`small.c':1, 3, 4, 6
> >> small.gcno: 688: 01450000: 36:LINES
> >> small.gcno: 700: block 3:`small.c':8, 9
> >> small.gcno: 732: 01450000: 32:LINES
> >> small.gcno: 744: block 5:`small.c':10
> >> -small.gcno: 772: 01450000: 32:LINES
> >> -small.gcno: 784: block 6:`small.c':12
> >> -small.gcno: 812: 01450000: 36:LINES
> >> -small.gcno: 824: block 7:`small.c':12, 13
> >> +small.gcno: 772: 01450000: 36:LINES
> >> +small.gcno: 784: block 6:`small.c':12, 13
> >> +small.gcno: 816: 01450000: 32:LINES
> >> +small.gcno: 828: block 8:`small.c':14
> >> small.gcno: 856: 01450000: 32:LINES
> >> -small.gcno: 868: block 8:`small.c':14
> >> -small.gcno: 896: 01450000: 32:LINES
> >> -small.gcno: 908: block 9:`small.c':17
> >> +small.gcno: 868: block 9:`small.c':17
> >
> > Looking at the CFG and the instrumentation shows
> >
> > <bb 2> :
> > PROF_edge_counter_17 = __gcov0.f[0];
> > PROF_edge_counter_18 = PROF_edge_counter_17 + 1;
> > __gcov0.f[0] = PROF_edge_counter_18;
> > [t.c:3:7] p_6 = 0;
> > [t.c:5:3] switch (s_7(D)) <default: <L6> [INV], [t.c:7:5] case 0:
> > <L0> [INV], [t.c:11:5] case 1: <L3> [INV]>
> >
> > <bb 3> :
> > # n_1 = PHI <n_8(D)(2), [t.c:8:28] n_13(4)>
> > # p_3 = PHI <[t.c:3:7] p_6(2), [t.c:8:15] p_12(4)>
> > [t.c:7:5] <L0>:
> > [t.c:8:15] p_12 = p_3 + 1;
> > [t.c:8:28] n_13 = n_1 + -1;
> > [t.c:8:28] if (n_13 != 0)
> > goto <bb 4>; [INV]
> > else
> > goto <bb 5>; [INV]
> >
> > <bb 4> :
> > PROF_edge_counter_21 = __gcov0.f[2];
> > PROF_edge_counter_22 = PROF_edge_counter_21 + 1;
> > __gcov0.f[2] = PROF_edge_counter_22;
> > [t.c:7:5] goto <bb 3>; [100.00%]
> >
> > <bb 5> :
> > PROF_edge_counter_23 = __gcov0.f[3];
> > PROF_edge_counter_24 = PROF_edge_counter_23 + 1;
> > __gcov0.f[3] = PROF_edge_counter_24;
> > [t.c:9:16] _14 = p_12;
> > [t.c:9:16] goto <bb 10>; [INV]
> >
> > so the reason this goes wrong is that gcov associates the "wrong"
> > counter with the block containing
> > the 'case' label(s), for the case 0 it should have chosen the counter
> > from bb5 but it likely
> > computed the count of bb3?
> >
> > It might be that ordering blocks differently puts the instrumentation
> > to different blocks or it
> > makes gcovs association chose different blocks but that means it's
> > just luck and not fixing
> > the actual issue?
> >
> > To me it looks like the correct thing to investigate is switch
> > statement and/or case label
> > handling. One can also see that <L0> having line number 7 is wrong to
> > the extent that
> > the position of the label doesn't match the number of times it
> > executes in the source. So
> > placement of the label is wrong here, possibly caused by CFG cleanup
> > after CFG build
> > (but generally labels are not used for anything once the CFG is built
> > and coverage
> > instrumentation is late so it might fail due to us moving labels). It
> > might be OK to
> > avoid moving labels for --coverage but then coverage should possibly
> > look at edges
> > rather than labels?
> >
>
> Thanks, I investigated the Labels, it seems wrong at the beginning from
> .gimple to .cfg very early quite like PR90574:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90574
>
> .gimple:
>
> int f (int s, int n)
> [small.c:2:1] {
> int D.2755;
> int p;
>
> [small.c:3:7] p = 0;
> [small.c:5:3] switch (s) <default: <D.2756>, [small.c:7:5] case 0:
> <D.2743>, [small.c:11:5] case 1: <D.2744>>
> [small.c:7:5] <D.2743>: <= case label
> <D.2748>: <= loop label
> [small.c:8:13] p = p + 1;
> [small.c:8:26] n = n + -1;
> [small.c:8:26] if (n != 0) goto <D.2748>; else goto <D.2746>;
> <D.2746>:
> [small.c:9:14] D.2755 = p;
> [small.c:9:14] return D.2755;
> [small.c:11:5] <D.2744>:
> <D.2751>:
> [small.c:12:13] p = p + 1;
> [small.c:12:26] n = n + -1;
> [small.c:12:26] if (n != 0) goto <D.2751>; else goto <D.2749>;
> <D.2749>:
> [small.c:13:14] D.2755 = p;
> [small.c:13:14] return D.2755;
> <D.2756>:
> [small.c:16:10] D.2755 = 0;
> [small.c:16:10] return D.2755;
> }
>
> .cfg:
>
> int f (int s, int n)
> {
> int p;
> int D.2755;
>
> <bb 2> :
> [small.c:3:7] p = 0;
> [small.c:5:3] switch (s) <default: <L6> [INV], [small.c:7:5] case 0: <L0>
> [INV], [small.c:11:5] case 1: <L3> [INV]>
>
> <bb 3> :
> [small.c:7:5] <L0>: <= case 0
> [small.c:8:13 discrim 1] p = p + 1;
> [small.c:8:26 discrim 1] n = n + -1;
> [small.c:8:26 discrim 1] if (n != 0)
> goto <bb 3>; [INV]
> else
> goto <bb 4>; [INV]
>
> <bb 4> :
> [small.c:9:14] D.2755 = p;
> [small.c:9:14] goto <bb 8>; [INV]
>
> <bb 5> :
> [small.c:11:5] <L3>: <= case 1
> [small.c:12:13 discrim 1] p = p + 1;
> [small.c:12:26 discrim 1] n = n + -1;
> [small.c:12:26 discrim 1] if (n != 0)
> goto <bb 5>; [INV]
> else
> goto <bb 6>; [INV]
>
>
> The labels are merged into the loop unexpected, so I tried below fix
> for --coverage if two labels are not on same line to start new basic block:
>
>
> index 10ca86714f4..b788198ac31 100644
> --- a/gcc/tree-cfg.cc
> +++ b/gcc/tree-cfg.cc
> @@ -2860,6 +2860,13 @@ stmt_starts_bb_p (gimple *stmt, gimple *prev_stmt)
> || !DECL_ARTIFICIAL (gimple_label_label (plabel)))
> return true;
>
> + location_t loc_prev = gimple_location (plabel);
> + location_t locus = gimple_location (label_stmt);
> + expanded_location locus_e = expand_location (locus);
> +
> + if (flag_test_coverage && !same_line_p (locus, &locus_e, loc_prev))
> + return true;
> +
> cfg_stats.num_merged_labels++;
> return false;
> }
Interesting. Note that in CFG cleanup we have the following condition
when deciding
whether to merge a forwarder block with the destination:
locus = single_succ_edge (bb)->goto_locus;
...
/* Now walk through the statements backward. We can ignore labels,
anything else means this is not a forwarder block. */
for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
switch (gimple_code (stmt))
{
case GIMPLE_LABEL:
if (DECL_NONLOCAL (gimple_label_label (as_a <glabel *> (stmt))))
return false;
if (!optimize
&& (gimple_has_location (stmt)
|| LOCATION_LOCUS (locus) != UNKNOWN_LOCATION)
&& gimple_location (stmt) != locus)
return false;
it would be nice to sync the behavior between CFG creation and this.
In particular
a missing piece of the puzzle is how CFG creation sets ->goto_locus of the edge
after your change and whether that goto_locus and the label locus
compare matches
your condition (the CFG cleanup one is even stricter but special cases
UNKNOWN_LOCATION).
I also notice the !optimize vs. flag_test_coverage condition mismatch.
That said - I think your change to stmt_starts_bb_p is definitely the
correct place to fix,
I'm wondering how to match up with CFG cleanup - possibly using
!optimize instead
of flag_test_coverage would even make sense for debugging as well - we should be
able to put a breakpoint on the label hitting once rather than once
each loop iteration.
Thanks,
Richard.
> .profile:
>
> <bb 2> :
> PROF_edge_counter_17 = __gcov0.f[0];
> PROF_edge_counter_18 = PROF_edge_counter_17 + 1;
> __gcov0.f[0] = PROF_edge_counter_18;
> [small.c:3:7] p_6 = 0;
> [small.c:5:3] switch (s_7(D)) <default: <L6> [INV], [small.c:7:5] case 0:
> <L0> [INV], [small.c:11:5] case 1: <L3> [INV]>
>
> <bb 3> :
> [small.c:7:5] <L0>: <= case 0
> PROF_edge_counter_19 = __gcov0.f[1];
> PROF_edge_counter_20 = PROF_edge_counter_19 + 1;
> __gcov0.f[1] = PROF_edge_counter_20;
>
> <bb 4> :
> # n_1 = PHI <n_8(D)(3), [small.c:8:26 discrim 1] n_13(5)>
> # p_3 = PHI <[small.c:3:7] p_6(3), [small.c:8:13 discrim 1] p_12(5)>
> [small.c:8:13 discrim 1] p_12 = p_3 + 1;
> [small.c:8:26 discrim 1] n_13 = n_1 + -1;
> [small.c:8:26 discrim 1] if (n_13 != 0)
> goto <bb 5>; [INV]
> else
> goto <bb 6>; [INV]
>
> <bb 5> :
> PROF_edge_counter_23 = __gcov0.f[3];
> PROF_edge_counter_24 = PROF_edge_counter_23 + 1;
> __gcov0.f[3] = PROF_edge_counter_24;
> goto <bb 4>; [100.00%]
>
> <bb 6> :
> [small.c:9:14] _14 = p_12;
> [small.c:9:14] goto <bb 12>; [INV]
>
> <bb 7> :
> [small.c:11:5] <L3>: <= case 1
> PROF_edge_counter_21 = __gcov0.f[2];
> PROF_edge_counter_22 = PROF_edge_counter_21 + 1;
> __gcov0.f[2] = PROF_edge_counter_22;
>
>
> <bb 8> :
> # n_2 = PHI <n_8(D)(7), [small.c:12:26 discrim 1] n_10(9)>
> # p_4 = PHI <[small.c:3:7] p_6(7), [small.c:12:13 discrim 1] p_9(9)>
> [small.c:12:13 discrim 1] p_9 = p_4 + 1;
> [small.c:12:26 discrim 1] n_10 = n_2 + -1;
> [small.c:12:26 discrim 1] if (n_10 != 0)
> goto <bb 9>; [INV]
> else
> goto <bb 10>; [INV]
>
> <bb 9> :
> PROF_edge_counter_25 = __gcov0.f[4];
> PROF_edge_counter_26 = PROF_edge_counter_25 + 1;
> __gcov0.f[4] = PROF_edge_counter_26;
> goto <bb 8>; [100.00%]
>
>
> then label lines are also correct now:
>
> .c.gcov:
>
> Lines executed:90.91% of 11
> -: 0:Source:small.c
> -: 0:Graph:small.gcno
> -: 0:Data:small.gcda
> -: 0:Runs:1
> 2: 1:int f(int s, int n)
> -: 2:{
> 2: 3: int p = 0;
> -: 4:
> 2: 5: switch (s)
> -: 6: {
> 1: 7: case 0:
> 5: 8: do { p++; } while (--n);
> 1: 9: return p;
> -: 10:
> 1: 11: case 1:
> 5: 12: do { p++; } while (--n);
> 1: 13: return p;
> -: 14: }
> -: 15:
> #####: 16: return 0;
> -: 17:}
> -: 18:
> 1: 19:int main() { f(0, 5); f(1, 5);}