Patch by Tomas Bily <tbily@suse.cz>

Patch by Tomas Bily  <tbily@suse.cz>
        * 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

	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.

From-SVN: r120975
This commit is contained in:
Tomas Bily 2007-01-19 19:34:02 +01:00 committed by Jan Hubicka
parent b7c75af4b5
commit 6bad26173d
17 changed files with 635 additions and 13 deletions

View File

@ -1,3 +1,44 @@
2007-01-19 Tomas Bily <tbily@suse.cz>
* 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 <basile@starynkevitch.net>
* doc/gty.texi (Options): Document the mark_hook option to

View File

@ -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) \

View File

@ -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),

View File

@ -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
{

View File

@ -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;

View File

@ -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) \

View File

@ -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

View File

@ -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;
}

View File

@ -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 ();
}

View File

@ -1,3 +1,10 @@
2007-01-19 Tomas Bily <tbily@suse.cz>
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 <manu@gcc.gnu.org>
PR c++/17947

View File

@ -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

View File

@ -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" } } */

View File

@ -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
}

View File

@ -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" } } */

View File

@ -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"

View File

@ -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 ();
}

View File

@ -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);