> gcc/ChangeLog:
>
> * builtins.cc (expand_builtin_fork_or_exec): Check
> condition_coverage_flag.
> * collect2.cc (main): Add -fno-condition-coverage to OBSTACK.
> * common.opt: Add new options -fcondition-coverage and
> -Wcoverage-too-many-conditions.
> * doc/gcov.texi: Add --conditions documentation.
> * doc/invoke.texi: Add -fcondition-coverage documentation.
> * function.cc (free_after_compilation): Clear conditions.
> (allocate_struct_function): Allocate conditions.
> (basic_condition_uid): New.
> * function.h (struct function): Add conditions.
> (basic_condition_uid): New declaration.
> * gcc.cc: Link gcov on -fcondition-coverage.
> * gcov-counter.def (GCOV_COUNTER_CONDS): New.
> * gcov-dump.cc (tag_conditions): New.
> * gcov-io.h (GCOV_TAG_CONDS): New.
> (GCOV_TAG_CONDS_LENGTH): New.
> (GCOV_TAG_CONDS_NUM): New.
> * gcov.cc (class condition_info): New.
> (condition_info::condition_info): New.
> (condition_info::popcount): New.
> (struct coverage_info): New.
> (add_condition_counts): New.
> (output_conditions): New.
> (print_usage): Add -g, --conditions.
> (process_args): Likewise.
> (output_intermediate_json_line): Output conditions.
> (read_graph_file): Read condition counters.
> (read_count_file): Likewise.
> (file_summary): Print conditions.
> (accumulate_line_info): Accumulate conditions.
> (output_line_details): Print conditions.
> * gimplify.cc (next_cond_uid): New.
> (reset_cond_uid): New.
> (shortcut_cond_r): Set condition discriminator.
> (tag_shortcut_cond): New.
> (shortcut_cond_expr): Set condition discriminator.
> (gimplify_cond_expr): Likewise.
> (gimplify_function_tree): Call reset_cond_uid.
> * ipa-inline.cc (can_early_inline_edge_p): Check
> condition_coverage_flag.
> * ipa-split.cc (pass_split_functions::gate): Likewise.
> * passes.cc (finish_optimization_passes): Likewise.
> * profile.cc (struct condcov): New declaration.
> (cov_length): Likewise.
> (cov_blocks): Likewise.
> (cov_masks): Likewise.
> (cov_maps): Likewise.
> (cov_free): Likewise.
> (instrument_decisions): New.
> (read_thunk_profile): Control output to file.
> (branch_prob): Call find_conditions, instrument_decisions.
> (init_branch_prob): Add total_num_conds.
> (end_branch_prob): Likewise.
> * tree-core.h (struct tree_exp): Add condition_uid.
> * tree-profile.cc (struct conds_ctx): New.
> (CONDITIONS_MAX_TERMS): New.
> (EDGE_CONDITION): New.
> (topological_cmp): New.
> (index_of): New.
> (single_p): New.
> (single_edge): New.
> (contract_edge_up): New.
> (struct outcomes): New.
> (conditional_succs): New.
> (condition_index): New.
> (masking_vectors): New.
> (emit_assign): New.
> (emit_bitwise_op): New.
> (make_top_index_visit): New.
> (make_top_index): New.
> (paths_between): New.
> (struct condcov): New.
> (cov_length): New.
> (cov_blocks): New.
> (cov_masks): New.
> (cov_maps): New.
> (cov_free): New.
> (gimple_cond_uid): New.
> (find_conditions): New.
> (struct counters): New.
> (find_counters): New.
> (resolve_counter): New.
> (resolve_counters): New.
> (instrument_decisions): New.
> (tree_profiling): Check condition_coverage_flag.
> (pass_ipa_tree_profile::gate): Likewise.
> * tree.h (SET_EXPR_UID): New.
> (EXPR_COND_UID): New.
>
> libgcc/ChangeLog:
>
> * libgcov-merge.c (__gcov_merge_ior): New.
>
> gcc/testsuite/ChangeLog:
>
> * lib/gcov.exp: Add condition coverage test function.
> * g++.dg/gcov/gcov-18.C: New test.
> * gcc.misc-tests/gcov-19.c: New test.
> * gcc.misc-tests/gcov-20.c: New test.
> * gcc.misc-tests/gcov-21.c: New test.
> * gcc.misc-tests/gcov-22.c: New test.
> * gcc.misc-tests/gcov-23.c: New test.
Sorry for taking so long on this - I needed some time to actually try
the patch since generally we will need more changes in frontend to
preserve conditionals intanct till gimple.
> This revision brings quite a few changes, some of which warrant a more
> careful review.
>
> 1. Basic conditions are tied to the Boolean expression during
> gimplification, not through CFG analysis. The CFG analysis seemed to
> work well up until constructs like a && fn (b && c) && d where
> fn(...) seems indistinguishible from then-blocks. This wipes out much
> of the implementation in tree-profile.cc.
> 2. I changed the flag from -fprofile-conditions to -fcondition-coverage.
> -fprofile-conditions was chosen because of its symmetry with
> -fprofile-arcs, condition-coverage does feel more appropriate.
This seems good. Profile-arcs is rarely used by itself - most of time it
is implied by -fprofile-generate and -ftest-coverage and since
condition coverage is more associated to the second, I guess
-fcondition-coverage is better name.
Since -fcondition-coverage now affects gimplification (which makes me
less worried about its ability to map things back to conditionals), it
should be marked as incompatible with -fprofile-generate and
-fprofile-use. It would only work with -fcondtion-coverage was used
both with -fprofile-generate and -fprofile-use and I do not see much use
of this. On the other hand I can imagine users wanting both coverage and
-fprofile-generate run at once, so we should output sorry message that
this is not implemented
> --- a/gcc/function.h
> +++ b/gcc/function.h
> @@ -287,6 +287,10 @@ struct GTY(()) function {
> /* Vector of function local variables, functions, types and constants. */
> vec<tree, va_gc> *local_decls;
>
> + /* Map from conditions to a (function) unique identifier, so that two basic
> + conditions that belong to the same expression have the same id. */
> + hash_map<gcond *, unsigned> *conditions;
> +
> /* For md files. */
>
> /* tm.h can use this to store whatever it likes. */
> @@ -737,4 +741,6 @@ extern void used_types_insert (tree);
>
> extern bool currently_expanding_function_start;
>
> +extern unsigned basic_condition_uid (const struct function *, basic_block);
> +
> #endif /* GCC_FUNCTION_H */
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 89841787ff8..5319b182a6a 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -85,6 +85,7 @@ along with GCC; see the file COPYING3. If not see
> #include "value-range.h"
> #include "gimple-range.h"
> #include "insn-attr.h"
> +#include "gimple-iterator.h"
>
> /* So we can assign to cfun in this file. */
> #undef cfun
> @@ -213,6 +214,7 @@ free_after_compilation (struct function *f)
>
> memset (crtl, 0, sizeof (struct rtl_data));
> f->eh = NULL;
> + f->conditions = NULL;
> f->machine = NULL;
> f->cfg = NULL;
> f->curr_properties &= ~PROP_cfg;
> @@ -2385,7 +2387,7 @@ split_complex_args (vec<tree> *args)
> the hidden struct return argument, and (abi willing) complex args.
> Return the new parameter list. */
>
> -static vec<tree>
> +static vec<tree>
> assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
> {
> tree fndecl = current_function_decl;
> @@ -4794,6 +4796,7 @@ allocate_struct_function (tree fndecl, bool abstract_p)
> cfun = ggc_cleared_alloc<function> ();
>
> init_eh_for_function ();
> + cfun->conditions = hash_map<gcond*, unsigned>::create_ggc ();
>
> if (init_machine_status)
> cfun->machine = (*init_machine_status) ();
> @@ -6977,6 +6980,22 @@ add_local_decl (struct function *fun, tree d)
> vec_safe_push (fun->local_decls, d);
> }
>
> +/* Get the basic condition identifier for the basic block in the
> + function fn, which is set in gimplification of conditional expressions.
> If
> + a basic block does not end with a conditional jump, or the jump was
> created
> + implicitly (e.g. from a C++ destructor) this function returns 0. */
> +unsigned
> +basic_condition_uid (const struct function *fn, basic_block bb)
> +{
> + gimple *stmt = gsi_stmt (gsi_last_nondebug_bb (bb));
> + if (!stmt)
> + return 0;
> + if (!is_a<gcond *> (stmt))
> + return 0;
> + unsigned *v = fn->conditions->get (as_a<gcond *> (stmt));
> + return v ? *v : 0;
> +}
> +
If condition statement is removed before profiling is done, you will end
up with a stale entry in the hash table.
We already keep EH regions and profile histogram in on-side hashtables,
so I think you need to follow gimple_.*histograms calls in
gimple-iterator.cc and be sure that the hashtable is updated.
Also if function is inlined (which may be forced at -O0 using
always_inline) the conditional annotations will be lost.
I think you should simply make condition coverage to happen before
pass_early_inline is run. But that may be done incrementally.
> namespace {
>
> const pass_data pass_data_match_asm_constraints =
> diff --git a/gcc/function.h b/gcc/function.h
> index 833c35e3da6..a3ce5a70aa8 100644
> --- a/gcc/function.h
> +++ b/gcc/function.h
> @@ -287,6 +287,10 @@ struct GTY(()) function {
> /* Vector of function local variables, functions, types and constants. */
> vec<tree, va_gc> *local_decls;
>
> + /* Map from conditions to a (function) unique identifier, so that two basic
> + conditions that belong to the same expression have the same id. */
> + hash_map<gcond *, unsigned> *conditions;
> +
> /* For md files. */
>
> /* tm.h can use this to store whatever it likes. */
> @@ -737,4 +741,6 @@ extern void used_types_insert (tree);
>
> extern bool currently_expanding_function_start;
>
> +extern unsigned basic_condition_uid (const struct function *, basic_block);
> +
> #endif /* GCC_FUNCTION_H */
> +/* Describes a single conditional expression and the (recorded) conditions
> + shown to independently affect the outcome. */
> +class condition_info
> +{
> +public:
> + condition_info ();
> +
> + int popcount () const;
> +
> + /* Bitsets storing the independently significant outcomes for true and
> false,
> + * respectively. */
^ extra * ..
I skipped the gcov.cc changes - I suppose they did not changed
significantly since last version of patch, right?
> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index 342e43a7f25..6b5f604eecf 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -71,6 +71,14 @@ along with GCC; see the file COPYING3. If not see
> #include "context.h"
> #include "tree-nested.h"
>
> +/* Initial value for the condition identifier, which it can be reset to
> + on when entering functions. 0 is already the default/untouched value, so
> + start at non-zero. A valid and set id should always be > 0. */
Please add mention of condition coverage to the comment, so it is clear
what kind of identifier we look for here.
> +static const unsigned initial_cond_uid = 1;
> +static unsigned nextuid = 0;
> +unsigned next_cond_uid () { return nextuid++; }
> +void reset_cond_uid () { nextuid = initial_cond_uid; }
and reformat this to gnu style (with extra lines for { return and }.
> +
> /* Hash set of poisoned variables in a bind expr. */
> static hash_set<tree> *asan_poisoned_variables = NULL;
>
> @@ -4137,7 +4145,7 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p,
> bool want_value)
>
> static tree
> shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p,
> - location_t locus)
> + location_t locus, unsigned condition_uid)
You should document the new parameter (and make clear it is associated
to condition coverage. */
> +
> +/* Given a multi-term condition (ANDIF, ORIF), walk the predicate and tag
> every
> + term with uid. When two basic conditions share the uid discriminator when
> + they belong to the same predicate, which used by the condition coverage.
> + Doing this as an explicit step makes for a simpler implementation than
> + weaving it into the splitting code as the splitting code eventually
> reaches
> + back up to gimplfiy_expr which makes bookkeeping complicated. */
> +static void
> +tag_shortcut_cond (tree pred, unsigned condition_uid)
> +{
> + if (TREE_CODE (pred) == TRUTH_ANDIF_EXPR
> + || TREE_CODE (pred) == TRUTH_ORIF_EXPR)
> + {
> + tree fst = TREE_OPERAND (pred, 0);
> + tree lst = TREE_OPERAND (pred, 1);
> +
> + if (TREE_CODE (fst) == TRUTH_ANDIF_EXPR
> + || TREE_CODE (fst) == TRUTH_ORIF_EXPR)
> + tag_shortcut_cond (fst, condition_uid);
> + else if (TREE_CODE (fst) == COND_EXPR)
> + SET_EXPR_UID (fst, condition_uid);
> +
> + if (TREE_CODE (lst) == TRUTH_ANDIF_EXPR
> + || TREE_CODE (lst) == TRUTH_ORIF_EXPR)
> + tag_shortcut_cond (lst, condition_uid);
> + else if (TREE_CODE (lst) == COND_EXPR)
> + SET_EXPR_UID (lst, condition_uid);
So you store the uids into tree expression to later copy it to gimple.
It is not a good idea to add condition_uid into core tree type:
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 65e51b939a2..44b9fa10431 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1582,6 +1582,9 @@ enum omp_clause_linear_kind
> struct GTY(()) tree_exp {
> struct tree_typed typed;
> location_t locus;
> + /* Discriminator for basic conditions in a Boolean expressions. Trees that
> + are operands of the same Boolean expression should have the same uid.
> */
> + unsigned condition_uid;
> tree GTY ((length ("TREE_OPERAND_LENGTH ((tree)&%h)"))) operands[1];
> };
Instead of that it can live in on-side hashtable, since most of time
(when condition coverage is off) it will not be necessary.
You can see how this is done with decl_debug_expr_lookup and
decl_debug_expr_insert that is kind of similar situation.
I would like to see the patch with updated and Richard to have chance to
comment on gimplify.cc/function.cc and tree.h changes, but otherwise I
think the patch is ready to go.
Honza