diff mbox

[GOOGLE] Fix LIPO COMDAT fixup and gcov-tool interactions

Message ID CAAe5K+UWHKvyeo9o1t16KsAn54A8FObES-sXnXT5Vycj_mTv7g@mail.gmail.com
State New
Headers show

Commit Message

Teresa Johnson Sept. 12, 2014, 11:31 p.m. UTC
This patch addresses issues when running gcov-tool after performing
COMDAT fixup during dyn-ipa. Functions that were previously all zero
counts are marked, and the counts are discarded when being read in
by gcov-tool before recalculating module groups and summary info.

While here, cleaned up the gcov-tool output (remove an overly-verbose output,
make all output consistently go to stderr).

Passes regression tests and manual tests. Ok for google branches?

2014-09-12  Teresa Johnson  <tejohnson@google.com>

        * gcc/coverage.c (read_counts_file): Handle new section.
        * gcc/gcov.c (read_count_file): Ditto.
        * gcc/gcov-dump.c (dump_gcov_file): Ditto.
        (tag_function): Ditto.
        (tag_zero_fixup): New function.
        * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
        * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
        * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
        (new_checksum_alias): Ditto.
        (cfg_checksum_insert): Ditto.
        (checksum_set_insert): Ditto.
        (gcov_build_callgraph): New parameter.
        (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
        (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
        (__gcov_compute_module_groups): New parameter.
        * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
        (get_gcov_fn_fixed_up): Ditto.
        (gcov_exit_merge_gcda): Handle new section.
        (gcov_write_comdat_zero_fixup): Ditto.
        (gcov_write_build_info): Ditto.
        (gcov_write_comdat_zero_fixup): New function.
        (gcov_write_func_counters): Fix indent.
        (gcov_dump_module_info): Write new flag section.
        * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
        (gcov_get_counter_target): Ditto.
        * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
        remove overly verbose output.
        (tag_counters): Clear fixed-up counters.
        (lipo_process_substitute_string_1): Send all verbose output to stderr.
        (tag_zero_fixup): New function.
        (read_gcda_file): Deallocate flag array.
        (gcov_profile_scale): Send all verbose output to stderr.
        (gcov_profile_normalize): Ditto.


@@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
     }

   read_gcda_finalize (obj_info);
+  free (zero_fixup_flags);
   gcov_close ();

   return obj_info;
@@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
   unsigned f_ix;

   if (verbose)
-    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
+    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);

   /* Scaling the counters.  */
   for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
@@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
   scale_factor = (float)max_val / curr_max_val;
 #if !defined (_WIN32)
   if (verbose)
-    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
+    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
 #endif

   return gcov_profile_scale (profile, scale_factor, 0, 0);

Comments

Xinliang David Li Sept. 16, 2014, 4:29 a.m. UTC | #1
Is it necessary to declare zero_counts array at all?  Can a flag field
be added to dyn_cgraph_node structure to indicate if it is fixed up?

David

On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohnson@google.com> wrote:
> This patch addresses issues when running gcov-tool after performing
> COMDAT fixup during dyn-ipa. Functions that were previously all zero
> counts are marked, and the counts are discarded when being read in
> by gcov-tool before recalculating module groups and summary info.
>
> While here, cleaned up the gcov-tool output (remove an overly-verbose output,
> make all output consistently go to stderr).
>
> Passes regression tests and manual tests. Ok for google branches?
>
> 2014-09-12  Teresa Johnson  <tejohnson@google.com>
>
>         * gcc/coverage.c (read_counts_file): Handle new section.
>         * gcc/gcov.c (read_count_file): Ditto.
>         * gcc/gcov-dump.c (dump_gcov_file): Ditto.
>         (tag_function): Ditto.
>         (tag_zero_fixup): New function.
>         * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
>         * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
>         * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
>         (new_checksum_alias): Ditto.
>         (cfg_checksum_insert): Ditto.
>         (checksum_set_insert): Ditto.
>         (gcov_build_callgraph): New parameter.
>         (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
>         (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
>         (__gcov_compute_module_groups): New parameter.
>         * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
>         (get_gcov_fn_fixed_up): Ditto.
>         (gcov_exit_merge_gcda): Handle new section.
>         (gcov_write_comdat_zero_fixup): Ditto.
>         (gcov_write_build_info): Ditto.
>         (gcov_write_comdat_zero_fixup): New function.
>         (gcov_write_func_counters): Fix indent.
>         (gcov_dump_module_info): Write new flag section.
>         * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
>         (gcov_get_counter_target): Ditto.
>         * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
>         remove overly verbose output.
>         (tag_counters): Clear fixed-up counters.
>         (lipo_process_substitute_string_1): Send all verbose output to stderr.
>         (tag_zero_fixup): New function.
>         (read_gcda_file): Deallocate flag array.
>         (gcov_profile_scale): Send all verbose output to stderr.
>         (gcov_profile_normalize): Ditto.
>
> Index: gcc/coverage.c
> ===================================================================
> --- gcc/coverage.c      (revision 215230)
> +++ gcc/coverage.c      (working copy)
> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign
>              free (build_info_strings[i]);
>            free (build_info_strings);
>          }
> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
> +        {
> +          /* Zero-profile fixup flags are not used by the compiler, read and
> +             ignore.  */
> +          gcov_unsigned_t num_fn;
> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
> (length, &num_fn);
> +          free (zero_fixup_flags);
> +        }
>        else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
>         {
>           counts_entry_t **slot, *entry, elt;
> Index: gcc/gcov.c
> ===================================================================
> --- gcc/gcov.c  (revision 215230)
> +++ gcc/gcov.c  (working copy)
> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns)
>              free (build_info_strings[i]);
>            free (build_info_strings);
>          }
> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
> +        {
> +          gcov_unsigned_t num_fn;
> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
> (length, &num_fn);
> +          free (zero_fixup_flags);
> +        }
>        else if (tag == GCOV_TAG_FUNCTION && !length)
>         ; /* placeholder  */
>        else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
> Index: gcc/gcov-dump.c
> ===================================================================
> --- gcc/gcov-dump.c     (revision 215230)
> +++ gcc/gcov-dump.c     (working copy)
> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u
>  static void tag_module_info (const char *, unsigned, unsigned);
>  static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
>                                 const struct gcov_ctr_summary *summary);
> +static void tag_zero_fixup (const char *, unsigned, unsigned);
>  static void tag_build_info (const char *, unsigned, unsigned);
>  extern int main (int, char **);
>
> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0;
>  static int flag_dump_aux_modules_only = 0;
>  static int flag_dump_working_sets = 0;
>
> +static unsigned num_fn_info;
> +static int *zero_fixup_flags = NULL;
> +
>  static const struct option options[] =
>  {
>    { "help",                 no_argument,       NULL, 'h' },
> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] =
>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>    {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
>    {0, NULL, NULL}
>  };
> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename)
>      printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
>    }
>
> +  num_fn_info = 0;
> +
>    while (1)
>      {
>        gcov_position_t base, position = gcov_position ();
> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename)
>           break;
>         }
>      }
> +  free (zero_fixup_flags);
>    gcov_close ();
>  }
>
> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>      printf (" placeholder");
>    else
>      {
> -      printf (" ident=%u", gcov_read_unsigned ());
> +      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
> +      printf (" ident=%u%s", gcov_read_unsigned (),
> +              had_fixup ? " (Was 0-count COMDAT)" : "");
>        printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
>        printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());
>
> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>           printf (":%u", gcov_read_unsigned ());
>         }
>      }
> +  num_fn_info++;
>  }
>
>  static void
> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN
>  }
>
>  static void
> +tag_zero_fixup (const char *filename,
> +                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
> +{
> +  gcov_unsigned_t num_fns = 0;
> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
> +  if (!zero_fixup_flags)
> +    {
> +      printf ("%s:error reading zero fixup flags\n", filename);
> +      return;
> +    }
> +  printf (" num_fns=%u", num_fns);
> +  for (unsigned i = 0; i < num_fns; i++)
> +    {
> +      if (!(i % 32))
> +        {
> +          printf ("\n");
> +          print_prefix (filename, 0, 0);
> +          printf ("\t\t");
> +        }
> +      if (!(i % 8))
> +        printf ("%s%4u:", (i%32)?" ":"", i);
> +      printf ("%u", zero_fixup_flags[i]);
> +    }
> +}
> +
> +static void
>  tag_build_info (const char *filename,
>                 unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>  {
> Index: gcc/gcov-io.c
> ===================================================================
> --- gcc/gcov-io.c       (revision 215230)
> +++ gcc/gcov-io.c       (working copy)
> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary)
>      }
>  }
>
> +/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
> +   number of function flags saved in NUM_FNS.  Returns the int flag
> array, which
> +   should be deallocated by caller, or NULL on error.  */
> +
> +GCOV_LINKAGE int *
> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
> +                             gcov_unsigned_t *num_fns)
> +{
> +  unsigned ix, f_ix;
> +  gcov_unsigned_t num = gcov_read_unsigned ();
> +  /* The length consists of 1 word to hold the number of functions,
> +     plus enough 32-bit words to hold 1 bit/function.  */
> +  gcc_assert ((num + 31) / 32 + 1 == length);
> +  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
> +  for (ix = 0; ix < length - 1; ix++)
> +    {
> +      gcov_unsigned_t bitvector = gcov_read_unsigned ();
> +      f_ix = ix * 32;
> +      while (bitvector)
> +        {
> +          if (bitvector & 0x1)
> +            zero_fixup_flags[f_ix] = 1;
> +          f_ix++;
> +          bitvector >>= 1;
> +        }
> +    }
> +  *num_fns = num;
> +  return zero_fixup_flags;
> +}
> +
>  /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
>     the number of words read.  */
>
> Index: gcc/gcov-io.h
> ===================================================================
> --- gcc/gcov-io.h       (revision 215230)
> +++ gcc/gcov-io.h       (working copy)
> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>     blocks they are for.
>
>     The data file contains the following records.
> -        data: {unit summary:program* build_info function-data*}*
> +        data: {unit summary:program* build_info zero_fixup function-data*}*
>         unit: header int32:checksum
>          function-data: announce_function present counts
>         announce_function: header int32:ident
> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>          histogram: {int32:bitvector}8 histogram-buckets*
>          histogram-buckets: int32:num int64:min int64:sum
>          build_info: string:info*
> +        zero_fixup: int32:num int32:bitvector*
>
>     The ANNOUNCE_FUNCTION record is the same as that in the note file,
>     but without the source location.  The COUNTS gives the
> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect
>     build.  For example, it can be used to include source revision
>     information that is useful in diagnosing profile mis-matches.
>
> +   ZERO_FIXUP record contains a count of functions in the gcda file
> +   and an array of bitvectors indexed by the function index's in the
> +   function-data section. Each bit flags whether the function was a
> +   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
> +   using profiles from functions with matching checksums in other modules.
> +
>     This file is included by both the compiler, gcov tools and the
>     runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
>     distinguish which case is which.  If IN_LIBGCOV is nonzero,
> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
>  #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
>  #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
>  #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
>  #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
>          (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
>  #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
>  GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
>  GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
>  GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
> +                                               gcov_unsigned_t *)
> +    ATTRIBUTE_HIDDEN;
>  GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
>    ATTRIBUTE_HIDDEN;
>  GCOV_LINKAGE const char *gcov_read_string (void);
> Index: libgcc/dyn-ipa.c
> ===================================================================
> --- libgcc/dyn-ipa.c    (revision 215230)
> +++ libgcc/dyn-ipa.c    (working copy)
> @@ -107,8 +107,9 @@ struct checksum_alias
>    struct checksum_alias *next_alias;
>    gcov_type guid;
>    const struct gcov_fn_info *fi_ptr;
> -  /* Does this function have all-zero arc counts?  */
> -  int zero_counts;
> +  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
> +     set if we perform fixup.  */
> +  int *zero_count_fixup;
>  };
>
>  /* Module info is stored in dyn_caph->sup_modules
> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
>  extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
>
>  #if defined(inhibit_libc)
> -void __gcov_build_callgraph (void) {}
> +void __gcov_build_callgraph (int **zero_counts) {}
>  #else
>
> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>  static void gcov_dump_callgraph (gcov_type);
>  static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p)
>  }
>
>  /* Create a new checksum_alias struct for function with GUID, FI_PTR,
> -   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
> +   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
> +   new struct.  */
>
>  static struct checksum_alias *
>  new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
> -                    int zero_counts,
> +                    int *zero_count_fixup,
>                      struct checksum_alias *next)
>  {
>    struct checksum_alias *alias = XNEW (struct checksum_alias);
>    alias->next_alias = next;
>    alias->fi_ptr = fi_ptr;
>    alias->guid = guid;
> -  alias->zero_counts = zero_counts;
> +  alias->zero_count_fixup = zero_count_fixup;
>    return alias;
>  }
>
> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis
>  }
>
>  /* Insert a new checksum_alias struct into LIST for function with
> -   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
> +   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
> +   flag pointer.  */
>
>  static struct checksum_alias_info *
>  cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
> -                     const struct gcov_fn_info *fi_ptr, int zero_counts,
> +                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
>                       struct checksum_alias_info *list)
>  {
>    struct checksum_alias_info *alias_info;
> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>    if (alias_info)
>      {
>        gcc_assert (alias_info->alias_list);
> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
> +                                                   zero_count_fixup,
>                                                     alias_info->alias_list);
>        return list;
>      }
> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>        alias_info = XNEW (struct checksum_alias_info);
>        alias_info->next_cfg_checksum = list;
>        alias_info->cfg_checksum = cfg_checksum;
> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
> +                                                   zero_count_fixup,
>                                                     NULL);
>        return alias_info;
>      }
> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>
>  /* Insert a new checksum_alias struct into lineno_pointer_sets for
> function with
>     LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
> -   ZERO_COUNTS flag.  */
> +   ZERO_COUNT_FIXUP flag pointer.  */
>
>  static void
>  checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
>                       gcov_type guid, const struct gcov_fn_info *fi_ptr,
> -                     int zero_counts)
> +                     int *zero_count_fixup)
>  {
>    struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
>    if (!p)
> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns
>    if (*m)
>      {
>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
> -                                                     fi_ptr, zero_counts,
> +                                                     fi_ptr, zero_count_fixup,
>                                                       (*m)->cfg_checksum_list);
>      }
>    else
> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns
>        *m = XNEW (struct lineno_checksum_alias);
>        (*m)->lineno_checksum = lineno_checksum;
>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
> -                                                     fi_ptr,
> zero_counts, NULL);
> +                                                     fi_ptr, zero_count_fixup,
> +                                                     NULL);
>        p->n_elements++;
>      }
>  }
> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
>      }
>  }
>
> -/* Build the dynamic call graph.  */
> +/* Build the dynamic call graph and update ZERO_COUNTS flags.  */
>
>  static void
> -gcov_build_callgraph (void)
> +gcov_build_callgraph (int **zero_counts)
>  {
>    struct gcov_info *gi_ptr;
>    unsigned m_ix;
> @@ -852,9 +858,19 @@ static void
>                    if (total_arc_count != 0)
>                      the_dyn_call_graph.num_nodes_executed++;
>                    if (fixup_type)
> -                    checksum_set_insert (fi_ptr->lineno_checksum,
> -                                         fi_ptr->cfg_checksum, caller->guid,
> -                                         fi_ptr, total_arc_count == 0);
> +                    {
> +                      int *zero_count_fixup = NULL;
> +                      /* Passing in a non-NULL zero_count_fixup pointer
> +                         indicates that the counts were all zero for this
> +                         function, and the fixup routine will set the flag
> +                         if the function's counters are updated to non-zero
> +                         values.  */
> +                      if (total_arc_count == 0)
> +                        zero_count_fixup = &zero_counts[m_ix][f_ix];
> +                      checksum_set_insert (fi_ptr->lineno_checksum,
> +                                           fi_ptr->cfg_checksum, caller->guid,
> +                                           fi_ptr, zero_count_fixup);
> +                    }
>                  }
>                ci_ptr++;
>              }
> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value,
>    out_array = (struct gcov_import_mod_array *) data1;
>
>    if (m->imp_mod != out_array->importing_module)
> +  {
>      out_array->imported_modules[out_array->len++] = m;
> +    /* Sanity check that the importing (primary) module is not
> +       actually the same as the new aux module. This could happen if
> +       we accidentally read in the same gcda file twice.  */
> +    gcc_assert (m->imp_mod->mod_info->ident !=
> +                out_array->importing_module->mod_info->ident);
> +  }
>
>    return 1;
>  }
> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu
>    for (alias = info->alias_list; alias;
>         alias = alias->next_alias)
>      {
> -      if (alias->zero_counts)
> +      if (alias->zero_count_fixup)
>          {
>            found = 1;
>            break;
> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu
>    for (alias = info->alias_list; alias;
>         alias = alias->next_alias)
>      {
> -      if (alias->zero_counts)
> +      if (alias->zero_count_fixup)
>          continue;
>        merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
>        found = 1;
> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu
>    for (alias = info->alias_list; alias;
>         alias = alias->next_alias)
>      {
> -      if (!alias->zero_counts)
> +      if (!alias->zero_count_fixup)
>          continue;
>        copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
> +      *alias->zero_count_fixup = 1;
>      }
>
>    return 1;
> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void)
>    return changed;
>  }
>
> -/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
> -   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
> +/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
> +   flags are set for functions with zero count fixups applied. Returns 1
> +   if any counter fixups were applied, requiring a profile rewrite,
> +   0 otherwise.  */
>
>  int
> -__gcov_compute_module_groups (void)
> +__gcov_compute_module_groups (int **zero_counts)
>  {
>    gcov_type cut_off_count;
>    char *seed = getenv ("LIPO_RANDOM_GROUPING");
> @@ -3091,7 +3117,7 @@ int
>      fixup_type = atoi (do_fixup);
>
>    /* First compute dynamic call graph.  */
> -  gcov_build_callgraph ();
> +  gcov_build_callgraph (zero_counts);
>
>    cut_off_count = gcov_compute_cutoff_count ();
>
> Index: libgcc/libgcov-driver.c
> ===================================================================
> --- libgcc/libgcov-driver.c     (revision 215230)
> +++ libgcc/libgcov-driver.c     (working copy)
> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
>
>
>  /* Dynamic call graph build and form module groups.  */
> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>
>  /* The following functions can be called from outside of this file.  */
> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head)
>    __gcov_list = head;
>  }
>
> +/* Flag if the current function being read was marked as having fixed-up
> +   zero counters.  */
> +static int __gcov_curr_fn_fixed_up;
> +
> +/* Set function fixed up flag.  */
> +void
> +set_gcov_fn_fixed_up (int fixed_up)
> +{
> +  __gcov_curr_fn_fixed_up = fixed_up;
> +}
> +
> +/* Return function fixed up flag.  */
> +int
> +get_gcov_fn_fixed_up (void)
> +{
> +  return __gcov_curr_fn_fixed_up;
> +}
> +
>  /* Size of the longest file name. */
>  /* We need to expose this static variable when compiling for gcov-tool.  */
>  #ifndef IN_GCOV_TOOL
> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>    int error = 0;
>    struct gcov_fn_buffer **fn_tail = &fn_buffer;
>    struct gcov_summary_buffer **sum_tail = &sum_buffer;
> +  int *zero_fixup_flags = NULL;
>
>    length = gcov_read_unsigned ();
>    if (!gcov_version (gi_ptr, length, gi_filename))
> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>        tag = gcov_read_unsigned ();
>      }
>
> +  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
> +    {
> +      length = gcov_read_unsigned ();
> +      gcov_unsigned_t num_fns = 0;
> +      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
> +      if (!zero_fixup_flags)
> +        {
> +          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
> +                      gi_filename);
> +          return -1;
> +        }
> +
> +      tag = gcov_read_unsigned ();
> +    }
> +
>    /* Merge execution counts for each function.  */
>    for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
>         f_ix++, tag = gcov_read_unsigned ())
> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>            continue;
>          }
>
> +      if (zero_fixup_flags)
> +        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
> +
>        length = gcov_read_unsigned ();
>        if (length != gfi_ptr->ident)
>          goto read_mismatch;
> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>        if ((error = gcov_is_error ()))
>          goto read_error;
>      }
> +  free (zero_fixup_flags);
>
>    if (tag && tag != GCOV_TAG_MODULE_INFO)
>      {
> @@ -706,6 +744,34 @@ read_error:
>    return -1;
>  }
>
> +
> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
> +   current location.  */
> +
> +static void
> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
> +{
> +  unsigned f_ix;
> +  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
> +  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
> +
> +  gcov_write_unsigned (num_fns);
> +  gcov_unsigned_t bitvector = 0, b_ix = 0;
> +  for (f_ix = 0; f_ix != num_fns; f_ix++)
> +    {
> +      if (zero_counts[f_ix])
> +        bitvector |= 1 << b_ix;
> +      if (++b_ix == 32)
> +        {
> +          gcov_write_unsigned (bitvector);
> +          b_ix = 0;
> +          bitvector = 0;
> +        }
> +    }
> +  if (b_ix > 0)
> +    gcov_write_unsigned (bitvector);
> +}
> +
>  /* Write build_info strings from GI_PTR to a gcda file starting from
> its current
>     location.  */
>
> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr
>            if (gfi_ptr && gfi_ptr->key == gi_ptr)
>              length = GCOV_TAG_FUNCTION_LENGTH;
>            else
> -                length = 0;
> +            length = 0;
>          }
>
>        gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>  {
>    struct gcov_info *gi_ptr;
>
> +  unsigned max_module_id = 0;
> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
> +    {
> +      unsigned mod_id = gi_ptr->mod_info->ident;
> +      if (max_module_id < mod_id)
> +        max_module_id = mod_id;
> +    }
> +  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
> +    {
> +      unsigned mod_id = gi_ptr->mod_info->ident;
> +      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
> +                                               sizeof (int));
> +    }
> +
>    /* Compute the module groups and record whether there were any
>       counter fixups applied that require rewriting the counters.  */
> -  int changed = __gcov_compute_module_groups ();
> +  int changed = __gcov_compute_module_groups (zero_counts);
>
>    /* Now write out module group info.  */
>    for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>                gcov_position_t eof_pos = gi_ptr->eof_pos;
>                gcov_rewrite ();
>                gcov_seek (summary_end_pos);
> +
> +              unsigned mod_id = gi_ptr->mod_info->ident;
> +              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
> +                                            gi_ptr->n_functions);
> +              gcov_position_t zero_fixup_eof_pos = gcov_position ();
> +
>                gcov_write_func_counters (gi_ptr);
> -              gcc_assert (eof_pos == gi_ptr->eof_pos);
> +              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
> +                          == gi_ptr->eof_pos);
>              }
>          }
>        else
> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>                                    "profiling:%s:Error writing\n",
>                                    gi_filename);
>        gcov_write_import_file (gi_filename, gi_ptr);
> +      free (zero_counts[gi_ptr->mod_info->ident-1]);
>      }
> +
> +  free (zero_counts);
> +
>    __gcov_finalize_dyn_callgraph ();
>  }
>
> Index: libgcc/libgcov.h
> ===================================================================
> --- libgcc/libgcov.h    (revision 215230)
> +++ libgcc/libgcov.h    (working copy)
> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i
>      ATTRIBUTE_HIDDEN;
>  GCOV_LINKAGE inline void gcov_rewrite (void);
>
> +extern void set_gcov_fn_fixed_up (int fixed_up);
> +extern int get_gcov_fn_fixed_up (void);
> +
>  /* "Counts" stored in gcda files can be a real counter value, or
>     an target address. When differentiate these two types because
>     when manipulating counts, we should only change real counter values,
> @@ -361,7 +364,13 @@ gcov_get_counter (void)
>    /* This version is for reading count values in libgcov runtime:
>       we read from gcda files.  */
>
> -  return gcov_read_counter ();
> +  if (get_gcov_fn_fixed_up ())
> +    {
> +      gcov_read_counter ();
> +      return 0;
> +    }
> +  else
> +    return gcov_read_counter ();
>  #else
>    /* This version is for gcov-tool. We read the value from memory and
>       multiply it by the merge weight.  */
> @@ -380,7 +389,13 @@ gcov_get_counter_target (void)
>    /* This version is for reading count target values in libgcov runtime:
>       we read from gcda files.  */
>
> -  return gcov_read_counter ();
> +  if (get_gcov_fn_fixed_up ())
> +    {
> +      gcov_read_counter ();
> +      return 0;
> +    }
> +  else
> +    return gcov_read_counter ();
>  #else
>    /* This version is for gcov-tool.  We read the value from memory
> and we do NOT
>       multiply it by the merge weight.  */
> Index: libgcc/libgcov-util.c
> ===================================================================
> --- libgcc/libgcov-util.c       (revision 215230)
> +++ libgcc/libgcov-util.c       (working copy)
> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned);
>  static void tag_counters (unsigned, unsigned);
>  static void tag_summary (unsigned, unsigned);
>  static void tag_module_info (unsigned, unsigned);
> +static void tag_zero_fixup (unsigned, unsigned);
>
>  /* The gcov_info for the first module.  */
>  static struct gcov_info *curr_gcov_info;
> @@ -88,6 +89,8 @@ static int k_ctrs_types;
>  /* The longest length of all the filenames.  */
>  static int max_filename_len;
>
> +static int *zero_fixup_flags = NULL;
> +
>  /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
>     functions in dyn-ipa.c, which were derived from these, except
>     the versions in dyn-ipa are used when merging from another array.  */
> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] =
>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>    {0, NULL, NULL}
>  };
>
> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
>       k_ctrs[i].num = 0;
>    k_ctrs_types = 0;
>
> +  if (zero_fixup_flags)
> +    {
> +      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
> +      if (get_gcov_fn_fixed_up () && verbose)
> +        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
> +    }
> +
>    curr_fn_info->key = curr_gcov_info;
>    curr_fn_info->ident = gcov_read_unsigned ();
>    curr_fn_info->lineno_checksum = gcov_read_unsigned ();
>    curr_fn_info->cfg_checksum = gcov_read_unsigned ();
>    num_fn_info++;
> -
> -  if (verbose)
> -    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
>  }
>
>  /* Handler for reading block tag.  */
> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length)
>    gcc_assert (values);
>
>    for (ix = 0; ix != n_counts; ix++)
> -    values[ix] = gcov_read_counter ();
> +    {
> +      gcov_type val = gcov_read_counter ();
> +      if (!get_gcov_fn_fixed_up ())
> +        values[ix] = val;
> +      else
> +        values[ix] = 0;
> +    }
>  }
>
>  /* Handler for reading summary tag.  */
> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str,
>        char *t;
>
>        if (verbose)
> -        printf ("Substitute: %s \n", input_str);
> +        fprintf (stderr, "Substitute: %s \n", input_str);
>        t = (char*) xmalloc (strlen (input_str) + 1
>            + strlen (new_str) - strlen (cur_str));
>        *p = 0;
> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str,
>        strcat (t, new_str);
>        strcat (t, p + strlen (cur_str));
>        if (verbose)
> -        printf ("       -->  %s\n", t);
> +        fprintf (stderr, "       -->  %s\n", t);
>        return t;
>      }
>
> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
>      free (mod_info);
>  }
>
> +/* Handler for reading the COMDAT zero-profile fixup section.  */
> +
> +static void
> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
> +{
> +  gcov_unsigned_t num_fns = 0;
> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
> +  gcc_assert (zero_fixup_flags);
> +}
> +
>  /* Read the content of a gcda file FILENAME, and return a gcov_info
> data structure.
>     Program level summary CURRENT_SUMMARY will also be updated.  */
>
> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
>      }
>
>    read_gcda_finalize (obj_info);
> +  free (zero_fixup_flags);
>    gcov_close ();
>
>    return obj_info;
> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
>    unsigned f_ix;
>
>    if (verbose)
> -    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
> +    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>
>    /* Scaling the counters.  */
>    for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
>    scale_factor = (float)max_val / curr_max_val;
>  #if !defined (_WIN32)
>    if (verbose)
> -    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
> +    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
>  #endif
>
>    return gcov_profile_scale (profile, scale_factor, 0, 0);
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Teresa Johnson Sept. 16, 2014, 2:57 p.m. UTC | #2
On Mon, Sep 15, 2014 at 9:29 PM, Xinliang David Li <davidxl@google.com> wrote:
> Is it necessary to declare zero_counts array at all?  Can a flag field
> be added to dyn_cgraph_node structure to indicate if it is fixed up?

The zero_counts array is used to pass info back to the caller in
libgcov-driver.cc (dyn_cgraph_node), which is where it is allocated.
That routine does not have access to the dyn-ipa cgraph.

Teresa

>
> David
>
> On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> This patch addresses issues when running gcov-tool after performing
>> COMDAT fixup during dyn-ipa. Functions that were previously all zero
>> counts are marked, and the counts are discarded when being read in
>> by gcov-tool before recalculating module groups and summary info.
>>
>> While here, cleaned up the gcov-tool output (remove an overly-verbose output,
>> make all output consistently go to stderr).
>>
>> Passes regression tests and manual tests. Ok for google branches?
>>
>> 2014-09-12  Teresa Johnson  <tejohnson@google.com>
>>
>>         * gcc/coverage.c (read_counts_file): Handle new section.
>>         * gcc/gcov.c (read_count_file): Ditto.
>>         * gcc/gcov-dump.c (dump_gcov_file): Ditto.
>>         (tag_function): Ditto.
>>         (tag_zero_fixup): New function.
>>         * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
>>         * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
>>         * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
>>         (new_checksum_alias): Ditto.
>>         (cfg_checksum_insert): Ditto.
>>         (checksum_set_insert): Ditto.
>>         (gcov_build_callgraph): New parameter.
>>         (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
>>         (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
>>         (__gcov_compute_module_groups): New parameter.
>>         * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
>>         (get_gcov_fn_fixed_up): Ditto.
>>         (gcov_exit_merge_gcda): Handle new section.
>>         (gcov_write_comdat_zero_fixup): Ditto.
>>         (gcov_write_build_info): Ditto.
>>         (gcov_write_comdat_zero_fixup): New function.
>>         (gcov_write_func_counters): Fix indent.
>>         (gcov_dump_module_info): Write new flag section.
>>         * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
>>         (gcov_get_counter_target): Ditto.
>>         * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
>>         remove overly verbose output.
>>         (tag_counters): Clear fixed-up counters.
>>         (lipo_process_substitute_string_1): Send all verbose output to stderr.
>>         (tag_zero_fixup): New function.
>>         (read_gcda_file): Deallocate flag array.
>>         (gcov_profile_scale): Send all verbose output to stderr.
>>         (gcov_profile_normalize): Ditto.
>>
>> Index: gcc/coverage.c
>> ===================================================================
>> --- gcc/coverage.c      (revision 215230)
>> +++ gcc/coverage.c      (working copy)
>> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign
>>              free (build_info_strings[i]);
>>            free (build_info_strings);
>>          }
>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>> +        {
>> +          /* Zero-profile fixup flags are not used by the compiler, read and
>> +             ignore.  */
>> +          gcov_unsigned_t num_fn;
>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>> (length, &num_fn);
>> +          free (zero_fixup_flags);
>> +        }
>>        else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
>>         {
>>           counts_entry_t **slot, *entry, elt;
>> Index: gcc/gcov.c
>> ===================================================================
>> --- gcc/gcov.c  (revision 215230)
>> +++ gcc/gcov.c  (working copy)
>> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns)
>>              free (build_info_strings[i]);
>>            free (build_info_strings);
>>          }
>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>> +        {
>> +          gcov_unsigned_t num_fn;
>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>> (length, &num_fn);
>> +          free (zero_fixup_flags);
>> +        }
>>        else if (tag == GCOV_TAG_FUNCTION && !length)
>>         ; /* placeholder  */
>>        else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
>> Index: gcc/gcov-dump.c
>> ===================================================================
>> --- gcc/gcov-dump.c     (revision 215230)
>> +++ gcc/gcov-dump.c     (working copy)
>> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u
>>  static void tag_module_info (const char *, unsigned, unsigned);
>>  static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
>>                                 const struct gcov_ctr_summary *summary);
>> +static void tag_zero_fixup (const char *, unsigned, unsigned);
>>  static void tag_build_info (const char *, unsigned, unsigned);
>>  extern int main (int, char **);
>>
>> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0;
>>  static int flag_dump_aux_modules_only = 0;
>>  static int flag_dump_working_sets = 0;
>>
>> +static unsigned num_fn_info;
>> +static int *zero_fixup_flags = NULL;
>> +
>>  static const struct option options[] =
>>  {
>>    { "help",                 no_argument,       NULL, 'h' },
>> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] =
>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>    {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
>>    {0, NULL, NULL}
>>  };
>> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename)
>>      printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
>>    }
>>
>> +  num_fn_info = 0;
>> +
>>    while (1)
>>      {
>>        gcov_position_t base, position = gcov_position ();
>> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename)
>>           break;
>>         }
>>      }
>> +  free (zero_fixup_flags);
>>    gcov_close ();
>>  }
>>
>> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>      printf (" placeholder");
>>    else
>>      {
>> -      printf (" ident=%u", gcov_read_unsigned ());
>> +      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
>> +      printf (" ident=%u%s", gcov_read_unsigned (),
>> +              had_fixup ? " (Was 0-count COMDAT)" : "");
>>        printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
>>        printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());
>>
>> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>           printf (":%u", gcov_read_unsigned ());
>>         }
>>      }
>> +  num_fn_info++;
>>  }
>>
>>  static void
>> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN
>>  }
>>
>>  static void
>> +tag_zero_fixup (const char *filename,
>> +                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>> +{
>> +  gcov_unsigned_t num_fns = 0;
>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>> +  if (!zero_fixup_flags)
>> +    {
>> +      printf ("%s:error reading zero fixup flags\n", filename);
>> +      return;
>> +    }
>> +  printf (" num_fns=%u", num_fns);
>> +  for (unsigned i = 0; i < num_fns; i++)
>> +    {
>> +      if (!(i % 32))
>> +        {
>> +          printf ("\n");
>> +          print_prefix (filename, 0, 0);
>> +          printf ("\t\t");
>> +        }
>> +      if (!(i % 8))
>> +        printf ("%s%4u:", (i%32)?" ":"", i);
>> +      printf ("%u", zero_fixup_flags[i]);
>> +    }
>> +}
>> +
>> +static void
>>  tag_build_info (const char *filename,
>>                 unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>  {
>> Index: gcc/gcov-io.c
>> ===================================================================
>> --- gcc/gcov-io.c       (revision 215230)
>> +++ gcc/gcov-io.c       (working copy)
>> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary)
>>      }
>>  }
>>
>> +/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
>> +   number of function flags saved in NUM_FNS.  Returns the int flag
>> array, which
>> +   should be deallocated by caller, or NULL on error.  */
>> +
>> +GCOV_LINKAGE int *
>> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
>> +                             gcov_unsigned_t *num_fns)
>> +{
>> +  unsigned ix, f_ix;
>> +  gcov_unsigned_t num = gcov_read_unsigned ();
>> +  /* The length consists of 1 word to hold the number of functions,
>> +     plus enough 32-bit words to hold 1 bit/function.  */
>> +  gcc_assert ((num + 31) / 32 + 1 == length);
>> +  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
>> +  for (ix = 0; ix < length - 1; ix++)
>> +    {
>> +      gcov_unsigned_t bitvector = gcov_read_unsigned ();
>> +      f_ix = ix * 32;
>> +      while (bitvector)
>> +        {
>> +          if (bitvector & 0x1)
>> +            zero_fixup_flags[f_ix] = 1;
>> +          f_ix++;
>> +          bitvector >>= 1;
>> +        }
>> +    }
>> +  *num_fns = num;
>> +  return zero_fixup_flags;
>> +}
>> +
>>  /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
>>     the number of words read.  */
>>
>> Index: gcc/gcov-io.h
>> ===================================================================
>> --- gcc/gcov-io.h       (revision 215230)
>> +++ gcc/gcov-io.h       (working copy)
>> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>     blocks they are for.
>>
>>     The data file contains the following records.
>> -        data: {unit summary:program* build_info function-data*}*
>> +        data: {unit summary:program* build_info zero_fixup function-data*}*
>>         unit: header int32:checksum
>>          function-data: announce_function present counts
>>         announce_function: header int32:ident
>> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>          histogram: {int32:bitvector}8 histogram-buckets*
>>          histogram-buckets: int32:num int64:min int64:sum
>>          build_info: string:info*
>> +        zero_fixup: int32:num int32:bitvector*
>>
>>     The ANNOUNCE_FUNCTION record is the same as that in the note file,
>>     but without the source location.  The COUNTS gives the
>> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>     build.  For example, it can be used to include source revision
>>     information that is useful in diagnosing profile mis-matches.
>>
>> +   ZERO_FIXUP record contains a count of functions in the gcda file
>> +   and an array of bitvectors indexed by the function index's in the
>> +   function-data section. Each bit flags whether the function was a
>> +   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
>> +   using profiles from functions with matching checksums in other modules.
>> +
>>     This file is included by both the compiler, gcov tools and the
>>     runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
>>     distinguish which case is which.  If IN_LIBGCOV is nonzero,
>> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
>>  #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
>>  #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
>>  #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
>> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
>>  #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
>>          (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
>>  #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
>> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
>>  GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
>>  GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
>>  GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
>> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
>> +                                               gcov_unsigned_t *)
>> +    ATTRIBUTE_HIDDEN;
>>  GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
>>    ATTRIBUTE_HIDDEN;
>>  GCOV_LINKAGE const char *gcov_read_string (void);
>> Index: libgcc/dyn-ipa.c
>> ===================================================================
>> --- libgcc/dyn-ipa.c    (revision 215230)
>> +++ libgcc/dyn-ipa.c    (working copy)
>> @@ -107,8 +107,9 @@ struct checksum_alias
>>    struct checksum_alias *next_alias;
>>    gcov_type guid;
>>    const struct gcov_fn_info *fi_ptr;
>> -  /* Does this function have all-zero arc counts?  */
>> -  int zero_counts;
>> +  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
>> +     set if we perform fixup.  */
>> +  int *zero_count_fixup;
>>  };
>>
>>  /* Module info is stored in dyn_caph->sup_modules
>> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
>>  extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
>>
>>  #if defined(inhibit_libc)
>> -void __gcov_build_callgraph (void) {}
>> +void __gcov_build_callgraph (int **zero_counts) {}
>>  #else
>>
>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>  static void gcov_dump_callgraph (gcov_type);
>>  static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
>> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p)
>>  }
>>
>>  /* Create a new checksum_alias struct for function with GUID, FI_PTR,
>> -   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
>> +   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
>> +   new struct.  */
>>
>>  static struct checksum_alias *
>>  new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
>> -                    int zero_counts,
>> +                    int *zero_count_fixup,
>>                      struct checksum_alias *next)
>>  {
>>    struct checksum_alias *alias = XNEW (struct checksum_alias);
>>    alias->next_alias = next;
>>    alias->fi_ptr = fi_ptr;
>>    alias->guid = guid;
>> -  alias->zero_counts = zero_counts;
>> +  alias->zero_count_fixup = zero_count_fixup;
>>    return alias;
>>  }
>>
>> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis
>>  }
>>
>>  /* Insert a new checksum_alias struct into LIST for function with
>> -   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
>> +   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
>> +   flag pointer.  */
>>
>>  static struct checksum_alias_info *
>>  cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
>> -                     const struct gcov_fn_info *fi_ptr, int zero_counts,
>> +                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
>>                       struct checksum_alias_info *list)
>>  {
>>    struct checksum_alias_info *alias_info;
>> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>    if (alias_info)
>>      {
>>        gcc_assert (alias_info->alias_list);
>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>> +                                                   zero_count_fixup,
>>                                                     alias_info->alias_list);
>>        return list;
>>      }
>> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>        alias_info = XNEW (struct checksum_alias_info);
>>        alias_info->next_cfg_checksum = list;
>>        alias_info->cfg_checksum = cfg_checksum;
>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>> +                                                   zero_count_fixup,
>>                                                     NULL);
>>        return alias_info;
>>      }
>> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>
>>  /* Insert a new checksum_alias struct into lineno_pointer_sets for
>> function with
>>     LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
>> -   ZERO_COUNTS flag.  */
>> +   ZERO_COUNT_FIXUP flag pointer.  */
>>
>>  static void
>>  checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
>>                       gcov_type guid, const struct gcov_fn_info *fi_ptr,
>> -                     int zero_counts)
>> +                     int *zero_count_fixup)
>>  {
>>    struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
>>    if (!p)
>> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>    if (*m)
>>      {
>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>> -                                                     fi_ptr, zero_counts,
>> +                                                     fi_ptr, zero_count_fixup,
>>                                                       (*m)->cfg_checksum_list);
>>      }
>>    else
>> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>        *m = XNEW (struct lineno_checksum_alias);
>>        (*m)->lineno_checksum = lineno_checksum;
>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>> -                                                     fi_ptr,
>> zero_counts, NULL);
>> +                                                     fi_ptr, zero_count_fixup,
>> +                                                     NULL);
>>        p->n_elements++;
>>      }
>>  }
>> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
>>      }
>>  }
>>
>> -/* Build the dynamic call graph.  */
>> +/* Build the dynamic call graph and update ZERO_COUNTS flags.  */
>>
>>  static void
>> -gcov_build_callgraph (void)
>> +gcov_build_callgraph (int **zero_counts)
>>  {
>>    struct gcov_info *gi_ptr;
>>    unsigned m_ix;
>> @@ -852,9 +858,19 @@ static void
>>                    if (total_arc_count != 0)
>>                      the_dyn_call_graph.num_nodes_executed++;
>>                    if (fixup_type)
>> -                    checksum_set_insert (fi_ptr->lineno_checksum,
>> -                                         fi_ptr->cfg_checksum, caller->guid,
>> -                                         fi_ptr, total_arc_count == 0);
>> +                    {
>> +                      int *zero_count_fixup = NULL;
>> +                      /* Passing in a non-NULL zero_count_fixup pointer
>> +                         indicates that the counts were all zero for this
>> +                         function, and the fixup routine will set the flag
>> +                         if the function's counters are updated to non-zero
>> +                         values.  */
>> +                      if (total_arc_count == 0)
>> +                        zero_count_fixup = &zero_counts[m_ix][f_ix];
>> +                      checksum_set_insert (fi_ptr->lineno_checksum,
>> +                                           fi_ptr->cfg_checksum, caller->guid,
>> +                                           fi_ptr, zero_count_fixup);
>> +                    }
>>                  }
>>                ci_ptr++;
>>              }
>> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value,
>>    out_array = (struct gcov_import_mod_array *) data1;
>>
>>    if (m->imp_mod != out_array->importing_module)
>> +  {
>>      out_array->imported_modules[out_array->len++] = m;
>> +    /* Sanity check that the importing (primary) module is not
>> +       actually the same as the new aux module. This could happen if
>> +       we accidentally read in the same gcda file twice.  */
>> +    gcc_assert (m->imp_mod->mod_info->ident !=
>> +                out_array->importing_module->mod_info->ident);
>> +  }
>>
>>    return 1;
>>  }
>> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>    for (alias = info->alias_list; alias;
>>         alias = alias->next_alias)
>>      {
>> -      if (alias->zero_counts)
>> +      if (alias->zero_count_fixup)
>>          {
>>            found = 1;
>>            break;
>> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>    for (alias = info->alias_list; alias;
>>         alias = alias->next_alias)
>>      {
>> -      if (alias->zero_counts)
>> +      if (alias->zero_count_fixup)
>>          continue;
>>        merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
>>        found = 1;
>> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu
>>    for (alias = info->alias_list; alias;
>>         alias = alias->next_alias)
>>      {
>> -      if (!alias->zero_counts)
>> +      if (!alias->zero_count_fixup)
>>          continue;
>>        copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
>> +      *alias->zero_count_fixup = 1;
>>      }
>>
>>    return 1;
>> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void)
>>    return changed;
>>  }
>>
>> -/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
>> -   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
>> +/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
>> +   flags are set for functions with zero count fixups applied. Returns 1
>> +   if any counter fixups were applied, requiring a profile rewrite,
>> +   0 otherwise.  */
>>
>>  int
>> -__gcov_compute_module_groups (void)
>> +__gcov_compute_module_groups (int **zero_counts)
>>  {
>>    gcov_type cut_off_count;
>>    char *seed = getenv ("LIPO_RANDOM_GROUPING");
>> @@ -3091,7 +3117,7 @@ int
>>      fixup_type = atoi (do_fixup);
>>
>>    /* First compute dynamic call graph.  */
>> -  gcov_build_callgraph ();
>> +  gcov_build_callgraph (zero_counts);
>>
>>    cut_off_count = gcov_compute_cutoff_count ();
>>
>> Index: libgcc/libgcov-driver.c
>> ===================================================================
>> --- libgcc/libgcov-driver.c     (revision 215230)
>> +++ libgcc/libgcov-driver.c     (working copy)
>> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
>>
>>
>>  /* Dynamic call graph build and form module groups.  */
>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>
>>  /* The following functions can be called from outside of this file.  */
>> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head)
>>    __gcov_list = head;
>>  }
>>
>> +/* Flag if the current function being read was marked as having fixed-up
>> +   zero counters.  */
>> +static int __gcov_curr_fn_fixed_up;
>> +
>> +/* Set function fixed up flag.  */
>> +void
>> +set_gcov_fn_fixed_up (int fixed_up)
>> +{
>> +  __gcov_curr_fn_fixed_up = fixed_up;
>> +}
>> +
>> +/* Return function fixed up flag.  */
>> +int
>> +get_gcov_fn_fixed_up (void)
>> +{
>> +  return __gcov_curr_fn_fixed_up;
>> +}
>> +
>>  /* Size of the longest file name. */
>>  /* We need to expose this static variable when compiling for gcov-tool.  */
>>  #ifndef IN_GCOV_TOOL
>> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>    int error = 0;
>>    struct gcov_fn_buffer **fn_tail = &fn_buffer;
>>    struct gcov_summary_buffer **sum_tail = &sum_buffer;
>> +  int *zero_fixup_flags = NULL;
>>
>>    length = gcov_read_unsigned ();
>>    if (!gcov_version (gi_ptr, length, gi_filename))
>> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>        tag = gcov_read_unsigned ();
>>      }
>>
>> +  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>> +    {
>> +      length = gcov_read_unsigned ();
>> +      gcov_unsigned_t num_fns = 0;
>> +      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>> +      if (!zero_fixup_flags)
>> +        {
>> +          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
>> +                      gi_filename);
>> +          return -1;
>> +        }
>> +
>> +      tag = gcov_read_unsigned ();
>> +    }
>> +
>>    /* Merge execution counts for each function.  */
>>    for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
>>         f_ix++, tag = gcov_read_unsigned ())
>> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>            continue;
>>          }
>>
>> +      if (zero_fixup_flags)
>> +        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
>> +
>>        length = gcov_read_unsigned ();
>>        if (length != gfi_ptr->ident)
>>          goto read_mismatch;
>> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>        if ((error = gcov_is_error ()))
>>          goto read_error;
>>      }
>> +  free (zero_fixup_flags);
>>
>>    if (tag && tag != GCOV_TAG_MODULE_INFO)
>>      {
>> @@ -706,6 +744,34 @@ read_error:
>>    return -1;
>>  }
>>
>> +
>> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
>> +   current location.  */
>> +
>> +static void
>> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
>> +{
>> +  unsigned f_ix;
>> +  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
>> +  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
>> +
>> +  gcov_write_unsigned (num_fns);
>> +  gcov_unsigned_t bitvector = 0, b_ix = 0;
>> +  for (f_ix = 0; f_ix != num_fns; f_ix++)
>> +    {
>> +      if (zero_counts[f_ix])
>> +        bitvector |= 1 << b_ix;
>> +      if (++b_ix == 32)
>> +        {
>> +          gcov_write_unsigned (bitvector);
>> +          b_ix = 0;
>> +          bitvector = 0;
>> +        }
>> +    }
>> +  if (b_ix > 0)
>> +    gcov_write_unsigned (bitvector);
>> +}
>> +
>>  /* Write build_info strings from GI_PTR to a gcda file starting from
>> its current
>>     location.  */
>>
>> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr
>>            if (gfi_ptr && gfi_ptr->key == gi_ptr)
>>              length = GCOV_TAG_FUNCTION_LENGTH;
>>            else
>> -                length = 0;
>> +            length = 0;
>>          }
>>
>>        gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
>> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>  {
>>    struct gcov_info *gi_ptr;
>>
>> +  unsigned max_module_id = 0;
>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>> +    {
>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>> +      if (max_module_id < mod_id)
>> +        max_module_id = mod_id;
>> +    }
>> +  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>> +    {
>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>> +      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
>> +                                               sizeof (int));
>> +    }
>> +
>>    /* Compute the module groups and record whether there were any
>>       counter fixups applied that require rewriting the counters.  */
>> -  int changed = __gcov_compute_module_groups ();
>> +  int changed = __gcov_compute_module_groups (zero_counts);
>>
>>    /* Now write out module group info.  */
>>    for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>                gcov_position_t eof_pos = gi_ptr->eof_pos;
>>                gcov_rewrite ();
>>                gcov_seek (summary_end_pos);
>> +
>> +              unsigned mod_id = gi_ptr->mod_info->ident;
>> +              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
>> +                                            gi_ptr->n_functions);
>> +              gcov_position_t zero_fixup_eof_pos = gcov_position ();
>> +
>>                gcov_write_func_counters (gi_ptr);
>> -              gcc_assert (eof_pos == gi_ptr->eof_pos);
>> +              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
>> +                          == gi_ptr->eof_pos);
>>              }
>>          }
>>        else
>> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>                                    "profiling:%s:Error writing\n",
>>                                    gi_filename);
>>        gcov_write_import_file (gi_filename, gi_ptr);
>> +      free (zero_counts[gi_ptr->mod_info->ident-1]);
>>      }
>> +
>> +  free (zero_counts);
>> +
>>    __gcov_finalize_dyn_callgraph ();
>>  }
>>
>> Index: libgcc/libgcov.h
>> ===================================================================
>> --- libgcc/libgcov.h    (revision 215230)
>> +++ libgcc/libgcov.h    (working copy)
>> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i
>>      ATTRIBUTE_HIDDEN;
>>  GCOV_LINKAGE inline void gcov_rewrite (void);
>>
>> +extern void set_gcov_fn_fixed_up (int fixed_up);
>> +extern int get_gcov_fn_fixed_up (void);
>> +
>>  /* "Counts" stored in gcda files can be a real counter value, or
>>     an target address. When differentiate these two types because
>>     when manipulating counts, we should only change real counter values,
>> @@ -361,7 +364,13 @@ gcov_get_counter (void)
>>    /* This version is for reading count values in libgcov runtime:
>>       we read from gcda files.  */
>>
>> -  return gcov_read_counter ();
>> +  if (get_gcov_fn_fixed_up ())
>> +    {
>> +      gcov_read_counter ();
>> +      return 0;
>> +    }
>> +  else
>> +    return gcov_read_counter ();
>>  #else
>>    /* This version is for gcov-tool. We read the value from memory and
>>       multiply it by the merge weight.  */
>> @@ -380,7 +389,13 @@ gcov_get_counter_target (void)
>>    /* This version is for reading count target values in libgcov runtime:
>>       we read from gcda files.  */
>>
>> -  return gcov_read_counter ();
>> +  if (get_gcov_fn_fixed_up ())
>> +    {
>> +      gcov_read_counter ();
>> +      return 0;
>> +    }
>> +  else
>> +    return gcov_read_counter ();
>>  #else
>>    /* This version is for gcov-tool.  We read the value from memory
>> and we do NOT
>>       multiply it by the merge weight.  */
>> Index: libgcc/libgcov-util.c
>> ===================================================================
>> --- libgcc/libgcov-util.c       (revision 215230)
>> +++ libgcc/libgcov-util.c       (working copy)
>> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned);
>>  static void tag_counters (unsigned, unsigned);
>>  static void tag_summary (unsigned, unsigned);
>>  static void tag_module_info (unsigned, unsigned);
>> +static void tag_zero_fixup (unsigned, unsigned);
>>
>>  /* The gcov_info for the first module.  */
>>  static struct gcov_info *curr_gcov_info;
>> @@ -88,6 +89,8 @@ static int k_ctrs_types;
>>  /* The longest length of all the filenames.  */
>>  static int max_filename_len;
>>
>> +static int *zero_fixup_flags = NULL;
>> +
>>  /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
>>     functions in dyn-ipa.c, which were derived from these, except
>>     the versions in dyn-ipa are used when merging from another array.  */
>> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] =
>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>    {0, NULL, NULL}
>>  };
>>
>> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
>>       k_ctrs[i].num = 0;
>>    k_ctrs_types = 0;
>>
>> +  if (zero_fixup_flags)
>> +    {
>> +      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
>> +      if (get_gcov_fn_fixed_up () && verbose)
>> +        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
>> +    }
>> +
>>    curr_fn_info->key = curr_gcov_info;
>>    curr_fn_info->ident = gcov_read_unsigned ();
>>    curr_fn_info->lineno_checksum = gcov_read_unsigned ();
>>    curr_fn_info->cfg_checksum = gcov_read_unsigned ();
>>    num_fn_info++;
>> -
>> -  if (verbose)
>> -    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
>>  }
>>
>>  /* Handler for reading block tag.  */
>> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length)
>>    gcc_assert (values);
>>
>>    for (ix = 0; ix != n_counts; ix++)
>> -    values[ix] = gcov_read_counter ();
>> +    {
>> +      gcov_type val = gcov_read_counter ();
>> +      if (!get_gcov_fn_fixed_up ())
>> +        values[ix] = val;
>> +      else
>> +        values[ix] = 0;
>> +    }
>>  }
>>
>>  /* Handler for reading summary tag.  */
>> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>        char *t;
>>
>>        if (verbose)
>> -        printf ("Substitute: %s \n", input_str);
>> +        fprintf (stderr, "Substitute: %s \n", input_str);
>>        t = (char*) xmalloc (strlen (input_str) + 1
>>            + strlen (new_str) - strlen (cur_str));
>>        *p = 0;
>> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>        strcat (t, new_str);
>>        strcat (t, p + strlen (cur_str));
>>        if (verbose)
>> -        printf ("       -->  %s\n", t);
>> +        fprintf (stderr, "       -->  %s\n", t);
>>        return t;
>>      }
>>
>> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
>>      free (mod_info);
>>  }
>>
>> +/* Handler for reading the COMDAT zero-profile fixup section.  */
>> +
>> +static void
>> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>> +{
>> +  gcov_unsigned_t num_fns = 0;
>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>> +  gcc_assert (zero_fixup_flags);
>> +}
>> +
>>  /* Read the content of a gcda file FILENAME, and return a gcov_info
>> data structure.
>>     Program level summary CURRENT_SUMMARY will also be updated.  */
>>
>> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
>>      }
>>
>>    read_gcda_finalize (obj_info);
>> +  free (zero_fixup_flags);
>>    gcov_close ();
>>
>>    return obj_info;
>> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
>>    unsigned f_ix;
>>
>>    if (verbose)
>> -    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>> +    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>
>>    /* Scaling the counters.  */
>>    for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
>> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
>>    scale_factor = (float)max_val / curr_max_val;
>>  #if !defined (_WIN32)
>>    if (verbose)
>> -    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
>> +    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
>>  #endif
>>
>>    return gcov_profile_scale (profile, scale_factor, 0, 0);
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Xinliang David Li Sept. 16, 2014, 3:07 p.m. UTC | #3
The zero_counts array is passed to gcov_build_callgraph but not used
until the dyn-cgraph is initialized. We should avoid increasing
runtime memory overhead by not creating it if possible.

David

On Tue, Sep 16, 2014 at 7:57 AM, Teresa Johnson <tejohnson@google.com> wrote:
> On Mon, Sep 15, 2014 at 9:29 PM, Xinliang David Li <davidxl@google.com> wrote:
>> Is it necessary to declare zero_counts array at all?  Can a flag field
>> be added to dyn_cgraph_node structure to indicate if it is fixed up?
>
> The zero_counts array is used to pass info back to the caller in
> libgcov-driver.cc (dyn_cgraph_node), which is where it is allocated.
> That routine does not have access to the dyn-ipa cgraph.
>
> Teresa
>
>>
>> David
>>
>> On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>> This patch addresses issues when running gcov-tool after performing
>>> COMDAT fixup during dyn-ipa. Functions that were previously all zero
>>> counts are marked, and the counts are discarded when being read in
>>> by gcov-tool before recalculating module groups and summary info.
>>>
>>> While here, cleaned up the gcov-tool output (remove an overly-verbose output,
>>> make all output consistently go to stderr).
>>>
>>> Passes regression tests and manual tests. Ok for google branches?
>>>
>>> 2014-09-12  Teresa Johnson  <tejohnson@google.com>
>>>
>>>         * gcc/coverage.c (read_counts_file): Handle new section.
>>>         * gcc/gcov.c (read_count_file): Ditto.
>>>         * gcc/gcov-dump.c (dump_gcov_file): Ditto.
>>>         (tag_function): Ditto.
>>>         (tag_zero_fixup): New function.
>>>         * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
>>>         * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
>>>         * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
>>>         (new_checksum_alias): Ditto.
>>>         (cfg_checksum_insert): Ditto.
>>>         (checksum_set_insert): Ditto.
>>>         (gcov_build_callgraph): New parameter.
>>>         (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
>>>         (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
>>>         (__gcov_compute_module_groups): New parameter.
>>>         * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
>>>         (get_gcov_fn_fixed_up): Ditto.
>>>         (gcov_exit_merge_gcda): Handle new section.
>>>         (gcov_write_comdat_zero_fixup): Ditto.
>>>         (gcov_write_build_info): Ditto.
>>>         (gcov_write_comdat_zero_fixup): New function.
>>>         (gcov_write_func_counters): Fix indent.
>>>         (gcov_dump_module_info): Write new flag section.
>>>         * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
>>>         (gcov_get_counter_target): Ditto.
>>>         * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
>>>         remove overly verbose output.
>>>         (tag_counters): Clear fixed-up counters.
>>>         (lipo_process_substitute_string_1): Send all verbose output to stderr.
>>>         (tag_zero_fixup): New function.
>>>         (read_gcda_file): Deallocate flag array.
>>>         (gcov_profile_scale): Send all verbose output to stderr.
>>>         (gcov_profile_normalize): Ditto.
>>>
>>> Index: gcc/coverage.c
>>> ===================================================================
>>> --- gcc/coverage.c      (revision 215230)
>>> +++ gcc/coverage.c      (working copy)
>>> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign
>>>              free (build_info_strings[i]);
>>>            free (build_info_strings);
>>>          }
>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>> +        {
>>> +          /* Zero-profile fixup flags are not used by the compiler, read and
>>> +             ignore.  */
>>> +          gcov_unsigned_t num_fn;
>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>> (length, &num_fn);
>>> +          free (zero_fixup_flags);
>>> +        }
>>>        else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
>>>         {
>>>           counts_entry_t **slot, *entry, elt;
>>> Index: gcc/gcov.c
>>> ===================================================================
>>> --- gcc/gcov.c  (revision 215230)
>>> +++ gcc/gcov.c  (working copy)
>>> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns)
>>>              free (build_info_strings[i]);
>>>            free (build_info_strings);
>>>          }
>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>> +        {
>>> +          gcov_unsigned_t num_fn;
>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>> (length, &num_fn);
>>> +          free (zero_fixup_flags);
>>> +        }
>>>        else if (tag == GCOV_TAG_FUNCTION && !length)
>>>         ; /* placeholder  */
>>>        else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
>>> Index: gcc/gcov-dump.c
>>> ===================================================================
>>> --- gcc/gcov-dump.c     (revision 215230)
>>> +++ gcc/gcov-dump.c     (working copy)
>>> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u
>>>  static void tag_module_info (const char *, unsigned, unsigned);
>>>  static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
>>>                                 const struct gcov_ctr_summary *summary);
>>> +static void tag_zero_fixup (const char *, unsigned, unsigned);
>>>  static void tag_build_info (const char *, unsigned, unsigned);
>>>  extern int main (int, char **);
>>>
>>> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0;
>>>  static int flag_dump_aux_modules_only = 0;
>>>  static int flag_dump_working_sets = 0;
>>>
>>> +static unsigned num_fn_info;
>>> +static int *zero_fixup_flags = NULL;
>>> +
>>>  static const struct option options[] =
>>>  {
>>>    { "help",                 no_argument,       NULL, 'h' },
>>> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] =
>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>    {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
>>>    {0, NULL, NULL}
>>>  };
>>> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename)
>>>      printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
>>>    }
>>>
>>> +  num_fn_info = 0;
>>> +
>>>    while (1)
>>>      {
>>>        gcov_position_t base, position = gcov_position ();
>>> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename)
>>>           break;
>>>         }
>>>      }
>>> +  free (zero_fixup_flags);
>>>    gcov_close ();
>>>  }
>>>
>>> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>      printf (" placeholder");
>>>    else
>>>      {
>>> -      printf (" ident=%u", gcov_read_unsigned ());
>>> +      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
>>> +      printf (" ident=%u%s", gcov_read_unsigned (),
>>> +              had_fixup ? " (Was 0-count COMDAT)" : "");
>>>        printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
>>>        printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());
>>>
>>> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>           printf (":%u", gcov_read_unsigned ());
>>>         }
>>>      }
>>> +  num_fn_info++;
>>>  }
>>>
>>>  static void
>>> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN
>>>  }
>>>
>>>  static void
>>> +tag_zero_fixup (const char *filename,
>>> +                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>> +{
>>> +  gcov_unsigned_t num_fns = 0;
>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>> +  if (!zero_fixup_flags)
>>> +    {
>>> +      printf ("%s:error reading zero fixup flags\n", filename);
>>> +      return;
>>> +    }
>>> +  printf (" num_fns=%u", num_fns);
>>> +  for (unsigned i = 0; i < num_fns; i++)
>>> +    {
>>> +      if (!(i % 32))
>>> +        {
>>> +          printf ("\n");
>>> +          print_prefix (filename, 0, 0);
>>> +          printf ("\t\t");
>>> +        }
>>> +      if (!(i % 8))
>>> +        printf ("%s%4u:", (i%32)?" ":"", i);
>>> +      printf ("%u", zero_fixup_flags[i]);
>>> +    }
>>> +}
>>> +
>>> +static void
>>>  tag_build_info (const char *filename,
>>>                 unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>  {
>>> Index: gcc/gcov-io.c
>>> ===================================================================
>>> --- gcc/gcov-io.c       (revision 215230)
>>> +++ gcc/gcov-io.c       (working copy)
>>> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary)
>>>      }
>>>  }
>>>
>>> +/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
>>> +   number of function flags saved in NUM_FNS.  Returns the int flag
>>> array, which
>>> +   should be deallocated by caller, or NULL on error.  */
>>> +
>>> +GCOV_LINKAGE int *
>>> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
>>> +                             gcov_unsigned_t *num_fns)
>>> +{
>>> +  unsigned ix, f_ix;
>>> +  gcov_unsigned_t num = gcov_read_unsigned ();
>>> +  /* The length consists of 1 word to hold the number of functions,
>>> +     plus enough 32-bit words to hold 1 bit/function.  */
>>> +  gcc_assert ((num + 31) / 32 + 1 == length);
>>> +  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
>>> +  for (ix = 0; ix < length - 1; ix++)
>>> +    {
>>> +      gcov_unsigned_t bitvector = gcov_read_unsigned ();
>>> +      f_ix = ix * 32;
>>> +      while (bitvector)
>>> +        {
>>> +          if (bitvector & 0x1)
>>> +            zero_fixup_flags[f_ix] = 1;
>>> +          f_ix++;
>>> +          bitvector >>= 1;
>>> +        }
>>> +    }
>>> +  *num_fns = num;
>>> +  return zero_fixup_flags;
>>> +}
>>> +
>>>  /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
>>>     the number of words read.  */
>>>
>>> Index: gcc/gcov-io.h
>>> ===================================================================
>>> --- gcc/gcov-io.h       (revision 215230)
>>> +++ gcc/gcov-io.h       (working copy)
>>> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>     blocks they are for.
>>>
>>>     The data file contains the following records.
>>> -        data: {unit summary:program* build_info function-data*}*
>>> +        data: {unit summary:program* build_info zero_fixup function-data*}*
>>>         unit: header int32:checksum
>>>          function-data: announce_function present counts
>>>         announce_function: header int32:ident
>>> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>          histogram: {int32:bitvector}8 histogram-buckets*
>>>          histogram-buckets: int32:num int64:min int64:sum
>>>          build_info: string:info*
>>> +        zero_fixup: int32:num int32:bitvector*
>>>
>>>     The ANNOUNCE_FUNCTION record is the same as that in the note file,
>>>     but without the source location.  The COUNTS gives the
>>> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>     build.  For example, it can be used to include source revision
>>>     information that is useful in diagnosing profile mis-matches.
>>>
>>> +   ZERO_FIXUP record contains a count of functions in the gcda file
>>> +   and an array of bitvectors indexed by the function index's in the
>>> +   function-data section. Each bit flags whether the function was a
>>> +   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
>>> +   using profiles from functions with matching checksums in other modules.
>>> +
>>>     This file is included by both the compiler, gcov tools and the
>>>     runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
>>>     distinguish which case is which.  If IN_LIBGCOV is nonzero,
>>> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
>>>  #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
>>>  #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
>>>  #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
>>> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
>>>  #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
>>>          (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
>>>  #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
>>> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
>>>  GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
>>>  GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
>>>  GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
>>> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
>>> +                                               gcov_unsigned_t *)
>>> +    ATTRIBUTE_HIDDEN;
>>>  GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
>>>    ATTRIBUTE_HIDDEN;
>>>  GCOV_LINKAGE const char *gcov_read_string (void);
>>> Index: libgcc/dyn-ipa.c
>>> ===================================================================
>>> --- libgcc/dyn-ipa.c    (revision 215230)
>>> +++ libgcc/dyn-ipa.c    (working copy)
>>> @@ -107,8 +107,9 @@ struct checksum_alias
>>>    struct checksum_alias *next_alias;
>>>    gcov_type guid;
>>>    const struct gcov_fn_info *fi_ptr;
>>> -  /* Does this function have all-zero arc counts?  */
>>> -  int zero_counts;
>>> +  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
>>> +     set if we perform fixup.  */
>>> +  int *zero_count_fixup;
>>>  };
>>>
>>>  /* Module info is stored in dyn_caph->sup_modules
>>> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
>>>  extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
>>>
>>>  #if defined(inhibit_libc)
>>> -void __gcov_build_callgraph (void) {}
>>> +void __gcov_build_callgraph (int **zero_counts) {}
>>>  #else
>>>
>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>  static void gcov_dump_callgraph (gcov_type);
>>>  static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
>>> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p)
>>>  }
>>>
>>>  /* Create a new checksum_alias struct for function with GUID, FI_PTR,
>>> -   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
>>> +   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
>>> +   new struct.  */
>>>
>>>  static struct checksum_alias *
>>>  new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>> -                    int zero_counts,
>>> +                    int *zero_count_fixup,
>>>                      struct checksum_alias *next)
>>>  {
>>>    struct checksum_alias *alias = XNEW (struct checksum_alias);
>>>    alias->next_alias = next;
>>>    alias->fi_ptr = fi_ptr;
>>>    alias->guid = guid;
>>> -  alias->zero_counts = zero_counts;
>>> +  alias->zero_count_fixup = zero_count_fixup;
>>>    return alias;
>>>  }
>>>
>>> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis
>>>  }
>>>
>>>  /* Insert a new checksum_alias struct into LIST for function with
>>> -   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
>>> +   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
>>> +   flag pointer.  */
>>>
>>>  static struct checksum_alias_info *
>>>  cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
>>> -                     const struct gcov_fn_info *fi_ptr, int zero_counts,
>>> +                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
>>>                       struct checksum_alias_info *list)
>>>  {
>>>    struct checksum_alias_info *alias_info;
>>> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>    if (alias_info)
>>>      {
>>>        gcc_assert (alias_info->alias_list);
>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>> +                                                   zero_count_fixup,
>>>                                                     alias_info->alias_list);
>>>        return list;
>>>      }
>>> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>        alias_info = XNEW (struct checksum_alias_info);
>>>        alias_info->next_cfg_checksum = list;
>>>        alias_info->cfg_checksum = cfg_checksum;
>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>> +                                                   zero_count_fixup,
>>>                                                     NULL);
>>>        return alias_info;
>>>      }
>>> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>
>>>  /* Insert a new checksum_alias struct into lineno_pointer_sets for
>>> function with
>>>     LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
>>> -   ZERO_COUNTS flag.  */
>>> +   ZERO_COUNT_FIXUP flag pointer.  */
>>>
>>>  static void
>>>  checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
>>>                       gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>> -                     int zero_counts)
>>> +                     int *zero_count_fixup)
>>>  {
>>>    struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
>>>    if (!p)
>>> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>    if (*m)
>>>      {
>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>> -                                                     fi_ptr, zero_counts,
>>> +                                                     fi_ptr, zero_count_fixup,
>>>                                                       (*m)->cfg_checksum_list);
>>>      }
>>>    else
>>> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>        *m = XNEW (struct lineno_checksum_alias);
>>>        (*m)->lineno_checksum = lineno_checksum;
>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>> -                                                     fi_ptr,
>>> zero_counts, NULL);
>>> +                                                     fi_ptr, zero_count_fixup,
>>> +                                                     NULL);
>>>        p->n_elements++;
>>>      }
>>>  }
>>> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
>>>      }
>>>  }
>>>
>>> -/* Build the dynamic call graph.  */
>>> +/* Build the dynamic call graph and update ZERO_COUNTS flags.  */
>>>
>>>  static void
>>> -gcov_build_callgraph (void)
>>> +gcov_build_callgraph (int **zero_counts)
>>>  {
>>>    struct gcov_info *gi_ptr;
>>>    unsigned m_ix;
>>> @@ -852,9 +858,19 @@ static void
>>>                    if (total_arc_count != 0)
>>>                      the_dyn_call_graph.num_nodes_executed++;
>>>                    if (fixup_type)
>>> -                    checksum_set_insert (fi_ptr->lineno_checksum,
>>> -                                         fi_ptr->cfg_checksum, caller->guid,
>>> -                                         fi_ptr, total_arc_count == 0);
>>> +                    {
>>> +                      int *zero_count_fixup = NULL;
>>> +                      /* Passing in a non-NULL zero_count_fixup pointer
>>> +                         indicates that the counts were all zero for this
>>> +                         function, and the fixup routine will set the flag
>>> +                         if the function's counters are updated to non-zero
>>> +                         values.  */
>>> +                      if (total_arc_count == 0)
>>> +                        zero_count_fixup = &zero_counts[m_ix][f_ix];
>>> +                      checksum_set_insert (fi_ptr->lineno_checksum,
>>> +                                           fi_ptr->cfg_checksum, caller->guid,
>>> +                                           fi_ptr, zero_count_fixup);
>>> +                    }
>>>                  }
>>>                ci_ptr++;
>>>              }
>>> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value,
>>>    out_array = (struct gcov_import_mod_array *) data1;
>>>
>>>    if (m->imp_mod != out_array->importing_module)
>>> +  {
>>>      out_array->imported_modules[out_array->len++] = m;
>>> +    /* Sanity check that the importing (primary) module is not
>>> +       actually the same as the new aux module. This could happen if
>>> +       we accidentally read in the same gcda file twice.  */
>>> +    gcc_assert (m->imp_mod->mod_info->ident !=
>>> +                out_array->importing_module->mod_info->ident);
>>> +  }
>>>
>>>    return 1;
>>>  }
>>> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>    for (alias = info->alias_list; alias;
>>>         alias = alias->next_alias)
>>>      {
>>> -      if (alias->zero_counts)
>>> +      if (alias->zero_count_fixup)
>>>          {
>>>            found = 1;
>>>            break;
>>> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>    for (alias = info->alias_list; alias;
>>>         alias = alias->next_alias)
>>>      {
>>> -      if (alias->zero_counts)
>>> +      if (alias->zero_count_fixup)
>>>          continue;
>>>        merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
>>>        found = 1;
>>> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu
>>>    for (alias = info->alias_list; alias;
>>>         alias = alias->next_alias)
>>>      {
>>> -      if (!alias->zero_counts)
>>> +      if (!alias->zero_count_fixup)
>>>          continue;
>>>        copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
>>> +      *alias->zero_count_fixup = 1;
>>>      }
>>>
>>>    return 1;
>>> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void)
>>>    return changed;
>>>  }
>>>
>>> -/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
>>> -   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
>>> +/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
>>> +   flags are set for functions with zero count fixups applied. Returns 1
>>> +   if any counter fixups were applied, requiring a profile rewrite,
>>> +   0 otherwise.  */
>>>
>>>  int
>>> -__gcov_compute_module_groups (void)
>>> +__gcov_compute_module_groups (int **zero_counts)
>>>  {
>>>    gcov_type cut_off_count;
>>>    char *seed = getenv ("LIPO_RANDOM_GROUPING");
>>> @@ -3091,7 +3117,7 @@ int
>>>      fixup_type = atoi (do_fixup);
>>>
>>>    /* First compute dynamic call graph.  */
>>> -  gcov_build_callgraph ();
>>> +  gcov_build_callgraph (zero_counts);
>>>
>>>    cut_off_count = gcov_compute_cutoff_count ();
>>>
>>> Index: libgcc/libgcov-driver.c
>>> ===================================================================
>>> --- libgcc/libgcov-driver.c     (revision 215230)
>>> +++ libgcc/libgcov-driver.c     (working copy)
>>> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
>>>
>>>
>>>  /* Dynamic call graph build and form module groups.  */
>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>
>>>  /* The following functions can be called from outside of this file.  */
>>> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head)
>>>    __gcov_list = head;
>>>  }
>>>
>>> +/* Flag if the current function being read was marked as having fixed-up
>>> +   zero counters.  */
>>> +static int __gcov_curr_fn_fixed_up;
>>> +
>>> +/* Set function fixed up flag.  */
>>> +void
>>> +set_gcov_fn_fixed_up (int fixed_up)
>>> +{
>>> +  __gcov_curr_fn_fixed_up = fixed_up;
>>> +}
>>> +
>>> +/* Return function fixed up flag.  */
>>> +int
>>> +get_gcov_fn_fixed_up (void)
>>> +{
>>> +  return __gcov_curr_fn_fixed_up;
>>> +}
>>> +
>>>  /* Size of the longest file name. */
>>>  /* We need to expose this static variable when compiling for gcov-tool.  */
>>>  #ifndef IN_GCOV_TOOL
>>> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>    int error = 0;
>>>    struct gcov_fn_buffer **fn_tail = &fn_buffer;
>>>    struct gcov_summary_buffer **sum_tail = &sum_buffer;
>>> +  int *zero_fixup_flags = NULL;
>>>
>>>    length = gcov_read_unsigned ();
>>>    if (!gcov_version (gi_ptr, length, gi_filename))
>>> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>        tag = gcov_read_unsigned ();
>>>      }
>>>
>>> +  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>> +    {
>>> +      length = gcov_read_unsigned ();
>>> +      gcov_unsigned_t num_fns = 0;
>>> +      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>> +      if (!zero_fixup_flags)
>>> +        {
>>> +          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
>>> +                      gi_filename);
>>> +          return -1;
>>> +        }
>>> +
>>> +      tag = gcov_read_unsigned ();
>>> +    }
>>> +
>>>    /* Merge execution counts for each function.  */
>>>    for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
>>>         f_ix++, tag = gcov_read_unsigned ())
>>> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>            continue;
>>>          }
>>>
>>> +      if (zero_fixup_flags)
>>> +        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
>>> +
>>>        length = gcov_read_unsigned ();
>>>        if (length != gfi_ptr->ident)
>>>          goto read_mismatch;
>>> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>        if ((error = gcov_is_error ()))
>>>          goto read_error;
>>>      }
>>> +  free (zero_fixup_flags);
>>>
>>>    if (tag && tag != GCOV_TAG_MODULE_INFO)
>>>      {
>>> @@ -706,6 +744,34 @@ read_error:
>>>    return -1;
>>>  }
>>>
>>> +
>>> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
>>> +   current location.  */
>>> +
>>> +static void
>>> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
>>> +{
>>> +  unsigned f_ix;
>>> +  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
>>> +  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
>>> +
>>> +  gcov_write_unsigned (num_fns);
>>> +  gcov_unsigned_t bitvector = 0, b_ix = 0;
>>> +  for (f_ix = 0; f_ix != num_fns; f_ix++)
>>> +    {
>>> +      if (zero_counts[f_ix])
>>> +        bitvector |= 1 << b_ix;
>>> +      if (++b_ix == 32)
>>> +        {
>>> +          gcov_write_unsigned (bitvector);
>>> +          b_ix = 0;
>>> +          bitvector = 0;
>>> +        }
>>> +    }
>>> +  if (b_ix > 0)
>>> +    gcov_write_unsigned (bitvector);
>>> +}
>>> +
>>>  /* Write build_info strings from GI_PTR to a gcda file starting from
>>> its current
>>>     location.  */
>>>
>>> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr
>>>            if (gfi_ptr && gfi_ptr->key == gi_ptr)
>>>              length = GCOV_TAG_FUNCTION_LENGTH;
>>>            else
>>> -                length = 0;
>>> +            length = 0;
>>>          }
>>>
>>>        gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
>>> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>  {
>>>    struct gcov_info *gi_ptr;
>>>
>>> +  unsigned max_module_id = 0;
>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>> +    {
>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>> +      if (max_module_id < mod_id)
>>> +        max_module_id = mod_id;
>>> +    }
>>> +  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>> +    {
>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>> +      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
>>> +                                               sizeof (int));
>>> +    }
>>> +
>>>    /* Compute the module groups and record whether there were any
>>>       counter fixups applied that require rewriting the counters.  */
>>> -  int changed = __gcov_compute_module_groups ();
>>> +  int changed = __gcov_compute_module_groups (zero_counts);
>>>
>>>    /* Now write out module group info.  */
>>>    for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>                gcov_position_t eof_pos = gi_ptr->eof_pos;
>>>                gcov_rewrite ();
>>>                gcov_seek (summary_end_pos);
>>> +
>>> +              unsigned mod_id = gi_ptr->mod_info->ident;
>>> +              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
>>> +                                            gi_ptr->n_functions);
>>> +              gcov_position_t zero_fixup_eof_pos = gcov_position ();
>>> +
>>>                gcov_write_func_counters (gi_ptr);
>>> -              gcc_assert (eof_pos == gi_ptr->eof_pos);
>>> +              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
>>> +                          == gi_ptr->eof_pos);
>>>              }
>>>          }
>>>        else
>>> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>                                    "profiling:%s:Error writing\n",
>>>                                    gi_filename);
>>>        gcov_write_import_file (gi_filename, gi_ptr);
>>> +      free (zero_counts[gi_ptr->mod_info->ident-1]);
>>>      }
>>> +
>>> +  free (zero_counts);
>>> +
>>>    __gcov_finalize_dyn_callgraph ();
>>>  }
>>>
>>> Index: libgcc/libgcov.h
>>> ===================================================================
>>> --- libgcc/libgcov.h    (revision 215230)
>>> +++ libgcc/libgcov.h    (working copy)
>>> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i
>>>      ATTRIBUTE_HIDDEN;
>>>  GCOV_LINKAGE inline void gcov_rewrite (void);
>>>
>>> +extern void set_gcov_fn_fixed_up (int fixed_up);
>>> +extern int get_gcov_fn_fixed_up (void);
>>> +
>>>  /* "Counts" stored in gcda files can be a real counter value, or
>>>     an target address. When differentiate these two types because
>>>     when manipulating counts, we should only change real counter values,
>>> @@ -361,7 +364,13 @@ gcov_get_counter (void)
>>>    /* This version is for reading count values in libgcov runtime:
>>>       we read from gcda files.  */
>>>
>>> -  return gcov_read_counter ();
>>> +  if (get_gcov_fn_fixed_up ())
>>> +    {
>>> +      gcov_read_counter ();
>>> +      return 0;
>>> +    }
>>> +  else
>>> +    return gcov_read_counter ();
>>>  #else
>>>    /* This version is for gcov-tool. We read the value from memory and
>>>       multiply it by the merge weight.  */
>>> @@ -380,7 +389,13 @@ gcov_get_counter_target (void)
>>>    /* This version is for reading count target values in libgcov runtime:
>>>       we read from gcda files.  */
>>>
>>> -  return gcov_read_counter ();
>>> +  if (get_gcov_fn_fixed_up ())
>>> +    {
>>> +      gcov_read_counter ();
>>> +      return 0;
>>> +    }
>>> +  else
>>> +    return gcov_read_counter ();
>>>  #else
>>>    /* This version is for gcov-tool.  We read the value from memory
>>> and we do NOT
>>>       multiply it by the merge weight.  */
>>> Index: libgcc/libgcov-util.c
>>> ===================================================================
>>> --- libgcc/libgcov-util.c       (revision 215230)
>>> +++ libgcc/libgcov-util.c       (working copy)
>>> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned);
>>>  static void tag_counters (unsigned, unsigned);
>>>  static void tag_summary (unsigned, unsigned);
>>>  static void tag_module_info (unsigned, unsigned);
>>> +static void tag_zero_fixup (unsigned, unsigned);
>>>
>>>  /* The gcov_info for the first module.  */
>>>  static struct gcov_info *curr_gcov_info;
>>> @@ -88,6 +89,8 @@ static int k_ctrs_types;
>>>  /* The longest length of all the filenames.  */
>>>  static int max_filename_len;
>>>
>>> +static int *zero_fixup_flags = NULL;
>>> +
>>>  /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
>>>     functions in dyn-ipa.c, which were derived from these, except
>>>     the versions in dyn-ipa are used when merging from another array.  */
>>> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] =
>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>    {0, NULL, NULL}
>>>  };
>>>
>>> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
>>>       k_ctrs[i].num = 0;
>>>    k_ctrs_types = 0;
>>>
>>> +  if (zero_fixup_flags)
>>> +    {
>>> +      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
>>> +      if (get_gcov_fn_fixed_up () && verbose)
>>> +        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
>>> +    }
>>> +
>>>    curr_fn_info->key = curr_gcov_info;
>>>    curr_fn_info->ident = gcov_read_unsigned ();
>>>    curr_fn_info->lineno_checksum = gcov_read_unsigned ();
>>>    curr_fn_info->cfg_checksum = gcov_read_unsigned ();
>>>    num_fn_info++;
>>> -
>>> -  if (verbose)
>>> -    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
>>>  }
>>>
>>>  /* Handler for reading block tag.  */
>>> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length)
>>>    gcc_assert (values);
>>>
>>>    for (ix = 0; ix != n_counts; ix++)
>>> -    values[ix] = gcov_read_counter ();
>>> +    {
>>> +      gcov_type val = gcov_read_counter ();
>>> +      if (!get_gcov_fn_fixed_up ())
>>> +        values[ix] = val;
>>> +      else
>>> +        values[ix] = 0;
>>> +    }
>>>  }
>>>
>>>  /* Handler for reading summary tag.  */
>>> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>        char *t;
>>>
>>>        if (verbose)
>>> -        printf ("Substitute: %s \n", input_str);
>>> +        fprintf (stderr, "Substitute: %s \n", input_str);
>>>        t = (char*) xmalloc (strlen (input_str) + 1
>>>            + strlen (new_str) - strlen (cur_str));
>>>        *p = 0;
>>> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>        strcat (t, new_str);
>>>        strcat (t, p + strlen (cur_str));
>>>        if (verbose)
>>> -        printf ("       -->  %s\n", t);
>>> +        fprintf (stderr, "       -->  %s\n", t);
>>>        return t;
>>>      }
>>>
>>> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
>>>      free (mod_info);
>>>  }
>>>
>>> +/* Handler for reading the COMDAT zero-profile fixup section.  */
>>> +
>>> +static void
>>> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>> +{
>>> +  gcov_unsigned_t num_fns = 0;
>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>> +  gcc_assert (zero_fixup_flags);
>>> +}
>>> +
>>>  /* Read the content of a gcda file FILENAME, and return a gcov_info
>>> data structure.
>>>     Program level summary CURRENT_SUMMARY will also be updated.  */
>>>
>>> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
>>>      }
>>>
>>>    read_gcda_finalize (obj_info);
>>> +  free (zero_fixup_flags);
>>>    gcov_close ();
>>>
>>>    return obj_info;
>>> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
>>>    unsigned f_ix;
>>>
>>>    if (verbose)
>>> -    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>> +    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>>
>>>    /* Scaling the counters.  */
>>>    for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
>>> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
>>>    scale_factor = (float)max_val / curr_max_val;
>>>  #if !defined (_WIN32)
>>>    if (verbose)
>>> -    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
>>> +    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
>>>  #endif
>>>
>>>    return gcov_profile_scale (profile, scale_factor, 0, 0);
>>>
>>> --
>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Teresa Johnson Sept. 16, 2014, 5:51 p.m. UTC | #4
On Tue, Sep 16, 2014 at 8:07 AM, Xinliang David Li <davidxl@google.com> wrote:
>  The zero_counts array is passed to gcov_build_callgraph but not used
> until the dyn-cgraph is initialized. We should avoid increasing
> runtime memory overhead by not creating it if possible.

We could delay creation a little bit, until the callgraph has been
initialized. But I am not sure this gains us much as it doesn't avoid
having the lifetime of the array overlap with the lifetime of the
callgraph. And unless fixups have been explicitly disabled or there
are no comdats we will need to allocate it (so likely in almost all
cases).

However, I can reduce the required overhead by using an array of chars
instead of ints. I measured the number of bytes in the array for a big
app and it was 5.6M with the int array and 1.5M with the char array.
Does this seem reasonable? To reduce it further I could encode as a
bitvector as we do in the gcda file.

Teresa

>
> David
>
> On Tue, Sep 16, 2014 at 7:57 AM, Teresa Johnson <tejohnson@google.com> wrote:
>> On Mon, Sep 15, 2014 at 9:29 PM, Xinliang David Li <davidxl@google.com> wrote:
>>> Is it necessary to declare zero_counts array at all?  Can a flag field
>>> be added to dyn_cgraph_node structure to indicate if it is fixed up?
>>
>> The zero_counts array is used to pass info back to the caller in
>> libgcov-driver.cc (dyn_cgraph_node), which is where it is allocated.
>> That routine does not have access to the dyn-ipa cgraph.
>>
>> Teresa
>>
>>>
>>> David
>>>
>>> On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>> This patch addresses issues when running gcov-tool after performing
>>>> COMDAT fixup during dyn-ipa. Functions that were previously all zero
>>>> counts are marked, and the counts are discarded when being read in
>>>> by gcov-tool before recalculating module groups and summary info.
>>>>
>>>> While here, cleaned up the gcov-tool output (remove an overly-verbose output,
>>>> make all output consistently go to stderr).
>>>>
>>>> Passes regression tests and manual tests. Ok for google branches?
>>>>
>>>> 2014-09-12  Teresa Johnson  <tejohnson@google.com>
>>>>
>>>>         * gcc/coverage.c (read_counts_file): Handle new section.
>>>>         * gcc/gcov.c (read_count_file): Ditto.
>>>>         * gcc/gcov-dump.c (dump_gcov_file): Ditto.
>>>>         (tag_function): Ditto.
>>>>         (tag_zero_fixup): New function.
>>>>         * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
>>>>         * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
>>>>         * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
>>>>         (new_checksum_alias): Ditto.
>>>>         (cfg_checksum_insert): Ditto.
>>>>         (checksum_set_insert): Ditto.
>>>>         (gcov_build_callgraph): New parameter.
>>>>         (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
>>>>         (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
>>>>         (__gcov_compute_module_groups): New parameter.
>>>>         * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
>>>>         (get_gcov_fn_fixed_up): Ditto.
>>>>         (gcov_exit_merge_gcda): Handle new section.
>>>>         (gcov_write_comdat_zero_fixup): Ditto.
>>>>         (gcov_write_build_info): Ditto.
>>>>         (gcov_write_comdat_zero_fixup): New function.
>>>>         (gcov_write_func_counters): Fix indent.
>>>>         (gcov_dump_module_info): Write new flag section.
>>>>         * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
>>>>         (gcov_get_counter_target): Ditto.
>>>>         * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
>>>>         remove overly verbose output.
>>>>         (tag_counters): Clear fixed-up counters.
>>>>         (lipo_process_substitute_string_1): Send all verbose output to stderr.
>>>>         (tag_zero_fixup): New function.
>>>>         (read_gcda_file): Deallocate flag array.
>>>>         (gcov_profile_scale): Send all verbose output to stderr.
>>>>         (gcov_profile_normalize): Ditto.
>>>>
>>>> Index: gcc/coverage.c
>>>> ===================================================================
>>>> --- gcc/coverage.c      (revision 215230)
>>>> +++ gcc/coverage.c      (working copy)
>>>> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign
>>>>              free (build_info_strings[i]);
>>>>            free (build_info_strings);
>>>>          }
>>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>> +        {
>>>> +          /* Zero-profile fixup flags are not used by the compiler, read and
>>>> +             ignore.  */
>>>> +          gcov_unsigned_t num_fn;
>>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>>> (length, &num_fn);
>>>> +          free (zero_fixup_flags);
>>>> +        }
>>>>        else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
>>>>         {
>>>>           counts_entry_t **slot, *entry, elt;
>>>> Index: gcc/gcov.c
>>>> ===================================================================
>>>> --- gcc/gcov.c  (revision 215230)
>>>> +++ gcc/gcov.c  (working copy)
>>>> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns)
>>>>              free (build_info_strings[i]);
>>>>            free (build_info_strings);
>>>>          }
>>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>> +        {
>>>> +          gcov_unsigned_t num_fn;
>>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>>> (length, &num_fn);
>>>> +          free (zero_fixup_flags);
>>>> +        }
>>>>        else if (tag == GCOV_TAG_FUNCTION && !length)
>>>>         ; /* placeholder  */
>>>>        else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
>>>> Index: gcc/gcov-dump.c
>>>> ===================================================================
>>>> --- gcc/gcov-dump.c     (revision 215230)
>>>> +++ gcc/gcov-dump.c     (working copy)
>>>> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u
>>>>  static void tag_module_info (const char *, unsigned, unsigned);
>>>>  static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
>>>>                                 const struct gcov_ctr_summary *summary);
>>>> +static void tag_zero_fixup (const char *, unsigned, unsigned);
>>>>  static void tag_build_info (const char *, unsigned, unsigned);
>>>>  extern int main (int, char **);
>>>>
>>>> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0;
>>>>  static int flag_dump_aux_modules_only = 0;
>>>>  static int flag_dump_working_sets = 0;
>>>>
>>>> +static unsigned num_fn_info;
>>>> +static int *zero_fixup_flags = NULL;
>>>> +
>>>>  static const struct option options[] =
>>>>  {
>>>>    { "help",                 no_argument,       NULL, 'h' },
>>>> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] =
>>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>>    {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
>>>>    {0, NULL, NULL}
>>>>  };
>>>> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename)
>>>>      printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
>>>>    }
>>>>
>>>> +  num_fn_info = 0;
>>>> +
>>>>    while (1)
>>>>      {
>>>>        gcov_position_t base, position = gcov_position ();
>>>> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename)
>>>>           break;
>>>>         }
>>>>      }
>>>> +  free (zero_fixup_flags);
>>>>    gcov_close ();
>>>>  }
>>>>
>>>> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>>      printf (" placeholder");
>>>>    else
>>>>      {
>>>> -      printf (" ident=%u", gcov_read_unsigned ());
>>>> +      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
>>>> +      printf (" ident=%u%s", gcov_read_unsigned (),
>>>> +              had_fixup ? " (Was 0-count COMDAT)" : "");
>>>>        printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
>>>>        printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());
>>>>
>>>> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>>           printf (":%u", gcov_read_unsigned ());
>>>>         }
>>>>      }
>>>> +  num_fn_info++;
>>>>  }
>>>>
>>>>  static void
>>>> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN
>>>>  }
>>>>
>>>>  static void
>>>> +tag_zero_fixup (const char *filename,
>>>> +                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>> +{
>>>> +  gcov_unsigned_t num_fns = 0;
>>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>> +  if (!zero_fixup_flags)
>>>> +    {
>>>> +      printf ("%s:error reading zero fixup flags\n", filename);
>>>> +      return;
>>>> +    }
>>>> +  printf (" num_fns=%u", num_fns);
>>>> +  for (unsigned i = 0; i < num_fns; i++)
>>>> +    {
>>>> +      if (!(i % 32))
>>>> +        {
>>>> +          printf ("\n");
>>>> +          print_prefix (filename, 0, 0);
>>>> +          printf ("\t\t");
>>>> +        }
>>>> +      if (!(i % 8))
>>>> +        printf ("%s%4u:", (i%32)?" ":"", i);
>>>> +      printf ("%u", zero_fixup_flags[i]);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>>  tag_build_info (const char *filename,
>>>>                 unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>>  {
>>>> Index: gcc/gcov-io.c
>>>> ===================================================================
>>>> --- gcc/gcov-io.c       (revision 215230)
>>>> +++ gcc/gcov-io.c       (working copy)
>>>> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary)
>>>>      }
>>>>  }
>>>>
>>>> +/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
>>>> +   number of function flags saved in NUM_FNS.  Returns the int flag
>>>> array, which
>>>> +   should be deallocated by caller, or NULL on error.  */
>>>> +
>>>> +GCOV_LINKAGE int *
>>>> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
>>>> +                             gcov_unsigned_t *num_fns)
>>>> +{
>>>> +  unsigned ix, f_ix;
>>>> +  gcov_unsigned_t num = gcov_read_unsigned ();
>>>> +  /* The length consists of 1 word to hold the number of functions,
>>>> +     plus enough 32-bit words to hold 1 bit/function.  */
>>>> +  gcc_assert ((num + 31) / 32 + 1 == length);
>>>> +  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
>>>> +  for (ix = 0; ix < length - 1; ix++)
>>>> +    {
>>>> +      gcov_unsigned_t bitvector = gcov_read_unsigned ();
>>>> +      f_ix = ix * 32;
>>>> +      while (bitvector)
>>>> +        {
>>>> +          if (bitvector & 0x1)
>>>> +            zero_fixup_flags[f_ix] = 1;
>>>> +          f_ix++;
>>>> +          bitvector >>= 1;
>>>> +        }
>>>> +    }
>>>> +  *num_fns = num;
>>>> +  return zero_fixup_flags;
>>>> +}
>>>> +
>>>>  /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
>>>>     the number of words read.  */
>>>>
>>>> Index: gcc/gcov-io.h
>>>> ===================================================================
>>>> --- gcc/gcov-io.h       (revision 215230)
>>>> +++ gcc/gcov-io.h       (working copy)
>>>> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>     blocks they are for.
>>>>
>>>>     The data file contains the following records.
>>>> -        data: {unit summary:program* build_info function-data*}*
>>>> +        data: {unit summary:program* build_info zero_fixup function-data*}*
>>>>         unit: header int32:checksum
>>>>          function-data: announce_function present counts
>>>>         announce_function: header int32:ident
>>>> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>          histogram: {int32:bitvector}8 histogram-buckets*
>>>>          histogram-buckets: int32:num int64:min int64:sum
>>>>          build_info: string:info*
>>>> +        zero_fixup: int32:num int32:bitvector*
>>>>
>>>>     The ANNOUNCE_FUNCTION record is the same as that in the note file,
>>>>     but without the source location.  The COUNTS gives the
>>>> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>     build.  For example, it can be used to include source revision
>>>>     information that is useful in diagnosing profile mis-matches.
>>>>
>>>> +   ZERO_FIXUP record contains a count of functions in the gcda file
>>>> +   and an array of bitvectors indexed by the function index's in the
>>>> +   function-data section. Each bit flags whether the function was a
>>>> +   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
>>>> +   using profiles from functions with matching checksums in other modules.
>>>> +
>>>>     This file is included by both the compiler, gcov tools and the
>>>>     runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
>>>>     distinguish which case is which.  If IN_LIBGCOV is nonzero,
>>>> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
>>>>  #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
>>>>  #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
>>>>  #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
>>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
>>>> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
>>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
>>>>  #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
>>>>          (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
>>>>  #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
>>>> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
>>>>  GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
>>>>  GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
>>>>  GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
>>>> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
>>>> +                                               gcov_unsigned_t *)
>>>> +    ATTRIBUTE_HIDDEN;
>>>>  GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
>>>>    ATTRIBUTE_HIDDEN;
>>>>  GCOV_LINKAGE const char *gcov_read_string (void);
>>>> Index: libgcc/dyn-ipa.c
>>>> ===================================================================
>>>> --- libgcc/dyn-ipa.c    (revision 215230)
>>>> +++ libgcc/dyn-ipa.c    (working copy)
>>>> @@ -107,8 +107,9 @@ struct checksum_alias
>>>>    struct checksum_alias *next_alias;
>>>>    gcov_type guid;
>>>>    const struct gcov_fn_info *fi_ptr;
>>>> -  /* Does this function have all-zero arc counts?  */
>>>> -  int zero_counts;
>>>> +  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
>>>> +     set if we perform fixup.  */
>>>> +  int *zero_count_fixup;
>>>>  };
>>>>
>>>>  /* Module info is stored in dyn_caph->sup_modules
>>>> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
>>>>  extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
>>>>
>>>>  #if defined(inhibit_libc)
>>>> -void __gcov_build_callgraph (void) {}
>>>> +void __gcov_build_callgraph (int **zero_counts) {}
>>>>  #else
>>>>
>>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>>  static void gcov_dump_callgraph (gcov_type);
>>>>  static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
>>>> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p)
>>>>  }
>>>>
>>>>  /* Create a new checksum_alias struct for function with GUID, FI_PTR,
>>>> -   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
>>>> +   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
>>>> +   new struct.  */
>>>>
>>>>  static struct checksum_alias *
>>>>  new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>>> -                    int zero_counts,
>>>> +                    int *zero_count_fixup,
>>>>                      struct checksum_alias *next)
>>>>  {
>>>>    struct checksum_alias *alias = XNEW (struct checksum_alias);
>>>>    alias->next_alias = next;
>>>>    alias->fi_ptr = fi_ptr;
>>>>    alias->guid = guid;
>>>> -  alias->zero_counts = zero_counts;
>>>> +  alias->zero_count_fixup = zero_count_fixup;
>>>>    return alias;
>>>>  }
>>>>
>>>> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis
>>>>  }
>>>>
>>>>  /* Insert a new checksum_alias struct into LIST for function with
>>>> -   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
>>>> +   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
>>>> +   flag pointer.  */
>>>>
>>>>  static struct checksum_alias_info *
>>>>  cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
>>>> -                     const struct gcov_fn_info *fi_ptr, int zero_counts,
>>>> +                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
>>>>                       struct checksum_alias_info *list)
>>>>  {
>>>>    struct checksum_alias_info *alias_info;
>>>> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>    if (alias_info)
>>>>      {
>>>>        gcc_assert (alias_info->alias_list);
>>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>>> +                                                   zero_count_fixup,
>>>>                                                     alias_info->alias_list);
>>>>        return list;
>>>>      }
>>>> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>        alias_info = XNEW (struct checksum_alias_info);
>>>>        alias_info->next_cfg_checksum = list;
>>>>        alias_info->cfg_checksum = cfg_checksum;
>>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>>> +                                                   zero_count_fixup,
>>>>                                                     NULL);
>>>>        return alias_info;
>>>>      }
>>>> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>
>>>>  /* Insert a new checksum_alias struct into lineno_pointer_sets for
>>>> function with
>>>>     LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
>>>> -   ZERO_COUNTS flag.  */
>>>> +   ZERO_COUNT_FIXUP flag pointer.  */
>>>>
>>>>  static void
>>>>  checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
>>>>                       gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>>> -                     int zero_counts)
>>>> +                     int *zero_count_fixup)
>>>>  {
>>>>    struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
>>>>    if (!p)
>>>> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>>    if (*m)
>>>>      {
>>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>>> -                                                     fi_ptr, zero_counts,
>>>> +                                                     fi_ptr, zero_count_fixup,
>>>>                                                       (*m)->cfg_checksum_list);
>>>>      }
>>>>    else
>>>> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>>        *m = XNEW (struct lineno_checksum_alias);
>>>>        (*m)->lineno_checksum = lineno_checksum;
>>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>>> -                                                     fi_ptr,
>>>> zero_counts, NULL);
>>>> +                                                     fi_ptr, zero_count_fixup,
>>>> +                                                     NULL);
>>>>        p->n_elements++;
>>>>      }
>>>>  }
>>>> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
>>>>      }
>>>>  }
>>>>
>>>> -/* Build the dynamic call graph.  */
>>>> +/* Build the dynamic call graph and update ZERO_COUNTS flags.  */
>>>>
>>>>  static void
>>>> -gcov_build_callgraph (void)
>>>> +gcov_build_callgraph (int **zero_counts)
>>>>  {
>>>>    struct gcov_info *gi_ptr;
>>>>    unsigned m_ix;
>>>> @@ -852,9 +858,19 @@ static void
>>>>                    if (total_arc_count != 0)
>>>>                      the_dyn_call_graph.num_nodes_executed++;
>>>>                    if (fixup_type)
>>>> -                    checksum_set_insert (fi_ptr->lineno_checksum,
>>>> -                                         fi_ptr->cfg_checksum, caller->guid,
>>>> -                                         fi_ptr, total_arc_count == 0);
>>>> +                    {
>>>> +                      int *zero_count_fixup = NULL;
>>>> +                      /* Passing in a non-NULL zero_count_fixup pointer
>>>> +                         indicates that the counts were all zero for this
>>>> +                         function, and the fixup routine will set the flag
>>>> +                         if the function's counters are updated to non-zero
>>>> +                         values.  */
>>>> +                      if (total_arc_count == 0)
>>>> +                        zero_count_fixup = &zero_counts[m_ix][f_ix];
>>>> +                      checksum_set_insert (fi_ptr->lineno_checksum,
>>>> +                                           fi_ptr->cfg_checksum, caller->guid,
>>>> +                                           fi_ptr, zero_count_fixup);
>>>> +                    }
>>>>                  }
>>>>                ci_ptr++;
>>>>              }
>>>> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value,
>>>>    out_array = (struct gcov_import_mod_array *) data1;
>>>>
>>>>    if (m->imp_mod != out_array->importing_module)
>>>> +  {
>>>>      out_array->imported_modules[out_array->len++] = m;
>>>> +    /* Sanity check that the importing (primary) module is not
>>>> +       actually the same as the new aux module. This could happen if
>>>> +       we accidentally read in the same gcda file twice.  */
>>>> +    gcc_assert (m->imp_mod->mod_info->ident !=
>>>> +                out_array->importing_module->mod_info->ident);
>>>> +  }
>>>>
>>>>    return 1;
>>>>  }
>>>> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>    for (alias = info->alias_list; alias;
>>>>         alias = alias->next_alias)
>>>>      {
>>>> -      if (alias->zero_counts)
>>>> +      if (alias->zero_count_fixup)
>>>>          {
>>>>            found = 1;
>>>>            break;
>>>> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>    for (alias = info->alias_list; alias;
>>>>         alias = alias->next_alias)
>>>>      {
>>>> -      if (alias->zero_counts)
>>>> +      if (alias->zero_count_fixup)
>>>>          continue;
>>>>        merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
>>>>        found = 1;
>>>> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>    for (alias = info->alias_list; alias;
>>>>         alias = alias->next_alias)
>>>>      {
>>>> -      if (!alias->zero_counts)
>>>> +      if (!alias->zero_count_fixup)
>>>>          continue;
>>>>        copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
>>>> +      *alias->zero_count_fixup = 1;
>>>>      }
>>>>
>>>>    return 1;
>>>> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void)
>>>>    return changed;
>>>>  }
>>>>
>>>> -/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
>>>> -   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
>>>> +/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
>>>> +   flags are set for functions with zero count fixups applied. Returns 1
>>>> +   if any counter fixups were applied, requiring a profile rewrite,
>>>> +   0 otherwise.  */
>>>>
>>>>  int
>>>> -__gcov_compute_module_groups (void)
>>>> +__gcov_compute_module_groups (int **zero_counts)
>>>>  {
>>>>    gcov_type cut_off_count;
>>>>    char *seed = getenv ("LIPO_RANDOM_GROUPING");
>>>> @@ -3091,7 +3117,7 @@ int
>>>>      fixup_type = atoi (do_fixup);
>>>>
>>>>    /* First compute dynamic call graph.  */
>>>> -  gcov_build_callgraph ();
>>>> +  gcov_build_callgraph (zero_counts);
>>>>
>>>>    cut_off_count = gcov_compute_cutoff_count ();
>>>>
>>>> Index: libgcc/libgcov-driver.c
>>>> ===================================================================
>>>> --- libgcc/libgcov-driver.c     (revision 215230)
>>>> +++ libgcc/libgcov-driver.c     (working copy)
>>>> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
>>>>
>>>>
>>>>  /* Dynamic call graph build and form module groups.  */
>>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>>
>>>>  /* The following functions can be called from outside of this file.  */
>>>> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head)
>>>>    __gcov_list = head;
>>>>  }
>>>>
>>>> +/* Flag if the current function being read was marked as having fixed-up
>>>> +   zero counters.  */
>>>> +static int __gcov_curr_fn_fixed_up;
>>>> +
>>>> +/* Set function fixed up flag.  */
>>>> +void
>>>> +set_gcov_fn_fixed_up (int fixed_up)
>>>> +{
>>>> +  __gcov_curr_fn_fixed_up = fixed_up;
>>>> +}
>>>> +
>>>> +/* Return function fixed up flag.  */
>>>> +int
>>>> +get_gcov_fn_fixed_up (void)
>>>> +{
>>>> +  return __gcov_curr_fn_fixed_up;
>>>> +}
>>>> +
>>>>  /* Size of the longest file name. */
>>>>  /* We need to expose this static variable when compiling for gcov-tool.  */
>>>>  #ifndef IN_GCOV_TOOL
>>>> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>    int error = 0;
>>>>    struct gcov_fn_buffer **fn_tail = &fn_buffer;
>>>>    struct gcov_summary_buffer **sum_tail = &sum_buffer;
>>>> +  int *zero_fixup_flags = NULL;
>>>>
>>>>    length = gcov_read_unsigned ();
>>>>    if (!gcov_version (gi_ptr, length, gi_filename))
>>>> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>        tag = gcov_read_unsigned ();
>>>>      }
>>>>
>>>> +  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>> +    {
>>>> +      length = gcov_read_unsigned ();
>>>> +      gcov_unsigned_t num_fns = 0;
>>>> +      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>> +      if (!zero_fixup_flags)
>>>> +        {
>>>> +          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
>>>> +                      gi_filename);
>>>> +          return -1;
>>>> +        }
>>>> +
>>>> +      tag = gcov_read_unsigned ();
>>>> +    }
>>>> +
>>>>    /* Merge execution counts for each function.  */
>>>>    for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
>>>>         f_ix++, tag = gcov_read_unsigned ())
>>>> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>            continue;
>>>>          }
>>>>
>>>> +      if (zero_fixup_flags)
>>>> +        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
>>>> +
>>>>        length = gcov_read_unsigned ();
>>>>        if (length != gfi_ptr->ident)
>>>>          goto read_mismatch;
>>>> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>        if ((error = gcov_is_error ()))
>>>>          goto read_error;
>>>>      }
>>>> +  free (zero_fixup_flags);
>>>>
>>>>    if (tag && tag != GCOV_TAG_MODULE_INFO)
>>>>      {
>>>> @@ -706,6 +744,34 @@ read_error:
>>>>    return -1;
>>>>  }
>>>>
>>>> +
>>>> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
>>>> +   current location.  */
>>>> +
>>>> +static void
>>>> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
>>>> +{
>>>> +  unsigned f_ix;
>>>> +  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
>>>> +  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
>>>> +
>>>> +  gcov_write_unsigned (num_fns);
>>>> +  gcov_unsigned_t bitvector = 0, b_ix = 0;
>>>> +  for (f_ix = 0; f_ix != num_fns; f_ix++)
>>>> +    {
>>>> +      if (zero_counts[f_ix])
>>>> +        bitvector |= 1 << b_ix;
>>>> +      if (++b_ix == 32)
>>>> +        {
>>>> +          gcov_write_unsigned (bitvector);
>>>> +          b_ix = 0;
>>>> +          bitvector = 0;
>>>> +        }
>>>> +    }
>>>> +  if (b_ix > 0)
>>>> +    gcov_write_unsigned (bitvector);
>>>> +}
>>>> +
>>>>  /* Write build_info strings from GI_PTR to a gcda file starting from
>>>> its current
>>>>     location.  */
>>>>
>>>> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr
>>>>            if (gfi_ptr && gfi_ptr->key == gi_ptr)
>>>>              length = GCOV_TAG_FUNCTION_LENGTH;
>>>>            else
>>>> -                length = 0;
>>>> +            length = 0;
>>>>          }
>>>>
>>>>        gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
>>>> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>  {
>>>>    struct gcov_info *gi_ptr;
>>>>
>>>> +  unsigned max_module_id = 0;
>>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>> +    {
>>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>>> +      if (max_module_id < mod_id)
>>>> +        max_module_id = mod_id;
>>>> +    }
>>>> +  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
>>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>> +    {
>>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>>> +      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
>>>> +                                               sizeof (int));
>>>> +    }
>>>> +
>>>>    /* Compute the module groups and record whether there were any
>>>>       counter fixups applied that require rewriting the counters.  */
>>>> -  int changed = __gcov_compute_module_groups ();
>>>> +  int changed = __gcov_compute_module_groups (zero_counts);
>>>>
>>>>    /* Now write out module group info.  */
>>>>    for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>                gcov_position_t eof_pos = gi_ptr->eof_pos;
>>>>                gcov_rewrite ();
>>>>                gcov_seek (summary_end_pos);
>>>> +
>>>> +              unsigned mod_id = gi_ptr->mod_info->ident;
>>>> +              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
>>>> +                                            gi_ptr->n_functions);
>>>> +              gcov_position_t zero_fixup_eof_pos = gcov_position ();
>>>> +
>>>>                gcov_write_func_counters (gi_ptr);
>>>> -              gcc_assert (eof_pos == gi_ptr->eof_pos);
>>>> +              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
>>>> +                          == gi_ptr->eof_pos);
>>>>              }
>>>>          }
>>>>        else
>>>> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>                                    "profiling:%s:Error writing\n",
>>>>                                    gi_filename);
>>>>        gcov_write_import_file (gi_filename, gi_ptr);
>>>> +      free (zero_counts[gi_ptr->mod_info->ident-1]);
>>>>      }
>>>> +
>>>> +  free (zero_counts);
>>>> +
>>>>    __gcov_finalize_dyn_callgraph ();
>>>>  }
>>>>
>>>> Index: libgcc/libgcov.h
>>>> ===================================================================
>>>> --- libgcc/libgcov.h    (revision 215230)
>>>> +++ libgcc/libgcov.h    (working copy)
>>>> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i
>>>>      ATTRIBUTE_HIDDEN;
>>>>  GCOV_LINKAGE inline void gcov_rewrite (void);
>>>>
>>>> +extern void set_gcov_fn_fixed_up (int fixed_up);
>>>> +extern int get_gcov_fn_fixed_up (void);
>>>> +
>>>>  /* "Counts" stored in gcda files can be a real counter value, or
>>>>     an target address. When differentiate these two types because
>>>>     when manipulating counts, we should only change real counter values,
>>>> @@ -361,7 +364,13 @@ gcov_get_counter (void)
>>>>    /* This version is for reading count values in libgcov runtime:
>>>>       we read from gcda files.  */
>>>>
>>>> -  return gcov_read_counter ();
>>>> +  if (get_gcov_fn_fixed_up ())
>>>> +    {
>>>> +      gcov_read_counter ();
>>>> +      return 0;
>>>> +    }
>>>> +  else
>>>> +    return gcov_read_counter ();
>>>>  #else
>>>>    /* This version is for gcov-tool. We read the value from memory and
>>>>       multiply it by the merge weight.  */
>>>> @@ -380,7 +389,13 @@ gcov_get_counter_target (void)
>>>>    /* This version is for reading count target values in libgcov runtime:
>>>>       we read from gcda files.  */
>>>>
>>>> -  return gcov_read_counter ();
>>>> +  if (get_gcov_fn_fixed_up ())
>>>> +    {
>>>> +      gcov_read_counter ();
>>>> +      return 0;
>>>> +    }
>>>> +  else
>>>> +    return gcov_read_counter ();
>>>>  #else
>>>>    /* This version is for gcov-tool.  We read the value from memory
>>>> and we do NOT
>>>>       multiply it by the merge weight.  */
>>>> Index: libgcc/libgcov-util.c
>>>> ===================================================================
>>>> --- libgcc/libgcov-util.c       (revision 215230)
>>>> +++ libgcc/libgcov-util.c       (working copy)
>>>> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned);
>>>>  static void tag_counters (unsigned, unsigned);
>>>>  static void tag_summary (unsigned, unsigned);
>>>>  static void tag_module_info (unsigned, unsigned);
>>>> +static void tag_zero_fixup (unsigned, unsigned);
>>>>
>>>>  /* The gcov_info for the first module.  */
>>>>  static struct gcov_info *curr_gcov_info;
>>>> @@ -88,6 +89,8 @@ static int k_ctrs_types;
>>>>  /* The longest length of all the filenames.  */
>>>>  static int max_filename_len;
>>>>
>>>> +static int *zero_fixup_flags = NULL;
>>>> +
>>>>  /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
>>>>     functions in dyn-ipa.c, which were derived from these, except
>>>>     the versions in dyn-ipa are used when merging from another array.  */
>>>> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] =
>>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>>    {0, NULL, NULL}
>>>>  };
>>>>
>>>> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
>>>>       k_ctrs[i].num = 0;
>>>>    k_ctrs_types = 0;
>>>>
>>>> +  if (zero_fixup_flags)
>>>> +    {
>>>> +      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
>>>> +      if (get_gcov_fn_fixed_up () && verbose)
>>>> +        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
>>>> +    }
>>>> +
>>>>    curr_fn_info->key = curr_gcov_info;
>>>>    curr_fn_info->ident = gcov_read_unsigned ();
>>>>    curr_fn_info->lineno_checksum = gcov_read_unsigned ();
>>>>    curr_fn_info->cfg_checksum = gcov_read_unsigned ();
>>>>    num_fn_info++;
>>>> -
>>>> -  if (verbose)
>>>> -    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
>>>>  }
>>>>
>>>>  /* Handler for reading block tag.  */
>>>> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length)
>>>>    gcc_assert (values);
>>>>
>>>>    for (ix = 0; ix != n_counts; ix++)
>>>> -    values[ix] = gcov_read_counter ();
>>>> +    {
>>>> +      gcov_type val = gcov_read_counter ();
>>>> +      if (!get_gcov_fn_fixed_up ())
>>>> +        values[ix] = val;
>>>> +      else
>>>> +        values[ix] = 0;
>>>> +    }
>>>>  }
>>>>
>>>>  /* Handler for reading summary tag.  */
>>>> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>>        char *t;
>>>>
>>>>        if (verbose)
>>>> -        printf ("Substitute: %s \n", input_str);
>>>> +        fprintf (stderr, "Substitute: %s \n", input_str);
>>>>        t = (char*) xmalloc (strlen (input_str) + 1
>>>>            + strlen (new_str) - strlen (cur_str));
>>>>        *p = 0;
>>>> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>>        strcat (t, new_str);
>>>>        strcat (t, p + strlen (cur_str));
>>>>        if (verbose)
>>>> -        printf ("       -->  %s\n", t);
>>>> +        fprintf (stderr, "       -->  %s\n", t);
>>>>        return t;
>>>>      }
>>>>
>>>> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
>>>>      free (mod_info);
>>>>  }
>>>>
>>>> +/* Handler for reading the COMDAT zero-profile fixup section.  */
>>>> +
>>>> +static void
>>>> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>> +{
>>>> +  gcov_unsigned_t num_fns = 0;
>>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>> +  gcc_assert (zero_fixup_flags);
>>>> +}
>>>> +
>>>>  /* Read the content of a gcda file FILENAME, and return a gcov_info
>>>> data structure.
>>>>     Program level summary CURRENT_SUMMARY will also be updated.  */
>>>>
>>>> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
>>>>      }
>>>>
>>>>    read_gcda_finalize (obj_info);
>>>> +  free (zero_fixup_flags);
>>>>    gcov_close ();
>>>>
>>>>    return obj_info;
>>>> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
>>>>    unsigned f_ix;
>>>>
>>>>    if (verbose)
>>>> -    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>>> +    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>>>
>>>>    /* Scaling the counters.  */
>>>>    for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
>>>> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
>>>>    scale_factor = (float)max_val / curr_max_val;
>>>>  #if !defined (_WIN32)
>>>>    if (verbose)
>>>> -    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
>>>> +    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
>>>>  #endif
>>>>
>>>>    return gcov_profile_scale (profile, scale_factor, 0, 0);
>>>>
>>>> --
>>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>
>>
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Xinliang David Li Sept. 16, 2014, 6:57 p.m. UTC | #5
1.5M  increase is not big concern. Ok with that.

David

On Tue, Sep 16, 2014 at 10:51 AM, Teresa Johnson <tejohnson@google.com> wrote:
> On Tue, Sep 16, 2014 at 8:07 AM, Xinliang David Li <davidxl@google.com> wrote:
>>  The zero_counts array is passed to gcov_build_callgraph but not used
>> until the dyn-cgraph is initialized. We should avoid increasing
>> runtime memory overhead by not creating it if possible.
>
> We could delay creation a little bit, until the callgraph has been
> initialized. But I am not sure this gains us much as it doesn't avoid
> having the lifetime of the array overlap with the lifetime of the
> callgraph. And unless fixups have been explicitly disabled or there
> are no comdats we will need to allocate it (so likely in almost all
> cases).
>
> However, I can reduce the required overhead by using an array of chars
> instead of ints. I measured the number of bytes in the array for a big
> app and it was 5.6M with the int array and 1.5M with the char array.
> Does this seem reasonable? To reduce it further I could encode as a
> bitvector as we do in the gcda file.
>
> Teresa
>
>>
>> David
>>
>> On Tue, Sep 16, 2014 at 7:57 AM, Teresa Johnson <tejohnson@google.com> wrote:
>>> On Mon, Sep 15, 2014 at 9:29 PM, Xinliang David Li <davidxl@google.com> wrote:
>>>> Is it necessary to declare zero_counts array at all?  Can a flag field
>>>> be added to dyn_cgraph_node structure to indicate if it is fixed up?
>>>
>>> The zero_counts array is used to pass info back to the caller in
>>> libgcov-driver.cc (dyn_cgraph_node), which is where it is allocated.
>>> That routine does not have access to the dyn-ipa cgraph.
>>>
>>> Teresa
>>>
>>>>
>>>> David
>>>>
>>>> On Fri, Sep 12, 2014 at 4:31 PM, Teresa Johnson <tejohnson@google.com> wrote:
>>>>> This patch addresses issues when running gcov-tool after performing
>>>>> COMDAT fixup during dyn-ipa. Functions that were previously all zero
>>>>> counts are marked, and the counts are discarded when being read in
>>>>> by gcov-tool before recalculating module groups and summary info.
>>>>>
>>>>> While here, cleaned up the gcov-tool output (remove an overly-verbose output,
>>>>> make all output consistently go to stderr).
>>>>>
>>>>> Passes regression tests and manual tests. Ok for google branches?
>>>>>
>>>>> 2014-09-12  Teresa Johnson  <tejohnson@google.com>
>>>>>
>>>>>         * gcc/coverage.c (read_counts_file): Handle new section.
>>>>>         * gcc/gcov.c (read_count_file): Ditto.
>>>>>         * gcc/gcov-dump.c (dump_gcov_file): Ditto.
>>>>>         (tag_function): Ditto.
>>>>>         (tag_zero_fixup): New function.
>>>>>         * gcc/gcov-io.c (gcov_read_comdat_zero_fixup): Ditto.
>>>>>         * gcc/gcov-io.h (gcov_read_comdat_zero_fixup): Ditto.
>>>>>         * libgcc/dyn-ipa.c (struct checksum_alias): Change flag to pointer.
>>>>>         (new_checksum_alias): Ditto.
>>>>>         (cfg_checksum_insert): Ditto.
>>>>>         (checksum_set_insert): Ditto.
>>>>>         (gcov_build_callgraph): New parameter.
>>>>>         (gcov_collect_imported_modules): Add assert for duplicate gcda reads.
>>>>>         (gcov_fixup_counters_checksum): Change flag to pointer to flag, set it.
>>>>>         (__gcov_compute_module_groups): New parameter.
>>>>>         * libgcc/libgcov-driver.c (set_gcov_fn_fixed_up): New function.
>>>>>         (get_gcov_fn_fixed_up): Ditto.
>>>>>         (gcov_exit_merge_gcda): Handle new section.
>>>>>         (gcov_write_comdat_zero_fixup): Ditto.
>>>>>         (gcov_write_build_info): Ditto.
>>>>>         (gcov_write_comdat_zero_fixup): New function.
>>>>>         (gcov_write_func_counters): Fix indent.
>>>>>         (gcov_dump_module_info): Write new flag section.
>>>>>         * libgcc/libgcov.h (gcov_get_counter): Clear fixed-up counters.
>>>>>         (gcov_get_counter_target): Ditto.
>>>>>         * libgcc/libgcov-util.c (tag_function): Annotate fixed-up functions,
>>>>>         remove overly verbose output.
>>>>>         (tag_counters): Clear fixed-up counters.
>>>>>         (lipo_process_substitute_string_1): Send all verbose output to stderr.
>>>>>         (tag_zero_fixup): New function.
>>>>>         (read_gcda_file): Deallocate flag array.
>>>>>         (gcov_profile_scale): Send all verbose output to stderr.
>>>>>         (gcov_profile_normalize): Ditto.
>>>>>
>>>>> Index: gcc/coverage.c
>>>>> ===================================================================
>>>>> --- gcc/coverage.c      (revision 215230)
>>>>> +++ gcc/coverage.c      (working copy)
>>>>> @@ -820,6 +820,14 @@ read_counts_file (const char *da_file_name, unsign
>>>>>              free (build_info_strings[i]);
>>>>>            free (build_info_strings);
>>>>>          }
>>>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>>> +        {
>>>>> +          /* Zero-profile fixup flags are not used by the compiler, read and
>>>>> +             ignore.  */
>>>>> +          gcov_unsigned_t num_fn;
>>>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>>>> (length, &num_fn);
>>>>> +          free (zero_fixup_flags);
>>>>> +        }
>>>>>        else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
>>>>>         {
>>>>>           counts_entry_t **slot, *entry, elt;
>>>>> Index: gcc/gcov.c
>>>>> ===================================================================
>>>>> --- gcc/gcov.c  (revision 215230)
>>>>> +++ gcc/gcov.c  (working copy)
>>>>> @@ -1441,6 +1441,12 @@ read_count_file (function_t *fns)
>>>>>              free (build_info_strings[i]);
>>>>>            free (build_info_strings);
>>>>>          }
>>>>> +      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>>> +        {
>>>>> +          gcov_unsigned_t num_fn;
>>>>> +          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
>>>>> (length, &num_fn);
>>>>> +          free (zero_fixup_flags);
>>>>> +        }
>>>>>        else if (tag == GCOV_TAG_FUNCTION && !length)
>>>>>         ; /* placeholder  */
>>>>>        else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
>>>>> Index: gcc/gcov-dump.c
>>>>> ===================================================================
>>>>> --- gcc/gcov-dump.c     (revision 215230)
>>>>> +++ gcc/gcov-dump.c     (working copy)
>>>>> @@ -42,6 +42,7 @@ static void tag_summary (const char *, unsigned, u
>>>>>  static void tag_module_info (const char *, unsigned, unsigned);
>>>>>  static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
>>>>>                                 const struct gcov_ctr_summary *summary);
>>>>> +static void tag_zero_fixup (const char *, unsigned, unsigned);
>>>>>  static void tag_build_info (const char *, unsigned, unsigned);
>>>>>  extern int main (int, char **);
>>>>>
>>>>> @@ -57,6 +58,9 @@ static int flag_dump_positions = 0;
>>>>>  static int flag_dump_aux_modules_only = 0;
>>>>>  static int flag_dump_working_sets = 0;
>>>>>
>>>>> +static unsigned num_fn_info;
>>>>> +static int *zero_fixup_flags = NULL;
>>>>> +
>>>>>  static const struct option options[] =
>>>>>  {
>>>>>    { "help",                 no_argument,       NULL, 'h' },
>>>>> @@ -79,6 +83,7 @@ static const tag_format_t tag_table[] =
>>>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>>>    {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
>>>>>    {0, NULL, NULL}
>>>>>  };
>>>>> @@ -274,6 +279,8 @@ dump_gcov_file (const char *filename)
>>>>>      printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
>>>>>    }
>>>>>
>>>>> +  num_fn_info = 0;
>>>>> +
>>>>>    while (1)
>>>>>      {
>>>>>        gcov_position_t base, position = gcov_position ();
>>>>> @@ -341,6 +348,7 @@ dump_gcov_file (const char *filename)
>>>>>           break;
>>>>>         }
>>>>>      }
>>>>> +  free (zero_fixup_flags);
>>>>>    gcov_close ();
>>>>>  }
>>>>>
>>>>> @@ -354,7 +362,9 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>>>      printf (" placeholder");
>>>>>    else
>>>>>      {
>>>>> -      printf (" ident=%u", gcov_read_unsigned ());
>>>>> +      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
>>>>> +      printf (" ident=%u%s", gcov_read_unsigned (),
>>>>> +              had_fixup ? " (Was 0-count COMDAT)" : "");
>>>>>        printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
>>>>>        printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());
>>>>>
>>>>> @@ -369,6 +379,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSE
>>>>>           printf (":%u", gcov_read_unsigned ());
>>>>>         }
>>>>>      }
>>>>> +  num_fn_info++;
>>>>>  }
>>>>>
>>>>>  static void
>>>>> @@ -600,6 +611,32 @@ tag_module_info (const char *filename ATTRIBUTE_UN
>>>>>  }
>>>>>
>>>>>  static void
>>>>> +tag_zero_fixup (const char *filename,
>>>>> +                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>>> +{
>>>>> +  gcov_unsigned_t num_fns = 0;
>>>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>>> +  if (!zero_fixup_flags)
>>>>> +    {
>>>>> +      printf ("%s:error reading zero fixup flags\n", filename);
>>>>> +      return;
>>>>> +    }
>>>>> +  printf (" num_fns=%u", num_fns);
>>>>> +  for (unsigned i = 0; i < num_fns; i++)
>>>>> +    {
>>>>> +      if (!(i % 32))
>>>>> +        {
>>>>> +          printf ("\n");
>>>>> +          print_prefix (filename, 0, 0);
>>>>> +          printf ("\t\t");
>>>>> +        }
>>>>> +      if (!(i % 8))
>>>>> +        printf ("%s%4u:", (i%32)?" ":"", i);
>>>>> +      printf ("%u", zero_fixup_flags[i]);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>>  tag_build_info (const char *filename,
>>>>>                 unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>>>  {
>>>>> Index: gcc/gcov-io.c
>>>>> ===================================================================
>>>>> --- gcc/gcov-io.c       (revision 215230)
>>>>> +++ gcc/gcov-io.c       (working copy)
>>>>> @@ -691,6 +691,36 @@ gcov_read_summary (struct gcov_summary *summary)
>>>>>      }
>>>>>  }
>>>>>
>>>>> +/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
>>>>> +   number of function flags saved in NUM_FNS.  Returns the int flag
>>>>> array, which
>>>>> +   should be deallocated by caller, or NULL on error.  */
>>>>> +
>>>>> +GCOV_LINKAGE int *
>>>>> +gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
>>>>> +                             gcov_unsigned_t *num_fns)
>>>>> +{
>>>>> +  unsigned ix, f_ix;
>>>>> +  gcov_unsigned_t num = gcov_read_unsigned ();
>>>>> +  /* The length consists of 1 word to hold the number of functions,
>>>>> +     plus enough 32-bit words to hold 1 bit/function.  */
>>>>> +  gcc_assert ((num + 31) / 32 + 1 == length);
>>>>> +  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
>>>>> +  for (ix = 0; ix < length - 1; ix++)
>>>>> +    {
>>>>> +      gcov_unsigned_t bitvector = gcov_read_unsigned ();
>>>>> +      f_ix = ix * 32;
>>>>> +      while (bitvector)
>>>>> +        {
>>>>> +          if (bitvector & 0x1)
>>>>> +            zero_fixup_flags[f_ix] = 1;
>>>>> +          f_ix++;
>>>>> +          bitvector >>= 1;
>>>>> +        }
>>>>> +    }
>>>>> +  *num_fns = num;
>>>>> +  return zero_fixup_flags;
>>>>> +}
>>>>> +
>>>>>  /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
>>>>>     the number of words read.  */
>>>>>
>>>>> Index: gcc/gcov-io.h
>>>>> ===================================================================
>>>>> --- gcc/gcov-io.h       (revision 215230)
>>>>> +++ gcc/gcov-io.h       (working copy)
>>>>> @@ -129,7 +129,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>>     blocks they are for.
>>>>>
>>>>>     The data file contains the following records.
>>>>> -        data: {unit summary:program* build_info function-data*}*
>>>>> +        data: {unit summary:program* build_info zero_fixup function-data*}*
>>>>>         unit: header int32:checksum
>>>>>          function-data: announce_function present counts
>>>>>         announce_function: header int32:ident
>>>>> @@ -142,6 +142,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>>          histogram: {int32:bitvector}8 histogram-buckets*
>>>>>          histogram-buckets: int32:num int64:min int64:sum
>>>>>          build_info: string:info*
>>>>> +        zero_fixup: int32:num int32:bitvector*
>>>>>
>>>>>     The ANNOUNCE_FUNCTION record is the same as that in the note file,
>>>>>     but without the source location.  The COUNTS gives the
>>>>> @@ -158,6 +159,12 @@ see the files COPYING3 and COPYING.RUNTIME respect
>>>>>     build.  For example, it can be used to include source revision
>>>>>     information that is useful in diagnosing profile mis-matches.
>>>>>
>>>>> +   ZERO_FIXUP record contains a count of functions in the gcda file
>>>>> +   and an array of bitvectors indexed by the function index's in the
>>>>> +   function-data section. Each bit flags whether the function was a
>>>>> +   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
>>>>> +   using profiles from functions with matching checksums in other modules.
>>>>> +
>>>>>     This file is included by both the compiler, gcov tools and the
>>>>>     runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
>>>>>     distinguish which case is which.  If IN_LIBGCOV is nonzero,
>>>>> @@ -261,6 +268,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
>>>>>  #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
>>>>>  #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
>>>>>  #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
>>>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
>>>>> +/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
>>>>> +#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
>>>>>  #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
>>>>>          (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
>>>>>  #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
>>>>> @@ -441,6 +451,9 @@ GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
>>>>>  GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
>>>>>  GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
>>>>>  GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
>>>>> +GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
>>>>> +                                               gcov_unsigned_t *)
>>>>> +    ATTRIBUTE_HIDDEN;
>>>>>  GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
>>>>>    ATTRIBUTE_HIDDEN;
>>>>>  GCOV_LINKAGE const char *gcov_read_string (void);
>>>>> Index: libgcc/dyn-ipa.c
>>>>> ===================================================================
>>>>> --- libgcc/dyn-ipa.c    (revision 215230)
>>>>> +++ libgcc/dyn-ipa.c    (working copy)
>>>>> @@ -107,8 +107,9 @@ struct checksum_alias
>>>>>    struct checksum_alias *next_alias;
>>>>>    gcov_type guid;
>>>>>    const struct gcov_fn_info *fi_ptr;
>>>>> -  /* Does this function have all-zero arc counts?  */
>>>>> -  int zero_counts;
>>>>> +  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
>>>>> +     set if we perform fixup.  */
>>>>> +  int *zero_count_fixup;
>>>>>  };
>>>>>
>>>>>  /* Module info is stored in dyn_caph->sup_modules
>>>>> @@ -178,10 +179,10 @@ extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
>>>>>  extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
>>>>>
>>>>>  #if defined(inhibit_libc)
>>>>> -void __gcov_build_callgraph (void) {}
>>>>> +void __gcov_build_callgraph (int **zero_counts) {}
>>>>>  #else
>>>>>
>>>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>>>  static void gcov_dump_callgraph (gcov_type);
>>>>>  static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
>>>>> @@ -378,18 +379,19 @@ lineno_checksum_get_key (const void *p)
>>>>>  }
>>>>>
>>>>>  /* Create a new checksum_alias struct for function with GUID, FI_PTR,
>>>>> -   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
>>>>> +   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
>>>>> +   new struct.  */
>>>>>
>>>>>  static struct checksum_alias *
>>>>>  new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>>>> -                    int zero_counts,
>>>>> +                    int *zero_count_fixup,
>>>>>                      struct checksum_alias *next)
>>>>>  {
>>>>>    struct checksum_alias *alias = XNEW (struct checksum_alias);
>>>>>    alias->next_alias = next;
>>>>>    alias->fi_ptr = fi_ptr;
>>>>>    alias->guid = guid;
>>>>> -  alias->zero_counts = zero_counts;
>>>>> +  alias->zero_count_fixup = zero_count_fixup;
>>>>>    return alias;
>>>>>  }
>>>>>
>>>>> @@ -407,11 +409,12 @@ find_cfg_checksum (struct checksum_alias_info *lis
>>>>>  }
>>>>>
>>>>>  /* Insert a new checksum_alias struct into LIST for function with
>>>>> -   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
>>>>> +   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
>>>>> +   flag pointer.  */
>>>>>
>>>>>  static struct checksum_alias_info *
>>>>>  cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
>>>>> -                     const struct gcov_fn_info *fi_ptr, int zero_counts,
>>>>> +                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
>>>>>                       struct checksum_alias_info *list)
>>>>>  {
>>>>>    struct checksum_alias_info *alias_info;
>>>>> @@ -419,7 +422,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>>    if (alias_info)
>>>>>      {
>>>>>        gcc_assert (alias_info->alias_list);
>>>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>>>> +                                                   zero_count_fixup,
>>>>>                                                     alias_info->alias_list);
>>>>>        return list;
>>>>>      }
>>>>> @@ -428,7 +432,8 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>>        alias_info = XNEW (struct checksum_alias_info);
>>>>>        alias_info->next_cfg_checksum = list;
>>>>>        alias_info->cfg_checksum = cfg_checksum;
>>>>> -      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
>>>>> +      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
>>>>> +                                                   zero_count_fixup,
>>>>>                                                     NULL);
>>>>>        return alias_info;
>>>>>      }
>>>>> @@ -436,12 +441,12 @@ cfg_checksum_insert (unsigned cfg_checksum, gcov_t
>>>>>
>>>>>  /* Insert a new checksum_alias struct into lineno_pointer_sets for
>>>>> function with
>>>>>     LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
>>>>> -   ZERO_COUNTS flag.  */
>>>>> +   ZERO_COUNT_FIXUP flag pointer.  */
>>>>>
>>>>>  static void
>>>>>  checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
>>>>>                       gcov_type guid, const struct gcov_fn_info *fi_ptr,
>>>>> -                     int zero_counts)
>>>>> +                     int *zero_count_fixup)
>>>>>  {
>>>>>    struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
>>>>>    if (!p)
>>>>> @@ -452,7 +457,7 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>>>    if (*m)
>>>>>      {
>>>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>>>> -                                                     fi_ptr, zero_counts,
>>>>> +                                                     fi_ptr, zero_count_fixup,
>>>>>                                                       (*m)->cfg_checksum_list);
>>>>>      }
>>>>>    else
>>>>> @@ -460,7 +465,8 @@ checksum_set_insert (unsigned lineno_checksum, uns
>>>>>        *m = XNEW (struct lineno_checksum_alias);
>>>>>        (*m)->lineno_checksum = lineno_checksum;
>>>>>        (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
>>>>> -                                                     fi_ptr,
>>>>> zero_counts, NULL);
>>>>> +                                                     fi_ptr, zero_count_fixup,
>>>>> +                                                     NULL);
>>>>>        p->n_elements++;
>>>>>      }
>>>>>  }
>>>>> @@ -801,10 +807,10 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
>>>>>      }
>>>>>  }
>>>>>
>>>>> -/* Build the dynamic call graph.  */
>>>>> +/* Build the dynamic call graph and update ZERO_COUNTS flags.  */
>>>>>
>>>>>  static void
>>>>> -gcov_build_callgraph (void)
>>>>> +gcov_build_callgraph (int **zero_counts)
>>>>>  {
>>>>>    struct gcov_info *gi_ptr;
>>>>>    unsigned m_ix;
>>>>> @@ -852,9 +858,19 @@ static void
>>>>>                    if (total_arc_count != 0)
>>>>>                      the_dyn_call_graph.num_nodes_executed++;
>>>>>                    if (fixup_type)
>>>>> -                    checksum_set_insert (fi_ptr->lineno_checksum,
>>>>> -                                         fi_ptr->cfg_checksum, caller->guid,
>>>>> -                                         fi_ptr, total_arc_count == 0);
>>>>> +                    {
>>>>> +                      int *zero_count_fixup = NULL;
>>>>> +                      /* Passing in a non-NULL zero_count_fixup pointer
>>>>> +                         indicates that the counts were all zero for this
>>>>> +                         function, and the fixup routine will set the flag
>>>>> +                         if the function's counters are updated to non-zero
>>>>> +                         values.  */
>>>>> +                      if (total_arc_count == 0)
>>>>> +                        zero_count_fixup = &zero_counts[m_ix][f_ix];
>>>>> +                      checksum_set_insert (fi_ptr->lineno_checksum,
>>>>> +                                           fi_ptr->cfg_checksum, caller->guid,
>>>>> +                                           fi_ptr, zero_count_fixup);
>>>>> +                    }
>>>>>                  }
>>>>>                ci_ptr++;
>>>>>              }
>>>>> @@ -1251,7 +1267,14 @@ gcov_collect_imported_modules (const void *value,
>>>>>    out_array = (struct gcov_import_mod_array *) data1;
>>>>>
>>>>>    if (m->imp_mod != out_array->importing_module)
>>>>> +  {
>>>>>      out_array->imported_modules[out_array->len++] = m;
>>>>> +    /* Sanity check that the importing (primary) module is not
>>>>> +       actually the same as the new aux module. This could happen if
>>>>> +       we accidentally read in the same gcda file twice.  */
>>>>> +    gcc_assert (m->imp_mod->mod_info->ident !=
>>>>> +                out_array->importing_module->mod_info->ident);
>>>>> +  }
>>>>>
>>>>>    return 1;
>>>>>  }
>>>>> @@ -2957,7 +2980,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>>    for (alias = info->alias_list; alias;
>>>>>         alias = alias->next_alias)
>>>>>      {
>>>>> -      if (alias->zero_counts)
>>>>> +      if (alias->zero_count_fixup)
>>>>>          {
>>>>>            found = 1;
>>>>>            break;
>>>>> @@ -2972,7 +2995,7 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>>    for (alias = info->alias_list; alias;
>>>>>         alias = alias->next_alias)
>>>>>      {
>>>>> -      if (alias->zero_counts)
>>>>> +      if (alias->zero_count_fixup)
>>>>>          continue;
>>>>>        merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
>>>>>        found = 1;
>>>>> @@ -2990,9 +3013,10 @@ gcov_fixup_counters_checksum (const struct checksu
>>>>>    for (alias = info->alias_list; alias;
>>>>>         alias = alias->next_alias)
>>>>>      {
>>>>> -      if (!alias->zero_counts)
>>>>> +      if (!alias->zero_count_fixup)
>>>>>          continue;
>>>>>        copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
>>>>> +      *alias->zero_count_fixup = 1;
>>>>>      }
>>>>>
>>>>>    return 1;
>>>>> @@ -3040,11 +3064,13 @@ gcov_fixup_zero_counters (void)
>>>>>    return changed;
>>>>>  }
>>>>>
>>>>> -/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
>>>>> -   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
>>>>> +/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
>>>>> +   flags are set for functions with zero count fixups applied. Returns 1
>>>>> +   if any counter fixups were applied, requiring a profile rewrite,
>>>>> +   0 otherwise.  */
>>>>>
>>>>>  int
>>>>> -__gcov_compute_module_groups (void)
>>>>> +__gcov_compute_module_groups (int **zero_counts)
>>>>>  {
>>>>>    gcov_type cut_off_count;
>>>>>    char *seed = getenv ("LIPO_RANDOM_GROUPING");
>>>>> @@ -3091,7 +3117,7 @@ int
>>>>>      fixup_type = atoi (do_fixup);
>>>>>
>>>>>    /* First compute dynamic call graph.  */
>>>>> -  gcov_build_callgraph ();
>>>>> +  gcov_build_callgraph (zero_counts);
>>>>>
>>>>>    cut_off_count = gcov_compute_cutoff_count ();
>>>>>
>>>>> Index: libgcc/libgcov-driver.c
>>>>> ===================================================================
>>>>> --- libgcc/libgcov-driver.c     (revision 215230)
>>>>> +++ libgcc/libgcov-driver.c     (working copy)
>>>>> @@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
>>>>>
>>>>>
>>>>>  /* Dynamic call graph build and form module groups.  */
>>>>> -int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
>>>>> +int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
>>>>>  void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
>>>>>
>>>>>  /* The following functions can be called from outside of this file.  */
>>>>> @@ -129,6 +129,24 @@ set_gcov_list (struct gcov_info *head)
>>>>>    __gcov_list = head;
>>>>>  }
>>>>>
>>>>> +/* Flag if the current function being read was marked as having fixed-up
>>>>> +   zero counters.  */
>>>>> +static int __gcov_curr_fn_fixed_up;
>>>>> +
>>>>> +/* Set function fixed up flag.  */
>>>>> +void
>>>>> +set_gcov_fn_fixed_up (int fixed_up)
>>>>> +{
>>>>> +  __gcov_curr_fn_fixed_up = fixed_up;
>>>>> +}
>>>>> +
>>>>> +/* Return function fixed up flag.  */
>>>>> +int
>>>>> +get_gcov_fn_fixed_up (void)
>>>>> +{
>>>>> +  return __gcov_curr_fn_fixed_up;
>>>>> +}
>>>>> +
>>>>>  /* Size of the longest file name. */
>>>>>  /* We need to expose this static variable when compiling for gcov-tool.  */
>>>>>  #ifndef IN_GCOV_TOOL
>>>>> @@ -564,6 +582,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>>    int error = 0;
>>>>>    struct gcov_fn_buffer **fn_tail = &fn_buffer;
>>>>>    struct gcov_summary_buffer **sum_tail = &sum_buffer;
>>>>> +  int *zero_fixup_flags = NULL;
>>>>>
>>>>>    length = gcov_read_unsigned ();
>>>>>    if (!gcov_version (gi_ptr, length, gi_filename))
>>>>> @@ -625,6 +644,21 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>>        tag = gcov_read_unsigned ();
>>>>>      }
>>>>>
>>>>> +  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
>>>>> +    {
>>>>> +      length = gcov_read_unsigned ();
>>>>> +      gcov_unsigned_t num_fns = 0;
>>>>> +      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>>> +      if (!zero_fixup_flags)
>>>>> +        {
>>>>> +          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
>>>>> +                      gi_filename);
>>>>> +          return -1;
>>>>> +        }
>>>>> +
>>>>> +      tag = gcov_read_unsigned ();
>>>>> +    }
>>>>> +
>>>>>    /* Merge execution counts for each function.  */
>>>>>    for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
>>>>>         f_ix++, tag = gcov_read_unsigned ())
>>>>> @@ -658,6 +692,9 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>>            continue;
>>>>>          }
>>>>>
>>>>> +      if (zero_fixup_flags)
>>>>> +        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
>>>>> +
>>>>>        length = gcov_read_unsigned ();
>>>>>        if (length != gfi_ptr->ident)
>>>>>          goto read_mismatch;
>>>>> @@ -689,6 +726,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>>>>        if ((error = gcov_is_error ()))
>>>>>          goto read_error;
>>>>>      }
>>>>> +  free (zero_fixup_flags);
>>>>>
>>>>>    if (tag && tag != GCOV_TAG_MODULE_INFO)
>>>>>      {
>>>>> @@ -706,6 +744,34 @@ read_error:
>>>>>    return -1;
>>>>>  }
>>>>>
>>>>> +
>>>>> +/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
>>>>> +   current location.  */
>>>>> +
>>>>> +static void
>>>>> +gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
>>>>> +{
>>>>> +  unsigned f_ix;
>>>>> +  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
>>>>> +  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
>>>>> +
>>>>> +  gcov_write_unsigned (num_fns);
>>>>> +  gcov_unsigned_t bitvector = 0, b_ix = 0;
>>>>> +  for (f_ix = 0; f_ix != num_fns; f_ix++)
>>>>> +    {
>>>>> +      if (zero_counts[f_ix])
>>>>> +        bitvector |= 1 << b_ix;
>>>>> +      if (++b_ix == 32)
>>>>> +        {
>>>>> +          gcov_write_unsigned (bitvector);
>>>>> +          b_ix = 0;
>>>>> +          bitvector = 0;
>>>>> +        }
>>>>> +    }
>>>>> +  if (b_ix > 0)
>>>>> +    gcov_write_unsigned (bitvector);
>>>>> +}
>>>>> +
>>>>>  /* Write build_info strings from GI_PTR to a gcda file starting from
>>>>> its current
>>>>>     location.  */
>>>>>
>>>>> @@ -758,7 +824,7 @@ gcov_write_func_counters (struct gcov_info *gi_ptr
>>>>>            if (gfi_ptr && gfi_ptr->key == gi_ptr)
>>>>>              length = GCOV_TAG_FUNCTION_LENGTH;
>>>>>            else
>>>>> -                length = 0;
>>>>> +            length = 0;
>>>>>          }
>>>>>
>>>>>        gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
>>>>> @@ -1104,9 +1170,24 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>>  {
>>>>>    struct gcov_info *gi_ptr;
>>>>>
>>>>> +  unsigned max_module_id = 0;
>>>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>>> +    {
>>>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>>>> +      if (max_module_id < mod_id)
>>>>> +        max_module_id = mod_id;
>>>>> +    }
>>>>> +  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
>>>>> +  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>>> +    {
>>>>> +      unsigned mod_id = gi_ptr->mod_info->ident;
>>>>> +      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
>>>>> +                                               sizeof (int));
>>>>> +    }
>>>>> +
>>>>>    /* Compute the module groups and record whether there were any
>>>>>       counter fixups applied that require rewriting the counters.  */
>>>>> -  int changed = __gcov_compute_module_groups ();
>>>>> +  int changed = __gcov_compute_module_groups (zero_counts);
>>>>>
>>>>>    /* Now write out module group info.  */
>>>>>    for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
>>>>> @@ -1129,8 +1210,15 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>>                gcov_position_t eof_pos = gi_ptr->eof_pos;
>>>>>                gcov_rewrite ();
>>>>>                gcov_seek (summary_end_pos);
>>>>> +
>>>>> +              unsigned mod_id = gi_ptr->mod_info->ident;
>>>>> +              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
>>>>> +                                            gi_ptr->n_functions);
>>>>> +              gcov_position_t zero_fixup_eof_pos = gcov_position ();
>>>>> +
>>>>>                gcov_write_func_counters (gi_ptr);
>>>>> -              gcc_assert (eof_pos == gi_ptr->eof_pos);
>>>>> +              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
>>>>> +                          == gi_ptr->eof_pos);
>>>>>              }
>>>>>          }
>>>>>        else
>>>>> @@ -1149,7 +1237,11 @@ gcov_dump_module_info (struct gcov_filename_aux *g
>>>>>                                    "profiling:%s:Error writing\n",
>>>>>                                    gi_filename);
>>>>>        gcov_write_import_file (gi_filename, gi_ptr);
>>>>> +      free (zero_counts[gi_ptr->mod_info->ident-1]);
>>>>>      }
>>>>> +
>>>>> +  free (zero_counts);
>>>>> +
>>>>>    __gcov_finalize_dyn_callgraph ();
>>>>>  }
>>>>>
>>>>> Index: libgcc/libgcov.h
>>>>> ===================================================================
>>>>> --- libgcc/libgcov.h    (revision 215230)
>>>>> +++ libgcc/libgcov.h    (working copy)
>>>>> @@ -349,6 +349,9 @@ gcov_get_sorted_import_module_array (struct gcov_i
>>>>>      ATTRIBUTE_HIDDEN;
>>>>>  GCOV_LINKAGE inline void gcov_rewrite (void);
>>>>>
>>>>> +extern void set_gcov_fn_fixed_up (int fixed_up);
>>>>> +extern int get_gcov_fn_fixed_up (void);
>>>>> +
>>>>>  /* "Counts" stored in gcda files can be a real counter value, or
>>>>>     an target address. When differentiate these two types because
>>>>>     when manipulating counts, we should only change real counter values,
>>>>> @@ -361,7 +364,13 @@ gcov_get_counter (void)
>>>>>    /* This version is for reading count values in libgcov runtime:
>>>>>       we read from gcda files.  */
>>>>>
>>>>> -  return gcov_read_counter ();
>>>>> +  if (get_gcov_fn_fixed_up ())
>>>>> +    {
>>>>> +      gcov_read_counter ();
>>>>> +      return 0;
>>>>> +    }
>>>>> +  else
>>>>> +    return gcov_read_counter ();
>>>>>  #else
>>>>>    /* This version is for gcov-tool. We read the value from memory and
>>>>>       multiply it by the merge weight.  */
>>>>> @@ -380,7 +389,13 @@ gcov_get_counter_target (void)
>>>>>    /* This version is for reading count target values in libgcov runtime:
>>>>>       we read from gcda files.  */
>>>>>
>>>>> -  return gcov_read_counter ();
>>>>> +  if (get_gcov_fn_fixed_up ())
>>>>> +    {
>>>>> +      gcov_read_counter ();
>>>>> +      return 0;
>>>>> +    }
>>>>> +  else
>>>>> +    return gcov_read_counter ();
>>>>>  #else
>>>>>    /* This version is for gcov-tool.  We read the value from memory
>>>>> and we do NOT
>>>>>       multiply it by the merge weight.  */
>>>>> Index: libgcc/libgcov-util.c
>>>>> ===================================================================
>>>>> --- libgcc/libgcov-util.c       (revision 215230)
>>>>> +++ libgcc/libgcov-util.c       (working copy)
>>>>> @@ -66,6 +66,7 @@ static void tag_lines (unsigned, unsigned);
>>>>>  static void tag_counters (unsigned, unsigned);
>>>>>  static void tag_summary (unsigned, unsigned);
>>>>>  static void tag_module_info (unsigned, unsigned);
>>>>> +static void tag_zero_fixup (unsigned, unsigned);
>>>>>
>>>>>  /* The gcov_info for the first module.  */
>>>>>  static struct gcov_info *curr_gcov_info;
>>>>> @@ -88,6 +89,8 @@ static int k_ctrs_types;
>>>>>  /* The longest length of all the filenames.  */
>>>>>  static int max_filename_len;
>>>>>
>>>>> +static int *zero_fixup_flags = NULL;
>>>>> +
>>>>>  /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
>>>>>     functions in dyn-ipa.c, which were derived from these, except
>>>>>     the versions in dyn-ipa are used when merging from another array.  */
>>>>> @@ -143,6 +146,7 @@ static const tag_format_t tag_table[] =
>>>>>    {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>>>    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
>>>>>    {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
>>>>> +  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
>>>>>    {0, NULL, NULL}
>>>>>  };
>>>>>
>>>>> @@ -169,14 +173,18 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
>>>>>       k_ctrs[i].num = 0;
>>>>>    k_ctrs_types = 0;
>>>>>
>>>>> +  if (zero_fixup_flags)
>>>>> +    {
>>>>> +      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
>>>>> +      if (get_gcov_fn_fixed_up () && verbose)
>>>>> +        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
>>>>> +    }
>>>>> +
>>>>>    curr_fn_info->key = curr_gcov_info;
>>>>>    curr_fn_info->ident = gcov_read_unsigned ();
>>>>>    curr_fn_info->lineno_checksum = gcov_read_unsigned ();
>>>>>    curr_fn_info->cfg_checksum = gcov_read_unsigned ();
>>>>>    num_fn_info++;
>>>>> -
>>>>> -  if (verbose)
>>>>> -    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
>>>>>  }
>>>>>
>>>>>  /* Handler for reading block tag.  */
>>>>> @@ -226,7 +234,13 @@ tag_counters (unsigned tag, unsigned length)
>>>>>    gcc_assert (values);
>>>>>
>>>>>    for (ix = 0; ix != n_counts; ix++)
>>>>> -    values[ix] = gcov_read_counter ();
>>>>> +    {
>>>>> +      gcov_type val = gcov_read_counter ();
>>>>> +      if (!get_gcov_fn_fixed_up ())
>>>>> +        values[ix] = val;
>>>>> +      else
>>>>> +        values[ix] = 0;
>>>>> +    }
>>>>>  }
>>>>>
>>>>>  /* Handler for reading summary tag.  */
>>>>> @@ -323,7 +337,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>>>        char *t;
>>>>>
>>>>>        if (verbose)
>>>>> -        printf ("Substitute: %s \n", input_str);
>>>>> +        fprintf (stderr, "Substitute: %s \n", input_str);
>>>>>        t = (char*) xmalloc (strlen (input_str) + 1
>>>>>            + strlen (new_str) - strlen (cur_str));
>>>>>        *p = 0;
>>>>> @@ -332,7 +346,7 @@ lipo_process_substitute_string_1 (char *input_str,
>>>>>        strcat (t, new_str);
>>>>>        strcat (t, p + strlen (cur_str));
>>>>>        if (verbose)
>>>>> -        printf ("       -->  %s\n", t);
>>>>> +        fprintf (stderr, "       -->  %s\n", t);
>>>>>        return t;
>>>>>      }
>>>>>
>>>>> @@ -397,6 +411,16 @@ tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
>>>>>      free (mod_info);
>>>>>  }
>>>>>
>>>>> +/* Handler for reading the COMDAT zero-profile fixup section.  */
>>>>> +
>>>>> +static void
>>>>> +tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
>>>>> +{
>>>>> +  gcov_unsigned_t num_fns = 0;
>>>>> +  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
>>>>> +  gcc_assert (zero_fixup_flags);
>>>>> +}
>>>>> +
>>>>>  /* Read the content of a gcda file FILENAME, and return a gcov_info
>>>>> data structure.
>>>>>     Program level summary CURRENT_SUMMARY will also be updated.  */
>>>>>
>>>>> @@ -520,6 +544,7 @@ read_gcda_file (const char *filename)
>>>>>      }
>>>>>
>>>>>    read_gcda_finalize (obj_info);
>>>>> +  free (zero_fixup_flags);
>>>>>    gcov_close ();
>>>>>
>>>>>    return obj_info;
>>>>> @@ -1099,7 +1124,7 @@ gcov_profile_scale (struct gcov_info *profile, flo
>>>>>    unsigned f_ix;
>>>>>
>>>>>    if (verbose)
>>>>> -    fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>>>> +    fnotice (stderr, "scale_factor is %f or %d/%d\n", scale_factor, n, d);
>>>>>
>>>>>    /* Scaling the counters.  */
>>>>>    for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
>>>>> @@ -1167,7 +1192,7 @@ gcov_profile_normalize (struct gcov_info *profile,
>>>>>    scale_factor = (float)max_val / curr_max_val;
>>>>>  #if !defined (_WIN32)
>>>>>    if (verbose)
>>>>> -    fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
>>>>> +    fnotice (stderr, "max_val is %lld\n", (long long) curr_max_val);
>>>>>  #endif
>>>>>
>>>>>    return gcov_profile_scale (profile, scale_factor, 0, 0);
>>>>>
>>>>> --
>>>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>>>
>>>
>>>
>>> --
>>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Nathan Sidwell Sept. 21, 2014, 12:48 p.m. UTC | #6
On 09/13/14 00:31, Teresa Johnson wrote:
> This patch addresses issues when running gcov-tool after performing
> COMDAT fixup during dyn-ipa. Functions that were previously all zero
> counts are marked, and the counts are discarded when being read in
> by gcov-tool before recalculating module groups and summary info.

I'm not sure I understand the design here.  A few years ago I modified the gcov 
data structures to cope with comdat, but failed to get the compiler side changed 
to utilize that (without breaking firefox builds).  You'll see each function 
record (struct gcov_fn_info) has:

   const struct gcov_info *key;		/* comdat key */

the intent is that that points to the gcov_info object of the object file 
containing the live version of the function.  I couldn't quite get this to work 
though -- it involves emitting a function's gcov_fn_info decl in the same comdat 
group as the function itself.

You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.

Are you making use of this machinery, or inventing new machinery?

nathan
Xinliang David Li Sept. 21, 2014, 5:58 p.m. UTC | #7
On Sun, Sep 21, 2014 at 5:48 AM, Nathan Sidwell <nathan@acm.org> wrote:
> On 09/13/14 00:31, Teresa Johnson wrote:
>>
>> This patch addresses issues when running gcov-tool after performing
>> COMDAT fixup during dyn-ipa. Functions that were previously all zero
>> counts are marked, and the counts are discarded when being read in
>> by gcov-tool before recalculating module groups and summary info.
>
>
> I'm not sure I understand the design here.  A few years ago I modified the
> gcov data structures to cope with comdat, but failed to get the compiler
> side changed to utilize that (without breaking firefox builds).  You'll see
> each function record (struct gcov_fn_info) has:
>
>   const struct gcov_info *key;          /* comdat key */
>
> the intent is that that points to the gcov_info object of the object file
> containing the live version of the function.  I couldn't quite get this to
> work though -- it involves emitting a function's gcov_fn_info decl in the
> same comdat group as the function itself.

Another problem is that comdat functions may have different CFGs due
to different early inline decisions. Comdatting gcov counters can lead
to problems in profile use. Not comdatting profile counters have
another advantage -- it allows context sensitive profiling for comdat
function inline instances (IPA-inline).

>
> You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.
>
> Are you making use of this machinery, or inventing new machinery?

Teresa's method is a different machinery -- it tries to propagate
profile data from the selected comdat copy + inline instance copies to
comdat copies with zero counts.

David


>
> nathan
Nathan Sidwell Sept. 22, 2014, 8:36 a.m. UTC | #8
On 09/21/14 18:58, Xinliang David Li wrote:

>> the intent is that that points to the gcov_info object of the object file
>> containing the live version of the function.  I couldn't quite get this to
>> work though -- it involves emitting a function's gcov_fn_info decl in the
>> same comdat group as the function itself.
>
> Another problem is that comdat functions may have different CFGs due
> to different early inline decisions. Comdatting gcov counters can lead
> to problems in profile use. Not comdatting profile counters have
> another advantage -- it allows context sensitive profiling for comdat
> function inline instances (IPA-inline).

IIRC early inlining is done before the counters are created.  You're right later 
inlining may be a problem, and require a non-comdat set of cloned counters.   I 
can't recall exactly at what stage the counters are now inserted relative to 
inlining.  The CFG machinery had a number of significant changes while, and 
shortly after, I was working on this.

>> You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.
>>
>> Are you making use of this machinery, or inventing new machinery?
>
> Teresa's method is a different machinery -- it tries to propagate
> profile data from the selected comdat copy + inline instance copies to
> comdat copies with zero counts.

It'd be preferrable to complete the mechanism I outline above, rather than have 
a competing mechanism.  Also, this patch  is in effect lying because the data 
then makes it look like the unselected comdat instances are in fact being 
executed -- looking at the whole program it's going to be harder to understand 
whether the different inline instances are being executed multiple times, or are 
duplicate data.  Does the gcov user output indicate this subtlety in some way?

nathan
Teresa Johnson Sept. 22, 2014, 3:04 p.m. UTC | #9
On Mon, Sep 22, 2014 at 1:36 AM, Nathan Sidwell <nathan@acm.org> wrote:
> On 09/21/14 18:58, Xinliang David Li wrote:
>
>>> the intent is that that points to the gcov_info object of the object file
>>> containing the live version of the function.  I couldn't quite get this
>>> to
>>> work though -- it involves emitting a function's gcov_fn_info decl in the
>>> same comdat group as the function itself.
>>
>>
>> Another problem is that comdat functions may have different CFGs due
>> to different early inline decisions. Comdatting gcov counters can lead
>> to problems in profile use. Not comdatting profile counters have
>> another advantage -- it allows context sensitive profiling for comdat
>> function inline instances (IPA-inline).
>
>
> IIRC early inlining is done before the counters are created.  You're right
> later inlining may be a problem, and require a non-comdat set of cloned
> counters.   I can't recall exactly at what stage the counters are now
> inserted relative to inlining.  The CFG machinery had a number of
> significant changes while, and shortly after, I was working on this.
>
>>> You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.
>>>
>>> Are you making use of this machinery, or inventing new machinery?
>>
>>
>> Teresa's method is a different machinery -- it tries to propagate
>> profile data from the selected comdat copy + inline instance copies to
>> comdat copies with zero counts.
>
>
> It'd be preferrable to complete the mechanism I outline above, rather than
> have a competing mechanism.

I don't think the above mechanism helps the problem my patches are
trying to solve. Unless we are in whole-program mode, which we don't
use, the only profiles available at profile-use time are those for the
given module (and any other modules in the same module group in LIPO
mode). If the COMDAT copy selected by the linker in the profile-gen
binary is in a different module, we would see all-zero counts when
compiling modules containing the other copies. I had submitted some
patches to trunk awhile back in the 4.9 time frame to help deal with
this by using estimated frequencies for zero-count COMDAT copies, and
applying scaled counts when we inline them, but it is an imperfect
solution.

The approach we now take for LIPO builds is to propagate the counts
for the profiled copy of the COMDAT to other modules. (Additionally
the indirect call profiling we perform in LIPO mode would point to a
module that we didn't have access to, which is a related issue that
the COMDAT fixups we perform at the end of the LIPO profiling run are
trying to solve.)

> Also, this patch  is in effect lying because
> the data then makes it look like the unselected comdat instances are in fact
> being executed -- looking at the whole program it's going to be harder to
> understand whether the different inline instances are being executed
> multiple times, or are duplicate data.  Does the gcov user output indicate
> this subtlety in some way?

Correct in that it makes it look like these copies were executed. This
was causing some issues when we rewrote/merged profiles with
gcov-tool, which essentially operates in whole-program mode. To handle
this, this patch marks the modified (previously all-zero) copies in
the gcda file. So now gcov-tool can handle them appropriately (clear
them on read before doing any analysis), and gcov-dump will flag them.
My patch does not do anything special for these routines when they are
read into the profile-use build, because we do want the propagated
counts during optimization. Possibly in whole-program mode they should
be cleared on read just as in gcov-tool, or they could be flagged in
some way for downstream phases, but it is not a compilation mode we
are using so I have not experimented.

Thanks,
Teresa

>
> nathan
Xinliang David Li Sept. 22, 2014, 6:18 p.m. UTC | #10
On Mon, Sep 22, 2014 at 1:36 AM, Nathan Sidwell <nathan@acm.org> wrote:
> On 09/21/14 18:58, Xinliang David Li wrote:
>
>>> the intent is that that points to the gcov_info object of the object file
>>> containing the live version of the function.  I couldn't quite get this
>>> to
>>> work though -- it involves emitting a function's gcov_fn_info decl in the
>>> same comdat group as the function itself.
>>
>>
>> Another problem is that comdat functions may have different CFGs due
>> to different early inline decisions. Comdatting gcov counters can lead
>> to problems in profile use. Not comdatting profile counters have
>> another advantage -- it allows context sensitive profiling for comdat
>> function inline instances (IPA-inline).
>
>
> IIRC early inlining is done before the counters are created.

Yes, and that will be the cause of problem for coverage mismatch when
COMDATing profile counters -- due to difference in early inline
decisions, the counter array created may be different across modules.

>You're right
> later inlining may be a problem, and require a non-comdat set of cloned
> counters.   I can't recall exactly at what stage the counters are now
> inserted relative to inlining.  The CFG machinery had a number of
> significant changes while, and shortly after, I was working on this.

After early inining and before ipa inline.  The early inline can lead
to the coverage mismatch, and the latter can lead to loss of profile
precision with the counter comdat.


David

>
>>> You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.
>>>
>>> Are you making use of this machinery, or inventing new machinery?
>>
>>
>> Teresa's method is a different machinery -- it tries to propagate
>> profile data from the selected comdat copy + inline instance copies to
>> comdat copies with zero counts.
>
>
> It'd be preferrable to complete the mechanism I outline above, rather than
> have a competing mechanism.  Also, this patch  is in effect lying because
> the data then makes it look like the unselected comdat instances are in fact
> being executed -- looking at the whole program it's going to be harder to
> understand whether the different inline instances are being executed
> multiple times, or are duplicate data.  Does the gcov user output indicate
> this subtlety in some way?
>
> nathan
Xinliang David Li Sept. 22, 2014, 6:22 p.m. UTC | #11
On Mon, Sep 22, 2014 at 8:04 AM, Teresa Johnson <tejohnson@google.com> wrote:
> On Mon, Sep 22, 2014 at 1:36 AM, Nathan Sidwell <nathan@acm.org> wrote:
>> On 09/21/14 18:58, Xinliang David Li wrote:
>>
>>>> the intent is that that points to the gcov_info object of the object file
>>>> containing the live version of the function.  I couldn't quite get this
>>>> to
>>>> work though -- it involves emitting a function's gcov_fn_info decl in the
>>>> same comdat group as the function itself.
>>>
>>>
>>> Another problem is that comdat functions may have different CFGs due
>>> to different early inline decisions. Comdatting gcov counters can lead
>>> to problems in profile use. Not comdatting profile counters have
>>> another advantage -- it allows context sensitive profiling for comdat
>>> function inline instances (IPA-inline).
>>
>>
>> IIRC early inlining is done before the counters are created.  You're right
>> later inlining may be a problem, and require a non-comdat set of cloned
>> counters.   I can't recall exactly at what stage the counters are now
>> inserted relative to inlining.  The CFG machinery had a number of
>> significant changes while, and shortly after, I was working on this.
>>
>>>> You'll see the checking of gfi_ptr->key != gi_ptr in libgcov-driver.c.
>>>>
>>>> Are you making use of this machinery, or inventing new machinery?
>>>
>>>
>>> Teresa's method is a different machinery -- it tries to propagate
>>> profile data from the selected comdat copy + inline instance copies to
>>> comdat copies with zero counts.
>>
>>
>> It'd be preferrable to complete the mechanism I outline above, rather than
>> have a competing mechanism.
>
> I don't think the above mechanism helps the problem my patches are
> trying to solve. Unless we are in whole-program mode, which we don't
> use, the only profiles available at profile-use time are those for the
> given module (and any other modules in the same module group in LIPO
> mode).

It will be available for profile-use with Nathan's mechanism -- but
the problem is that the selected comdat copy (written to all gcda
files needed) may not actually usable.

>If the COMDAT copy selected by the linker in the profile-gen
> binary is in a different module, we would see all-zero counts when
> compiling modules containing the other copies.

This is the current behavior when profile counters are not in COMDAT
(and not fixed up by your method). With Nathan's method, the COMDAT
copy is the only one available for all modules referencing it.

David

>I had submitted some
> patches to trunk awhile back in the 4.9 time frame to help deal with
> this by using estimated frequencies for zero-count COMDAT copies, and
> applying scaled counts when we inline them, but it is an imperfect
> solution.
>
> The approach we now take for LIPO builds is to propagate the counts
> for the profiled copy of the COMDAT to other modules. (Additionally
> the indirect call profiling we perform in LIPO mode would point to a
> module that we didn't have access to, which is a related issue that
> the COMDAT fixups we perform at the end of the LIPO profiling run are
> trying to solve.)
>
>> Also, this patch  is in effect lying because
>> the data then makes it look like the unselected comdat instances are in fact
>> being executed -- looking at the whole program it's going to be harder to
>> understand whether the different inline instances are being executed
>> multiple times, or are duplicate data.  Does the gcov user output indicate
>> this subtlety in some way?
>
> Correct in that it makes it look like these copies were executed. This
> was causing some issues when we rewrote/merged profiles with
> gcov-tool, which essentially operates in whole-program mode. To handle
> this, this patch marks the modified (previously all-zero) copies in
> the gcda file. So now gcov-tool can handle them appropriately (clear
> them on read before doing any analysis), and gcov-dump will flag them.
> My patch does not do anything special for these routines when they are
> read into the profile-use build, because we do want the propagated
> counts during optimization. Possibly in whole-program mode they should
> be cleared on read just as in gcov-tool, or they could be flagged in
> some way for downstream phases, but it is not a compilation mode we
> are using so I have not experimented.
>
> Thanks,
> Teresa
>
>>
>> nathan
>
>
>
> --
> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
Nathan Sidwell Oct. 1, 2014, 4:28 a.m. UTC | #12
On 09/22/14 08:04, Teresa Johnson wrote:


> The approach we now take for LIPO builds is to propagate the counts

LIPO?

> for the profiled copy of the COMDAT to other modules. (Additionally
> the indirect call profiling we perform in LIPO mode would point to a
> module that we didn't have access to, which is a related issue that
> the COMDAT fixups we perform at the end of the LIPO profiling run are
> trying to solve.)

I'm presuming then that all modules of relevance are compiled with coverage 
information?  Propagating counters from one module to another still seems wrong. 
  It'll make it look like the function's executed more times than it really is 
(per copy).  Plus in the general case one can't rely on the copies being 
identical implementations -- COMDAT only requires they be functionally equivalent.


> Correct in that it makes it look like these copies were executed. This
> was causing some issues when we rewrote/merged profiles with
> gcov-tool, which essentially operates in whole-program mode. To handle
> this, this patch marks the modified (previously all-zero) copies in
> the gcda file. So now gcov-tool can handle them appropriately (clear
> them on read before doing any analysis), and gcov-dump will flag them.

Um, I don't understand why you're doing this then?  If gcov is ignoring the 
copies, what is the purpose?

nathan
Xinliang David Li Oct. 1, 2014, 4:49 a.m. UTC | #13
On Tue, Sep 30, 2014 at 9:28 PM, Nathan Sidwell <nathan@acm.org> wrote:
> On 09/22/14 08:04, Teresa Johnson wrote:
>
>
>> The approach we now take for LIPO builds is to propagate the counts
>
> LIPO?
>

See https://gcc.gnu.org/wiki/LightweightIpo It is a feature in google
GCC branches.

David

>> for the profiled copy of the COMDAT to other modules. (Additionally
>> the indirect call profiling we perform in LIPO mode would point to a
>> module that we didn't have access to, which is a related issue that
>> the COMDAT fixups we perform at the end of the LIPO profiling run are
>> trying to solve.)
>
>
> I'm presuming then that all modules of relevance are compiled with coverage
> information?  Propagating counters from one module to another still seems
> wrong.  It'll make it look like the function's executed more times than it
> really is (per copy).  Plus in the general case one can't rely on the copies
> being identical implementations -- COMDAT only requires they be functionally
> equivalent.
>
>
>> Correct in that it makes it look like these copies were executed. This
>> was causing some issues when we rewrote/merged profiles with
>> gcov-tool, which essentially operates in whole-program mode. To handle
>> this, this patch marks the modified (previously all-zero) copies in
>> the gcda file. So now gcov-tool can handle them appropriately (clear
>> them on read before doing any analysis), and gcov-dump will flag them.
>
>
> Um, I don't understand why you're doing this then?  If gcov is ignoring the
> copies, what is the purpose?
>
> nathan
Teresa Johnson Oct. 1, 2014, 4:55 a.m. UTC | #14
On Tue, Sep 30, 2014 at 9:28 PM, Nathan Sidwell <nathan@acm.org> wrote:
> On 09/22/14 08:04, Teresa Johnson wrote:
>
>
>> The approach we now take for LIPO builds is to propagate the counts
>
>
> LIPO?

Sorry, Lightweight IPO (https://gcc.gnu.org/wiki/LightweightIpo),
implemented and used on google branches. We don't use LTO for
scalability reasons. With LIPO the decisions about how to group
modules for cross-module optimization are done at the end of the
profiling run, which is where I have implemented these COMDAT fixups.

>
>> for the profiled copy of the COMDAT to other modules. (Additionally
>> the indirect call profiling we perform in LIPO mode would point to a
>> module that we didn't have access to, which is a related issue that
>> the COMDAT fixups we perform at the end of the LIPO profiling run are
>> trying to solve.)
>
>
> I'm presuming then that all modules of relevance are compiled with coverage
> information?  Propagating counters from one module to another still seems
> wrong.  It'll make it look like the function's executed more times than it
> really is (per copy).

That's already the case with the copy selected by the linker, which
contains counts from all out-of-line copies of the COMDAT. This just
enables other modules to have access to the same profile data for
their copy of the COMDAT when they are compiled. It is correct that if
you sum together all the counts across all copies of the COMDAT, it
would look hotter than it should. But in this case we essentially only
see one copy when compiling each module.


> Plus in the general case one can't rely on the copies
> being identical implementations -- COMDAT only requires they be functionally
> equivalent.

We only do the copying when the cfg and line checksums match, so they
are most likely identical.

>
>
>> Correct in that it makes it look like these copies were executed. This
>> was causing some issues when we rewrote/merged profiles with
>> gcov-tool, which essentially operates in whole-program mode. To handle
>> this, this patch marks the modified (previously all-zero) copies in
>> the gcda file. So now gcov-tool can handle them appropriately (clear
>> them on read before doing any analysis), and gcov-dump will flag them.
>
>
> Um, I don't understand why you're doing this then?  If gcov is ignoring the
> copies, what is the purpose?

The new gcov-tool is ignoring the copies, when it reads in the
profiles and redoes the LIPO call graph analysis (on the google
branch). But the compiler does not ignore the copied counts. So the
compiler sees non-zero counts for these COMDAT routines and can
optimize more effectively.

In any case, David pointed out that with your approach we wouldn't
need to do the copying that I have implemented here for LIPO.
Teresa

>
> nathan
diff mbox

Patch

Index: gcc/coverage.c
===================================================================
--- gcc/coverage.c      (revision 215230)
+++ gcc/coverage.c      (working copy)
@@ -820,6 +820,14 @@  read_counts_file (const char *da_file_name, unsign
             free (build_info_strings[i]);
           free (build_info_strings);
         }
+      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
+        {
+          /* Zero-profile fixup flags are not used by the compiler, read and
+             ignore.  */
+          gcov_unsigned_t num_fn;
+          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
(length, &num_fn);
+          free (zero_fixup_flags);
+        }
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
        {
          counts_entry_t **slot, *entry, elt;
Index: gcc/gcov.c
===================================================================
--- gcc/gcov.c  (revision 215230)
+++ gcc/gcov.c  (working copy)
@@ -1441,6 +1441,12 @@  read_count_file (function_t *fns)
             free (build_info_strings[i]);
           free (build_info_strings);
         }
+      else if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
+        {
+          gcov_unsigned_t num_fn;
+          int *zero_fixup_flags = gcov_read_comdat_zero_fixup
(length, &num_fn);
+          free (zero_fixup_flags);
+        }
       else if (tag == GCOV_TAG_FUNCTION && !length)
        ; /* placeholder  */
       else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
Index: gcc/gcov-dump.c
===================================================================
--- gcc/gcov-dump.c     (revision 215230)
+++ gcc/gcov-dump.c     (working copy)
@@ -42,6 +42,7 @@  static void tag_summary (const char *, unsigned, u
 static void tag_module_info (const char *, unsigned, unsigned);
 static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
                                const struct gcov_ctr_summary *summary);
+static void tag_zero_fixup (const char *, unsigned, unsigned);
 static void tag_build_info (const char *, unsigned, unsigned);
 extern int main (int, char **);

@@ -57,6 +58,9 @@  static int flag_dump_positions = 0;
 static int flag_dump_aux_modules_only = 0;
 static int flag_dump_working_sets = 0;

+static unsigned num_fn_info;
+static int *zero_fixup_flags = NULL;
+
 static const struct option options[] =
 {
   { "help",                 no_argument,       NULL, 'h' },
@@ -79,6 +83,7 @@  static const tag_format_t tag_table[] =
   {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
   {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
   {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
+  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
   {GCOV_TAG_BUILD_INFO, "BUILD INFO", tag_build_info},
   {0, NULL, NULL}
 };
@@ -274,6 +279,8 @@  dump_gcov_file (const char *filename)
     printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
   }

+  num_fn_info = 0;
+
   while (1)
     {
       gcov_position_t base, position = gcov_position ();
@@ -341,6 +348,7 @@  dump_gcov_file (const char *filename)
          break;
        }
     }
+  free (zero_fixup_flags);
   gcov_close ();
 }

@@ -354,7 +362,9 @@  tag_function (const char *filename ATTRIBUTE_UNUSE
     printf (" placeholder");
   else
     {
-      printf (" ident=%u", gcov_read_unsigned ());
+      int had_fixup = zero_fixup_flags && zero_fixup_flags[num_fn_info];
+      printf (" ident=%u%s", gcov_read_unsigned (),
+              had_fixup ? " (Was 0-count COMDAT)" : "");
       printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
       printf (", cfg_checksum=0x%08x", gcov_read_unsigned ());

@@ -369,6 +379,7 @@  tag_function (const char *filename ATTRIBUTE_UNUSE
          printf (":%u", gcov_read_unsigned ());
        }
     }
+  num_fn_info++;
 }

 static void
@@ -600,6 +611,32 @@  tag_module_info (const char *filename ATTRIBUTE_UN
 }

 static void
+tag_zero_fixup (const char *filename,
+                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
+{
+  gcov_unsigned_t num_fns = 0;
+  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
+  if (!zero_fixup_flags)
+    {
+      printf ("%s:error reading zero fixup flags\n", filename);
+      return;
+    }
+  printf (" num_fns=%u", num_fns);
+  for (unsigned i = 0; i < num_fns; i++)
+    {
+      if (!(i % 32))
+        {
+          printf ("\n");
+          print_prefix (filename, 0, 0);
+          printf ("\t\t");
+        }
+      if (!(i % 8))
+        printf ("%s%4u:", (i%32)?" ":"", i);
+      printf ("%u", zero_fixup_flags[i]);
+    }
+}
+
+static void
 tag_build_info (const char *filename,
                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
 {
Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c       (revision 215230)
+++ gcc/gcov-io.c       (working copy)
@@ -691,6 +691,36 @@  gcov_read_summary (struct gcov_summary *summary)
     }
 }

+/* Read LENGTH words (unsigned type) from a zero profile fixup record with the
+   number of function flags saved in NUM_FNS.  Returns the int flag
array, which
+   should be deallocated by caller, or NULL on error.  */
+
+GCOV_LINKAGE int *
+gcov_read_comdat_zero_fixup (gcov_unsigned_t length,
+                             gcov_unsigned_t *num_fns)
+{
+  unsigned ix, f_ix;
+  gcov_unsigned_t num = gcov_read_unsigned ();
+  /* The length consists of 1 word to hold the number of functions,
+     plus enough 32-bit words to hold 1 bit/function.  */
+  gcc_assert ((num + 31) / 32 + 1 == length);
+  int *zero_fixup_flags = (int *) xcalloc (num, sizeof (int));
+  for (ix = 0; ix < length - 1; ix++)
+    {
+      gcov_unsigned_t bitvector = gcov_read_unsigned ();
+      f_ix = ix * 32;
+      while (bitvector)
+        {
+          if (bitvector & 0x1)
+            zero_fixup_flags[f_ix] = 1;
+          f_ix++;
+          bitvector >>= 1;
+        }
+    }
+  *num_fns = num;
+  return zero_fixup_flags;
+}
+
 /* Read NUM_STRINGS strings (as an unsigned array) in STRING_ARRAY, and return
    the number of words read.  */

Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h       (revision 215230)
+++ gcc/gcov-io.h       (working copy)
@@ -129,7 +129,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
    blocks they are for.

    The data file contains the following records.
-        data: {unit summary:program* build_info function-data*}*
+        data: {unit summary:program* build_info zero_fixup function-data*}*
        unit: header int32:checksum
         function-data: announce_function present counts
        announce_function: header int32:ident
@@ -142,6 +142,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
         histogram: {int32:bitvector}8 histogram-buckets*
         histogram-buckets: int32:num int64:min int64:sum
         build_info: string:info*
+        zero_fixup: int32:num int32:bitvector*

    The ANNOUNCE_FUNCTION record is the same as that in the note file,
    but without the source location.  The COUNTS gives the
@@ -158,6 +159,12 @@  see the files COPYING3 and COPYING.RUNTIME respect
    build.  For example, it can be used to include source revision
    information that is useful in diagnosing profile mis-matches.

+   ZERO_FIXUP record contains a count of functions in the gcda file
+   and an array of bitvectors indexed by the function index's in the
+   function-data section. Each bit flags whether the function was a
+   COMDAT that had all-zero profiles that was fixed up by dyn-ipa
+   using profiles from functions with matching checksums in other modules.
+
    This file is included by both the compiler, gcov tools and the
    runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
    distinguish which case is which.  If IN_LIBGCOV is nonzero,
@@ -261,6 +268,9 @@  typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
 #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000) /* Obsolete */
 #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
+#define GCOV_TAG_COMDAT_ZERO_FIXUP ((gcov_unsigned_t)0xa9000000)
+/* Ceiling divide by 32 bit word size, plus one word to hold NUM.  */
+#define GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH(NUM) (1 + (NUM + 31) / 32)
 #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
         (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
 #define GCOV_TAG_BUILD_INFO ((gcov_unsigned_t)0xa7000000)
@@ -441,6 +451,9 @@  GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
 GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE int *gcov_read_comdat_zero_fixup (gcov_unsigned_t,
+                                               gcov_unsigned_t *)
+    ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE char **gcov_read_build_info (gcov_unsigned_t, gcov_unsigned_t *)
   ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE const char *gcov_read_string (void);
Index: libgcc/dyn-ipa.c
===================================================================
--- libgcc/dyn-ipa.c    (revision 215230)
+++ libgcc/dyn-ipa.c    (working copy)
@@ -107,8 +107,9 @@  struct checksum_alias
   struct checksum_alias *next_alias;
   gcov_type guid;
   const struct gcov_fn_info *fi_ptr;
-  /* Does this function have all-zero arc counts?  */
-  int zero_counts;
+  /* Non-NULL pointer to flag if this function has all-zero arc counts, to be
+     set if we perform fixup.  */
+  int *zero_count_fixup;
 };

 /* Module info is stored in dyn_caph->sup_modules
@@ -178,10 +179,10 @@  extern gcov_unsigned_t __gcov_lipo_merge_modu_edge
 extern gcov_unsigned_t __gcov_lipo_weak_inclusion;

 #if defined(inhibit_libc)
-void __gcov_build_callgraph (void) {}
+void __gcov_build_callgraph (int **zero_counts) {}
 #else

-int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
+int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
 void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
 static void gcov_dump_callgraph (gcov_type);
 static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
@@ -378,18 +379,19 @@  lineno_checksum_get_key (const void *p)
 }

 /* Create a new checksum_alias struct for function with GUID, FI_PTR,
-   and ZERO_COUNTS flag.  Prepends to list NEXT and returns new struct.  */
+   and ZERO_COUNT_FIXUP flag pointer.  Prepends to list NEXT and returns
+   new struct.  */

 static struct checksum_alias *
 new_checksum_alias (gcov_type guid, const struct gcov_fn_info *fi_ptr,
-                    int zero_counts,
+                    int *zero_count_fixup,
                     struct checksum_alias *next)
 {
   struct checksum_alias *alias = XNEW (struct checksum_alias);
   alias->next_alias = next;
   alias->fi_ptr = fi_ptr;
   alias->guid = guid;
-  alias->zero_counts = zero_counts;
+  alias->zero_count_fixup = zero_count_fixup;
   return alias;
 }

@@ -407,11 +409,12 @@  find_cfg_checksum (struct checksum_alias_info *lis
 }

 /* Insert a new checksum_alias struct into LIST for function with
-   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag.  */
+   CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNT_FIXUP
+   flag pointer.  */

 static struct checksum_alias_info *
 cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
-                     const struct gcov_fn_info *fi_ptr, int zero_counts,
+                     const struct gcov_fn_info *fi_ptr, int *zero_count_fixup,
                      struct checksum_alias_info *list)
 {
   struct checksum_alias_info *alias_info;
@@ -419,7 +422,8 @@  cfg_checksum_insert (unsigned cfg_checksum, gcov_t
   if (alias_info)
     {
       gcc_assert (alias_info->alias_list);
-      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
+      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
+                                                   zero_count_fixup,
                                                    alias_info->alias_list);
       return list;
     }
@@ -428,7 +432,8 @@  cfg_checksum_insert (unsigned cfg_checksum, gcov_t
       alias_info = XNEW (struct checksum_alias_info);
       alias_info->next_cfg_checksum = list;
       alias_info->cfg_checksum = cfg_checksum;
-      alias_info->alias_list = new_checksum_alias (guid, fi_ptr, zero_counts,
+      alias_info->alias_list = new_checksum_alias (guid, fi_ptr,
+                                                   zero_count_fixup,
                                                    NULL);
       return alias_info;
     }
@@ -436,12 +441,12 @@  cfg_checksum_insert (unsigned cfg_checksum, gcov_t

 /* Insert a new checksum_alias struct into lineno_pointer_sets for
function with
    LINENO_CHECKSUM and CFG_CHECKSUM with associated GUID, FI_PTR, and
-   ZERO_COUNTS flag.  */
+   ZERO_COUNT_FIXUP flag pointer.  */

 static void
 checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
                      gcov_type guid, const struct gcov_fn_info *fi_ptr,
-                     int zero_counts)
+                     int *zero_count_fixup)
 {
   struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
   if (!p)
@@ -452,7 +457,7 @@  checksum_set_insert (unsigned lineno_checksum, uns
   if (*m)
     {
       (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
-                                                     fi_ptr, zero_counts,
+                                                     fi_ptr, zero_count_fixup,
                                                      (*m)->cfg_checksum_list);
     }
   else
@@ -460,7 +465,8 @@  checksum_set_insert (unsigned lineno_checksum, uns
       *m = XNEW (struct lineno_checksum_alias);
       (*m)->lineno_checksum = lineno_checksum;
       (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
-                                                     fi_ptr,
zero_counts, NULL);
+                                                     fi_ptr, zero_count_fixup,
+                                                     NULL);
       p->n_elements++;
     }
 }
@@ -801,10 +807,10 @@  gcov_build_callgraph_ic_fn (struct dyn_cgraph_node
     }
 }

-/* Build the dynamic call graph.  */
+/* Build the dynamic call graph and update ZERO_COUNTS flags.  */

 static void
-gcov_build_callgraph (void)
+gcov_build_callgraph (int **zero_counts)
 {
   struct gcov_info *gi_ptr;
   unsigned m_ix;
@@ -852,9 +858,19 @@  static void
                   if (total_arc_count != 0)
                     the_dyn_call_graph.num_nodes_executed++;
                   if (fixup_type)
-                    checksum_set_insert (fi_ptr->lineno_checksum,
-                                         fi_ptr->cfg_checksum, caller->guid,
-                                         fi_ptr, total_arc_count == 0);
+                    {
+                      int *zero_count_fixup = NULL;
+                      /* Passing in a non-NULL zero_count_fixup pointer
+                         indicates that the counts were all zero for this
+                         function, and the fixup routine will set the flag
+                         if the function's counters are updated to non-zero
+                         values.  */
+                      if (total_arc_count == 0)
+                        zero_count_fixup = &zero_counts[m_ix][f_ix];
+                      checksum_set_insert (fi_ptr->lineno_checksum,
+                                           fi_ptr->cfg_checksum, caller->guid,
+                                           fi_ptr, zero_count_fixup);
+                    }
                 }
               ci_ptr++;
             }
@@ -1251,7 +1267,14 @@  gcov_collect_imported_modules (const void *value,
   out_array = (struct gcov_import_mod_array *) data1;

   if (m->imp_mod != out_array->importing_module)
+  {
     out_array->imported_modules[out_array->len++] = m;
+    /* Sanity check that the importing (primary) module is not
+       actually the same as the new aux module. This could happen if
+       we accidentally read in the same gcda file twice.  */
+    gcc_assert (m->imp_mod->mod_info->ident !=
+                out_array->importing_module->mod_info->ident);
+  }

   return 1;
 }
@@ -2957,7 +2980,7 @@  gcov_fixup_counters_checksum (const struct checksu
   for (alias = info->alias_list; alias;
        alias = alias->next_alias)
     {
-      if (alias->zero_counts)
+      if (alias->zero_count_fixup)
         {
           found = 1;
           break;
@@ -2972,7 +2995,7 @@  gcov_fixup_counters_checksum (const struct checksu
   for (alias = info->alias_list; alias;
        alias = alias->next_alias)
     {
-      if (alias->zero_counts)
+      if (alias->zero_count_fixup)
         continue;
       merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
       found = 1;
@@ -2990,9 +3013,10 @@  gcov_fixup_counters_checksum (const struct checksu
   for (alias = info->alias_list; alias;
        alias = alias->next_alias)
     {
-      if (!alias->zero_counts)
+      if (!alias->zero_count_fixup)
         continue;
       copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
+      *alias->zero_count_fixup = 1;
     }

   return 1;
@@ -3040,11 +3064,13 @@  gcov_fixup_zero_counters (void)
   return changed;
 }

-/* Compute module groups needed for L-IPO compilation.  Returns 1 if any
-   counter fixups were applied, requiring a profile rewrite, 0 otherwise.  */
+/* Compute module groups needed for L-IPO compilation.  The ZERO_COUNTS
+   flags are set for functions with zero count fixups applied. Returns 1
+   if any counter fixups were applied, requiring a profile rewrite,
+   0 otherwise.  */

 int
-__gcov_compute_module_groups (void)
+__gcov_compute_module_groups (int **zero_counts)
 {
   gcov_type cut_off_count;
   char *seed = getenv ("LIPO_RANDOM_GROUPING");
@@ -3091,7 +3117,7 @@  int
     fixup_type = atoi (do_fixup);

   /* First compute dynamic call graph.  */
-  gcov_build_callgraph ();
+  gcov_build_callgraph (zero_counts);

   cut_off_count = gcov_compute_cutoff_count ();

Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c     (revision 215230)
+++ libgcc/libgcov-driver.c     (working copy)
@@ -55,7 +55,7 @@  static gcov_unsigned_t gcov_cur_module_id = 0;


 /* Dynamic call graph build and form module groups.  */
-int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
+int __gcov_compute_module_groups (int **zero_counts) ATTRIBUTE_HIDDEN;
 void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;

 /* The following functions can be called from outside of this file.  */
@@ -129,6 +129,24 @@  set_gcov_list (struct gcov_info *head)
   __gcov_list = head;
 }

+/* Flag if the current function being read was marked as having fixed-up
+   zero counters.  */
+static int __gcov_curr_fn_fixed_up;
+
+/* Set function fixed up flag.  */
+void
+set_gcov_fn_fixed_up (int fixed_up)
+{
+  __gcov_curr_fn_fixed_up = fixed_up;
+}
+
+/* Return function fixed up flag.  */
+int
+get_gcov_fn_fixed_up (void)
+{
+  return __gcov_curr_fn_fixed_up;
+}
+
 /* Size of the longest file name. */
 /* We need to expose this static variable when compiling for gcov-tool.  */
 #ifndef IN_GCOV_TOOL
@@ -564,6 +582,7 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
   int error = 0;
   struct gcov_fn_buffer **fn_tail = &fn_buffer;
   struct gcov_summary_buffer **sum_tail = &sum_buffer;
+  int *zero_fixup_flags = NULL;

   length = gcov_read_unsigned ();
   if (!gcov_version (gi_ptr, length, gi_filename))
@@ -625,6 +644,21 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
       tag = gcov_read_unsigned ();
     }

+  if (tag == GCOV_TAG_COMDAT_ZERO_FIXUP)
+    {
+      length = gcov_read_unsigned ();
+      gcov_unsigned_t num_fns = 0;
+      zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
+      if (!zero_fixup_flags)
+        {
+          gcov_error ("profiling:%s:Error reading zero fixup flags\n",
+                      gi_filename);
+          return -1;
+        }
+
+      tag = gcov_read_unsigned ();
+    }
+
   /* Merge execution counts for each function.  */
   for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
        f_ix++, tag = gcov_read_unsigned ())
@@ -658,6 +692,9 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
           continue;
         }

+      if (zero_fixup_flags)
+        set_gcov_fn_fixed_up (zero_fixup_flags[f_ix]);
+
       length = gcov_read_unsigned ();
       if (length != gfi_ptr->ident)
         goto read_mismatch;
@@ -689,6 +726,7 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
       if ((error = gcov_is_error ()))
         goto read_error;
     }
+  free (zero_fixup_flags);

   if (tag && tag != GCOV_TAG_MODULE_INFO)
     {
@@ -706,6 +744,34 @@  read_error:
   return -1;
 }

+
+/* Write NUM_FNS ZERO_COUNTS fixup flags to a gcda file starting from its
+   current location.  */
+
+static void
+gcov_write_comdat_zero_fixup (int *zero_counts, unsigned num_fns)
+{
+  unsigned f_ix;
+  gcov_unsigned_t len = GCOV_TAG_COMDAT_ZERO_FIXUP_LENGTH (num_fns);
+  gcov_write_tag_length (GCOV_TAG_COMDAT_ZERO_FIXUP, len);
+
+  gcov_write_unsigned (num_fns);
+  gcov_unsigned_t bitvector = 0, b_ix = 0;
+  for (f_ix = 0; f_ix != num_fns; f_ix++)
+    {
+      if (zero_counts[f_ix])
+        bitvector |= 1 << b_ix;
+      if (++b_ix == 32)
+        {
+          gcov_write_unsigned (bitvector);
+          b_ix = 0;
+          bitvector = 0;
+        }
+    }
+  if (b_ix > 0)
+    gcov_write_unsigned (bitvector);
+}
+
 /* Write build_info strings from GI_PTR to a gcda file starting from
its current
    location.  */

@@ -758,7 +824,7 @@  gcov_write_func_counters (struct gcov_info *gi_ptr
           if (gfi_ptr && gfi_ptr->key == gi_ptr)
             length = GCOV_TAG_FUNCTION_LENGTH;
           else
-                length = 0;
+            length = 0;
         }

       gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
@@ -1104,9 +1170,24 @@  gcov_dump_module_info (struct gcov_filename_aux *g
 {
   struct gcov_info *gi_ptr;

+  unsigned max_module_id = 0;
+  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      unsigned mod_id = gi_ptr->mod_info->ident;
+      if (max_module_id < mod_id)
+        max_module_id = mod_id;
+    }
+  int **zero_counts = (int **) xcalloc (max_module_id, sizeof (int *));
+  for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+    {
+      unsigned mod_id = gi_ptr->mod_info->ident;
+      zero_counts[mod_id-1] = (int *) xcalloc (gi_ptr->n_functions,
+                                               sizeof (int));
+    }
+
   /* Compute the module groups and record whether there were any
      counter fixups applied that require rewriting the counters.  */
-  int changed = __gcov_compute_module_groups ();
+  int changed = __gcov_compute_module_groups (zero_counts);

   /* Now write out module group info.  */
   for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
@@ -1129,8 +1210,15 @@  gcov_dump_module_info (struct gcov_filename_aux *g
               gcov_position_t eof_pos = gi_ptr->eof_pos;
               gcov_rewrite ();
               gcov_seek (summary_end_pos);
+
+              unsigned mod_id = gi_ptr->mod_info->ident;
+              gcov_write_comdat_zero_fixup (zero_counts[mod_id-1],
+                                            gi_ptr->n_functions);
+              gcov_position_t zero_fixup_eof_pos = gcov_position ();
+
               gcov_write_func_counters (gi_ptr);
-              gcc_assert (eof_pos == gi_ptr->eof_pos);
+              gcc_assert (eof_pos + (zero_fixup_eof_pos - summary_end_pos)
+                          == gi_ptr->eof_pos);
             }
         }
       else
@@ -1149,7 +1237,11 @@  gcov_dump_module_info (struct gcov_filename_aux *g
                                   "profiling:%s:Error writing\n",
                                   gi_filename);
       gcov_write_import_file (gi_filename, gi_ptr);
+      free (zero_counts[gi_ptr->mod_info->ident-1]);
     }
+
+  free (zero_counts);
+
   __gcov_finalize_dyn_callgraph ();
 }

Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h    (revision 215230)
+++ libgcc/libgcov.h    (working copy)
@@ -349,6 +349,9 @@  gcov_get_sorted_import_module_array (struct gcov_i
     ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE inline void gcov_rewrite (void);

+extern void set_gcov_fn_fixed_up (int fixed_up);
+extern int get_gcov_fn_fixed_up (void);
+
 /* "Counts" stored in gcda files can be a real counter value, or
    an target address. When differentiate these two types because
    when manipulating counts, we should only change real counter values,
@@ -361,7 +364,13 @@  gcov_get_counter (void)
   /* This version is for reading count values in libgcov runtime:
      we read from gcda files.  */

-  return gcov_read_counter ();
+  if (get_gcov_fn_fixed_up ())
+    {
+      gcov_read_counter ();
+      return 0;
+    }
+  else
+    return gcov_read_counter ();
 #else
   /* This version is for gcov-tool. We read the value from memory and
      multiply it by the merge weight.  */
@@ -380,7 +389,13 @@  gcov_get_counter_target (void)
   /* This version is for reading count target values in libgcov runtime:
      we read from gcda files.  */

-  return gcov_read_counter ();
+  if (get_gcov_fn_fixed_up ())
+    {
+      gcov_read_counter ();
+      return 0;
+    }
+  else
+    return gcov_read_counter ();
 #else
   /* This version is for gcov-tool.  We read the value from memory
and we do NOT
      multiply it by the merge weight.  */
Index: libgcc/libgcov-util.c
===================================================================
--- libgcc/libgcov-util.c       (revision 215230)
+++ libgcc/libgcov-util.c       (working copy)
@@ -66,6 +66,7 @@  static void tag_lines (unsigned, unsigned);
 static void tag_counters (unsigned, unsigned);
 static void tag_summary (unsigned, unsigned);
 static void tag_module_info (unsigned, unsigned);
+static void tag_zero_fixup (unsigned, unsigned);

 /* The gcov_info for the first module.  */
 static struct gcov_info *curr_gcov_info;
@@ -88,6 +89,8 @@  static int k_ctrs_types;
 /* The longest length of all the filenames.  */
 static int max_filename_len;

+static int *zero_fixup_flags = NULL;
+
 /* Merge functions for counters.  Similar to __gcov_dyn_ipa_merge_*
    functions in dyn-ipa.c, which were derived from these, except
    the versions in dyn-ipa are used when merging from another array.  */
@@ -143,6 +146,7 @@  static const tag_format_t tag_table[] =
   {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
   {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
   {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
+  {GCOV_TAG_COMDAT_ZERO_FIXUP, "ZERO FIXUP", tag_zero_fixup},
   {0, NULL, NULL}
 };

@@ -169,14 +173,18 @@  tag_function (unsigned tag ATTRIBUTE_UNUSED, unsig
      k_ctrs[i].num = 0;
   k_ctrs_types = 0;

+  if (zero_fixup_flags)
+    {
+      set_gcov_fn_fixed_up (zero_fixup_flags[num_fn_info]);
+      if (get_gcov_fn_fixed_up () && verbose)
+        fprintf (stderr, "Function id=%d fixed up\n", curr_fn_info->ident);
+    }
+
   curr_fn_info->key = curr_gcov_info;
   curr_fn_info->ident = gcov_read_unsigned ();
   curr_fn_info->lineno_checksum = gcov_read_unsigned ();
   curr_fn_info->cfg_checksum = gcov_read_unsigned ();
   num_fn_info++;
-
-  if (verbose)
-    fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident);
 }

 /* Handler for reading block tag.  */
@@ -226,7 +234,13 @@  tag_counters (unsigned tag, unsigned length)
   gcc_assert (values);

   for (ix = 0; ix != n_counts; ix++)
-    values[ix] = gcov_read_counter ();
+    {
+      gcov_type val = gcov_read_counter ();
+      if (!get_gcov_fn_fixed_up ())
+        values[ix] = val;
+      else
+        values[ix] = 0;
+    }
 }

 /* Handler for reading summary tag.  */
@@ -323,7 +337,7 @@  lipo_process_substitute_string_1 (char *input_str,
       char *t;

       if (verbose)
-        printf ("Substitute: %s \n", input_str);
+        fprintf (stderr, "Substitute: %s \n", input_str);
       t = (char*) xmalloc (strlen (input_str) + 1
           + strlen (new_str) - strlen (cur_str));
       *p = 0;
@@ -332,7 +346,7 @@  lipo_process_substitute_string_1 (char *input_str,
       strcat (t, new_str);
       strcat (t, p + strlen (cur_str));
       if (verbose)
-        printf ("       -->  %s\n", t);
+        fprintf (stderr, "       -->  %s\n", t);
       return t;
     }

@@ -397,6 +411,16 @@  tag_module_info (unsigned tag ATTRIBUTE_UNUSED, un
     free (mod_info);
 }

+/* Handler for reading the COMDAT zero-profile fixup section.  */
+
+static void
+tag_zero_fixup (unsigned tag ATTRIBUTE_UNUSED, unsigned length)
+{
+  gcov_unsigned_t num_fns = 0;
+  zero_fixup_flags = gcov_read_comdat_zero_fixup (length, &num_fns);
+  gcc_assert (zero_fixup_flags);
+}
+
 /* Read the content of a gcda file FILENAME, and return a gcov_info
data structure.
    Program level summary CURRENT_SUMMARY will also be updated.  */