On Sun, Sep 24, 2023 at 3:09 PM Jørgen Kvalsvik <j...@lambda.is> wrote: > > This is a request for feedback and a proof-of-concept, not something I > intend to merge as-is. It would be nice if gcc, maybe just under some > circumstances, always generated an else-block for coverage purposes. > > I am working on the MC/DC support by CFG analysis for a while > https://gcc.gnu.org/pipermail/gcc-patches/2023-June/621449.html and have > ironed out a lot of problems. The last problem I know about, which is > impossible to actually fix right now, is the "fusing" of nested ifs. > Here is an example: > > if (a) if (b) if (c) { ... } // 3 conditions, 6 outcomes > if (a && b && c) { ... } // 3 conditions, 6 outcomes > > These form isomorphic CFGs which means there is no way for my algorithm > to distinguish them. This is sort-of acceptable since the coverage > measurements more accurately measure the semantics (and not the syntax), > but this also happens when there is code in-between the nesting: > > if (a) // measures to 2 conditions, 4 outcomes > { > a += b * 10; > b -= a + 2; > if (b) > { > ... > } > } > > You would expect this to be measured as: > > if (a) // 1 condition, 2 outcomes > { > a += b * 10; > b -= a + 2; > if (b) // 1 condition, 2 outcomes > { > ... > } > } > > The source of the problem is the missing (or empty) else block, as the > algorithm uses the outcome (then/else) edges to determine the limits of > expressions. If, however, the else blocks are generated, the conditions > are counted as you would expect. > > So I have a few questions: > > 1. Is something like this even acceptable? The semantics of the program > should not change, assuming the else-block only exists but is without > significant behavior. It will only be generated if there is no > explicit else in source. > 2. Should this only be generated when necessary (e.g. under condition > coverage? No optimization?) > 3. I used a simple int-init { int __mcdc_barrier = 0; } but there might > be better contents for the block that does not add anything > operationally. I am not very familiar with this part of gcc and would > like to see someting better. Any suggestions?
Can you in theory handle this by splitting the 'else' edge before coverage instrumentation rather than using a stmt inserted during gimplification? > --- > gcc/gimplify.cc | 8 ++++++++ > 1 file changed, 8 insertions(+) > > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc > index ade6e335da7..43af38df742 100644 > --- a/gcc/gimplify.cc > +++ b/gcc/gimplify.cc > @@ -4370,6 +4370,14 @@ gimplify_cond_expr (tree *expr_p, gimple_seq *pre_p, > fallback_t fallback) > enum tree_code pred_code; > gimple_seq seq = NULL; > > + if (TREE_OPERAND (expr, 2) == NULL_TREE) > + { > + tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier > + ("__mcdc_barrier"), integer_type_node); > + tree val = build_int_cst (integer_type_node, 0); > + TREE_OPERAND (expr, 2) = build2 (INIT_EXPR, TREE_TYPE (var), var, val); > + } > + > /* If this COND_EXPR has a value, copy the values into a temporary within > the arms. */ > if (!VOID_TYPE_P (type)) > -- > 2.30.2 >