gcc/gcc/tree-profile.c
Martin Liska 3dafb85c8e IPA C++ refactoring 4/N
* cgraph.h (symtab_node):
	(bool needed_p (void)): created from decide_is_symbol_needed
	(bool referred_to_p (void)): created from referred_to_p
	(static cgraph_node *get_for_asmname (tree asmname)): created from symtab_node_for_asm
	* cgraph.h (cgraph_node):
	(void assemble_thunks_and_aliases (void)): created from assemble_thunks_and_aliases
	(void expand (void)): created from expand_function
	(static void finalize_function (tree, bool)): created from cgraph_finalize_function
	(static cgraph_local_info *local_info (tree decl)): created from cgraph_local_info
	(static cgraph_global_info *global_info (tree)): created from cgraph_global_info
	(static cgraph_rtl_info *rtl_info (tree)): created from cgraph_rtl_info
	* cgraph.h (varpool_node):
	(static void add (tree decl): created from varpool_add_new_variable
	* cgraph.h (cgraph_edge):
	void remove (void);
	(void remove_caller (void)): created from cgraph_edge_remove_caller
	(void remove_callee (void)): created from cgraph_edge_remove_callee
	(void set_call_stmt (gimple new_stmt, bool update_speculative = true)):
	  created from cgraph_set_call_stmt	
	(void redirect_callee (cgraph_node *n)): created from cgraph_redirect_edge_callee
	(cgraph_edge *make_direct (cgraph_node *callee)): created from cgraph_make_edge_direct
	(cgraph_edge *make_speculative (cgraph_node *n2, gcov_type direct_count,
	  gimple redirect_call_stmt_to_callee (void)): created from cgraph_turn_edge_to_speculative
	(void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect, ipa_ref *&reference)):
	  created from cgraph_speculative_call_info
	(cgraph_edge * clone (cgraph_node *n, gimple call_stmt, unsigned stmt_uid, gcov_type count_scale,
	  int freq_scale, bool update_original)): created from cgraph_clone_edge
	(cgraph_edge *resolve_speculation (tree callee_decl)): created from cgraph_resolve_speculation
	(bool cannot_lead_to_return_p (void)): created from cannot_lead_to_return_p
	(bool recursive_p (void)): created from cgraph_edge_recursive_p
	(bool maybe_hot_p (void)): created from cgraph_maybe_hot_edge_p
	(static unsigned int rebuild_edges (void)): created from rebuild_cgraph_edges 
	(static void rebuild_references (void)): created from cgraph_rebuild_references
	* cgraph.h (symbol_table):
	(create_reference): renamed from add_reference
	(maybe_create_reference): renamed from maybe_add_reference
	(void register_symbol (symtab_node *node)): new function
	(void clear_asm_symbols (void)): new function
	(void unregister (symtab_node *node)): new function
	(void release_symbol (cgraph_node *node, int uid)): new function
	(cgraph_node * allocate_cgraph_symbol (void)): new function
	(void initialize (void)): created from cgraph_init
	(symtab_node *first_symbol (void)):new function
	(asm_node *first_asm_symbol (void)):new function
	(symtab_node *first_defined_symbol (void)):new function
	(varpool_node *first_variable (void)):new function
	(varpool_node *next_variable (varpool_node *node)):new function
	(varpool_node *first_static_initializer (void)):new function
	(varpool_node *next_static_initializer (varpool_node *node)):new function
	(varpool_node *first_defined_variable (void)):new function
	(varpool_node *next_defined_variable (varpool_node *node)):new function
	(cgraph_node *first_defined_function (void)):new function
	(cgraph_node *next_defined_function (cgraph_node *node)):new function
	(cgraph_node *first_function (void)):new function
	(cgraph_node *next_function (cgraph_node *node)):new function
	(cgraph_node *first_function_with_gimple_body (void)):new function
	(asm_node *finalize_toplevel_asm (tree asm_str)): created from add_asm_node
	(bool remove_unreachable_nodes (bool before_inlining_p, FILE *file)):
	  created from symtab_remove_unreachable_nodes
	(void remove_unreferenced_decls (void)): created from varpool_remove_unreferenced_decls
	(void process_new_functions (void)): created from cgraph_process_new_functions
	(void process_same_body_aliases (void)): created from cgraph_process_same_body_aliases
	(bool output_variables (void)): created from varpool_node::output_variables
	(void output_asm_statements (void)): created from output_asm_statements
	(void finalize_compilation_unit (void)): created from finalize_compilation_unit
	(void compile (void)): created from compile
	(void output_weakrefs (void)): created from output_weakrefs
	(cgraph_node *create_empty (void)): created from cgraph_node::create_empty
	(cgraph_edge *create_edge (cgraph_node *caller, cgraph_node *callee, gimple call_stmt,
	  gcov_type count, int freq, bool indir_unknown_callee)): created from cgraph_node::create_edge
	(void free_edge (cgraph_edge *e)): created from cgraph_free_edge
	(cgraph_node *next_function_with_gimple_body (cgraph_node *node)):
	  created from cgraph_next_function_with_gimple_body
	(void remove_edge_removal_hook (cgraph_edge_hook_list *)):
	  created from cgraph_remove_edge_removal_hook
	(cgraph_node_hook_list *add_cgraph_removal_hook (cgraph_node_hook, void *)):
	  created from cgraph_add_node_removal_hook
	(void remove_cgraph_removal_hook (cgraph_node_hook_list *)):
	  created from cgraph_remove_node_removal_hook
	(varpool_node_hook_list *add_varpool_removal_hook (varpool_node_hook, void *)):
	  created from varpool_add_node_removal_hook
	(void remove_varpool_removal_hook (varpool_node_hook_list *)):
	  created from varpool_remove_node_removal_hook
	(cgraph_node_hook_list *add_cgraph_insertion_hook (cgraph_node_hook, void *)):
	  created from cgraph_add_function_insertion_hook
	(void remove_cgraph_insertion_hook (cgraph_node_hook_list *)):
	  created from cgraph_remove_function_insertion_hook
	(varpool_node_hook_list *add_varpool_insertion_hook (varpool_node_hook, void *)):
	  created from varpool_add_variable_insertion_hook
	(void remove_varpool_insertion_hook (varpool_node_hook_list *)):
	  created from varpool_remove_variable_insertion_hook
	(cgraph_2edge_hook_list *add_edge_duplication_hook (cgraph_2edge_hook, void *)):
	  created from cgraph_add_edge_duplication_hook
	(void remove_edge_duplication_hook (cgraph_2edge_hook_list *)):
	  created from cgraph_remove_edge_duplication_hook
	(cgraph_2node_hook_list *add_cgraph_duplication_hook (cgraph_2node_hook, void *)):
	  created from cgraph_add_node_duplication_hook
	(void remove_cgraph_duplication_hook (cgraph_2node_hook_list *)):
	  created from cgraph_remove_node_duplication_hook
	(void call_edge_removal_hooks (cgraph_edge *e)):
	  created from cgraph_call_edge_removal_hooks
	(void call_cgraph_insertion_hooks (cgraph_node *node)):
	  created from call_function_insertion_hooks 
	(void call_cgraph_removal_hooks (cgraph_node *node)):
	  created from cgraph_call_node_removal_hooks 
	(void call_cgraph_duplication_hooks (cgraph_node *node, cgraph_node *node2)):
	  created from cgraph_node::call_duplication_hooks 
	(void call_edge_duplication_hooks (cgraph_edge *cs1, cgraph_edge *cs2)):
	  created from cgraph_call_edge_duplication_hooks
	(void call_varpool_removal_hooks (varpool_node *node)):
	  created from varpool_call_node_removal_hooks
	(void call_varpool_insertion_hooks (varpool_node *node)):
	  created from varpool_call_variable_insertion_hooks
	(void insert_to_assembler_name_hash (symtab_node *node, bool with_clones)):
	  created from insert_to_assembler_name_hash
	(void unlink_from_assembler_name_hash (symtab_node *node, bool with_clones)):
	  created from unlink_from_assembler_name_hash
	(void symtab_prevail_in_asm_name_hash (symtab_node *node)):
	  created from symtab_prevail_in_asm_name_hash
	(void symtab_initialize_asm_name_hash (void)):
	  created from symtab_initialize_asm_name_hash
	(void change_decl_assembler_name (tree decl, tree name)):
	  created from change_decl_assembler_name
	(void materialize_all_clones (void)): created from cgraph_materialize_all_clones
	(static hashval_t decl_assembler_name_hash (const_tree asmname)):
	  created from decl_assembler_name_hash
	(static bool decl_assembler_name_equal (tree decl, const_tree asmname)):
	  created from decl_assembler_name_equal
	(static hashval_t hash_node_by_assembler_name (const void *p)):
	  created from hash_node_by_assembler_name
	(static int eq_assembler_name (const void *p1, const void *p2)):
	  created from eq_assembler_name

From-SVN: r214422
2014-08-25 13:01:47 +00:00

716 lines
23 KiB
C

/* Calculate branch probabilities, and basic block execution counts.
Copyright (C) 1990-2014 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support.
Converted to use trees by Dale Johannesen, Apple Computer.
This file is part of GCC.
GCC 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 3, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* Generate basic block profile instrumentation and auxiliary files.
Tree-based version. See profile.c for overview. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "flags.h"
#include "function.h"
#include "basic-block.h"
#include "diagnostic-core.h"
#include "coverage.h"
#include "tree.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-expr.h"
#include "is-a.h"
#include "gimple.h"
#include "varasm.h"
#include "tree-nested.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimplify-me.h"
#include "gimple-ssa.h"
#include "cgraph.h"
#include "tree-cfg.h"
#include "stringpool.h"
#include "tree-ssanames.h"
#include "tree-into-ssa.h"
#include "tree-pass.h"
#include "value-prof.h"
#include "profile.h"
#include "target.h"
#include "tree-cfgcleanup.h"
#include "tree-nested.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 tree_time_profiler_fn;
static GTY(()) tree tree_average_profiler_fn;
static GTY(()) tree tree_ior_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:
__thread gcov* __gcov_indirect_call_counters; // pointer to actual counter
__thread void* __gcov_indirect_call_callee; // actual callee address
__thread int __gcov_function_counter; // time profiler function counter
*/
static void
init_ic_make_global_vars (void)
{
tree gcov_type_ptr;
ptr_void = build_pointer_type (void_type_node);
/* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */
if (flag_lto)
{
ic_void_ptr_var
= build_decl (UNKNOWN_LOCATION, VAR_DECL,
get_identifier ("__gcov_indirect_call_callee_ltopriv"),
ptr_void);
TREE_PUBLIC (ic_void_ptr_var) = 1;
DECL_COMMON (ic_void_ptr_var) = 1;
DECL_VISIBILITY (ic_void_ptr_var) = VISIBILITY_HIDDEN;
DECL_VISIBILITY_SPECIFIED (ic_void_ptr_var) = true;
}
else
{
ic_void_ptr_var
= build_decl (UNKNOWN_LOCATION, VAR_DECL,
get_identifier ("__gcov_indirect_call_callee"),
ptr_void);
TREE_PUBLIC (ic_void_ptr_var) = 1;
DECL_EXTERNAL (ic_void_ptr_var) = 1;
}
TREE_STATIC (ic_void_ptr_var) = 1;
DECL_ARTIFICIAL (ic_void_ptr_var) = 1;
DECL_INITIAL (ic_void_ptr_var) = NULL;
if (targetm.have_tls)
set_decl_tls_model (ic_void_ptr_var, decl_default_tls_model (ic_void_ptr_var));
varpool_node::finalize_decl (ic_void_ptr_var);
gcov_type_ptr = build_pointer_type (get_gcov_type ());
/* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */
if (flag_lto)
{
ic_gcov_type_ptr_var
= build_decl (UNKNOWN_LOCATION, VAR_DECL,
get_identifier ("__gcov_indirect_call_counters_ltopriv"),
gcov_type_ptr);
TREE_PUBLIC (ic_gcov_type_ptr_var) = 1;
DECL_COMMON (ic_gcov_type_ptr_var) = 1;
DECL_VISIBILITY (ic_gcov_type_ptr_var) = VISIBILITY_HIDDEN;
DECL_VISIBILITY_SPECIFIED (ic_gcov_type_ptr_var) = true;
}
else
{
ic_gcov_type_ptr_var
= build_decl (UNKNOWN_LOCATION, VAR_DECL,
get_identifier ("__gcov_indirect_call_counters"),
gcov_type_ptr);
TREE_PUBLIC (ic_gcov_type_ptr_var) = 1;
DECL_EXTERNAL (ic_gcov_type_ptr_var) = 1;
}
TREE_STATIC (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)
set_decl_tls_model (ic_gcov_type_ptr_var, decl_default_tls_model (ic_gcov_type_ptr_var));
varpool_node::finalize_decl (ic_gcov_type_ptr_var);
}
/* Create the type and function decls for the interface with gcov. */
void
gimple_init_edge_profiler (void)
{
tree interval_profiler_fn_type;
tree pow2_profiler_fn_type;
tree one_value_profiler_fn_type;
tree gcov_type_ptr;
tree ic_profiler_fn_type;
tree average_profiler_fn_type;
tree time_profiler_fn_type;
if (!gcov_type_node)
{
gcov_type_node = get_gcov_type ();
gcov_type_ptr = build_pointer_type (gcov_type_node);
/* void (*) (gcov_type *, gcov_type, int, unsigned) */
interval_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node,
integer_type_node,
unsigned_type_node, NULL_TREE);
tree_interval_profiler_fn
= build_fn_decl ("__gcov_interval_profiler",
interval_profiler_fn_type);
TREE_NOTHROW (tree_interval_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_interval_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_interval_profiler_fn));
/* void (*) (gcov_type *, gcov_type) */
pow2_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node,
NULL_TREE);
tree_pow2_profiler_fn = build_fn_decl ("__gcov_pow2_profiler",
pow2_profiler_fn_type);
TREE_NOTHROW (tree_pow2_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_pow2_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_pow2_profiler_fn));
/* void (*) (gcov_type *, gcov_type) */
one_value_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node,
NULL_TREE);
tree_one_value_profiler_fn
= build_fn_decl ("__gcov_one_value_profiler",
one_value_profiler_fn_type);
TREE_NOTHROW (tree_one_value_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_one_value_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_one_value_profiler_fn));
init_ic_make_global_vars ();
/* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */
if (flag_lto)
{
/* void (*) (gcov_type, 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);
}
else
{
/* void (*) (gcov_type, void *) */
ic_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_node,
ptr_void,
NULL_TREE);
tree_indirect_call_profiler_fn
= 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)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_indirect_call_profiler_fn));
/* void (*) (gcov_type *, gcov_type, void *) */
time_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_ptr, NULL_TREE);
tree_time_profiler_fn
= build_fn_decl ("__gcov_time_profiler",
time_profiler_fn_type);
TREE_NOTHROW (tree_time_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_time_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_time_profiler_fn));
/* void (*) (gcov_type *, gcov_type) */
average_profiler_fn_type
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node, NULL_TREE);
tree_average_profiler_fn
= build_fn_decl ("__gcov_average_profiler",
average_profiler_fn_type);
TREE_NOTHROW (tree_average_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_average_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_average_profiler_fn));
tree_ior_profiler_fn
= build_fn_decl ("__gcov_ior_profiler",
average_profiler_fn_type);
TREE_NOTHROW (tree_ior_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_ior_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL,
DECL_ATTRIBUTES (tree_ior_profiler_fn));
/* LTO streamer needs assembler names. Because we create these decls
late, we need to initialize them by hand. */
DECL_ASSEMBLER_NAME (tree_interval_profiler_fn);
DECL_ASSEMBLER_NAME (tree_pow2_profiler_fn);
DECL_ASSEMBLER_NAME (tree_one_value_profiler_fn);
DECL_ASSEMBLER_NAME (tree_indirect_call_profiler_fn);
DECL_ASSEMBLER_NAME (tree_time_profiler_fn);
DECL_ASSEMBLER_NAME (tree_average_profiler_fn);
DECL_ASSEMBLER_NAME (tree_ior_profiler_fn);
}
}
/* Output instructions as GIMPLE trees to increment the edge
execution count, and insert them on E. We rely on
gsi_insert_on_edge to preserve the order. */
void
gimple_gen_edge_profiler (int edgeno, edge e)
{
tree ref, one, gcov_type_tmp_var;
gimple stmt1, stmt2, stmt3;
ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno);
one = build_int_cst (gcov_type_node, 1);
gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node,
NULL, "PROF_edge_counter");
stmt1 = gimple_build_assign (gcov_type_tmp_var, ref);
gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node,
NULL, "PROF_edge_counter");
stmt2 = gimple_build_assign_with_ops (PLUS_EXPR, gcov_type_tmp_var,
gimple_assign_lhs (stmt1), one);
stmt3 = gimple_build_assign (unshare_expr (ref), gimple_assign_lhs (stmt2));
gsi_insert_on_edge (e, stmt1);
gsi_insert_on_edge (e, stmt2);
gsi_insert_on_edge (e, stmt3);
}
/* Emits code to get VALUE to instrument at GSI, and returns the
variable containing the value. */
static tree
prepare_instrumented_value (gimple_stmt_iterator *gsi, histogram_value value)
{
tree val = value->hvalue.value;
if (POINTER_TYPE_P (TREE_TYPE (val)))
val = fold_convert (build_nonstandard_integer_type
(TYPE_PRECISION (TREE_TYPE (val)), 1), val);
return force_gimple_operand_gsi (gsi, fold_convert (gcov_type_node, val),
true, NULL_TREE, true, GSI_SAME_STMT);
}
/* Output instructions as GIMPLE trees to increment the interval histogram
counter. VALUE is the expression whose value is profiled. TAG is the
tag of the section for counters, BASE is offset of the counter position. */
void
gimple_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base)
{
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref = tree_coverage_counter_ref (tag, base), ref_ptr;
gimple call;
tree val;
tree start = build_int_cst_type (integer_type_node,
value->hdata.intvl.int_start);
tree steps = build_int_cst_type (unsigned_type_node,
value->hdata.intvl.steps);
ref_ptr = force_gimple_operand_gsi (&gsi,
build_addr (ref, current_function_decl),
true, NULL_TREE, true, GSI_SAME_STMT);
val = prepare_instrumented_value (&gsi, value);
call = gimple_build_call (tree_interval_profiler_fn, 4,
ref_ptr, val, start, steps);
gsi_insert_before (&gsi, call, GSI_NEW_STMT);
}
/* Output instructions as GIMPLE trees to increment the power of two histogram
counter. VALUE is the expression whose value is profiled. TAG is the tag
of the section for counters, BASE is offset of the counter position. */
void
gimple_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
{
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref_ptr = tree_coverage_counter_addr (tag, base);
gimple call;
tree val;
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
val = prepare_instrumented_value (&gsi, value);
call = gimple_build_call (tree_pow2_profiler_fn, 2, ref_ptr, val);
gsi_insert_before (&gsi, call, GSI_NEW_STMT);
}
/* Output instructions as GIMPLE trees for code to find the most common value.
VALUE is the expression whose value is profiled. TAG is the tag of the
section for counters, BASE is offset of the counter position. */
void
gimple_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base)
{
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref_ptr = tree_coverage_counter_addr (tag, base);
gimple call;
tree val;
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
val = prepare_instrumented_value (&gsi, value);
call = gimple_build_call (tree_one_value_profiler_fn, 2, ref_ptr, val);
gsi_insert_before (&gsi, call, GSI_NEW_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 callee is profiled.
TAG is the tag of the section for counters, BASE is offset of the
counter position. */
void
gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
{
tree tmp1;
gimple stmt1, stmt2, stmt3;
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref_ptr = tree_coverage_counter_addr (tag, base);
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
/* Insert code:
stmt1: __gcov_indirect_call_counters = get_relevant_counter_ptr ();
stmt2: tmp1 = (void *) (indirect call argument value)
stmt3: __gcov_indirect_call_callee = tmp1;
*/
stmt1 = gimple_build_assign (ic_gcov_type_ptr_var, ref_ptr);
tmp1 = make_temp_ssa_name (ptr_void, NULL, "PROF");
stmt2 = gimple_build_assign (tmp1, unshare_expr (value->hvalue.value));
stmt3 = gimple_build_assign (ic_void_ptr_var, gimple_assign_lhs (stmt2));
gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT);
gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT);
gsi_insert_before (&gsi, stmt3, GSI_SAME_STMT);
}
/* Output instructions as GIMPLE trees for code to find the most
common called function in indirect call. Insert instructions at the
beginning of every possible called function.
*/
void
gimple_gen_ic_func_profiler (void)
{
struct cgraph_node * c_node = cgraph_node::get (current_function_decl);
gimple_stmt_iterator gsi;
gimple stmt1, stmt2;
tree tree_uid, cur_func, void0;
if (c_node->only_called_directly_p ())
return;
gimple_init_edge_profiler ();
/* Insert code:
stmt1: __gcov_indirect_call_profiler_v2 (profile_id,
&current_function_decl)
*/
gsi =
gsi_after_labels (split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun))));
cur_func = force_gimple_operand_gsi (&gsi,
build_addr (current_function_decl,
current_function_decl),
true, NULL_TREE,
true, GSI_SAME_STMT);
tree_uid = build_int_cst
(gcov_type_node, cgraph_node::get (current_function_decl)->profile_id);
/* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */
if (flag_lto)
{
tree counter_ptr, ptr_var;
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);
stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 4,
counter_ptr, tree_uid, cur_func, ptr_var);
}
else
{
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,
so that calls from other modules won't get misattributed
to the last caller of the current callee. */
void0 = build_int_cst (build_pointer_type (void_type_node), 0);
stmt2 = gimple_build_assign (ic_void_ptr_var, void0);
gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT);
}
/* Output instructions as GIMPLE tree at the beginning for each function.
TAG is the tag of the section for counters, BASE is offset of the
counter position and GSI is the iterator we place the counter. */
void
gimple_gen_time_profiler (unsigned tag, unsigned base,
gimple_stmt_iterator &gsi)
{
tree ref_ptr = tree_coverage_counter_addr (tag, base);
gimple call;
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
call = gimple_build_call (tree_time_profiler_fn, 1, ref_ptr);
gsi_insert_before (&gsi, call, GSI_NEW_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
section for counters, BASE is offset of the counter position. */
void
gimple_gen_const_delta_profiler (histogram_value value ATTRIBUTE_UNUSED,
unsigned tag ATTRIBUTE_UNUSED,
unsigned base ATTRIBUTE_UNUSED)
{
/* FIXME implement this. */
#ifdef ENABLE_CHECKING
internal_error ("unimplemented functionality");
#endif
gcc_unreachable ();
}
/* Output instructions as GIMPLE trees to increment the average histogram
counter. VALUE is the expression whose value is profiled. TAG is the
tag of the section for counters, BASE is offset of the counter position. */
void
gimple_gen_average_profiler (histogram_value value, unsigned tag, unsigned base)
{
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref_ptr = tree_coverage_counter_addr (tag, base);
gimple call;
tree val;
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE,
true, GSI_SAME_STMT);
val = prepare_instrumented_value (&gsi, value);
call = gimple_build_call (tree_average_profiler_fn, 2, ref_ptr, val);
gsi_insert_before (&gsi, call, GSI_NEW_STMT);
}
/* Output instructions as GIMPLE trees to increment the ior histogram
counter. VALUE is the expression whose value is profiled. TAG is the
tag of the section for counters, BASE is offset of the counter position. */
void
gimple_gen_ior_profiler (histogram_value value, unsigned tag, unsigned base)
{
gimple stmt = value->hvalue.stmt;
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
tree ref_ptr = tree_coverage_counter_addr (tag, base);
gimple call;
tree val;
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
val = prepare_instrumented_value (&gsi, value);
call = gimple_build_call (tree_ior_profiler_fn, 2, ref_ptr, val);
gsi_insert_before (&gsi, call, GSI_NEW_STMT);
}
/* Profile all functions in the callgraph. */
static unsigned int
tree_profiling (void)
{
struct cgraph_node *node;
/* This is a small-ipa pass that gets called only once, from
cgraphunit.c:ipa_passes(). */
gcc_assert (symtab->state == IPA_SSA);
init_node_map (true);
FOR_EACH_DEFINED_FUNCTION (node)
{
if (!gimple_has_body_p (node->decl))
continue;
/* Don't profile functions produced for builtin stuff. */
if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
continue;
/* Do not instrument extern inline functions when testing coverage.
While this is not perfectly consistent (early inlined extern inlines
will get acocunted), testsuite expects that. */
if (DECL_EXTERNAL (node->decl)
&& flag_test_coverage)
continue;
push_cfun (DECL_STRUCT_FUNCTION (node->decl));
/* Local pure-const may imply need to fixup the cfg. */
if (execute_fixup_cfg () & TODO_cleanup_cfg)
cleanup_tree_cfg ();
branch_prob ();
if (! flag_branch_probabilities
&& flag_profile_values)
gimple_gen_ic_func_profiler ();
if (flag_branch_probabilities
&& flag_profile_values
&& flag_value_profile_transformations)
gimple_value_profile_transformations ();
/* The above could hose dominator info. Currently there is
none coming in, this is a safety valve. It should be
easy to adjust it, if and when there is some. */
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
pop_cfun ();
}
/* Drop pure/const flags from instrumented functions. */
FOR_EACH_DEFINED_FUNCTION (node)
{
if (!gimple_has_body_p (node->decl)
|| !(!node->clone_of
|| node->decl != node->clone_of->decl))
continue;
/* Don't profile functions produced for builtin stuff. */
if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
continue;
node->set_const_flag (false, false);
node->set_pure_flag (false, false);
}
/* Update call statements and rebuild the cgraph. */
FOR_EACH_DEFINED_FUNCTION (node)
{
basic_block bb;
if (!gimple_has_body_p (node->decl)
|| !(!node->clone_of
|| node->decl != node->clone_of->decl))
continue;
/* Don't profile functions produced for builtin stuff. */
if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
continue;
push_cfun (DECL_STRUCT_FUNCTION (node->decl));
FOR_EACH_BB_FN (bb, cfun)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
if (is_gimple_call (stmt))
update_stmt (stmt);
}
}
/* re-merge split blocks. */
cleanup_tree_cfg ();
update_ssa (TODO_update_ssa);
cgraph_edge::rebuild_edges ();
pop_cfun ();
}
handle_missing_profiles ();
del_node_map ();
return 0;
}
namespace {
const pass_data pass_data_ipa_tree_profile =
{
SIMPLE_IPA_PASS, /* type */
"profile", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_IPA_PROFILE, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_ipa_tree_profile : public simple_ipa_opt_pass
{
public:
pass_ipa_tree_profile (gcc::context *ctxt)
: simple_ipa_opt_pass (pass_data_ipa_tree_profile, ctxt)
{}
/* opt_pass methods: */
virtual bool gate (function *);
virtual unsigned int execute (function *) { return tree_profiling (); }
}; // class pass_ipa_tree_profile
bool
pass_ipa_tree_profile::gate (function *)
{
/* When profile instrumentation, use or test coverage shall be performed. */
return (!in_lto_p
&& (flag_branch_probabilities || flag_test_coverage
|| profile_arc_flag));
}
} // anon namespace
simple_ipa_opt_pass *
make_pass_ipa_tree_profile (gcc::context *ctxt)
{
return new pass_ipa_tree_profile (ctxt);
}
#include "gt-tree-profile.h"