> 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

Reply via email to