aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/libgcc
diff options
context:
space:
mode:
authorRong Xu <xur@google.com>2014-08-07 00:50:42 (GMT)
committerRong Xu <xur@google.com>2014-08-07 00:50:42 (GMT)
commitf1c18afafc2b321465ae6b07ede127095942d7dc (patch)
tree812093eebfa8510367718c12c02f7da03c0e73bf /gcc-4.9/libgcc
parent38a8aecfb882072900434499696b5c32a2274515 (diff)
downloadtoolchain_gcc-f1c18afafc2b321465ae6b07ede127095942d7dc.zip
toolchain_gcc-f1c18afafc2b321465ae6b07ede127095942d7dc.tar.gz
toolchain_gcc-f1c18afafc2b321465ae6b07ede127095942d7dc.tar.bz2
[gcc-4.9] Merge svn r213650 from google/gcc-4_9 branch
Merge svn r213650 from google/gcc-4_9 branch. Tested with arm,x86,mips,arm64,x86_64,mips64 build in liunux/windows. Change-Id: I0c07f67d516074172aa393003eee664d01f2e0f2
Diffstat (limited to 'gcc-4.9/libgcc')
-rw-r--r--gcc-4.9/libgcc/ChangeLog4
-rw-r--r--gcc-4.9/libgcc/config/libbid/ChangeLog4
-rw-r--r--gcc-4.9/libgcc/dyn-ipa.c803
-rw-r--r--gcc-4.9/libgcc/libgcov-driver.c151
-rw-r--r--gcc-4.9/libgcc/libgcov-interface.c94
-rw-r--r--gcc-4.9/libgcc/libgcov-util.c10
-rw-r--r--gcc-4.9/libgcc/libgcov.h11
7 files changed, 1022 insertions, 55 deletions
diff --git a/gcc-4.9/libgcc/ChangeLog b/gcc-4.9/libgcc/ChangeLog
index 9f17a7d..1edf552 100644
--- a/gcc-4.9/libgcc/ChangeLog
+++ b/gcc-4.9/libgcc/ChangeLog
@@ -1,3 +1,7 @@
+2014-07-16 Release Manager
+
+ * GCC 4.9.1 released.
+
2014-07-14 Richard Biener <rguenther@suse.de>
Backport r212520 from trunk.
* libgcov.h (struct gcov_fn_info): Make ctrs size 1.
diff --git a/gcc-4.9/libgcc/config/libbid/ChangeLog b/gcc-4.9/libgcc/config/libbid/ChangeLog
index 60132c4..c3f8386 100644
--- a/gcc-4.9/libgcc/config/libbid/ChangeLog
+++ b/gcc-4.9/libgcc/config/libbid/ChangeLog
@@ -1,3 +1,7 @@
+2014-07-16 Release Manager
+
+ * GCC 4.9.1 released.
+
2014-04-22 Release Manager
* GCC 4.9.0 released.
diff --git a/gcc-4.9/libgcc/dyn-ipa.c b/gcc-4.9/libgcc/dyn-ipa.c
index eb5ae9c..e66b02b 100644
--- a/gcc-4.9/libgcc/dyn-ipa.c
+++ b/gcc-4.9/libgcc/dyn-ipa.c
@@ -54,6 +54,7 @@ struct dyn_cgraph_edge
struct dyn_cgraph_edge *next_caller;
struct dyn_cgraph_edge *next_callee;
gcov_type count;
+ int indirect;
};
struct dyn_module_info
@@ -78,6 +79,36 @@ struct dyn_cgraph
unsigned num_nodes_executed;
/* used by new algorithm */
struct modu_node *modu_nodes;
+ /* Set indexed by lineno_checksum, returns a linked list of
+ checksum_alias_info structs. */
+ struct dyn_pointer_set *lineno_pointer_sets;
+};
+
+/* Struct holding information for functions with the same lineno_checksum. */
+struct lineno_checksum_alias
+{
+ struct checksum_alias_info *cfg_checksum_list;
+ unsigned lineno_checksum;
+};
+
+/* Struct holding information about functions with the same lineno and cfg
+ checksums. */
+struct checksum_alias_info
+{
+ struct checksum_alias_info *next_cfg_checksum;
+ struct checksum_alias *alias_list;
+ unsigned cfg_checksum;
+};
+
+/* Implements list of guid and corresponding fi_ptr for functions with matching
+ checksums. */
+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;
};
/* Module info is stored in dyn_caph->sup_modules
@@ -141,6 +172,7 @@ extern gcov_unsigned_t __gcov_lipo_random_group_size;
extern gcov_unsigned_t __gcov_lipo_propagate_scale;
extern gcov_unsigned_t __gcov_lipo_dump_cgraph;
extern gcov_unsigned_t __gcov_lipo_max_mem;
+extern gcov_unsigned_t __gcov_lipo_comdat_algorithm;
extern gcov_unsigned_t __gcov_lipo_grouping_algorithm;
extern gcov_unsigned_t __gcov_lipo_merge_modu_edges;
extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
@@ -149,7 +181,7 @@ extern gcov_unsigned_t __gcov_lipo_weak_inclusion;
void __gcov_build_callgraph (void) {}
#else
-void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
+int __gcov_compute_module_groups (void) 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);
@@ -173,6 +205,7 @@ pointer_set_create (unsigned (*get_key) (const void *));
static struct dyn_cgraph the_dyn_call_graph;
static int total_zero_count = 0;
static int total_insane_count = 0;
+static int fixup_type = 0;
enum GROUPING_ALGORITHM
{
@@ -185,6 +218,24 @@ static int flag_weak_inclusion;
static int flag_use_existing_grouping;
static gcov_unsigned_t mem_threshold;
+gcov_type
+gcov_find_new_ic_target (gcov_type caller_guid, gcov_type callee_guid);
+void
+__gcov_dyn_ipa_merge_add (gcov_type *dest, gcov_type *src, unsigned n_counters);
+void
+__gcov_dyn_ipa_merge_ior (gcov_type *dest, gcov_type *src, unsigned n_counters);
+void
+__gcov_dyn_ipa_merge_dc (gcov_type *dest, gcov_type *src, unsigned n_counters);
+void
+__gcov_dyn_ipa_merge_icall_topn (gcov_type *dest, gcov_type *src,
+ unsigned n_counters);
+void
+__gcov_dyn_ipa_merge_single (gcov_type *dest, gcov_type *src,
+ unsigned n_counters);
+void
+__gcov_dyn_ipa_merge_delta (gcov_type *dest, gcov_type *src,
+ unsigned n_counters);
+
/* Returns 0 if no dump is enabled. Returns 1 if text form graph
dump is enabled. Returns 2 if .dot form dump is enabled. */
@@ -318,6 +369,102 @@ gcov_info_get_key (const void *p)
return get_module_ident ((const struct gcov_info *)p);
}
+/* The lineno_checksum value in P is the key for lineno_pointer_sets. */
+
+static inline unsigned
+lineno_checksum_get_key (const void *p)
+{
+ return ((const struct lineno_checksum_alias *) p)->lineno_checksum;
+}
+
+/* Create a new checksum_alias struct for function with GUID, FI_PTR,
+ and ZERO_COUNTS flag. 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,
+ 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;
+ return alias;
+}
+
+/* Locate the checksum_alias_info in LIST that matches CFG_CHECKSUM. */
+
+static struct checksum_alias_info *
+find_cfg_checksum (struct checksum_alias_info *list, unsigned cfg_checksum)
+{
+ for (; list; list = list->next_cfg_checksum)
+ {
+ if (list->cfg_checksum == cfg_checksum)
+ return list;
+ }
+ return NULL;
+}
+
+/* Insert a new checksum_alias struct into LIST for function with
+ CFG_CHECKSUM and associated GUID, FI_PTR, and ZERO_COUNTS flag. */
+
+static struct checksum_alias_info *
+cfg_checksum_insert (unsigned cfg_checksum, gcov_type guid,
+ const struct gcov_fn_info *fi_ptr, int zero_counts,
+ struct checksum_alias_info *list)
+{
+ struct checksum_alias_info *alias_info;
+ alias_info = find_cfg_checksum (list, cfg_checksum);
+ 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);
+ return list;
+ }
+ else
+ {
+ 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,
+ NULL);
+ return alias_info;
+ }
+}
+
+/* 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. */
+
+static void
+checksum_set_insert (unsigned lineno_checksum, unsigned cfg_checksum,
+ gcov_type guid, const struct gcov_fn_info *fi_ptr,
+ int zero_counts)
+{
+ struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
+ if (!p)
+ the_dyn_call_graph.lineno_pointer_sets = p =
+ pointer_set_create (lineno_checksum_get_key);
+ struct lineno_checksum_alias **m = (struct lineno_checksum_alias **)
+ pointer_set_find_or_insert (p, lineno_checksum);
+ if (*m)
+ {
+ (*m)->cfg_checksum_list = cfg_checksum_insert (cfg_checksum, guid,
+ fi_ptr, zero_counts,
+ (*m)->cfg_checksum_list);
+ }
+ else
+ {
+ *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);
+ p->n_elements++;
+ }
+}
+
static struct dyn_pointer_set *
get_exported_to (unsigned module_ident)
{
@@ -428,6 +575,10 @@ init_dyn_call_graph (void)
fprintf (stderr, "Group mem limit: %u KB \n",
__gcov_lipo_max_mem);
+ if (do_dump)
+ fprintf (stderr, "COMDAT fixup algorithm: %u\n",
+ __gcov_lipo_comdat_algorithm);
+
for (; gi_ptr; gi_ptr = gi_ptr->next)
{
/* mod_idx is module_ident - 1. */
@@ -563,12 +714,13 @@ gcov_add_in_edge (struct dyn_cgraph_node *callee,
}
/* Add a call graph edge between caller CALLER and callee CALLEE.
- The edge count is COUNT. */
+ The edge count is COUNT and INDIRECT flags whether the call was
+ direct or indirect. */
static void
gcov_add_cgraph_edge (struct dyn_cgraph_node *caller,
struct dyn_cgraph_node *callee,
- gcov_type count)
+ gcov_type count, int indirect)
{
struct dyn_cgraph_edge *new_edge = XNEW (struct dyn_cgraph_edge);
new_edge->caller = caller;
@@ -576,6 +728,7 @@ gcov_add_cgraph_edge (struct dyn_cgraph_node *caller,
new_edge->count = count;
new_edge->next_caller = 0;
new_edge->next_callee = 0;
+ new_edge->indirect = indirect;
gcov_add_out_edge (caller, new_edge);
gcov_add_in_edge (callee, new_edge);
@@ -609,7 +762,7 @@ gcov_build_callgraph_dc_fn (struct dyn_cgraph_node *caller,
total_insane_count++;
continue;
}
- gcov_add_cgraph_edge (caller, callee, count);
+ gcov_add_cgraph_edge (caller, callee, count, 0);
}
}
@@ -643,7 +796,7 @@ gcov_build_callgraph_ic_fn (struct dyn_cgraph_node *caller,
total_insane_count++;
continue;
}
- gcov_add_cgraph_edge (caller, callee, count);
+ gcov_add_cgraph_edge (caller, callee, count, 1);
}
}
}
@@ -690,7 +843,7 @@ gcov_build_callgraph (void)
if (i == GCOV_COUNTER_ICALL_TOPNV)
gcov_build_callgraph_ic_fn (caller, ci_ptr->values, ci_ptr->num);
- if (i == GCOV_COUNTER_ARCS && 0)
+ if (i == GCOV_COUNTER_ARCS)
{
gcov_type total_arc_count = 0;
unsigned arc;
@@ -698,6 +851,10 @@ gcov_build_callgraph (void)
total_arc_count += ci_ptr->values[arc];
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);
}
ci_ptr++;
}
@@ -2290,9 +2447,622 @@ read_modu_groups_from_imports_files (void)
#endif /* IN_GCOV_TOOL */
}
-/* Compute module groups needed for L-IPO compilation. */
+/* Scan functions in MOD_INFO and return gcov_fn_info for matching
+ FUNC_ID. */
+
+static const struct gcov_fn_info *
+find_fn_info_from_func_id (struct gcov_info *mod_info, unsigned func_id)
+{
+ unsigned j;
+ for (j = 0; j < mod_info->n_functions; j++)
+ {
+ const struct gcov_fn_info *fi_ptr = mod_info->functions[j];
+ if (fi_ptr->ident == func_id)
+ return fi_ptr;
+ }
+ gcc_assert (0);
+ return NULL;
+}
+
+/* Look for a function in the same module as CALLER_GUID that has
+ the same lineno and cfg checksums as CALLEE_GUID. Return the
+ guid of the matching function if exactly one is found, 0 otherwise. */
+
+gcov_type
+gcov_find_new_ic_target (gcov_type caller_guid, gcov_type callee_guid)
+{
+ /* Obtain the callee's function info. */
+ unsigned callee_mod_id = get_module_ident_from_func_glob_uid (callee_guid);
+ struct gcov_info *callee_mod_info = get_module_info (callee_mod_id);
+ unsigned callee_func_id = get_intra_module_func_id (callee_guid);
+ const struct gcov_fn_info *callee_fi_ptr
+ = find_fn_info_from_func_id (callee_mod_info, callee_func_id);
+
+ /* Obtain the list of checksum_alias structures for functions with
+ the same lineno and cfg checksum as callee. */
+ struct dyn_pointer_set *p = the_dyn_call_graph.lineno_pointer_sets;
+ gcc_assert (p);
+ struct lineno_checksum_alias **line_alias = (struct lineno_checksum_alias **)
+ pointer_set_find_or_insert (p, callee_fi_ptr->lineno_checksum);
+ gcc_assert (*line_alias);
+ struct checksum_alias_info *cfg_alias
+ = find_cfg_checksum ((*line_alias)->cfg_checksum_list,
+ callee_fi_ptr->cfg_checksum);
+ gcc_assert (cfg_alias);
+
+
+ /* Scan the list of checksum aliases for one that is located in caller's
+ module. */
+ gcov_type new_guid = 0;
+ unsigned caller_mod_id = get_module_ident_from_func_glob_uid (caller_guid);
+ struct checksum_alias *alias;
+ for (alias = cfg_alias->alias_list; alias;
+ alias = alias->next_alias)
+ {
+ if (get_module_ident_from_func_glob_uid (alias->guid)
+ == caller_mod_id)
+ {
+ /* Give up if we found multiple matches. */
+ if (new_guid)
+ return 0;
+ new_guid = alias->guid;
+ }
+ }
+
+ /* We found exactly one match, return it. */
+ return new_guid;
+}
+
+/* If any of CALLER's indirect call counters in CI_PTR has a target that is
+ not in CALLER's module group, see if we can find a copy of the function in
+ CALLER's module. If so, replace the target to point to that copy. See
+ comments for gcov_fixup_icall_profile on why we would want to do this.
+ Return 1 if any icall profiles were updated, 0 otherwise. */
+
+static int
+gcov_fixup_ic_fn (struct dyn_cgraph_node *caller,
+ const struct gcov_ctr_info *ci_ptr)
+{
+ unsigned i, j;
+ int changed = 0;
+ gcov_type *icall_counters = ci_ptr->values;
+ unsigned n_counts = ci_ptr->num;
+ int do_dump = (do_cgraph_dump () != 0);
+
+ unsigned caller_mod_id = get_module_ident_from_func_glob_uid (caller->guid);
+ struct dyn_pointer_set *imported_mods = get_imported_modus (caller_mod_id);
+ for (i = 0; i < n_counts; i += GCOV_ICALL_TOPN_NCOUNTS)
+ {
+ gcov_type *value_array = &icall_counters[i + 1];
+ for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+ {
+ struct dyn_cgraph_node *callee;
+ gcov_type count;
+ gcov_type callee_guid = value_array[j];
+
+ count = value_array[j + 1];
+ if (count == 0)
+ continue;
+
+ callee = get_cgraph_node (callee_guid);
+ if (!callee)
+ continue;
+
+ /* Now check if callee is in the module group of caller. If so,
+ no need for any fixup. */
+ unsigned callee_mod_id
+ = get_module_ident_from_func_glob_uid (callee_guid);
+ if (pointer_set_contains (imported_mods, callee_mod_id))
+ continue;
+
+ /* Attempt to find a copy of callee in caller's module. */
+ gcov_type new_callee_guid
+ = gcov_find_new_ic_target (caller->guid, callee_guid);
+
+ if (do_dump == 1)
+ {
+ struct gcov_info *caller_mod_info
+ = the_dyn_call_graph.modules[caller_mod_id - 1];
+ struct gcov_info *callee_mod_info
+ = the_dyn_call_graph.modules[callee_mod_id - 1];
+ fprintf (stderr,
+ "Fixup icall %u:%u -> %u:%u with count %lld "
+ "(%s -> %s): ",
+ caller_mod_id, get_intra_module_func_id (caller->guid),
+ callee_mod_id, get_intra_module_func_id (callee_guid),
+ (long long) count,
+ caller_mod_info->mod_info->source_filename,
+ callee_mod_info->mod_info->source_filename);
+ if (!new_callee_guid)
+ fprintf (stderr,"No target found\n");
+ else
+ fprintf (stderr,"Found new target %u:%u (%llx)\n",
+ get_module_ident_from_func_glob_uid (new_callee_guid),
+ get_intra_module_func_id (new_callee_guid),
+ (long long) new_callee_guid);
+ }
+
+ if (new_callee_guid)
+ {
+ /* Update the profile info and note the need for a profile
+ counter rewrite. */
+ value_array[j] = new_callee_guid;
+ changed = 1;
+ }
+ }
+ }
+ return changed;
+}
+
+/* Look for indirect call profiles that target callee's outside of the caller's
+ module group, and see if we can find a copy of the function in caller's own
+ module. If so, replace the profile target to point to that copy. This is
+ useful when the callee is a COMDAT, in which case there should be a copy
+ within CALLER's module. The linker would have selected a single copy of
+ COMDAT and all indirect call profiles would target that copy of the callee,
+ which might not end up in the module group of CALLER.
+ Return 1 if any icall profiles were updated, 0 otherwise. */
+
+static int
+gcov_fixup_icall_profile (void)
+{
+ struct gcov_info *gi_ptr;
+ unsigned m_ix;
+ int changed = 0;
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix, i;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+ if (gi_ptr == NULL)
+ continue;
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *caller;
+ const struct gcov_ctr_info *ci_ptr = 0;
+
+ fi_ptr = gi_ptr->functions[f_ix];
+ ci_ptr = fi_ptr->ctrs;
+
+ caller = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert
+ (the_dyn_call_graph.call_graph_nodes[m_ix],
+ fi_ptr->ident));
+ gcc_assert (caller);
+
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ {
+ if (!gi_ptr->merge[i])
+ continue;
+
+ if (i == GCOV_COUNTER_ICALL_TOPNV)
+ changed |= gcov_fixup_ic_fn (caller, ci_ptr);
+
+ ci_ptr++;
+ }
+ }
+ }
+ return changed;
+}
+
+/* Create, zero-initialize and return an array to hold merged counter
+ values. */
+
+static struct gcov_ctr_info *
+init_merged_ctrs (void)
+{
+ struct gcov_ctr_info *merged_ctrs = XNEWVEC (struct gcov_ctr_info,
+ GCOV_COUNTERS);
+ int i;
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ {
+ merged_ctrs[i].num = 0;
+ merged_ctrs[i].values = NULL;
+ }
+ return merged_ctrs;
+}
+
+/* The profile merging function that just adds N_COUNTERS counters from array
+ SRC to those in DEST. Adapted from __gcov_merge_add. */
+
+void
+__gcov_dyn_ipa_merge_add (gcov_type *dest, gcov_type *src, unsigned n_counters)
+{
+ for (; n_counters; dest++, src++, n_counters--)
+ *dest += *src;
+}
+
+/* The profile merging function that just ors N_COUNTERS counters from array
+ SRC to those in DEST. Adapted from __gcov_merge_ior. */
+
+void
+__gcov_dyn_ipa_merge_ior (gcov_type *dest, gcov_type *src, unsigned n_counters)
+{
+ for (; n_counters; dest++, src++, n_counters--)
+ *dest |= *src;
+}
+
+
+/* The profile merging function that just merges N_COUNTERS direct call counters
+ from array SRC to those in DEST. Adapted from __gcov_merge_dc. */
+
+void
+__gcov_dyn_ipa_merge_dc (gcov_type *dest, gcov_type *src, unsigned n_counters)
+{
+ unsigned i;
+
+ gcc_assert (!(n_counters % 2));
+ for (i = 0; i < n_counters; i += 2)
+ {
+ gcov_type global_id = src[i];
+ if (!global_id)
+ continue;
+
+ /* Simply skip non-matching call targets. */
+ if (dest[i] && dest[i] != global_id)
+ {
+ continue;
+ }
+ dest[i] = global_id;
+
+ dest[i + 1] += src[i + 1];
+ }
+}
+
+/* The profile merging function that just merges N_COUNTERS indirect call
+ counters from array SRC to those in DEST. Adapted from
+ __gcov_merge_icall_topn. */
+
+void
+__gcov_dyn_ipa_merge_icall_topn (gcov_type *dest, gcov_type *src,
+ unsigned n_counters)
+{
+ unsigned i, j, k, m;
+
+ gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
+ for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
+ {
+ /* Skip the number_of_eviction entry (in dest[i]). */
+ gcov_type *value_array = &dest[i + 1];
+ unsigned tmp_size = 2 * (GCOV_ICALL_TOPN_NCOUNTS - 1);
+ gcov_type *tmp_array
+ = (gcov_type *) alloca (tmp_size * sizeof (gcov_type));
+
+ for (j = 0; j < tmp_size; j++)
+ tmp_array[j] = 0;
+
+ for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+ {
+ tmp_array[j] = value_array[j];
+ tmp_array[j + 1] = value_array [j + 1];
+ }
+
+ /* Skip the number_of_eviction entry (in src[i]). */
+ gcov_type *src_value_array = &src[i + 1];
+ for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
+ {
+ int found = 0;
+ gcov_type global_id = src_value_array[k];
+ gcov_type call_count = src_value_array[k + 1];
+ for (m = 0; m < j; m += 2)
+ {
+ if (tmp_array[m] == global_id)
+ {
+ found = 1;
+ tmp_array[m + 1] += call_count;
+ break;
+ }
+ }
+ if (!found)
+ {
+ tmp_array[j] = global_id;
+ tmp_array[j + 1] = call_count;
+ j += 2;
+ }
+ }
+ /* Now sort the temp array. */
+ gcov_sort_n_vals (tmp_array, j);
+
+ /* Now copy back the top half of the temp array. */
+ for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
+ {
+ value_array[k] = tmp_array[k];
+ value_array[k + 1] = tmp_array[k + 1];
+ }
+ }
+}
+
+
+/* Time profiles are merged so that minimum from all valid (greater than zero)
+ is stored. There could be a fork that creates new counters. To have
+ the profile stable, we chosen to pick the smallest function visit time. */
+
void
+__gcov_dyn_ipa_merge_time_profile (gcov_type *dest, gcov_type *src,
+ unsigned n_counters)
+{
+ unsigned i;
+ gcov_type value;
+
+ for (i = 0; i < n_counters; i++)
+ {
+ value = src[i];
+
+ if (value && (!dest[i] || value < dest[i]))
+ dest[i] = value;
+ }
+}
+
+
+/* The profile merging function that just merges N_COUNTERS most common value
+ counters from array SRC to those in DEST. Adapted from
+ __gcov_merge_single. */
+
+void
+__gcov_dyn_ipa_merge_single (gcov_type *dest, gcov_type *src,
+ unsigned n_counters)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+
+ gcc_assert (!(n_counters % 3));
+ n_measures = n_counters / 3;
+ for (i = 0; i < n_measures; i++, dest += 3, src += 3)
+ {
+ value = src[0];
+ counter = src[1];
+ all = src[2];
+
+ if (dest[0] == value)
+ dest[1] += counter;
+ else if (counter > dest[1])
+ {
+ dest[0] = value;
+ dest[1] = counter - dest[1];
+ }
+ else
+ dest[1] -= counter;
+ dest[2] += all;
+ }
+}
+
+/* The profile merging function that just merges N_COUNTERS most common
+ difference counters from array SRC to those in DEST. Adapted from
+ __gcov_merge_delta. */
+
+void
+__gcov_dyn_ipa_merge_delta (gcov_type *dest, gcov_type *src,
+ unsigned n_counters)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+
+ gcc_assert (!(n_counters % 4));
+ n_measures = n_counters / 4;
+ for (i = 0; i < n_measures; i++, dest += 4, src += 4)
+ {
+ value = src[1];
+ counter = src[2];
+ all = src[3];
+
+ if (dest[1] == value)
+ dest[2] += counter;
+ else if (counter > dest[2])
+ {
+ dest[1] = value;
+ dest[2] = counter - dest[2];
+ }
+ else
+ dest[2] -= counter;
+ dest[3] += all;
+ }
+}
+
+/* Type of function used to merge counters. */
+typedef void (*gcov_dyn_ipa_merge_fn) (gcov_type *, gcov_type *,
+ gcov_unsigned_t);
+
+/* Merge functions for counters. */
+#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_dyn_ipa_merge ## FN_TYPE,
+static gcov_dyn_ipa_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+#include "gcov-counter.def"
+};
+#undef DEF_GCOV_COUNTER
+
+#if 0
+static gcov_dyn_ipa_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+ __gcov_dyn_ipa_merge_add,
+ __gcov_dyn_ipa_merge_add,
+ __gcov_dyn_ipa_merge_add,
+ __gcov_dyn_ipa_merge_single,
+ __gcov_dyn_ipa_merge_delta,
+ __gcov_dyn_ipa_merge_single,
+ __gcov_dyn_ipa_merge_add,
+ __gcov_dyn_ipa_merge_ior,
+ __gcov_dyn_ipa_merge_icall_topn,
+ __gcov_dyn_ipa_merge_dc,
+};
+#endif
+
+/* Copy counters from SRC_CTRS array to DEST_CTRS array, where SRC_CTRS is
+ indexed by the GCOV_COUNTER type, and DEST_CTRS is an array holding only
+ the mergable counters to emit to the gcda file for DEST_GUID. */
+
+static void
+copy_ctrs (const struct gcov_ctr_info *dest_ctrs, gcov_type dest_guid,
+ const struct gcov_ctr_info *src_ctrs)
+{
+ unsigned dest_mod_id
+ = get_module_ident_from_func_glob_uid (dest_guid);
+ struct gcov_info *dest_mod_info = the_dyn_call_graph.modules[dest_mod_id - 1];
+ int i;
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ {
+ if (!dest_mod_info->merge[i])
+ continue;
+
+ gcov_unsigned_t num = dest_ctrs->num;
+ // This could be different if code was optimized differently
+ // (e.g. early-inlined in some modules but not others).
+ // If they are different then just punt on merge of this counter
+ // (what about other counters?).
+ //gcc_assert (dest_ctrs[i].num == num);
+ if (num && src_ctrs[i].num == num)
+ (*ctr_merge_functions[i]) (dest_ctrs->values,
+ src_ctrs[i].values, num);
+ dest_ctrs++;
+ }
+}
+
+/* Merge counters from SRC_CTRS array to DEST_CTRS array, where DEST_CTRS is
+ indexed by the GCOV_COUNTER type, and SRC_CTRS is an array holding only
+ the mergable counters from the gcda file for SRC_GUID. */
+
+static void
+merge_ctrs (struct gcov_ctr_info *dest_ctrs,
+ const struct gcov_ctr_info *src_ctrs, gcov_type src_guid)
+{
+ unsigned src_mod_id
+ = get_module_ident_from_func_glob_uid (src_guid);
+ struct gcov_info *src_mod_info = the_dyn_call_graph.modules[src_mod_id - 1];
+ unsigned i, j;
+
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ {
+ if (!src_mod_info->merge[i])
+ continue;
+
+ gcov_unsigned_t num = src_ctrs->num;
+ // This could be different if code was optimized differently
+ // (e.g. call counters when ipa-inlined in some modules but not others?).
+ // If they are different then just punt on merge of this counter
+ // (what about other counters?).
+ //gcc_assert (dest_ctrs[i].num == num);
+ if (num)
+ {
+ /* If this is the first source counter array containing counters for
+ this counter type, allocate the associated number of counter values
+ in the dest counter array. */
+ if (!dest_ctrs[i].num)
+ {
+ dest_ctrs[i].values = XNEWVEC (gcov_type, num);
+ for (j = 0; j < num; j++)
+ dest_ctrs[i].values[j] = 0;
+ dest_ctrs[i].num = num;
+ }
+ if (dest_ctrs[i].num == num)
+ (*ctr_merge_functions[i]) (dest_ctrs[i].values,
+ src_ctrs->values, num);
+ }
+ src_ctrs++;
+ }
+}
+
+/* Walks the set of functions that have the same lineno and cfg checksum, and
+ performs counter merging. INFO contains the checksum_alias_info structure
+ for a given lineno and cfg checksum combination. CHANGED points
+ to a flag that should be set to 1 if any fixups were applied. */
+
+static int
+gcov_fixup_counters_checksum (const struct checksum_alias_info *info,
+ int *changed)
+{
+ /* See if there are any zero count functions to fix. */
+ int found = 0;
+ struct checksum_alias *alias;
+ for (alias = info->alias_list; alias;
+ alias = alias->next_alias)
+ {
+ if (alias->zero_counts)
+ {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return 1;
+
+ /* Walk the aliases and merge the non-zero counters into a dummy copy. */
+ struct gcov_ctr_info *merged_ctrs = init_merged_ctrs ();
+ found = 0;
+ for (alias = info->alias_list; alias;
+ alias = alias->next_alias)
+ {
+ if (alias->zero_counts)
+ continue;
+ merge_ctrs (merged_ctrs, alias->fi_ptr->ctrs, alias->guid);
+ found = 1;
+ }
+
+ /* Check if we found a non-zero count function to fix up from. */
+ if (!found)
+ return 1;
+
+ /* At this point we know we have a zero count function to fixup, and data
+ from which to fix it up. */
+ *changed = 1;
+
+ /* Walk them again and copy the merged counters into 0-count copies. */
+ for (alias = info->alias_list; alias;
+ alias = alias->next_alias)
+ {
+ if (!alias->zero_counts)
+ continue;
+ copy_ctrs (alias->fi_ptr->ctrs, alias->guid, merged_ctrs);
+ }
+
+ return 1;
+}
+
+/* Walks the set of functions that have the same lineno_checksum, and
+ performs counter merging for functions that have the same cfg_checksum
+ as well. VALUE contains the lineno_checksum_alias structure for a
+ given lineno_checksum, and DATA1 contains a pointer to a flag that
+ should be set to 1 if any fixups were applied. */
+
+static int
+gcov_fixup_counters_lineno (const void *value,
+ void *data1,
+ void *data2 ATTRIBUTE_UNUSED,
+ void *data3 ATTRIBUTE_UNUSED)
+{
+ const struct lineno_checksum_alias *a
+ = (const struct lineno_checksum_alias*) value;
+ int *changed = (int *) data1;
+ struct checksum_alias_info *cfg_alias_list = a->cfg_checksum_list;
+ for (; cfg_alias_list; cfg_alias_list = cfg_alias_list->next_cfg_checksum)
+ {
+ gcov_fixup_counters_checksum (cfg_alias_list, changed);
+ }
+ return 1;
+}
+
+/* Routine to perform counter fixup for COMDAT functions with missing counters.
+ Returns 1 if any updates were performed, 0 otherwise. Walks the sets of
+ functions having the same lineno and cfg checksums and merges all non-zero
+ counters, copying the merged counters into any copies with all-zero counts.
+ This is done because the linker will chose one out-of-line copy of a COMDAT,
+ and only that copy will get non-zero counters. Other copies that were IPA
+ inlined may have non-zero counts, which we don't overwrite as they contain
+ more context-sensitive data. */
+
+static int
+gcov_fixup_zero_counters (void)
+{
+ int changed = 0;
+ pointer_set_traverse (the_dyn_call_graph.lineno_pointer_sets,
+ gcov_fixup_counters_lineno,
+ &changed, 0, 0);
+ return changed;
+}
+
+/* Compute module groups needed for L-IPO compilation. Returns 1 if any
+ counter fixups were applied, requiring a profile rewrite, 0 otherwise. */
+
+int
__gcov_compute_module_groups (void)
{
gcov_type cut_off_count;
@@ -2310,7 +3080,7 @@ __gcov_compute_module_groups (void)
fprintf (stderr, " Creating random grouping with %u:%u\n",
__gcov_lipo_random_seed, __gcov_lipo_random_group_size);
}
- return;
+ return 0;
}
else if (seed && max_group_size)
{
@@ -2324,15 +3094,21 @@ __gcov_compute_module_groups (void)
fprintf (stderr, " Creating random grouping with %s:%s\n",
seed, max_group_size);
}
- return;
+ return 0;
}
if (flag_use_existing_grouping)
{
read_modu_groups_from_imports_files ();
- return;
+ return 0;
}
+ const char *do_fixup = 0;
+ fixup_type = __gcov_lipo_comdat_algorithm;
+ do_fixup = getenv ("GCOV_DYN_DO_FIXUP");
+ if (do_fixup)
+ fixup_type = atoi (do_fixup);
+
/* First compute dynamic call graph. */
gcov_build_callgraph ();
@@ -2342,6 +3118,13 @@ __gcov_compute_module_groups (void)
gcov_dump_callgraph (cut_off_count);
+ int changed = 0;
+ if (fixup_type & 0x2)
+ changed |= gcov_fixup_zero_counters ();
+ if (fixup_type & 0x1)
+ changed |= gcov_fixup_icall_profile ();
+
+ return changed;
}
/* Dumper function for NODE. */
diff --git a/gcc-4.9/libgcc/libgcov-driver.c b/gcc-4.9/libgcc/libgcov-driver.c
index e829fe5..dc8cf36 100644
--- a/gcc-4.9/libgcc/libgcov-driver.c
+++ b/gcc-4.9/libgcc/libgcov-driver.c
@@ -55,7 +55,7 @@ static gcov_unsigned_t gcov_cur_module_id = 0;
/* Dynamic call graph build and form module groups. */
-void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
+int __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
/* The following functions can be called from outside of this file. */
@@ -453,6 +453,49 @@ struct gcov_filename_aux{
/* Including system dependent components. */
#include "libgcov-driver-system.c"
+/* Scan through the current open gcda file corresponding to GI_PTR
+ to locate the end position of the last summary, returned in
+ SUMMARY_END_POS_P. Return 0 on success, -1 on error. */
+static int
+gcov_scan_summary_end (struct gcov_info *gi_ptr,
+ gcov_position_t *summary_end_pos_p)
+{
+ gcov_unsigned_t tag, version, stamp;
+ tag = gcov_read_unsigned ();
+ if (tag != GCOV_DATA_MAGIC)
+ {
+ gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
+ return -1;
+ }
+
+ version = gcov_read_unsigned ();
+ if (!gcov_version (gi_ptr, version, gi_filename))
+ return -1;
+
+ stamp = gcov_read_unsigned ();
+ if (stamp != gi_ptr->stamp)
+ /* Read from a different compilation. Overwrite the file. */
+ return -1;
+
+ /* Look for program summary. */
+ while (1)
+ {
+ struct gcov_summary tmp;
+
+ *summary_end_pos_p = gcov_position ();
+ tag = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_PROGRAM_SUMMARY)
+ break;
+
+ gcov_read_unsigned ();
+ gcov_read_summary (&tmp);
+ if (gcov_is_error ())
+ return -1;
+ }
+
+ return 0;
+}
+
/* This function merges counters in GI_PTR to an existing gcda file.
Return 0 on success.
Return -1 on error. In this case, caller will goto read_fatal. */
@@ -603,44 +646,13 @@ read_error:
return -1;
}
-/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
- the case of appending to an existing file, SUMMARY_POS will be non-zero.
- We will write the file starting from SUMMAY_POS. */
+/* Write counters in GI_PTR to a gcda file starting from its current
+ location. */
static void
-gcov_exit_write_gcda (struct gcov_info *gi_ptr,
- const struct gcov_summary *prg_p,
- const gcov_position_t eof_pos,
- const gcov_position_t summary_pos)
+gcov_write_func_counters (struct gcov_info *gi_ptr)
{
unsigned f_ix;
- struct gcov_summary_buffer *next_sum_buffer;
-
- /* Write out the data. */
- if (!eof_pos)
- {
- gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
- gcov_write_unsigned (gi_ptr->stamp);
- }
-
- if (summary_pos)
- gcov_seek (summary_pos);
-
- /* Generate whole program statistics. */
- gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, prg_p);
-
- /* Rewrite all the summaries that were after the summary we merged
- into. This is necessary as the merged summary may have a different
- size due to the number of non-zero histogram entries changing after
- merging. */
-
- while (sum_buffer)
- {
- gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
- next_sum_buffer = sum_buffer->next;
- free (sum_buffer);
- sum_buffer = next_sum_buffer;
- }
/* Write execution counts for each function. */
for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
@@ -700,6 +712,50 @@ gcov_exit_write_gcda (struct gcov_info *gi_ptr,
gcov_write_unsigned (0);
}
+/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
+ the case of appending to an existing file, SUMMARY_POS will be non-zero.
+ We will write the file starting from SUMMAY_POS. */
+
+static void
+gcov_exit_write_gcda (struct gcov_info *gi_ptr,
+ const struct gcov_summary *prg_p,
+ const gcov_position_t eof_pos,
+ const gcov_position_t summary_pos)
+
+{
+ struct gcov_summary_buffer *next_sum_buffer;
+
+ /* Write out the data. */
+ if (!eof_pos)
+ {
+ gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+ gcov_write_unsigned (gi_ptr->stamp);
+ }
+
+ if (summary_pos)
+ gcov_seek (summary_pos);
+ gcc_assert (!summary_pos || summary_pos == gcov_position ());
+
+ /* Generate whole program statistics. */
+ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, prg_p);
+
+ /* Rewrite all the summaries that were after the summary we merged
+ into. This is necessary as the merged summary may have a different
+ size due to the number of non-zero histogram entries changing after
+ merging. */
+
+ while (sum_buffer)
+ {
+ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
+ next_sum_buffer = sum_buffer->next;
+ free (sum_buffer);
+ sum_buffer = next_sum_buffer;
+ }
+
+ /* Write the counters. */
+ gcov_write_func_counters (gi_ptr);
+}
+
/* Helper function for merging summary.
Return -1 on error. Return 0 on success. */
@@ -964,7 +1020,9 @@ gcov_dump_module_info (struct gcov_filename_aux *gf)
{
struct gcov_info *gi_ptr;
- __gcov_compute_module_groups ();
+ /* Compute the module groups and record whether there were any
+ counter fixups applied that require rewriting the counters. */
+ int changed = __gcov_compute_module_groups ();
/* Now write out module group info. */
for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
@@ -974,8 +1032,27 @@ gcov_dump_module_info (struct gcov_filename_aux *gf)
if (gcov_exit_open_gcda_file (gi_ptr, gf) == -1)
continue;
+ if (changed)
+ {
+ /* Scan file to find the end of the summary section, which is
+ where we will start re-writing the counters. */
+ gcov_position_t summary_end_pos;
+ if (gcov_scan_summary_end (gi_ptr, &summary_end_pos) == -1)
+ gcov_error ("profiling:%s:Error scanning summaries\n",
+ gi_filename);
+ else
+ {
+ gcov_position_t eof_pos = gi_ptr->eof_pos;
+ gcov_rewrite ();
+ gcov_seek (summary_end_pos);
+ gcov_write_func_counters (gi_ptr);
+ gcc_assert (eof_pos == gi_ptr->eof_pos);
+ }
+ }
+ else
+ gcov_rewrite ();
+
/* Overwrite the zero word at the of the file. */
- gcov_rewrite ();
gcov_seek (gi_ptr->eof_pos);
gcov_write_module_infos (gi_ptr);
diff --git a/gcc-4.9/libgcc/libgcov-interface.c b/gcc-4.9/libgcc/libgcov-interface.c
index 77d8395..a3effa4 100644
--- a/gcc-4.9/libgcc/libgcov-interface.c
+++ b/gcc-4.9/libgcc/libgcov-interface.c
@@ -129,6 +129,100 @@ unsigned int __gcov_profiling_for_test_coverage (void)
return __gcov_test_coverage;
}
+typedef void (*gcov_dumper_type) (void);
+struct dumper_entry
+{
+ gcov_dumper_type dumper;
+ struct dumper_entry *next_dumper;
+};
+
+static struct dumper_entry this_dumper = {&__gcov_dump, 0};
+
+/* global dumper list with default visibilty. */
+struct dumper_entry *__gcov_dumper_list;
+
+#ifdef __GTHREAD_MUTEX_INIT
+__gthread_mutex_t __gcov_dump_mx = __GTHREAD_MUTEX_INIT;
+#define init_mx_once()
+#else
+__gthread_mutex_t __gcov_dump_mx;
+
+static void
+init_mx (void)
+{
+ __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_dump_mx);
+}
+static void
+init_mx_once (void)
+{
+ static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+ __gthread_once (&once, init_mx);
+}
+#endif
+
+/* Register the library private __gcov_dump method
+ to the global list. */
+
+__attribute__((constructor))
+static void
+register_dumper (void)
+{
+ init_mx_once ();
+ __gthread_mutex_lock (&__gcov_dump_mx);
+ this_dumper.next_dumper = __gcov_dumper_list;
+ __gcov_dumper_list = &this_dumper;
+ __gthread_mutex_unlock (&__gcov_dump_mx);
+}
+
+__attribute__((destructor))
+static void
+unregister_dumper (void)
+{
+ struct dumper_entry *dumper;
+ struct dumper_entry *prev_dumper = 0;
+
+ init_mx_once ();
+ __gthread_mutex_lock (&__gcov_dump_mx);
+ dumper = __gcov_dumper_list;
+
+ while (dumper)
+ {
+ if (dumper->dumper == &__gcov_dump)
+ {
+ if (prev_dumper)
+ prev_dumper->next_dumper = dumper->next_dumper;
+ else
+ __gcov_dumper_list = dumper->next_dumper;
+ break;
+ }
+ prev_dumper = dumper;
+ dumper = dumper->next_dumper;
+ }
+ __gthread_mutex_unlock (&__gcov_dump_mx);
+}
+
+/* Public interface to dump profile data for all shared libraries
+ via registered dumpers from the libraries. This interface
+ has default visibility (unlike gcov_dump which has hidden
+ visbility. */
+
+void
+__gcov_dump_all (void)
+{
+ struct dumper_entry *dumper;
+
+ init_mx_once ();
+ __gthread_mutex_lock (&__gcov_dump_mx);
+
+ dumper = __gcov_dumper_list;
+ while (dumper)
+ {
+ dumper->dumper ();
+ dumper = dumper->next_dumper;
+ }
+ __gthread_mutex_unlock (&__gcov_dump_mx);
+}
+
#endif /* L_gcov_dump */
#ifdef L_gcov_sampling
diff --git a/gcc-4.9/libgcc/libgcov-util.c b/gcc-4.9/libgcc/libgcov-util.c
index d1401b5..09d5886 100644
--- a/gcc-4.9/libgcc/libgcov-util.c
+++ b/gcc-4.9/libgcc/libgcov-util.c
@@ -88,7 +88,9 @@ static int k_ctrs_types;
/* The longest length of all the filenames. */
static int max_filename_len;
-/* Merge functions for counters. */
+/* 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. */
#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE,
static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
#include "gcov-counter.def"
@@ -430,11 +432,7 @@ read_gcda_file (const char *filename)
/* Read version. */
version = gcov_read_unsigned ();
if (version != GCOV_VERSION)
- {
- fnotice (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
- gcov_close ();
- return NULL;
- }
+ warning (0, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
/* Instantiate a gcov_info object. */
curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
diff --git a/gcc-4.9/libgcc/libgcov.h b/gcc-4.9/libgcc/libgcov.h
index 25534ac..fdfb650 100644
--- a/gcc-4.9/libgcc/libgcov.h
+++ b/gcc-4.9/libgcc/libgcov.h
@@ -272,8 +272,15 @@ extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
/* Function to reset all counters to 0. */
extern void __gcov_reset (void);
-/* Function to enable early write of profile information so far. */
-extern void __gcov_dump (void);
+/* Function to enable early write of profile information so far.
+ __gcov_dump is also used by __gcov_dump_all. The latter
+ depends on __GCOV_DUMP to have hidden or protected visibility
+ so that each library has its own copy of the registered dumper. */
+extern void __gcov_dump (void) ATTRIBUTE_HIDDEN;
+
+/* Call __gcov_dump registered from each shared library.
+ This function must have default visibility. */
+void __gcov_dump_all (void);
/* The merge function that just sums the counters. */
extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;