diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7fff5e85176..7f8ac0e9622 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,44 @@ +2007-01-19 Tomas Bily + + * cgraphunit.c (cgraph_finalize_function): Updating of pid + * tree-profile.c: + (tree_init_ic_make_global_vars): New function + (tree_init_edge_profiler): call of tree_init_ic_make_global_vars + (tree_gen_ic_profiler): New function + (tree_gen_ic_func_profiler): New function + (tree_profiling): Added calling of tree_gen_ic_func_profiler + (tree_profile_hooks): Added hook for indirec/virtual calls + * value-prof.c (tree_find_values_to_profile): New case for + indirect calls + (tree_values_to_profile): Call for determining indirect/virtual + counters + (tree_indirect_call_to_profile): New function + (tree_ic_transform): New function + (tree_ic): New function + (find_func_by_pid): New function + (init_pid_map): New function + (tree_value_profile_transformations): Added check for + indirect/virtual call transformation + * value-prof.h (enum hist_type): New counter type for + indirect/virtual calls + (profile_hooks): Added new hook for profiling indirect/virtual + calls + * profile.c (instrument_values): New case for indirect/virtual + call added + * gcov-io.h (GCOV_LAST_VALUE_COUNTER): Changed to 6 + (GCOV_COUNTER_V_INDIR): New counter type + (GCOV_COUNTER_NAMES): New name of counter "indirect" added + (GCOV_MERGE_FUNCTIONS): New merge function for indirect/virtual + call added + * cgraph.c: Definition of cgraph_max_pid + (cgraph_create_node): Default init of pid attribute + * cgraph.h: Declaration of cgraph_max_pid + (struct cgraph_node): Added pid attribute + * libgcov.c (__gcov_indirect_call_profiler): New function + (__gcov_one_value_profiler_body): New function + (__gcov_one_value_profiler): Body was moved to + __gcov_one_value_profiler_body and calls it + 2007-01-19 Basile Starynkevitch * doc/gty.texi (Options): Document the mark_hook option to diff --git a/gcc/Makefile.in b/gcc/Makefile.in index c63dec01989..59ac3dd8b4f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1013,7 +1013,8 @@ LIB2FUNCS_ST = _eprintf __gcc_bcmp LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \ _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \ _gcov_execv _gcov_execvp _gcov_execve \ - _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler + _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \ + _gcov_indirect_call_profiler FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \ _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \ @@ -2355,7 +2356,7 @@ profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ tree-profile.o : tree-profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) \ $(FUNCTION_H) toplev.h $(COVERAGE_H) $(TREE_H) value-prof.h $(TREE_DUMP_H) \ - tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) $(GGC_H) gt-tree-profile.h + tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) $(GGC_H) gt-tree-profile.h $(CGRAPH_H) value-prof.o : value-prof.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(BASIC_BLOCK_H) hard-reg-set.h value-prof.h $(EXPR_H) output.h $(FLAGS_H) \ $(RECOG_H) insn-config.h $(OPTABS_H) $(REGS_H) $(GGC_H) $(DIAGNOSTIC_H) \ diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 1f7f0ad6742..13b7fcd2938 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -109,6 +109,9 @@ int cgraph_n_nodes; /* Maximal uid used in cgraph nodes. */ int cgraph_max_uid; +/* Maximal pid used for profiling */ +int cgraph_max_pid; + /* Set when whole unit has been analyzed so we can access global info. */ bool cgraph_global_info_ready = false; @@ -161,6 +164,7 @@ cgraph_create_node (void) node = GGC_CNEW (struct cgraph_node); node->next = cgraph_nodes; node->uid = cgraph_max_uid++; + node->pid = -1; node->order = cgraph_order++; if (cgraph_nodes) cgraph_nodes->previous = node; @@ -655,7 +659,7 @@ void dump_cgraph_node (FILE *f, struct cgraph_node *node) { struct cgraph_edge *edge; - fprintf (f, "%s/%i:", cgraph_node_name (node), node->uid); + fprintf (f, "%s/%i(%i):", cgraph_node_name (node), node->uid, node->pid); if (node->global.inlined_to) fprintf (f, " (inline copy in %s/%i)", cgraph_node_name (node->global.inlined_to), diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 3a5805e9b81..179a5f1714e 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -180,6 +180,10 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) into clone before compiling so the function in original form can be inlined later. This pointer points to the clone. */ tree inline_decl; + + /* unique id for profiling. pid is not suitable because of different + number of cfg nodes with -fprofile-generate and -fprofile-use */ + int pid; }; struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) @@ -253,6 +257,7 @@ struct cgraph_asm_node GTY(()) extern GTY(()) struct cgraph_node *cgraph_nodes; extern GTY(()) int cgraph_n_nodes; extern GTY(()) int cgraph_max_uid; +extern GTY(()) int cgraph_max_pid; extern bool cgraph_global_info_ready; enum cgraph_state { diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 5a0b9a99c49..055399e8330 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -444,6 +444,7 @@ cgraph_finalize_function (tree decl, bool nested) if (node->local.finalized) cgraph_reset_node (node); + node->pid = cgraph_max_pid ++; notice_global_symbol (decl); node->decl = decl; node->local.finalized = true; diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index f3607d97890..e819eb3fa2a 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -327,23 +327,26 @@ typedef HOST_WIDEST_INT gcov_type; #define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */ #define GCOV_COUNTER_V_DELTA 4 /* The most common difference between consecutive values of expression. */ -#define GCOV_LAST_VALUE_COUNTER 4 /* The last of counters used for value + +#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */ +#define GCOV_LAST_VALUE_COUNTER 5 /* The last of counters used for value profiling. */ -#define GCOV_COUNTERS 5 +#define GCOV_COUNTERS 6 /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) /* A list of human readable names of the counters */ -#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", "delta"} +#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", "delta", "indirect_call"} /* Names of merge functions for counters. */ #define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ "__gcov_merge_add", \ "__gcov_merge_add", \ "__gcov_merge_single", \ - "__gcov_merge_delta"} + "__gcov_merge_delta", \ + "__gcov_merge_single" } /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ diff --git a/gcc/libgcov.c b/gcc/libgcov.c index 494759e6bed..880686eba04 100644 --- a/gcc/libgcov.c +++ b/gcc/libgcov.c @@ -726,7 +726,6 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value) } #endif -#ifdef L_gcov_one_value_profiler /* Tries to determine the most common value among its inputs. Checks if the value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1] is incremented. If this is not the case and COUNTERS[1] is not zero, @@ -737,8 +736,8 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value) In any case, COUNTERS[2] is incremented. */ -void -__gcov_one_value_profiler (gcov_type *counters, gcov_type value) +static inline void +__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value) { if (value == counters[0]) counters[1]++; @@ -751,6 +750,24 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value) counters[1]--; counters[2]++; } + +#ifdef L_gcov_one_value_profiler +void +__gcov_one_value_profiler (gcov_type *counters, gcov_type value) +{ + __gcov_one_value_profiler_body (counters, value); +} +#endif + +#ifdef L_gcov_indirect_call_profiler +/* 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) +{ + if (cur_func == callee_func) + __gcov_one_value_profiler_body (counter, value); +} #endif #ifdef L_gcov_fork diff --git a/gcc/predict.c b/gcc/predict.c index 61d8547def0..b29d04a56cf 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -1638,6 +1638,7 @@ counts_to_freqs (void) count_max = MAX (true_count_max, 1); FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) bb->frequency = (bb->count * BB_FREQ_MAX + count_max / 2) / count_max; + return true_count_max; } diff --git a/gcc/profile.c b/gcc/profile.c index 7c38b7fe149..ef6b3266157 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -192,6 +192,10 @@ instrument_values (histogram_values values) t = GCOV_COUNTER_V_DELTA; break; + case HIST_TYPE_INDIR_CALL: + t = GCOV_COUNTER_V_INDIR; + break; + default: gcc_unreachable (); } @@ -216,6 +220,10 @@ instrument_values (histogram_values values) (profile_hooks->gen_const_delta_profiler) (hist, t, 0); break; + case HIST_TYPE_INDIR_CALL: + (profile_hooks->gen_ic_profiler) (hist, t, 0); + break; + default: gcc_unreachable (); } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 34b02acefa8..e0178ad1a1d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2007-01-19 Tomas Bily + + gcc.dg/tree-prof/indir-call-prof.c: New. + g++.dg/dg.exp: Add tree-prof subdirectory. + g++.dg/tree-prof/indir-call-prof.C: New. + g++.dg/tree-prof/tree-prof.exp: New. + 2007-01-19 Manuel Lopez-Ibanez PR c++/17947 diff --git a/gcc/testsuite/g++.dg/dg.exp b/gcc/testsuite/g++.dg/dg.exp index 5ecb161bee7..2581139eac3 100644 --- a/gcc/testsuite/g++.dg/dg.exp +++ b/gcc/testsuite/g++.dg/dg.exp @@ -41,6 +41,7 @@ set tests [prune $tests $srcdir/$subdir/special/*] set tests [prune $tests $srcdir/$subdir/tls/*] set tests [prune $tests $srcdir/$subdir/vect/*] set tests [prune $tests $srcdir/$subdir/gomp/*] +set tests [prune $tests $srcdir/$subdir/tree-prof/*] # Main loop. dg-runtest $tests "" $DEFAULT_CXXFLAGS diff --git a/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C b/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C new file mode 100644 index 00000000000..98ab302663f --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C @@ -0,0 +1,39 @@ +/* { dg-options "-O2 -fdump-tree-optimized -fdump-tree-tree_profile" } */ + +struct A { + A () {} + + virtual int AA (void) + { return 0; } + +}; + +struct B : public A { + B () {} + + virtual int AA (void) + { return 1; } +}; + +int +main (void) +{ + A a; + B b; + + A* p; + + p = &a; + p->AA (); + + p = &b; + p->AA (); + + return 0; +} + +/* { dg-final-use { scan-tree-dump "Indirect call -> direct call.* AA transformation on insn" "tree_profile"} } */ +/* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */ +/* { dg-final-use { cleanup-tree-dump "optimized" } } */ +/* { dg-final-use { cleanup-tree-dump "tree_profile" } } */ + diff --git a/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp b/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp new file mode 100644 index 00000000000..f9ebbd69595 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp @@ -0,0 +1,53 @@ +# Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Test the functionality of programs compiled with profile-directed block +# ordering using -fprofile-generate followed by -fbranch-use. + +load_lib target-supports.exp + +# Some targets don't support tree profiling. +if { ![check_profiling_available ""] } { + return +} + +# The procedures in profopt.exp need these parameters. +set tool g++ +set prof_ext "gcda gcno" + +# Override the list defined in profopt.exp. +set PROFOPT_OPTIONS [list {}] + +if $tracelevel then { + strace $tracelevel +} + +# Load support procs. +load_lib profopt.exp + +# These are globals used by profopt-execute. The first is options +# needed to generate profile data, the second is options to use the +# profile data. +set profile_option "-fprofile-generate" +set feedback_option "-fprofile-use" + +foreach src [lsort [glob -nocomplain $srcdir/$subdir/*.C]] { + # If we're only testing specific files and this isn't one of them, skip it. + if ![runtest_file_p $runtests $src] then { + continue + } + profopt-execute $src +} diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c new file mode 100644 index 00000000000..101b9725f86 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c @@ -0,0 +1,45 @@ +/* { dg-options "-O2 -fdump-tree-optimized -fdump-tree-tree_profile" } */ + +static int a1 (void) +{ + return 10; +} + +static int a2 (void) +{ + return 0; +} + +typedef int (*tp) (void); + +static tp aa [] = {a2, a1, a1, a1, a1}; + +void setp (int (**pp) (void), int i) +{ + if (!i) + *pp = aa [i]; + else + *pp = aa [(i & 2) + 1]; +} + +int +main (void) +{ + int (*p) (void); + int i; + + for (i = 0; i < 10; i ++) + { + setp (&p, i); + p (); + } + + return 0; +} + +/* { dg-final-use { scan-tree-dump "Indirect call -> direct call.* a1 transformation on insn" "tree_profile"} } */ +/* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */ +/* { dg-final-use { cleanup-tree-dump "optimized" } } */ +/* { dg-final-use { cleanup-tree-dump "tree_profile" } } */ + + diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c index 3ff39f34aec..2a4ec2ae9eb 100644 --- a/gcc/tree-profile.c +++ b/gcc/tree-profile.c @@ -45,15 +45,54 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "timevar.h" #include "value-prof.h" #include "ggc.h" +#include "cgraph.h" static GTY(()) tree gcov_type_node; static GTY(()) tree tree_interval_profiler_fn; static GTY(()) tree tree_pow2_profiler_fn; static GTY(()) tree tree_one_value_profiler_fn; +static GTY(()) tree tree_indirect_call_profiler_fn; +static GTY(()) tree ic_void_ptr_var; +static GTY(()) tree ic_gcov_type_ptr_var; +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 callie addres +*/ +static void +tree_init_ic_make_global_vars (void) +{ + tree gcov_type_ptr; + + ptr_void = build_pointer_type (void_type_node); + + ic_void_ptr_var + = build_decl (VAR_DECL, + get_identifier ("__gcov_indirect_call_callee"), + ptr_void); + TREE_STATIC (ic_void_ptr_var) = 1; + TREE_PUBLIC (ic_void_ptr_var) = 0; + DECL_ARTIFICIAL (ic_void_ptr_var) = 1; + DECL_INITIAL (ic_void_ptr_var) = NULL; + assemble_variable (ic_void_ptr_var, 0, 0, 0); + + gcov_type_ptr = build_pointer_type (get_gcov_type ()); + ic_gcov_type_ptr_var + = build_decl (VAR_DECL, + 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; + DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1; + DECL_INITIAL (ic_gcov_type_ptr_var) = NULL; + assemble_variable (ic_gcov_type_ptr_var, 0, 0, 0); +} + static void tree_init_edge_profiler (void) { @@ -61,6 +100,7 @@ tree_init_edge_profiler (void) tree pow2_profiler_fn_type; tree one_value_profiler_fn_type; tree gcov_type_ptr; + tree ic_profiler_fn_type; if (!gcov_type_node) { @@ -93,6 +133,18 @@ tree_init_edge_profiler (void) tree_one_value_profiler_fn = build_fn_decl ("__gcov_one_value_profiler", one_value_profiler_fn_type); + + tree_init_ic_make_global_vars (); + + /* void (*) (gcov_type *, gcov_type, void *, void *) */ + ic_profiler_fn_type + = build_function_type_list (void_type_node, + gcov_type_ptr, gcov_type_node, + ptr_void, + ptr_void, NULL_TREE); + tree_indirect_call_profiler_fn + = build_fn_decl ("__gcov_indirect_call_profiler", + ic_profiler_fn_type); } } @@ -201,6 +253,90 @@ tree_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base) bsi_insert_before (&bsi, call, BSI_SAME_STMT); } + +/* Output instructions as GIMPLE trees for code to find the most + common called function in indirect call. + VALUE is the call expression whose indirect callie is profiled. + TAG is the tag of the section for counters, BASE is offset of the + counter position. */ + +static void +tree_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base) +{ + tree tmp1, stmt1, stmt2, stmt3; + tree stmt = value->hvalue.stmt; + block_stmt_iterator bsi = bsi_for_stmt (stmt); + tree ref = tree_coverage_counter_ref (tag, base), ref_ptr; + + ref_ptr = force_gimple_operand_bsi (&bsi, + build_addr (ref, current_function_decl), + true, NULL_TREE); + + /* Insert code: + + __gcov_indirect_call_counters = get_relevant_counter_ptr (); + __gcov_indirect_call_callee = (void *) indirect call argument; + */ + + tmp1 = create_tmp_var (ptr_void, "PROF"); + stmt1 = build2 (GIMPLE_MODIFY_STMT, + build_pointer_type (get_gcov_type ()), + ic_gcov_type_ptr_var, ref_ptr); + stmt2 = build2 (GIMPLE_MODIFY_STMT, ptr_void, tmp1, + unshare_expr (value->hvalue.value)); + stmt3 = build2 (GIMPLE_MODIFY_STMT, ptr_void, + ic_void_ptr_var, tmp1); + + bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); + bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); + bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); +} + + +/* Output instructions as GIMPLE trees for code to find the most + common called function in indirect call. Insert instructions at the + begining of every possible called function. + */ + +static void +tree_gen_ic_func_profiler (void) +{ + struct cgraph_node * c_node = cgraph_node (current_function_decl); + block_stmt_iterator bsi; + edge e; + basic_block bb; + edge_iterator ei; + tree stmt1; + tree args, tree_uid, cur_func; + + if (flag_unit_at_a_time) + { + if (!c_node->needed) + return; + } + + tree_init_edge_profiler (); + + FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs) + { + bb = split_edge (e); + bsi = bsi_start (bb); + cur_func = force_gimple_operand_bsi (&bsi, + build_addr (current_function_decl, + current_function_decl), + true, NULL_TREE); + tree_uid = build_int_cst (gcov_type_node, c_node->pid); + args = tree_cons (NULL_TREE, ic_gcov_type_ptr_var, + tree_cons (NULL_TREE, tree_uid, + tree_cons (NULL_TREE, cur_func, + tree_cons (NULL_TREE, + ic_void_ptr_var, + NULL_TREE)))); + stmt1 = build_function_call_expr (tree_indirect_call_profiler_fn, args); + bsi_insert_after (&bsi, stmt1, BSI_SAME_STMT); + } +} + /* Output instructions as GIMPLE trees for code to find the most common value of a difference between two evaluations of an expression. VALUE is the expression whose value is profiled. TAG is the tag of the @@ -242,6 +378,11 @@ tree_profiling (void) if (cgraph_state == CGRAPH_STATE_FINISHED) return 0; branch_prob (); + + if (! flag_branch_probabilities + && flag_profile_values) + tree_gen_ic_func_profiler (); + if (flag_branch_probabilities && flag_profile_values && flag_value_profile_transformations) @@ -278,7 +419,8 @@ struct profile_hooks tree_profile_hooks = tree_gen_interval_profiler, /* gen_interval_profiler */ tree_gen_pow2_profiler, /* gen_pow2_profiler */ tree_gen_one_value_profiler, /* gen_one_value_profiler */ - tree_gen_const_delta_profiler /* gen_const_delta_profiler */ + tree_gen_const_delta_profiler,/* gen_const_delta_profiler */ + tree_gen_ic_profiler, /* gen_ic_profiler */ }; #include "gt-tree-profile.h" diff --git a/gcc/value-prof.c b/gcc/value-prof.c index 2b2bb1b6c1e..f23fd68dd6f 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -40,6 +40,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "coverage.h" #include "tree.h" #include "gcov-io.h" +#include "cgraph.h" #include "timevar.h" #include "tree-pass.h" #include "toplev.h" @@ -60,6 +61,11 @@ static struct value_prof_hooks *value_prof_hooks; FIXME: This transformation was removed together with RTL based value profiling. + 3) Indirect/virtual call specialization. If we can determine most + common function callee in indirect/virtual call. We can use this + information to improve code effectivity (espetialy info for + inliner). + Every such optimization should add its requirements for profiled values to insn_values_to_profile function. This function is called from branch_prob in profile.c and the requested values are instrumented by it in the first @@ -81,6 +87,7 @@ static bool tree_divmod_fixed_value_transform (tree); static bool tree_mod_pow2_value_transform (tree); static bool tree_mod_subtract_transform (tree); static bool tree_stringops_transform (block_stmt_iterator *); +static bool tree_ic_transform (tree); /* Allocate histogram value. */ @@ -254,6 +261,19 @@ dump_histogram_value (FILE *dump_file, histogram_value hist) } fprintf (dump_file, ".\n"); break; + case HIST_TYPE_INDIR_CALL: + fprintf (dump_file, "Indirect call "); + if (hist->hvalue.counters) + { + fprintf (dump_file, "value:"HOST_WIDEST_INT_PRINT_DEC + " match:"HOST_WIDEST_INT_PRINT_DEC + " all:"HOST_WIDEST_INT_PRINT_DEC, + (HOST_WIDEST_INT) hist->hvalue.counters[0], + (HOST_WIDEST_INT) hist->hvalue.counters[1], + (HOST_WIDEST_INT) hist->hvalue.counters[2]); + } + fprintf (dump_file, ".\n"); + break; } } @@ -432,7 +452,8 @@ tree_value_profile_transformations (void) && (tree_mod_subtract_transform (stmt) || tree_divmod_fixed_value_transform (stmt) || tree_mod_pow2_value_transform (stmt) - || tree_stringops_transform (&bsi))) + || tree_stringops_transform (&bsi) + || tree_ic_transform (stmt))) { stmt = bsi_stmt (bsi); changed = true; @@ -967,6 +988,201 @@ tree_mod_subtract_transform (tree stmt) return true; } +static struct cgraph_node** pid_map = NULL; + +/* Initialize map of pids (pid -> cgraph node) */ + +static void +init_pid_map (void) +{ + struct cgraph_node *n; + + if (pid_map != NULL) + return; + + pid_map + = (struct cgraph_node**) xmalloc (sizeof (struct cgraph_node*) * cgraph_max_pid); + + for (n = cgraph_nodes; n; n = n->next) + { + if (n->pid != -1) + pid_map [n->pid] = n; + } +} + +/* Return cgraph node for function with pid */ + +static inline struct cgraph_node* +find_func_by_pid (int pid) +{ + init_pid_map (); + + return pid_map [pid]; +} + +/* Do transformation + + if (actual_callee_addres == addres_of_most_common_function/method) + do direct call + else + old call + */ + +static tree +tree_ic (tree stmt, tree call, struct cgraph_node* direct_call, + int prob, gcov_type count, gcov_type all) +{ + tree stmt1, stmt2, stmt3; + tree tmp1, tmpv; + tree label_decl1 = create_artificial_label (); + tree label_decl2 = create_artificial_label (); + tree label1, label2; + tree bb1end, bb2end, bb3end; + tree new_call; + basic_block bb, bb2, bb3, bb4; + tree optype = build_pointer_type (void_type_node); + edge e12, e13, e23, e24, e34; + block_stmt_iterator bsi; + int region; + + bb = bb_for_stmt (stmt); + bsi = bsi_for_stmt (stmt); + + tmpv = create_tmp_var (optype, "PROF"); + tmp1 = create_tmp_var (optype, "PROF"); + stmt1 = build2 (GIMPLE_MODIFY_STMT, optype, tmpv, + unshare_expr (TREE_OPERAND (call, 0))); + stmt2 = build2 (GIMPLE_MODIFY_STMT, optype, tmp1, + fold_convert (optype, build_addr (direct_call->decl, + current_function_decl))); + stmt3 = build3 (COND_EXPR, void_type_node, + build2 (NE_EXPR, boolean_type_node, tmp1, tmpv), + build1 (GOTO_EXPR, void_type_node, label_decl2), + build1 (GOTO_EXPR, void_type_node, label_decl1)); + bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); + bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); + bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); + bb1end = stmt3; + + label1 = build1 (LABEL_EXPR, void_type_node, label_decl1); + stmt1 = unshare_expr (stmt); + new_call = get_call_expr_in (stmt1); + TREE_OPERAND (new_call, 0) = build_addr (direct_call->decl, + current_function_decl); + bsi_insert_before (&bsi, label1, BSI_SAME_STMT); + bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); + bb2end = stmt1; + + label2 = build1 (LABEL_EXPR, void_type_node, label_decl2); + bsi_insert_before (&bsi, label2, BSI_SAME_STMT); + bb3end = stmt; + + /* Fix CFG. */ + /* Edge e23 connects bb2 to bb3, etc. */ + e12 = split_block (bb, bb1end); + bb2 = e12->dest; + bb2->count = count; + e23 = split_block (bb2, bb2end); + bb3 = e23->dest; + bb3->count = all - count; + e34 = split_block (bb3, bb3end); + bb4 = e34->dest; + bb4->count = all; + + e12->flags &= ~EDGE_FALLTHRU; + e12->flags |= EDGE_FALSE_VALUE; + e12->probability = prob; + e12->count = count; + + e13 = make_edge (bb, bb3, EDGE_TRUE_VALUE); + e13->probability = REG_BR_PROB_BASE - prob; + e13->count = all - count; + + remove_edge (e23); + + e24 = make_edge (bb2, bb4, EDGE_FALLTHRU); + e24->probability = REG_BR_PROB_BASE; + e24->count = count; + e34->probability = REG_BR_PROB_BASE; + e34->count = all - count; + + /* Fix eh edges */ + region = lookup_stmt_eh_region (stmt); + if (region >=0 && tree_could_throw_p (stmt1)) + { + add_stmt_to_eh_region (stmt1, region); + make_eh_edges (stmt1); + } + + if (region >=0 && tree_could_throw_p (stmt)) + { + tree_purge_dead_eh_edges (bb4); + make_eh_edges (stmt); + } + + return stmt1; +} + +/* + For every checked indirect/virtual call determine if most common pid of + function/class method has probability more than 50%. If yes modify code of + this call to: + */ + +static bool +tree_ic_transform (tree stmt) +{ + histogram_value histogram; + gcov_type val, count, all; + int prob; + tree call, callee, modify; + struct cgraph_node *direct_call; + + call = get_call_expr_in (stmt); + + if (!call || TREE_CODE (call) != CALL_EXPR) + return false; + + callee = TREE_OPERAND (call, 0); + + if (TREE_CODE (callee) == ADDR_EXPR) + return false; + + histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL); + if (!histogram) + return false; + + 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; + + prob = (count * REG_BR_PROB_BASE + all / 2) / all; + direct_call = find_func_by_pid ((int)val); + + if (direct_call == NULL) + return false; + + modify = tree_ic (stmt, call, direct_call, prob, count, all); + + if (dump_file) + { + fprintf (dump_file, "Indirect call -> direct call "); + print_generic_expr (dump_file, call, TDF_SLIM); + fprintf (dump_file, "=> "); + print_generic_expr (dump_file, direct_call->decl, TDF_SLIM); + fprintf (dump_file, " transformation on insn "); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + fprintf (dump_file, " to "); + print_generic_stmt (dump_file, modify, TDF_SLIM); + } + + return true; +} + /* Return true if the stringop FNDECL with ARGLIST shall be profiled. */ static bool interesting_stringop_to_profile_p (tree fndecl, tree arglist) @@ -1260,6 +1476,34 @@ tree_divmod_values_to_profile (tree stmt, histogram_values *values) } } +/* Find calls inside STMT for that we want to measure histograms for + indirect/virtual call optimization. */ + +static void +tree_indirect_call_to_profile (tree stmt, histogram_values *values) +{ + tree call; + tree callee; + + call = get_call_expr_in (stmt); + + if (!call || TREE_CODE (call) != CALL_EXPR) + return; + + callee = TREE_OPERAND (call, 0); + + if (TREE_CODE (callee) == ADDR_EXPR) + return; + + VEC_reserve (histogram_value, heap, *values, 3); + + VEC_quick_push (histogram_value, *values, + gimple_alloc_histogram_value (cfun, HIST_TYPE_INDIR_CALL, + stmt, callee)); + + return; +} + /* Find values inside STMT for that we want to measure histograms for string operations. */ static void @@ -1303,6 +1547,7 @@ tree_values_to_profile (tree stmt, histogram_values *values) { tree_divmod_values_to_profile (stmt, values); tree_stringops_values_to_profile (stmt, values); + tree_indirect_call_to_profile (stmt, values); } } @@ -1339,6 +1584,10 @@ tree_find_values_to_profile (histogram_values *values) hist->n_counters = 4; break; + case HIST_TYPE_INDIR_CALL: + hist->n_counters = 3; + break; + default: gcc_unreachable (); } diff --git a/gcc/value-prof.h b/gcc/value-prof.h index 33670da4856..78c9e887a27 100644 --- a/gcc/value-prof.h +++ b/gcc/value-prof.h @@ -29,8 +29,10 @@ enum hist_type HIST_TYPE_POW2, /* Histogram of power of 2 values. */ HIST_TYPE_SINGLE_VALUE, /* Tries to identify the value that is (almost) always constant. */ - HIST_TYPE_CONST_DELTA /* Tries to identify the (almost) always constant + HIST_TYPE_CONST_DELTA, /* Tries to identify the (almost) always constant difference between two evaluations of a value. */ + HIST_TYPE_INDIR_CALL /* Tries to identify the function that is (almost) + called in indirect call */ }; #define COUNTER_FOR_HIST_TYPE(TYPE) ((int) (TYPE) + GCOV_FIRST_VALUE_COUNTER) @@ -94,6 +96,9 @@ struct profile_hooks { /* Insert code to find the most common value of a difference between two evaluations of an expression. */ void (*gen_const_delta_profiler) (histogram_value, unsigned, unsigned); + + /* Insert code to find the most common indirect call */ + void (*gen_ic_profiler) (histogram_value, unsigned, unsigned); }; histogram_value gimple_histogram_value (struct function *, tree);