From f1c18afafc2b321465ae6b07ede127095942d7dc Mon Sep 17 00:00:00 2001 From: Rong Xu Date: Wed, 6 Aug 2014 17:50:42 -0700 Subject: [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 --- gcc-4.9/libgcc/ChangeLog | 4 + gcc-4.9/libgcc/config/libbid/ChangeLog | 4 + gcc-4.9/libgcc/dyn-ipa.c | 803 ++++++++++++++++++++++++++++++++- gcc-4.9/libgcc/libgcov-driver.c | 151 +++++-- gcc-4.9/libgcc/libgcov-interface.c | 94 ++++ gcc-4.9/libgcc/libgcov-util.c | 10 +- gcc-4.9/libgcc/libgcov.h | 11 +- 7 files changed, 1022 insertions(+), 55 deletions(-) (limited to 'gcc-4.9/libgcc') diff --git a/gcc-4.9/libgcc/ChangeLog b/gcc-4.9/libgcc/ChangeLog index 9f17a7d14..1edf552db 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 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 60132c466..c3f83862a 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 eb5ae9cc9..e66b02bbc 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 e829fe588..dc8cf362b 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 77d839512..a3effa411 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 d1401b54d..09d588675 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 25534acd5..fdfb6502e 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; -- cgit v1.2.3