From fe37be54bf30dcc788901421a0dcb6b779363aab Mon Sep 17 00:00:00 2001 From: hubicka Date: Fri, 9 Aug 2013 18:23:23 +0000 Subject: [PATCH] * cgraph.h (cgraph_node): Add profile_id. * value-prof.c (cgraph_node_map): Turn into pointer_map. (init_node_map): Rewrite to handle hashes increas of incremental IDs. (del_node_map): Update. (find_func_by_funcdef_no): Replace by ... (find_func_by_profile_id): ... this one. (gimple_ic_transform): Do not remove useful histograms when speculation is not done; dump info when indirect call removal can happen at LTO. * value-prof.h (find_func_by_profile_id, gimple_ic): Declare. * gcov-io.h (__gcov_indirect_call_profiler): Replace by ... (__gcov_indirect_call_profiler_v2): .. this one. * profile.h (init_node_map): Update. * coverage.c (coverage_compute_profile_id): New function. * coverage.h (coverage_compute_profile_id): Declare. * tree-profile.c (init_ic_make_global_vars): Make __gcov_indirect_call_callee and __gcov_indirect_call_counters global. (gimple_init_edge_profiler): Update prototype of __gcov_indirect_call_profiler. (gimple_gen_ic_func_profiler): Simplify. (tree_profiling): Use init_node_map git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@201634 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 25 +++++++++++ gcc/cgraph.h | 2 + gcc/coverage.c | 22 ++++++++++ gcc/coverage.h | 3 ++ gcc/gcov-io.h | 2 +- gcc/profile.h | 2 +- gcc/tree-profile.c | 43 +++++++++---------- gcc/value-prof.c | 119 ++++++++++++++++++++++++++++++++++++++--------------- gcc/value-prof.h | 2 + libgcc/ChangeLog | 7 ++++ libgcc/libgcov.c | 26 +++++++++--- 11 files changed, 188 insertions(+), 65 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0207b56..bca5102 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,30 @@ 2013-08-09 Jan Hubicka + * cgraph.h (cgraph_node): Add profile_id. + * value-prof.c (cgraph_node_map): Turn into pointer_map. + (init_node_map): Rewrite to handle hashes increas of incremental + IDs. + (del_node_map): Update. + (find_func_by_funcdef_no): Replace by ... + (find_func_by_profile_id): ... this one. + (gimple_ic_transform): Do not remove useful histograms when + speculation is not done; dump info when indirect call removal + can happen at LTO. + * value-prof.h (find_func_by_profile_id, gimple_ic): Declare. + * gcov-io.h (__gcov_indirect_call_profiler): Replace by ... + (__gcov_indirect_call_profiler_v2): .. this one. + * profile.h (init_node_map): Update. + * coverage.c (coverage_compute_profile_id): New function. + * coverage.h (coverage_compute_profile_id): Declare. + * tree-profile.c (init_ic_make_global_vars): Make + __gcov_indirect_call_callee and __gcov_indirect_call_counters global. + (gimple_init_edge_profiler): Update prototype of + __gcov_indirect_call_profiler. + (gimple_gen_ic_func_profiler): Simplify. + (tree_profiling): Use init_node_map + +2013-08-09 Jan Hubicka + * cgraphbuild.c (cgraph_rebuild_references): Rebuild only non-speculative refs. * cgraph.c (cgraph_update_edge_in_call_site_hash): New function. diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 78cee29..f67287f 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -300,6 +300,8 @@ struct GTY(()) cgraph_node { int count_materialization_scale; /* Unique id of the node. */ int uid; + /* ID assigned by the profiling. */ + unsigned int profile_id; /* Set when decl is an abstract function pointed to by the ABSTRACT_DECL_ORIGIN of a reachable function. */ diff --git a/gcc/coverage.c b/gcc/coverage.c index 7c395f4..9b664cf 100644 --- a/gcc/coverage.c +++ b/gcc/coverage.c @@ -539,6 +539,28 @@ coverage_compute_lineno_checksum (void) return chksum; } +/* Compute profile ID. This is better to be unique in whole program. */ + +unsigned +coverage_compute_profile_id (struct cgraph_node *n) +{ + expanded_location xloc + = expand_location (DECL_SOURCE_LOCATION (n->symbol.decl)); + unsigned chksum = xloc.line; + + chksum = coverage_checksum_string (chksum, xloc.file); + chksum = coverage_checksum_string + (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->symbol.decl))); + if (first_global_object_name) + chksum = coverage_checksum_string + (chksum, first_global_object_name); + chksum = coverage_checksum_string + (chksum, aux_base_name); + + /* Non-negative integers are hopefully small enough to fit in all targets. */ + return chksum & 0x7fffffff; +} + /* Compute cfg checksum for the current function. The checksum is calculated carefully so that source code changes that doesn't affect the control flow graph diff --git a/gcc/coverage.h b/gcc/coverage.h index 21afe72..342d73e 100644 --- a/gcc/coverage.h +++ b/gcc/coverage.h @@ -35,6 +35,9 @@ extern void coverage_end_function (unsigned, unsigned); /* Compute the control flow checksum for the current function. */ extern unsigned coverage_compute_cfg_checksum (void); +/* Compute the profile id of function N. */ +extern unsigned coverage_compute_profile_id (struct cgraph_node *n); + /* Compute the line number checksum for the current function. */ extern unsigned coverage_compute_lineno_checksum (void); diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index 08fe7b9..db1a6bf 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -515,7 +515,7 @@ extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned); extern void __gcov_pow2_profiler (gcov_type *, gcov_type); extern void __gcov_one_value_profiler (gcov_type *, gcov_type); -extern void __gcov_indirect_call_profiler (gcov_type *, gcov_type, void *, void *); +extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *); extern void __gcov_average_profiler (gcov_type *, gcov_type); extern void __gcov_ior_profiler (gcov_type *, gcov_type); diff --git a/gcc/profile.h b/gcc/profile.h index d0b637d..b31cf78 100644 --- a/gcc/profile.h +++ b/gcc/profile.h @@ -43,7 +43,7 @@ extern void mcf_smooth_cfg (void); extern gcov_type sum_edge_counts (vec *edges); -extern void init_node_map (void); +extern void init_node_map (bool); extern void del_node_map (void); extern void get_working_sets (void); diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c index e276a59..3a508a0 100644 --- a/gcc/tree-profile.c +++ b/gcc/tree-profile.c @@ -57,8 +57,8 @@ static GTY(()) tree ptr_void; /* Do initialization work for the edge profiler. */ /* Add code: - static gcov* __gcov_indirect_call_counters; // pointer to actual counter - static void* __gcov_indirect_call_callee; // actual callee address + __thread gcov* __gcov_indirect_call_counters; // pointer to actual counter + __thread void* __gcov_indirect_call_callee; // actual callee address */ static void init_ic_make_global_vars (void) @@ -72,7 +72,8 @@ init_ic_make_global_vars (void) get_identifier ("__gcov_indirect_call_callee"), ptr_void); TREE_STATIC (ic_void_ptr_var) = 1; - TREE_PUBLIC (ic_void_ptr_var) = 0; + TREE_PUBLIC (ic_void_ptr_var) = 1; + DECL_EXTERNAL (ic_void_ptr_var) = 1; DECL_ARTIFICIAL (ic_void_ptr_var) = 1; DECL_INITIAL (ic_void_ptr_var) = NULL; if (targetm.have_tls) @@ -87,7 +88,8 @@ init_ic_make_global_vars (void) get_identifier ("__gcov_indirect_call_counters"), gcov_type_ptr); TREE_STATIC (ic_gcov_type_ptr_var) = 1; - TREE_PUBLIC (ic_gcov_type_ptr_var) = 0; + TREE_PUBLIC (ic_gcov_type_ptr_var) = 1; + DECL_EXTERNAL (ic_gcov_type_ptr_var) = 1; DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1; DECL_INITIAL (ic_gcov_type_ptr_var) = NULL; if (targetm.have_tls) @@ -155,14 +157,14 @@ gimple_init_edge_profiler (void) init_ic_make_global_vars (); - /* void (*) (gcov_type *, gcov_type, void *, void *) */ + /* void (*) (gcov_type, void *) */ ic_profiler_fn_type = build_function_type_list (void_type_node, - gcov_type_ptr, gcov_type_node, + gcov_type_node, ptr_void, - ptr_void, NULL_TREE); + NULL_TREE); tree_indirect_call_profiler_fn - = build_fn_decl ("__gcov_indirect_call_profiler", + = build_fn_decl ("__gcov_indirect_call_profiler_v2", ic_profiler_fn_type); TREE_NOTHROW (tree_indirect_call_profiler_fn) = 1; DECL_ATTRIBUTES (tree_indirect_call_profiler_fn) @@ -352,7 +354,7 @@ gimple_gen_ic_func_profiler (void) struct cgraph_node * c_node = cgraph_get_node (current_function_decl); gimple_stmt_iterator gsi; gimple stmt1, stmt2; - tree tree_uid, cur_func, counter_ptr, ptr_var, void0; + tree tree_uid, cur_func, void0; if (cgraph_only_called_directly_p (c_node)) return; @@ -361,27 +363,20 @@ gimple_gen_ic_func_profiler (void) /* Insert code: - stmt1: __gcov_indirect_call_profiler (__gcov_indirect_call_counters, - current_function_funcdef_no, - ¤t_function_decl, - __gcov_indirect_call_callee); + stmt1: __gcov_indirect_call_profiler_v2 (profile_id, + ¤t_function_decl) */ - gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR)); + gsi = gsi_after_labels (split_edge (single_succ_edge (ENTRY_BLOCK_PTR))); cur_func = force_gimple_operand_gsi (&gsi, build_addr (current_function_decl, current_function_decl), true, NULL_TREE, true, GSI_SAME_STMT); - counter_ptr = force_gimple_operand_gsi (&gsi, ic_gcov_type_ptr_var, - true, NULL_TREE, true, - GSI_SAME_STMT); - ptr_var = force_gimple_operand_gsi (&gsi, ic_void_ptr_var, - true, NULL_TREE, true, - GSI_SAME_STMT); - tree_uid = build_int_cst (gcov_type_node, current_function_funcdef_no); - stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 4, - counter_ptr, tree_uid, cur_func, ptr_var); + tree_uid = build_int_cst + (gcov_type_node, cgraph_get_node (current_function_decl)->profile_id); + stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 2, + tree_uid, cur_func); gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); /* Set __gcov_indirect_call_callee to 0, @@ -461,7 +456,7 @@ tree_profiling (void) cgraphunit.c:ipa_passes(). */ gcc_assert (cgraph_state == CGRAPH_STATE_IPA_SSA); - init_node_map(); + init_node_map (true); FOR_EACH_DEFINED_FUNCTION (node) { diff --git a/gcc/value-prof.c b/gcc/value-prof.c index c198c65..f110277 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -1173,24 +1173,67 @@ gimple_mod_subtract_transform (gimple_stmt_iterator *si) return true; } -static vec cgraph_node_map - = vNULL; +static pointer_map_t *cgraph_node_map; -/* Initialize map from FUNCDEF_NO to CGRAPH_NODE. */ +/* Initialize map from PROFILE_ID to CGRAPH_NODE. + When LOCAL is true, the PROFILE_IDs are computed. when it is false we assume + that the PROFILE_IDs was already assigned. */ void -init_node_map (void) +init_node_map (bool local) { struct cgraph_node *n; + cgraph_node_map = pointer_map_create (); - if (get_last_funcdef_no ()) - cgraph_node_map.safe_grow_cleared (get_last_funcdef_no ()); - - FOR_EACH_FUNCTION (n) - { - if (DECL_STRUCT_FUNCTION (n->symbol.decl)) - cgraph_node_map[DECL_STRUCT_FUNCTION (n->symbol.decl)->funcdef_no] = n; - } + FOR_EACH_DEFINED_FUNCTION (n) + if (cgraph_function_with_gimple_body_p (n) + && !cgraph_only_called_directly_p (n)) + { + void **val; + if (local) + { + n->profile_id = coverage_compute_profile_id (n); + while ((val = pointer_map_contains (cgraph_node_map, + (void *)(size_t)n->profile_id)) + || !n->profile_id) + { + if (dump_file) + fprintf (dump_file, "Local profile-id %i conflict" + " with nodes %s/%i %s/%i\n", + n->profile_id, + cgraph_node_name (n), + n->symbol.order, + symtab_node_name (*(symtab_node*)val), + (*(symtab_node *)val)->symbol.order); + n->profile_id = (n->profile_id + 1) & 0x7fffffff; + } + } + else if (!n->profile_id) + { + if (dump_file) + fprintf (dump_file, + "Node %s/%i has no profile-id" + " (profile feedback missing?)\n", + cgraph_node_name (n), + n->symbol.order); + continue; + } + else if ((val = pointer_map_contains (cgraph_node_map, + (void *)(size_t)n->profile_id))) + { + if (dump_file) + fprintf (dump_file, + "Node %s/%i has IP profile-id %i conflict. " + "Giving up.\n", + cgraph_node_name (n), + n->symbol.order, + n->profile_id); + *val = NULL; + continue; + } + *pointer_map_insert (cgraph_node_map, + (void *)(size_t)n->profile_id) = (void *)n; + } } /* Delete the CGRAPH_NODE_MAP. */ @@ -1198,27 +1241,20 @@ init_node_map (void) void del_node_map (void) { - cgraph_node_map.release (); + pointer_map_destroy (cgraph_node_map); } /* Return cgraph node for function with pid */ -static inline struct cgraph_node* -find_func_by_funcdef_no (int func_id) +struct cgraph_node* +find_func_by_profile_id (int profile_id) { - int max_id = get_last_funcdef_no (); - if (func_id >= max_id || cgraph_node_map[func_id] == NULL) - { - if (flag_profile_correction) - inform (DECL_SOURCE_LOCATION (current_function_decl), - "Inconsistent profile: indirect call target (%d) does not exist", func_id); - else - error ("Inconsistent profile: indirect call target (%d) does not exist", func_id); - - return NULL; - } - - return cgraph_node_map[func_id]; + void **val = pointer_map_contains (cgraph_node_map, + (void *)(size_t)profile_id); + if (val) + return (struct cgraph_node *)*val; + else + return NULL; } /* Perform sanity check on the indirect call target. Due to race conditions, @@ -1415,10 +1451,12 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) val = histogram->hvalue.counters [0]; count = histogram->hvalue.counters [1]; all = histogram->hvalue.counters [2]; - gimple_remove_histogram_value (cfun, stmt, histogram); if (4 * count <= 3 * all) - return false; + { + gimple_remove_histogram_value (cfun, stmt, histogram); + return false; + } bb_all = gimple_bb (stmt)->count; /* The order of CHECK_COUNTER calls is important - @@ -1426,16 +1464,31 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) and we want to make count <= all <= bb_all. */ if ( check_counter (stmt, "ic", &all, &bb_all, bb_all) || check_counter (stmt, "ic", &count, &all, all)) - return false; + { + gimple_remove_histogram_value (cfun, stmt, histogram); + return false; + } if (all > 0) prob = GCOV_COMPUTE_SCALE (count, all); else prob = 0; - direct_call = find_func_by_funcdef_no ((int)val); + direct_call = find_func_by_profile_id ((int)val); if (direct_call == NULL) - return false; + { + if (val) + { + if (dump_file) + { + fprintf (dump_file, "Indirect call -> direct call from other module"); + print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM); + fprintf (dump_file, "=> %i (will resolve only with LTO)\n", (int)val); + } + } + return false; + } + gimple_remove_histogram_value (cfun, stmt, histogram); if (!check_ic_target (stmt, direct_call)) return false; diff --git a/gcc/value-prof.h b/gcc/value-prof.h index fca6bc5..57f249d 100644 --- a/gcc/value-prof.h +++ b/gcc/value-prof.h @@ -103,6 +103,8 @@ extern void gimple_gen_average_profiler (histogram_value, unsigned, unsigned); extern void gimple_gen_ior_profiler (histogram_value, unsigned, unsigned); extern void stream_out_histogram_value (struct output_block *, histogram_value); extern void stream_in_histogram_value (struct lto_input_block *, gimple); +extern struct cgraph_node* find_func_by_profile_id (int func_id); + /* In profile.c. */ extern void init_branch_prob (void); diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 1eb277e..f098a42 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,10 @@ +2013-08-06 Jan Hubicka + + * libgcov.c (__gcov_indirect_call_callee, + __gcov_indirect_call_counters): New global vars. + (__gcov_indirect_call_profiler): replace by ... + (__gcov_indirect_call_profiler_v2) ... this one. + 2013-08-06 Caroline Tice * config.host (extra_parts): Add vtv_start.o, vtv_end.o diff --git a/libgcc/libgcov.c b/libgcc/libgcov.c index a93eddb..93b8bd9 100644 --- a/libgcc/libgcov.c +++ b/libgcc/libgcov.c @@ -1121,6 +1121,21 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value) #ifdef L_gcov_indirect_call_profiler +/* These two variables are used to actually track caller and callee. Keep + them in TLS memory so races are not common (they are written to often). + The variables are set directly by GCC instrumented code, so declaration + here must match one in tree-profile.c */ + +#ifdef HAVE_CC_TLS +__thread +#endif +void * __gcov_indirect_call_callee; +#ifdef HAVE_CC_TLS +__thread +#endif +gcov_type * __gcov_indirect_call_counters; + + /* By default, the C++ compiler will use function addresses in the vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero tells the compiler to use function descriptors instead. The value @@ -1140,16 +1155,15 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value) /* Tries to determine the most common value among its inputs. */ void -__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value, - void* cur_func, void* callee_func) +__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func) { /* If the C++ virtual tables contain function descriptors then one function may have multiple descriptors and we need to dereference the descriptors to see if they point to the same function. */ - if (cur_func == callee_func - || (VTABLE_USES_DESCRIPTORS && callee_func - && *(void **) cur_func == *(void **) callee_func)) - __gcov_one_value_profiler_body (counter, value); + if (cur_func == __gcov_indirect_call_callee + || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee + && *(void **) cur_func == *(void **) __gcov_indirect_call_callee)) + __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value); } #endif -- 2.7.4