On Sun, Sep 24, 2023 at 3:09 PM Jørgen Kvalsvik <[email protected]> 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
>