On 23/06/2023 19:28, Jan Hubicka wrote:
>>>
>>> gcc/ChangeLog:
>>>
>>>     * builtins.cc (expand_builtin_fork_or_exec): Check
>>>     profile_condition_flag.
>>>         * collect2.cc (main): Add -fno-profile-conditions to OBSTACK.
>>>     * common.opt: Add new options -fprofile-conditions and
>>>     * doc/gcov.texi: Add --conditions documentation.
>>>     * doc/invoke.texi: Add -fprofile-conditions documentation.
>>>     * gcc.cc: Link gcov on -fprofile-conditions.
>>>     * gcov-counter.def (GCOV_COUNTER_CONDS): New.
>>>     * gcov-dump.cc (tag_conditions): New.
>>>     * gcov-io.h (GCOV_TAG_CONDS): New.
>>>     (GCOV_TAG_CONDS_LENGTH): Likewise.
>>>     (GCOV_TAG_CONDS_NUM): Likewise.
>>>     * 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 conditions counters.
>>>     (read_count_file): Read conditions counters.
>>>     (file_summary): Print conditions.
>>>     (accumulate_line_info): Accumulate conditions.
>>>     (output_line_details): Print conditions.
>>>     * ipa-inline.cc (can_early_inline_edge_p): Check
>>>     profile_condition_flag.
>>>     * ipa-split.cc (pass_split_functions::gate): Likewise.
>>>     * passes.cc (finish_optimization_passes): Likewise.
>>>     * profile.cc (find_conditions): New declaration.
>>>     (cov_length): Likewise.
>>>     (cov_blocks): Likewise.
>>>     (cov_masks): 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-profile.cc (struct conds_ctx): New.
>>>     (CONDITIONS_MAX_TERMS): New.
>>>     (EDGE_CONDITION): New.
>>>     (cmp_index_map): New.
>>>     (index_of): New.
>>>     (block_conditional_p): New.
>>>     (edge_conditional_p): New.
>>>     (single): New.
>>>     (single_edge): New.
>>>     (contract_edge): New.
>>>     (contract_edge_up): New.
>>>     (ancestors_of): New.
>>>     (struct outcomes): New.
>>>     (conditional_succs): New.
>>>     (condition_index): New.
>>>     (masking_vectors): New.
>>>     (cond_reachable_from): New.
>>>     (neighborhood): New.
>>>     (isolate_expression): New.
>>>     (emit_bitwise_op): New.
>>>     (make_index_map_visit): New.
>>>     (make_index_map): New.
>>>     (collect_conditions): New.
>>>     (yes): New.
>>>     (struct condcov): New.
>>>     (cov_length): New.
>>>     (cov_blocks): New.
>>>     (cov_masks): New.
>>>     (cov_free): New.
>>>     (find_conditions): New.
>>>     (instrument_decisions): New.
>>>     (tree_profiling): Check profile_condition_flag.
>>>     (pass_ipa_tree_profile::gate): Likewise.
>>>
>>> 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.
>>> ---
>>>
>>> v2:
>>>     * Moved the docs to rst/sphinx
>>>     * Output and message uses the 'conditions outcomes' vocabulary
>>>     * Fixed errors reported by contrib/style-check. Note that a few
>>>       warnings persist but are either in comments (ascii art) or because
>>>       the surrounding code (typically lists) are formatted the same way
>>> v3:
>>>     * Revert docs from rst/sphinx to texinfo
>>> v4:
>>>     * Rebased on trunk, removed @gol from texi
>>>
>>>  gcc/builtins.cc                        |    2 +-
>>>  gcc/collect2.cc                        |    7 +-
>>>  gcc/common.opt                         |    8 +
>>>  gcc/doc/gcov.texi                      |   37 +
>>>  gcc/doc/invoke.texi                    |   19 +
>>>  gcc/gcc.cc                             |    4 +-
>>>  gcc/gcov-counter.def                   |    3 +
>>>  gcc/gcov-dump.cc                       |   24 +
>>>  gcc/gcov-io.h                          |    3 +
>>>  gcc/gcov.cc                            |  200 +++-
>>>  gcc/ipa-inline.cc                      |    2 +-
>>>  gcc/ipa-split.cc                       |    3 +-
>>>  gcc/passes.cc                          |    3 +-
>>>  gcc/profile.cc                         |   84 +-
>>>  gcc/testsuite/g++.dg/gcov/gcov-18.C    |  234 +++++
>>>  gcc/testsuite/gcc.misc-tests/gcov-19.c | 1250 ++++++++++++++++++++++++
>>>  gcc/testsuite/gcc.misc-tests/gcov-20.c |   22 +
>>>  gcc/testsuite/gcc.misc-tests/gcov-21.c |   16 +
>>>  gcc/testsuite/lib/gcov.exp             |  191 +++-
>>>  gcc/tree-profile.cc                    | 1048 +++++++++++++++++++-
>>>  libgcc/libgcov-merge.c                 |    5 +
>>>  21 files changed, 3137 insertions(+), 28 deletions(-)
>>>  create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-18.C
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-19.c
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-20.c
>>>  create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-21.c
>>>
>>> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
>>> index 8400adaf5b4..c71124a5615 100644
>>> --- a/gcc/builtins.cc
>>> +++ b/gcc/builtins.cc
>>> @@ -5902,7 +5902,7 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx 
>>> target, int ignore)
>>>    tree call;
>>>  
>>>    /* If we are not profiling, just call the function.  */
>>> -  if (!profile_arc_flag)
>>> +  if (!profile_arc_flag && !profile_condition_flag)
> 
> I think profile_condition_flag can simpy imply profile_arc_flag.  
> This is an information you always measure in adition to the usual bb
> profile, right?
> 
> This can be arranged in opts.c and would avoid need to test both flags
> throughout the compiler.
> Or can both profiles be used independently?

Both can be used independently, and you might actually want to - for branch
coverage (was every branch taken at least once?) the profile-arc instrumentation
was sufficient, but redundant if you also want condition coverage. However,
profile-arcs also give you branch *counts* which is useful for data-driven
decisions.

Now, we might want to have --coverage imply profile-conditions.

>>>      return NULL_RTX;
>>>  
>>>    /* Otherwise call the wrapper.  This should be equivalent for the rest of
>>> diff --git a/gcc/collect2.cc b/gcc/collect2.cc
>>> index 63b9a0c233a..12ff5d81424 100644
>>> --- a/gcc/collect2.cc
>>> +++ b/gcc/collect2.cc
>>> @@ -1032,9 +1032,9 @@ main (int argc, char **argv)
>>>        lto_mode = LTO_MODE_LTO;
>>>    }
>>>  
>>> -  /* -fno-profile-arcs -fno-test-coverage -fno-branch-probabilities
>>> -     -fno-exceptions -w -fno-whole-program */
>>> -  num_c_args += 6;
>>> +  /* -fno-profile-arcs -fno-profile-conditions -fno-test-coverage
>>> +     -fno-branch-probabilities -fno-exceptions -w -fno-whole-program */
>>> +  num_c_args += 7;
>>>  
>>>    c_argv = XCNEWVEC (char *, num_c_args);
>>>    c_ptr = CONST_CAST2 (const char **, char **, c_argv);
>>> @@ -1230,6 +1230,7 @@ main (int argc, char **argv)
>>>      }
>>>    obstack_free (&temporary_obstack, temporary_firstobj);
>>>    *c_ptr++ = "-fno-profile-arcs";
>>> +  *c_ptr++ = "-fno-profile-conditions";
> If this was initialized from an array of strings, we can avoid need for
> num_c_args to be computed by hand....
>>>    *c_ptr++ = "-fno-test-coverage";
>>>    *c_ptr++ = "-fno-branch-probabilities";
>>>    *c_ptr++ = "-fno-exceptions";
>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>> index a28ca13385a..e32a5f7b0db 100644
>>> --- a/gcc/common.opt
>>> +++ b/gcc/common.opt
>>> @@ -862,6 +862,10 @@ Wcoverage-invalid-line-number
>>>  Common Var(warn_coverage_invalid_linenum) Init(1) Warning
>>>  Warn in case a function ends earlier than it begins due to an invalid 
>>> linenum macros.
>>>  
>>> +Wcoverage-too-many-conditions
>>> +Common Var(warn_too_many_conditions) Init(1) Warning
>>> +Warn when a conditional has too many terms and coverage gives up.
> 
> Maybe when user reads it in --help, he/she will not know what coverage
> you means.  It is called condition coverage profiling later, so we can
> stick to this term here.
>>> +
>>>  Wmissing-profile
>>>  Common Var(warn_missing_profile) Init(1) Warning
>>>  Warn in case profiles in -fprofile-use do not exist.
>>> @@ -2351,6 +2355,10 @@ fprofile-arcs
>>>  Common Var(profile_arc_flag)
>>>  Insert arc-based program profiling code.
>>>  
>>> +fprofile-conditions
>>> +Common Var(profile_condition_flag)
>>> +Insert condition coverage profiling code.
>>> +
>>>  fprofile-dir=
>>>  Common Joined RejectNegative Var(profile_data_prefix)
>>>  Set the top-level directory for storing the profile data.
>>> diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
>>> index 3019efc4674..10cfdcf24aa 100644
>>> --- a/gcc/doc/gcov.texi
>>> +++ b/gcc/doc/gcov.texi
>>> @@ -124,6 +124,7 @@ gcov [@option{-v}|@option{--version}] 
>>> [@option{-h}|@option{--help}]
>>>       [@option{-a}|@option{--all-blocks}]
>>>       [@option{-b}|@option{--branch-probabilities}]
>>>       [@option{-c}|@option{--branch-counts}]
>>> +     [@option{-g}|@option{--conditions}]
>>>       [@option{-d}|@option{--display-progress}]
>>>       [@option{-f}|@option{--function-summaries}]
>>>       [@option{-j}|@option{--json-format}]
>>> @@ -169,6 +170,13 @@ be shown, unless the @option{-u} option is given.
>>>  Write branch frequencies as the number of branches taken, rather than
>>>  the percentage of branches taken.
>>>  
>>> +@item -g
>>> +@itemx --conditions
>>> +Write condition coverage to the output file, and write condition summary 
>>> info
>>> +to the standard output.  This option allows you to see if the conditions in
>>> +your program at least once had an independent effect on the outcome of the
>>> +boolean expression (modified condition/decision coverage).
> 
> Maybe you want to like -fprofile-conditions flag here, since that is
> needed at the instrumentaiton time.
>>> +@opindex Wno-coverage-too-many-conditions
>>> +@opindex Wcoverage-too-many-conditions
>>> +@item -Wno-coverage-too-many-conditions
>>> +Warn in case a condition have too many terms and GCC gives up coverage.
>>> +Coverage is given up when there are more terms in the conditional than 
>>> there
>>> +are bits in a @code{gcov_type_unsigned}.  This warning is enabled by 
>>> default.
> 
> Here you can explicitly mention -fprofile-conditions so user knows what
> kind of coverage you refer to.
>>> +
>>>  @opindex Wno-coverage-invalid-line-number
>>>  @opindex Wcoverage-invalid-line-number
>>>  @item -Wno-coverage-invalid-line-number
>>> @@ -16430,6 +16438,13 @@ Note that if a command line directly links source 
>>> files, the corresponding
>>>  E.g. @code{gcc a.c b.c -o binary} would generate @file{binary-a.gcda} and
>>>  @file{binary-b.gcda} files.
>>>  
>>> +@item -fprofile-conditions
>>> +@opindex fprofile-conditions
>>> +Add code so that program conditions are instrumented.  During execution the
>>> +program records what terms in a conditional contributes to a decision.  The
>>> +data may be used to verify that all terms in a booleans are tested and 
>>> have an
>>> +effect on the outcome of a condition.
> 
> I would say that the result can be read with gcov --conditions
>>> @@ -392,6 +394,28 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
>>>      }
>>>  }
>>>  
>>> +static void
>>> +tag_conditions (const char *filename ATTRIBUTE_UNUSED,
>>> +           unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
>>> +           unsigned depth)
> Coding style wants us to have a comment before functions
> summarizing what they do.
> Also with C++, one can ust skip "filename ATTRIBUTE_UNUSED" for unused
> args.
>>> diff --git a/gcc/gcov.cc b/gcc/gcov.cc
>>> index 2fad6aa7ede..ffedbb37483 100644
>>> --- a/gcc/gcov.cc
>>> +++ b/gcc/gcov.cc
>>> @@ -81,6 +81,7 @@ using namespace std;
>>>  class function_info;
>>>  class block_info;
>>>  class source_info;
>>> +class condition_info;
>>>  
>>>  /* Describes an arc between two basic blocks.  */
>>>  
>>> @@ -134,6 +135,28 @@ public:
>>>    vector<unsigned> lines;
>>>  };
>>>  
>>> +class condition_info
> Also block comment here.
>>> +{
>>> +public:
>>> +  condition_info ();
>>> +
>>> +  int popcount () const;
>>> +
>>> +  gcov_type_unsigned truev;
>>> +  gcov_type_unsigned falsev;
>>> +
>>> +  unsigned n_terms;
>>> +};
>>> +
>>> +condition_info::condition_info (): truev (0), falsev (0), n_terms (0)
>>> +{
>>> +}
>>> +
>>> +int condition_info::popcount () const
>>> +{
>>> +    return __builtin_popcountll (truev) + __builtin_popcountll (falsev);
> If you build with --disable-bootstrap and use non-GCC host compiler, I
> don't think we have a promise of __builtin_popcountll being supported.
> THere is popcount_hwi in hwint.h
>>> @@ -2456,6 +2573,13 @@ add_branch_counts (coverage_info *coverage, const 
>>> arc_info *arc)
>>>      }
>>>  }
>>>  
>>> +static void
>>> +add_condition_counts (coverage_info *coverage, const block_info *block)
> Similarly here block comment will make coding conventions happy
>>> +{
>>> +  coverage->conditions += 2 * block->conditions.n_terms;
>>> +  coverage->conditions_covered += block->conditions.popcount ();
>>> +}
>>> +
>>>  /* Format COUNT, if flag_human_readable_numbers is set, return it human
>>>     readable format.  */
>>>  
>>> @@ -2559,6 +2683,18 @@ file_summary (const coverage_info *coverage)
>>>              coverage->calls);
>>>        else
>>>     fnotice (stdout, "No calls\n");
>>> +
>>> +    }
>>> +
>>> +  if (flag_conditions)
>>> +    {
>>> +      if (coverage->conditions)
>>> +   fnotice (stdout, "Condition outcomes covered:%s of %d\n",
>>> +            format_gcov (coverage->conditions_covered,
>>> +                         coverage->conditions, 2),
>>> +            coverage->conditions);
>>> +      else
>>> +   fnotice (stdout, "No conditions\n");
>>>      }
>>>  }
>>>  
>>> @@ -2793,6 +2929,12 @@ static void accumulate_line_info (line_info *line, 
>>> source_info *src,
>>>      it != line->branches.end (); it++)
>>>        add_branch_counts (&src->coverage, *it);
>>>  
>>> +  if (add_coverage)
>>> +    for (vector<block_info *>::iterator it = line->blocks.begin ();
>>> +    it != line->blocks.end (); it++)
>>> +      add_condition_counts (&src->coverage, *it);
>>> +
>>> +
>>>    if (!line->blocks.empty ())
>>>      {
>>>        /* The user expects the line count to be the number of times
>>> @@ -2894,6 +3036,33 @@ accumulate_line_counts (source_info *src)
>>>        }
>>>  }
>>>  
>>> +static void
>>> +output_conditions (FILE *gcov_file, const block_info *binfo)
> Again, I think we want a comment (even though it is easy to figure out
> what it does).
>>> +{
>>> +    const condition_info& info = binfo->conditions;
>>> +    if (info.n_terms == 0)
>>> +   return;
>>> +
>>> +    const int expected = 2 * info.n_terms;
>>> +    const int got = info.popcount ();
>>> +
>>> +    fnotice (gcov_file, "condition outcomes covered %d/%d\n", got, 
>>> expected);
>>> +    if (expected == got)
>>> +   return;
>>> +
>>> +    for (unsigned i = 0; i < info.n_terms; i++)
>>> +    {
>>> +   gcov_type_unsigned index = 1;
>>> +   index <<= i;
>>> +   if ((index & info.truev & info.falsev))
>>> +       continue;
>>> +
>>> +   const char *t = (index & info.truev) ? "" : "true";
>>> +   const char *f = (index & info.falsev) ? "" : " false";
>>> +   fnotice (gcov_file, "condition %2u not covered (%s%s)\n", i, t, f + 
>>> !t[0]);
>>> +    }
>>> +}
>>> +
>>>  /* Output information about ARC number IX.  Returns nonzero if
>>>     anything is output.  */
>>>  
>>> @@ -3104,16 +3273,29 @@ output_line_details (FILE *f, const line_info 
>>> *line, unsigned line_num)
>>>       if (flag_branches)
>>>         for (arc = (*it)->succ; arc; arc = arc->succ_next)
>>>           jx += output_branch_count (f, jx, arc);
>>> +
>>> +     if (flag_conditions)
>>> +         output_conditions (f, *it);
>>>     }
>>>      }
>>> -  else if (flag_branches)
>>> +  else
>>>      {
>>> -      int ix;
>>> +      if (flag_branches)
>>> +   {
>>> +     int ix;
>>> +
>>> +     ix = 0;
>>> +     for (vector<arc_info *>::const_iterator it = line->branches.begin ();
>>> +             it != line->branches.end (); it++)
>>> +         ix += output_branch_count (f, ix, (*it));
>>> +   }
>>>  
>>> -      ix = 0;
>>> -      for (vector<arc_info *>::const_iterator it = line->branches.begin ();
>>> -      it != line->branches.end (); it++)
>>> -   ix += output_branch_count (f, ix, (*it));
>>> +      if (flag_conditions)
>>> +   {
>>> +     for (vector<block_info *>::const_iterator it = line->blocks.begin ();
>>> +          it != line->blocks.end (); it++)
>>> +         output_conditions (f, *it);
>>> +   }
> 
> The gcov tool bits seems good to me except the nits above.  I will
> continue reading the instrumentation part and write incrementally.
> 
> Honza

I kept the profile-arcs/profille-coverage inline, but for the other feedback you
can consider it applied. I'll create some new patches to address them, then
squash them post-review. Thanks!

Jørgen

Reply via email to