aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/tree-profile.c
diff options
context:
space:
mode:
authorRong Xu <xur@google.com>2014-07-21 16:47:22 -0700
committerRong Xu <xur@google.com>2014-07-29 15:31:03 -0700
commit38a8aecfb882072900434499696b5c32a2274515 (patch)
tree2aac97f0ae24b03cd98c1a06e989c031c173f889 /gcc-4.9/gcc/tree-profile.c
parentc231900e5dcc14d8296bd9f62b45997a49d4d5e7 (diff)
downloadtoolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.tar.gz
toolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.tar.bz2
toolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.zip
[4.9] Switch gcc-4.9 to use google/gcc-4_9 branch.
This source drop uses svn version r212828 of google/gcc-4.9 branch. We also cherry-picked r213062, r213063 and r213064 to fix windows build issues. All gcc-4.9 patches before July 3rd are ported to google/gcc-4.9. The following prior commits has not been merged to google branch yet. (They are included in this commit). e7af147f979e657fe2df00808e5b4319b0e088c6, baf87df3cb2683649ba7e9872362a7e721117c23, and c231900e5dcc14d8296bd9f62b45997a49d4d5e7. Change-Id: I4bea3ea470387ff751c2be4cb0d4a12059b9299b
Diffstat (limited to 'gcc-4.9/gcc/tree-profile.c')
-rw-r--r--gcc-4.9/gcc/tree-profile.c868
1 files changed, 845 insertions, 23 deletions
diff --git a/gcc-4.9/gcc/tree-profile.c b/gcc-4.9/gcc/tree-profile.c
index 02e9ff27e..4e2e8741e 100644
--- a/gcc-4.9/gcc/tree-profile.c
+++ b/gcc-4.9/gcc/tree-profile.c
@@ -29,6 +29,9 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "tm.h"
#include "flags.h"
+#include "target.h"
+#include "output.h"
+#include "regs.h"
#include "function.h"
#include "basic-block.h"
#include "diagnostic-core.h"
@@ -52,16 +55,35 @@ along with GCC; see the file COPYING3. If not see
#include "tree-into-ssa.h"
#include "tree-pass.h"
#include "value-prof.h"
+#include "output.h"
+#include "params.h"
+#include "profile.h"
+#include "l-ipo.h"
#include "profile.h"
#include "target.h"
#include "tree-cfgcleanup.h"
#include "tree-nested.h"
+#include "pointer-set.h"
+
+/* Default name for coverage callback function. */
+#define COVERAGE_CALLBACK_FUNC_NAME "__coverage_callback"
+
+/* True if we insert a callback to edge instrumentation code. Avoid this
+ for the callback function itself. */
+#define COVERAGE_INSERT_CALL ((PARAM_VALUE (PARAM_COVERAGE_CALLBACK) == 1) \
+ && strcmp (get_name (current_function_decl), \
+ COVERAGE_CALLBACK_FUNC_NAME))
+
+/* Number of statements inserted for each edge counter increment. */
+#define EDGE_COUNTER_STMT_COUNT 3
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_indirect_call_topn_profiler_fn;
+static GTY(()) tree tree_direct_call_profiler_fn;
static GTY(()) tree tree_time_profiler_fn;
static GTY(()) tree tree_average_profiler_fn;
static GTY(()) tree tree_ior_profiler_fn;
@@ -69,11 +91,19 @@ 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 dc_void_ptr_var;
+static GTY(()) tree dc_gcov_type_ptr_var;
static GTY(()) tree ptr_void;
+static GTY(()) tree gcov_info_decl;
/* Do initialization work for the edge profiler. */
/* Add code:
+ // if flag_dyn_ipa
+ extern gcov* __gcov_indirect_call_topn_counters; // pointer to actual counter
+ extern void* __gcov_indirect_call_topn_callee; // actual callee address
+
+ // else
__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
@@ -85,6 +115,31 @@ init_ic_make_global_vars (void)
ptr_void = build_pointer_type (void_type_node);
+ if (flag_dyn_ipa)
+ {
+ ic_void_ptr_var
+ = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__gcov_indirect_call_topn_callee"),
+ ptr_void);
+ TREE_PUBLIC (ic_void_ptr_var) = 1;
+ DECL_EXTERNAL (ic_void_ptr_var) = 1;
+ if (targetm.have_tls)
+ DECL_TLS_MODEL (ic_void_ptr_var) =
+ decl_default_tls_model (ic_void_ptr_var);
+ gcov_type_ptr = build_pointer_type (get_gcov_type ());
+ ic_gcov_type_ptr_var
+ = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__gcov_indirect_call_topn_counters"),
+ gcov_type_ptr);
+ TREE_PUBLIC (ic_gcov_type_ptr_var) = 1;
+ DECL_EXTERNAL (ic_gcov_type_ptr_var) = 1;
+ if (targetm.have_tls)
+ DECL_TLS_MODEL (ic_gcov_type_ptr_var) =
+ decl_default_tls_model (ic_gcov_type_ptr_var);
+ }
+ else
+ {
+ /* Do not fix indentation to avoid merge conflicts. */
/* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */
if (flag_lto)
{
@@ -145,6 +200,442 @@ init_ic_make_global_vars (void)
decl_default_tls_model (ic_gcov_type_ptr_var);
varpool_finalize_decl (ic_gcov_type_ptr_var);
+
+ } /* Indentation not fixed intentionally. */
+
+ if (!flag_dyn_ipa)
+ {
+ varpool_finalize_decl (ic_void_ptr_var);
+ varpool_finalize_decl (ic_gcov_type_ptr_var);
+ }
+}
+
+/* A pointer-set of the first statement in each block of statements that need to
+ be applied a sampling wrapper. */
+static struct pointer_set_t *instrumentation_to_be_sampled = NULL;
+
+/* extern __thread gcov_unsigned_t __gcov_sample_counter */
+static GTY(()) tree gcov_sample_counter_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_profile_prefix */
+static tree GTY(()) gcov_profile_prefix_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_test_coverage */
+static tree GTY(()) gcov_test_coverage_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_sampling_period */
+static GTY(()) tree gcov_sampling_period_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_has_sampling */
+static tree gcov_has_sampling_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_cutoff */
+static tree GTY(()) gcov_lipo_cutoff_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_random_seed */
+static tree GTY(()) gcov_lipo_random_seed_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_random_group_size */
+static tree GTY(()) gcov_lipo_random_group_size_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_propagate_scale */
+static tree GTY(()) gcov_lipo_propagate_scale_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_dump_cgraph */
+static tree GTY(()) gcov_lipo_dump_cgraph_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_max_mem */
+static tree GTY(()) gcov_lipo_max_mem_decl = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_grouping_algorithm */
+static tree GTY(()) gcov_lipo_grouping_algorithm = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_merge_modu_edges */
+static tree GTY(()) gcov_lipo_merge_modu_edges = NULL_TREE;
+
+/* extern gcov_unsigned_t __gcov_lipo_strict_inclusion */
+static tree GTY(()) gcov_lipo_strict_inclusion = NULL_TREE;
+
+/* Insert STMT_IF around given sequence of consecutive statements in the
+ same basic block starting with STMT_START, ending with STMT_END.
+ PROB is the probability of the taken branch. */
+
+static void
+insert_if_then (gimple stmt_start, gimple stmt_end, gimple stmt_if, int prob)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb_original, bb_before_if, bb_after_if;
+ edge e_if_taken, e_then_join, e_else;
+ int orig_frequency;
+
+ gsi = gsi_for_stmt (stmt_start);
+ gsi_insert_before (&gsi, stmt_if, GSI_SAME_STMT);
+ bb_original = gsi_bb (gsi);
+ e_if_taken = split_block (bb_original, stmt_if);
+ e_if_taken->flags &= ~EDGE_FALLTHRU;
+ e_if_taken->flags |= EDGE_TRUE_VALUE;
+ e_then_join = split_block (e_if_taken->dest, stmt_end);
+ bb_before_if = e_if_taken->src;
+ bb_after_if = e_then_join->dest;
+ e_else = make_edge (bb_before_if, bb_after_if, EDGE_FALSE_VALUE);
+ orig_frequency = bb_original->frequency;
+ e_if_taken->probability = prob;
+ e_else->probability = REG_BR_PROB_BASE - prob;
+ e_if_taken->dest->frequency = orig_frequency * (prob / REG_BR_PROB_BASE);
+}
+
+/* Transform:
+
+ ORIGINAL CODE
+
+ Into:
+
+ __gcov_sample_counter++;
+ if (__gcov_sample_counter >= __gcov_sampling_period)
+ {
+ __gcov_sample_counter = 0;
+ ORIGINAL CODE
+ }
+
+ The original code block starts with STMT_START, is made of STMT_COUNT
+ consecutive statements in the same basic block. */
+
+static void
+add_sampling_wrapper (gimple stmt_start, gimple stmt_end)
+{
+ tree zero, one, tmp_var, tmp1, tmp2, tmp3;
+ gimple stmt_inc_counter1, stmt_inc_counter2, stmt_inc_counter3;
+ gimple stmt_reset_counter, stmt_assign_period, stmt_if;
+ gimple_stmt_iterator gsi;
+
+ tmp_var = create_tmp_reg (get_gcov_unsigned_t (), "PROF_sample");
+ tmp1 = make_ssa_name (tmp_var, NULL);
+ tmp2 = make_ssa_name (tmp_var, NULL);
+
+ /* Create all the new statements needed. */
+ stmt_inc_counter1 = gimple_build_assign (tmp1, gcov_sample_counter_decl);
+ one = build_int_cst (get_gcov_unsigned_t (), 1);
+ stmt_inc_counter2 = gimple_build_assign_with_ops (
+ PLUS_EXPR, tmp2, tmp1, one);
+ stmt_inc_counter3 = gimple_build_assign (gcov_sample_counter_decl, tmp2);
+ zero = build_int_cst (get_gcov_unsigned_t (), 0);
+ stmt_reset_counter = gimple_build_assign (gcov_sample_counter_decl, zero);
+ tmp3 = make_ssa_name (tmp_var, NULL);
+ stmt_assign_period = gimple_build_assign (tmp3, gcov_sampling_period_decl);
+ stmt_if = gimple_build_cond (GE_EXPR, tmp2, tmp3, NULL_TREE, NULL_TREE);
+
+ /* Insert them for now in the original basic block. */
+ gsi = gsi_for_stmt (stmt_start);
+ gsi_insert_before (&gsi, stmt_inc_counter1, GSI_SAME_STMT);
+ gsi_insert_before (&gsi, stmt_inc_counter2, GSI_SAME_STMT);
+ gsi_insert_before (&gsi, stmt_inc_counter3, GSI_SAME_STMT);
+ gsi_insert_before (&gsi, stmt_assign_period, GSI_SAME_STMT);
+ gsi_insert_before (&gsi, stmt_reset_counter, GSI_SAME_STMT);
+
+ /* Insert IF block. */
+ /* Sampling rate can be changed at runtime: hard to guess the branch prob,
+ so make it 1. */
+ insert_if_then (stmt_reset_counter, stmt_end, stmt_if, REG_BR_PROB_BASE);
+}
+
+/* Add a conditional stmt so that counter update will only exec one time. */
+
+static void
+add_execonce_wrapper (gimple stmt_start, gimple stmt_end)
+{
+ tree zero, tmp_var, tmp1;
+ gimple stmt_if, stmt_assign;
+ gimple_stmt_iterator gsi;
+
+ /* Create all the new statements needed. */
+ tmp_var = create_tmp_reg (get_gcov_type (), "PROF_temp");
+ tmp1 = make_ssa_name (tmp_var, NULL);
+ stmt_assign = gimple_build_assign (tmp1, gimple_assign_lhs (stmt_end));
+
+ zero = build_int_cst (get_gcov_type (), 0);
+ stmt_if = gimple_build_cond (EQ_EXPR, tmp1, zero, NULL_TREE, NULL_TREE);
+
+ gsi = gsi_for_stmt (stmt_start);
+ gsi_insert_before (&gsi, stmt_assign, GSI_SAME_STMT);
+
+ /* Insert IF block. */
+ insert_if_then (stmt_start, stmt_end, stmt_if, 1);
+}
+
+/* Return whether STMT is the beginning of an instrumentation block to be
+ applied sampling. */
+
+static bool
+is_instrumentation_to_be_sampled (gimple stmt)
+{
+ return pointer_set_contains (instrumentation_to_be_sampled, stmt);
+}
+
+/* Add sampling wrappers around edge counter code in current function. */
+
+void
+add_sampling_to_edge_counters (void)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+
+ FOR_EACH_BB_REVERSE_FN (bb, cfun)
+ for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
+ {
+ gimple stmt_end = gsi_stmt (gsi);
+ if (is_instrumentation_to_be_sampled (stmt_end))
+ {
+ gimple stmt_beg;
+ int i;
+ int edge_counter_stmt_count = EDGE_COUNTER_STMT_COUNT;
+
+ /* The code for edge counter increment has EDGE_COUNTER_STMT_COUNT
+ gimple statements. Advance that many statements to find the
+ beginning statement. */
+ if (COVERAGE_INSERT_CALL)
+ edge_counter_stmt_count++;
+
+ for (i = 0; i < edge_counter_stmt_count - 1; i++)
+ gsi_prev (&gsi);
+ stmt_beg = gsi_stmt (gsi);
+ gcc_assert (stmt_beg);
+
+
+ if (flag_profile_generate_sampling)
+ add_sampling_wrapper (stmt_beg, stmt_end);
+ if (PARAM_VALUE (PARAM_COVERAGE_EXEC_ONCE))
+ add_execonce_wrapper (stmt_beg, stmt_end);
+
+ /* reset the iterator and continue. */
+ gsi = gsi_last_bb (bb);
+ }
+ }
+}
+
+/* Helper function to define a variable in comdat with initialization.
+ DECL is the variable, PARAM is the parameter to set init value. */
+
+static void
+init_comdat_decl (tree decl, int param)
+{
+ TREE_PUBLIC (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_COMDAT_GROUP (decl)
+ = DECL_ASSEMBLER_NAME (decl);
+ TREE_STATIC (decl) = 1;
+ DECL_INITIAL (decl) = build_int_cst (
+ get_gcov_unsigned_t (),
+ PARAM_VALUE (param));
+ varpool_finalize_decl (decl);
+}
+
+/* Initialization function for LIPO runtime parameters. */
+
+void
+tree_init_dyn_ipa_parameters (void)
+{
+ if (!gcov_lipo_cutoff_decl)
+ {
+ gcov_lipo_cutoff_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_cutoff"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_cutoff_decl, PARAM_LIPO_CUTOFF);
+ gcov_lipo_random_seed_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_random_seed"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_random_seed_decl, PARAM_LIPO_RANDOM_SEED);
+ gcov_lipo_random_group_size_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_random_group_size"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_random_group_size_decl, PARAM_LIPO_RANDOM_GROUP_SIZE);
+ gcov_lipo_propagate_scale_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_propagate_scale"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_propagate_scale_decl, PARAM_LIPO_PROPAGATE_SCALE);
+ gcov_lipo_dump_cgraph_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_dump_cgraph"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_dump_cgraph_decl, PARAM_LIPO_DUMP_CGRAPH);
+ gcov_lipo_max_mem_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_max_mem"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_max_mem_decl, PARAM_MAX_LIPO_MEMORY);
+ gcov_lipo_grouping_algorithm = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_grouping_algorithm"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_grouping_algorithm,
+ PARAM_LIPO_GROUPING_ALGORITHM);
+ gcov_lipo_merge_modu_edges = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_merge_modu_edges"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_merge_modu_edges,
+ PARAM_LIPO_MERGE_MODU_EDGES);
+ gcov_lipo_strict_inclusion = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_lipo_weak_inclusion"),
+ get_gcov_unsigned_t ());
+ init_comdat_decl (gcov_lipo_strict_inclusion,
+ PARAM_LIPO_WEAK_INCLUSION);
+ }
+}
+
+static void
+cleanup_instrumentation_sampling (void)
+{
+ /* Free the bitmap. */
+ if (flag_profile_generate_sampling && instrumentation_to_be_sampled)
+ {
+ pointer_set_destroy (instrumentation_to_be_sampled);
+ instrumentation_to_be_sampled = NULL;
+ }
+}
+
+/* Initialization function for FDO instrumentation. */
+
+void
+tree_init_instrumentation (void)
+{
+ if (!gcov_profile_prefix_decl)
+ {
+ tree prefix_ptr;
+ int prefix_len;
+ tree prefix_string;
+
+ /* Construct an initializer for __gcov_profile_prefix. */
+ gcov_profile_prefix_decl =
+ build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__gcov_profile_prefix"),
+ get_const_string_type ());
+ TREE_PUBLIC (gcov_profile_prefix_decl) = 1;
+ DECL_ARTIFICIAL (gcov_profile_prefix_decl) = 1;
+ make_decl_one_only (gcov_profile_prefix_decl,
+ DECL_ASSEMBLER_NAME (gcov_profile_prefix_decl));
+ TREE_STATIC (gcov_profile_prefix_decl) = 1;
+
+ const char null_prefix[] = "\0";
+ const char *prefix = null_prefix;
+ prefix_len = 0;
+ if (profile_data_prefix)
+ {
+ prefix_len = strlen (profile_data_prefix);
+ prefix = profile_data_prefix;
+ }
+ prefix_string = build_string (prefix_len + 1, prefix);
+ TREE_TYPE (prefix_string) = build_array_type
+ (char_type_node, build_index_type
+ (build_int_cst (NULL_TREE, prefix_len)));
+ prefix_ptr = build1 (ADDR_EXPR, get_const_string_type (),
+ prefix_string);
+
+ DECL_INITIAL (gcov_profile_prefix_decl) = prefix_ptr;
+ varpool_finalize_decl (gcov_profile_prefix_decl);
+ }
+
+ if (!gcov_test_coverage_decl)
+ {
+ /* Initialize __gcov_test_coverage to 1 if -ftest-coverage
+ specified, 0 otherwise. Used by libgcov to determine whether
+ a binary was instrumented for coverage or profile optimization. */
+ gcov_test_coverage_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_test_coverage"),
+ get_gcov_unsigned_t ());
+ TREE_PUBLIC (gcov_test_coverage_decl) = 1;
+ DECL_ARTIFICIAL (gcov_test_coverage_decl) = 1;
+ DECL_COMDAT_GROUP (gcov_test_coverage_decl)
+ = DECL_ASSEMBLER_NAME (gcov_test_coverage_decl);
+ TREE_STATIC (gcov_test_coverage_decl) = 1;
+ DECL_INITIAL (gcov_test_coverage_decl) = build_int_cst (
+ get_gcov_unsigned_t (),
+ flag_test_coverage ? 1 : 0);
+ varpool_finalize_decl (gcov_test_coverage_decl);
+ }
+}
+
+/* Initialization function for FDO sampling. */
+
+void
+tree_init_instrumentation_sampling (void)
+{
+ if (!gcov_sampling_period_decl)
+ {
+ /* Define __gcov_sampling_period regardless of
+ -fprofile-generate-sampling. Otherwise the extern reference to
+ it from libgcov becomes unmatched.
+ */
+ gcov_sampling_period_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_sampling_period"),
+ get_gcov_unsigned_t ());
+ TREE_PUBLIC (gcov_sampling_period_decl) = 1;
+ DECL_ARTIFICIAL (gcov_sampling_period_decl) = 1;
+ DECL_COMDAT_GROUP (gcov_sampling_period_decl)
+ = DECL_ASSEMBLER_NAME (gcov_sampling_period_decl);
+ TREE_STATIC (gcov_sampling_period_decl) = 1;
+ DECL_INITIAL (gcov_sampling_period_decl) = build_int_cst (
+ get_gcov_unsigned_t (),
+ PARAM_VALUE (PARAM_PROFILE_GENERATE_SAMPLING_PERIOD));
+ varpool_finalize_decl (gcov_sampling_period_decl);
+ }
+
+ if (!gcov_has_sampling_decl)
+ {
+ /* Initialize __gcov_has_sampling to 1 if -fprofile-generate-sampling
+ specified, 0 otherwise. Used by libgcov to determine whether
+ a request to set the sampling period makes sense. */
+ gcov_has_sampling_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_has_sampling"),
+ get_gcov_unsigned_t ());
+ TREE_PUBLIC (gcov_has_sampling_decl) = 1;
+ DECL_ARTIFICIAL (gcov_has_sampling_decl) = 1;
+ DECL_COMDAT_GROUP (gcov_has_sampling_decl)
+ = DECL_ASSEMBLER_NAME (gcov_has_sampling_decl);
+ TREE_STATIC (gcov_has_sampling_decl) = 1;
+ DECL_INITIAL (gcov_has_sampling_decl) = build_int_cst (
+ get_gcov_unsigned_t (),
+ flag_profile_generate_sampling ? 1 : 0);
+ varpool_finalize_decl (gcov_has_sampling_decl);
+ }
+
+ if (flag_profile_generate_sampling && !instrumentation_to_be_sampled)
+ {
+ instrumentation_to_be_sampled = pointer_set_create ();
+ gcov_sample_counter_decl = build_decl (
+ UNKNOWN_LOCATION,
+ VAR_DECL,
+ get_identifier ("__gcov_sample_counter"),
+ get_gcov_unsigned_t ());
+ TREE_PUBLIC (gcov_sample_counter_decl) = 1;
+ DECL_EXTERNAL (gcov_sample_counter_decl) = 1;
+ DECL_ARTIFICIAL (gcov_sample_counter_decl) = 1;
+ if (targetm.have_tls)
+ DECL_TLS_MODEL (gcov_sample_counter_decl) =
+ decl_default_tls_model (gcov_sample_counter_decl);
+ }
+ if (PARAM_VALUE (PARAM_COVERAGE_EXEC_ONCE)
+ && instrumentation_to_be_sampled == 0)
+ instrumentation_to_be_sampled = pointer_set_create ();
}
/* Create the type and function decls for the interface with gcov. */
@@ -157,14 +648,25 @@ gimple_init_edge_profiler (void)
tree one_value_profiler_fn_type;
tree gcov_type_ptr;
tree ic_profiler_fn_type;
+ tree ic_topn_profiler_fn_type;
+ tree dc_profiler_fn_type;
tree average_profiler_fn_type;
tree time_profiler_fn_type;
+
if (!gcov_type_node)
{
+ char name_buf[32];
gcov_type_node = get_gcov_type ();
gcov_type_ptr = build_pointer_type (gcov_type_node);
+ ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
+ gcov_info_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier (name_buf),
+ get_gcov_unsigned_t ());
+ DECL_EXTERNAL (gcov_info_decl) = 1;
+ TREE_ADDRESSABLE (gcov_info_decl) = 1;
+
/* void (*) (gcov_type *, gcov_type, int, unsigned) */
interval_profiler_fn_type
= build_function_type_list (void_type_node,
@@ -196,7 +698,12 @@ gimple_init_edge_profiler (void)
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node,
NULL_TREE);
- tree_one_value_profiler_fn
+ if (PROFILE_GEN_VALUE_ATOMIC)
+ tree_one_value_profiler_fn
+ = build_fn_decl ("__gcov_one_value_profiler_atomic",
+ one_value_profiler_fn_type);
+ else
+ 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;
@@ -215,6 +722,7 @@ gimple_init_edge_profiler (void)
gcov_type_ptr, gcov_type_node,
ptr_void, ptr_void,
NULL_TREE);
+ // TODO(xur): atomic support
tree_indirect_call_profiler_fn
= build_fn_decl ("__gcov_indirect_call_profiler",
ic_profiler_fn_type);
@@ -227,15 +735,44 @@ gimple_init_edge_profiler (void)
gcov_type_node,
ptr_void,
NULL_TREE);
- tree_indirect_call_profiler_fn
- = build_fn_decl ("__gcov_indirect_call_profiler_v2",
- ic_profiler_fn_type);
+ if (PROFILE_GEN_VALUE_ATOMIC)
+ tree_indirect_call_profiler_fn
+ = build_fn_decl ("__gcov_indirect_call_profiler_atomic_v2",
+ ic_profiler_fn_type);
+ else
+ 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 (*) (void *, void *, gcov_unsigned_t) */
+ ic_topn_profiler_fn_type
+ = build_function_type_list (void_type_node, ptr_void, ptr_void,
+ get_gcov_unsigned_t (), NULL_TREE);
+ tree_indirect_call_topn_profiler_fn
+ = build_fn_decl ("__gcov_indirect_call_topn_profiler",
+ ic_topn_profiler_fn_type);
+ TREE_NOTHROW (tree_indirect_call_topn_profiler_fn) = 1;
+ DECL_ATTRIBUTES (tree_indirect_call_topn_profiler_fn)
+ = tree_cons (get_identifier ("leaf"), NULL,
+ DECL_ATTRIBUTES (tree_indirect_call_topn_profiler_fn));
+
+ /* void (*) (void *, void *, gcov_unsigned_t) */
+ dc_profiler_fn_type
+ = build_function_type_list (void_type_node, ptr_void, ptr_void,
+ get_gcov_unsigned_t (), NULL_TREE);
+ tree_direct_call_profiler_fn
+ = build_fn_decl ("__gcov_direct_call_profiler",
+ dc_profiler_fn_type);
+ TREE_NOTHROW (tree_direct_call_profiler_fn) = 1;
+ DECL_ATTRIBUTES (tree_direct_call_profiler_fn)
+ = tree_cons (get_identifier ("leaf"), NULL,
+ DECL_ATTRIBUTES (tree_direct_call_profiler_fn));
+
/* void (*) (gcov_type *, gcov_type, void *) */
time_profiler_fn_type
= build_function_type_list (void_type_node,
@@ -288,20 +825,72 @@ gimple_gen_edge_profiler (int edgeno, edge e)
{
tree ref, one, gcov_type_tmp_var;
gimple stmt1, stmt2, stmt3;
+ bool is_atomic = PROFILE_GEN_EDGE_ATOMIC;
+
+ if (is_atomic)
+ ref = tree_coverage_counter_addr (GCOV_COUNTER_ARCS, edgeno);
+ else
+ ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno);
- 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);
+
+ /* insert a callback stmt stmt */
+ if (COVERAGE_INSERT_CALL)
+ {
+ gimple call;
+ tree tree_edgeno = build_int_cst (gcov_type_node, edgeno);
+ tree tree_uid = build_int_cst (gcov_type_node,
+ current_function_funcdef_no);
+ tree callback_fn_type
+ = build_function_type_list (void_type_node,
+ gcov_type_node,
+ integer_type_node,
+ NULL_TREE);
+ tree tree_callback_fn = build_fn_decl (COVERAGE_CALLBACK_FUNC_NAME,
+ callback_fn_type);
+ TREE_NOTHROW (tree_callback_fn) = 1;
+ DECL_ATTRIBUTES (tree_callback_fn)
+ = tree_cons (get_identifier ("leaf"), NULL,
+ DECL_ATTRIBUTES (tree_callback_fn));
+
+ call = gimple_build_call (tree_callback_fn, 2, tree_uid, tree_edgeno);
+ gsi_insert_on_edge(e, call);
+ }
+
+ if (is_atomic)
+ {
+ /* __atomic_fetch_add (&counter, 1, MEMMODEL_RELAXED); */
+ stmt3 = gimple_build_call (builtin_decl_explicit (
+ GCOV_TYPE_ATOMIC_FETCH_ADD),
+ 3, ref, one,
+ build_int_cst (integer_type_node,
+ MEMMODEL_RELAXED));
+ /* Suppress "'stmt1' may be used uninitialized" warning. */
+ stmt1 = stmt2 = 0;
+ }
+ else
+ {
+ 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));
+ }
+
+ if (flag_profile_generate_sampling
+ || PARAM_VALUE (PARAM_COVERAGE_EXEC_ONCE))
+ pointer_set_insert (instrumentation_to_be_sampled, stmt3);
+
+ if (!is_atomic)
+ {
+ 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
@@ -396,10 +985,13 @@ 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);
+ gimple stmt;
+ gimple_stmt_iterator gsi;
+ tree ref_ptr;
+ stmt = value->hvalue.stmt;
+ gsi = gsi_for_stmt (stmt);
+ ref_ptr = tree_coverage_counter_addr (tag, base);
ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
true, NULL_TREE, true, GSI_SAME_STMT);
@@ -429,7 +1021,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
void
gimple_gen_ic_func_profiler (void)
{
- struct cgraph_node * c_node = cgraph_get_node (current_function_decl);
+ struct cgraph_node * c_node = cgraph_get_create_node (current_function_decl);
gimple_stmt_iterator gsi;
gimple stmt1, stmt2;
tree tree_uid, cur_func, void0;
@@ -483,6 +1075,105 @@ gimple_gen_ic_func_profiler (void)
gsi_insert_before (&gsi, stmt2, 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.
+ */
+
+static void
+gimple_gen_ic_func_topn_profiler (void)
+{
+ gimple_stmt_iterator gsi;
+ gimple stmt1;
+ tree cur_func, gcov_info, cur_func_id;
+
+ if (DECL_STATIC_CONSTRUCTOR (current_function_decl)
+ || DECL_STATIC_CONSTRUCTOR (current_function_decl)
+ || DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (current_function_decl))
+ return;
+
+ gimple_init_edge_profiler ();
+
+ gsi = gsi_after_labels (single_succ (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);
+ gcov_info = build_fold_addr_expr (gcov_info_decl);
+ cur_func_id = build_int_cst (get_gcov_unsigned_t (),
+ FUNC_DECL_FUNC_ID (cfun));
+ stmt1 = gimple_build_call (tree_indirect_call_topn_profiler_fn,
+ 3, cur_func, gcov_info, cur_func_id);
+ gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT);
+}
+
+
+/* Output instructions as GIMPLE trees for code to find the number of
+ calls at each direct call site.
+ BASE is offset of the counter position, CALL_STMT is the direct call
+ whose call-count is profiled. */
+
+static void
+gimple_gen_dc_profiler (unsigned base, gimple call_stmt)
+{
+ gimple stmt1, stmt2, stmt3;
+ gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+ tree tmp1, tmp2, tmp3, callee = gimple_call_fn (call_stmt);
+
+ /* Insert code:
+ __gcov_direct_call_counters = get_relevant_counter_ptr ();
+ __gcov_callee = (void *) callee;
+ */
+ tmp1 = tree_coverage_counter_addr (GCOV_COUNTER_DIRECT_CALL, base);
+ tmp1 = force_gimple_operand_gsi (&gsi, tmp1, true, NULL_TREE,
+ true, GSI_SAME_STMT);
+ stmt1 = gimple_build_assign (dc_gcov_type_ptr_var, tmp1);
+ tmp2 = create_tmp_var (ptr_void, "PROF_dc");
+ stmt2 = gimple_build_assign (tmp2, unshare_expr (callee));
+ tmp3 = make_ssa_name (tmp2, stmt2);
+ gimple_assign_set_lhs (stmt2, tmp3);
+ stmt3 = gimple_build_assign (dc_void_ptr_var, tmp3);
+ 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 number of
+ calls at each direct call site. Insert instructions at the beginning of
+ every possible called function. */
+
+static void
+gimple_gen_dc_func_profiler (void)
+{
+ gimple_stmt_iterator gsi;
+ gimple stmt1;
+ tree cur_func, gcov_info, cur_func_id;
+
+ if (DECL_STATIC_CONSTRUCTOR (current_function_decl)
+ || DECL_STATIC_CONSTRUCTOR (current_function_decl)
+ || DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (current_function_decl))
+ return;
+
+ gimple_init_edge_profiler ();
+
+ gsi = gsi_after_labels (single_succ (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);
+ gcov_info = build_fold_addr_expr (gcov_info_decl);
+ cur_func_id = build_int_cst (get_gcov_unsigned_t (),
+ FUNC_DECL_FUNC_ID (cfun));
+ stmt1 = gimple_build_call (tree_direct_call_profiler_fn, 3, cur_func,
+ gcov_info, cur_func_id);
+ gsi_insert_before (&gsi, stmt1, 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. */
@@ -569,8 +1260,13 @@ tree_profiling (void)
cgraphunit.c:ipa_passes(). */
gcc_assert (cgraph_state == CGRAPH_STATE_IPA_SSA);
+ /* After value profile transformation, artificial edges (that keep
+ function body from being deleted) won't be needed. */
+ if (L_IPO_COMP_MODE)
+ lipo_link_and_fixup ();
init_node_map (true);
+
FOR_EACH_DEFINED_FUNCTION (node)
{
if (!gimple_has_body_p (node->decl))
@@ -582,6 +1278,9 @@ tree_profiling (void)
push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+ if (flag_emit_function_names)
+ emit_function_name ();
+
/* Local pure-const may imply need to fixup the cfg. */
if (execute_fixup_cfg () & TODO_cleanup_cfg)
cleanup_tree_cfg ();
@@ -589,7 +1288,8 @@ tree_profiling (void)
branch_prob ();
if (! flag_branch_probabilities
- && flag_profile_values)
+ && flag_profile_values
+ && !flag_dyn_ipa)
gimple_gen_ic_func_profiler ();
if (flag_branch_probabilities
@@ -644,7 +1344,7 @@ tree_profiling (void)
{
gimple stmt = gsi_stmt (gsi);
if (is_gimple_call (stmt))
- update_stmt (stmt);
+ update_stmt (stmt);
}
}
@@ -653,13 +1353,100 @@ tree_profiling (void)
update_ssa (TODO_update_ssa);
rebuild_cgraph_edges ();
-
pop_cfun ();
}
handle_missing_profiles ();
del_node_map ();
+ cleanup_instrumentation_sampling();
+ return 0;
+}
+
+/* Return true if tree-based direct-call profiling is in effect, else false. */
+
+static bool
+do_direct_call_profiling (void)
+{
+ return !flag_branch_probabilities
+ && (profile_arc_flag || flag_test_coverage)
+ && flag_dyn_ipa;
+}
+
+/* Instrument current function to collect direct call profile information. */
+
+static unsigned int
+direct_call_profiling (void)
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+
+ /* Add code:
+ extern gcov* __gcov_direct_call_counters; // pointer to actual counter
+ extern void* __gcov_direct_call_callee; // actual callee address
+ */
+ if (!dc_gcov_type_ptr_var)
+ {
+ dc_gcov_type_ptr_var
+ = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__gcov_direct_call_counters"),
+ build_pointer_type (gcov_type_node));
+ DECL_ARTIFICIAL (dc_gcov_type_ptr_var) = 1;
+ DECL_EXTERNAL (dc_gcov_type_ptr_var) = 1;
+ DECL_TLS_MODEL (dc_gcov_type_ptr_var) =
+ decl_default_tls_model (dc_gcov_type_ptr_var);
+
+ dc_void_ptr_var =
+ build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier ("__gcov_direct_call_callee"),
+ ptr_void);
+ DECL_ARTIFICIAL (dc_void_ptr_var) = 1;
+ DECL_EXTERNAL (dc_void_ptr_var) = 1;
+ DECL_TLS_MODEL (dc_void_ptr_var) =
+ decl_default_tls_model (dc_void_ptr_var);
+ }
+
+ if (!DECL_STATIC_CONSTRUCTOR (current_function_decl))
+ {
+ FOR_EACH_BB_FN (bb, cfun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ /* Check if this is a direct call, and not a builtin call. */
+ if (gimple_code (stmt) != GIMPLE_CALL
+ || gimple_call_fndecl (stmt) == NULL_TREE
+ || DECL_BUILT_IN (gimple_call_fndecl (stmt))
+ || DECL_IS_BUILTIN (gimple_call_fndecl (stmt)))
+ continue;
+
+ if (PARAM_VALUE (PARAM_LIPO_SKIP_SPECIAL_SECTIONS))
+ {
+ tree callee = gimple_call_fndecl (stmt);
+ if (DECL_IS_MALLOC (callee)
+ || DECL_IS_OPERATOR_NEW (callee)
+ || (DECL_ASSEMBLER_NAME_SET_P (callee)
+ && (!strcmp (IDENTIFIER_POINTER (
+ DECL_ASSEMBLER_NAME (callee)), "_ZdlPv")
+ || !strcmp (IDENTIFIER_POINTER (
+ DECL_ASSEMBLER_NAME (callee)), "_ZdaPv"))))
+ continue;
+ }
+
+ if (!coverage_counter_alloc (GCOV_COUNTER_DIRECT_CALL, 2))
+ continue;
+ gimple_gen_dc_profiler (0, stmt);
+ }
+ coverage_dc_end_function ();
+ }
+
+ if (coverage_function_present (FUNC_DECL_FUNC_ID (cfun)))
+ {
+ gimple_gen_dc_func_profiler ();
+ if (! flag_branch_probabilities
+ && flag_profile_values)
+ gimple_gen_ic_func_topn_profiler ();
+ }
+
return 0;
}
@@ -668,7 +1455,7 @@ tree_profiling (void)
static bool
gate_tree_profile_ipa (void)
{
- return (!in_lto_p
+ return (!in_lto_p && !flag_auto_profile
&& (flag_branch_probabilities || flag_test_coverage
|| profile_arc_flag));
}
@@ -703,6 +1490,35 @@ public:
}; // class pass_ipa_tree_profile
+const pass_data pass_data_direct_call_profile =
+{
+ GIMPLE_PASS,
+ "dc_profile", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_BRANCH_PROB, /* tv_id */
+ ( PROP_ssa | PROP_cfg), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa /* todo_flags_finish */
+};
+class pass_direct_call_profile : public gimple_opt_pass
+{
+public:
+ pass_direct_call_profile (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_direct_call_profile, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ opt_pass * clone () { return new pass_direct_call_profile (m_ctxt); }
+ bool gate () { return do_direct_call_profiling (); }
+ unsigned int execute () { return direct_call_profiling (); }
+
+}; // class pass_direct_call_profiling
+
+
} // anon namespace
simple_ipa_opt_pass *
@@ -711,4 +1527,10 @@ make_pass_ipa_tree_profile (gcc::context *ctxt)
return new pass_ipa_tree_profile (ctxt);
}
+gimple_opt_pass *
+make_pass_direct_call_profile (gcc::context *ctxt)
+{
+ return new pass_direct_call_profile (ctxt);
+}
+
#include "gt-tree-profile.h"