Hello,

I played some more with odd programs and the effect on control flow graph construction (as a part of condition coverage support [1]) and came across this:

int fn (int a, int b, int c) {
    int x = 0;
    if (a && b) {
        if (c) {
            goto a_;
        } else {
            x = a;
        }
    } else {
a_:
        x = (a - b);
    }

    return x;
}

Run through gcov --conditions I get:

        4:    5:    if (a && b) {
condition outcomes covered 2/2
condition outcomes covered 2/2
        2:    6:        if (c) {
condition outcomes covered 2/2

Which is clearly not correct. So I started digging into why and dump the CFG as the coverage profiling sees it https://i.imgur.com/d0q72rA.png [2]. I apologize for the labeling, but A2 = a, A3 = b, A5 = c and A9 the else block. The problem, which is what confuses the algorithm, is that a and b don't share A9 as a successor (on false) as I would expect.

If I add some operation before the label the problem disappears and a and b share false-destination again https://i.imgur.com/PSrfaLC.png [3].

    } else {
        x++;
a_:
        x = (a - b);
    }

        4:    5:    if (a && b) {
condition outcomes covered 4/4
        2:    6:        if (c) {
condition outcomes covered 2/2


When dumping the cfg in the former case with -fdump-tree-cfg-graph I get a CFG without the split destinations in a and b https://i.imgur.com/05MCjzp.png [3]. I would assume from this that the graph dump happens after _more_ CFG transformations than the branch profiling.

So my questions are:

1. Is the control flow graph expected to be constructed as such where a and b don't share outcome, or is it to be considered a bug? 2. If yes, would it be problematic to push the branch coverage and condition profiling to a later stage where the cfg has been fixed?

Thanks,
Jørgen

[1] https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598165.html

[2] dot file:

digraph {
    A0 -> A2
    A2 -> A3
    A2 -> A8
    A3 -> A5
    A3 -> A4
    A8 -> A9
    A9 -> A10
    A10 -> A11
    A11 -> A1
    A5 -> A6
    A5 -> A7
    A4 -> A9
    A6 -> A9
    A7 -> A10
}

[3] dot file:

digraph {
    A0 -> A2
    A2 -> A3
    A2 -> A7
    A3 -> A4
    A3 -> A7
    A7 -> A8
    A8 -> A9
    A9 -> A10
    A10 -> A1
    A4 -> A5
    A4 -> A6
    A5 -> A8
    A6 -> A9
}

[3] dot file:


overlap=false;
subgraph "cluster_fn" {
        style="dashed";
        color="black";
        label="fn ()";
fn_0_basic_block_0 [shape=Mdiamond,style=filled,fillcolor=white,label="ENTRY"];

fn_0_basic_block_1 [shape=Mdiamond,style=filled,fillcolor=white,label="EXIT"];

fn_0_basic_block_2 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 2\>:\l\
|x\ =\ 0;\l\
|if\ (a\ !=\ 0)\l\
\ \ goto\ \<bb\ 3\>;\ [INV]\l\
else\l\
\ \ goto\ \<bb\ 7\>;\ [INV]\l\
}"];

fn_0_basic_block_3 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 3\>:\l\
|if\ (b\ !=\ 0)\l\
\ \ goto\ \<bb\ 4\>;\ [INV]\l\
else\l\
\ \ goto\ \<bb\ 7\>;\ [INV]\l\
}"];

fn_0_basic_block_4 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 4\>:\l\
|if\ (c\ !=\ 0)\l\
\ \ goto\ \<bb\ 5\>;\ [INV]\l\
else\l\
\ \ goto\ \<bb\ 6\>;\ [INV]\l\
}"];

fn_0_basic_block_5 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 5\>:\l\
|//\ predicted\ unlikely\ by\ goto\ predictor.\l\
goto\ \<bb\ 7\>;\ [INV]\l\
}"];

fn_0_basic_block_6 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 6\>:\l\
|x\ =\ a;\l\
goto\ \<bb\ 8\>;\ [INV]\l\
}"];

fn_0_basic_block_7 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 7\>:\l\
|a_:\l\
|x\ =\ a\ -\ b;\l\
}"];

fn_0_basic_block_8 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 8\>:\l\
|D.2398\ =\ x;\l\
}"];

fn_0_basic_block_9 [shape=record,style=filled,fillcolor=lightgrey,label="{\<bb\ 9\>:\l\
|\<L7\>:\l\
|return\ D.2398;\l\
}"];

fn_0_basic_block_0:s -> fn_0_basic_block_2:n [style="solid,bold",color=black,weight=100,constraint=true]; fn_0_basic_block_2:s -> fn_0_basic_block_3:n [style="solid,bold",color=forestgreen,weight=10,constraint=true]; fn_0_basic_block_2:s -> fn_0_basic_block_7:n [style="solid,bold",color=darkorange,weight=10,constraint=true]; fn_0_basic_block_3:s -> fn_0_basic_block_4:n [style="solid,bold",color=forestgreen,weight=10,constraint=true]; fn_0_basic_block_3:s -> fn_0_basic_block_7:n [style="solid,bold",color=darkorange,weight=10,constraint=true]; fn_0_basic_block_4:s -> fn_0_basic_block_5:n [style="solid,bold",color=forestgreen,weight=10,constraint=true]; fn_0_basic_block_4:s -> fn_0_basic_block_6:n [style="solid,bold",color=darkorange,weight=10,constraint=true]; fn_0_basic_block_5:s -> fn_0_basic_block_7:n [style="solid,bold",color=black,weight=100,constraint=true]; fn_0_basic_block_6:s -> fn_0_basic_block_8:n [style="solid,bold",color=black,weight=100,constraint=true]; fn_0_basic_block_7:s -> fn_0_basic_block_8:n [style="solid,bold",color=black,weight=100,constraint=true]; fn_0_basic_block_8:s -> fn_0_basic_block_9:n [style="solid,bold",color=black,weight=100,constraint=true]; fn_0_basic_block_9:s -> fn_0_basic_block_1:n [style="solid,bold",color=black,weight=10,constraint=true]; fn_0_basic_block_0:s -> fn_0_basic_block_1:n [style="invis",constraint=true];
}
}

Reply via email to