On Mon, Mar 6, 2023 at 8:22 AM Xionghu Luo <yinyuefen...@gmail.com> 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);}