Mercurial > hg > CbC > CbC_gcc
diff gcc/tree-profile.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/tree-profile.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/tree-profile.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,7 +1,5 @@ /* Calculate branch probabilities, and basic block execution counts. - Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 - Free Software Foundation, Inc. + Copyright (C) 1990-2017 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. @@ -29,30 +27,41 @@ #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" -#include "flags.h" -#include "regs.h" -#include "function.h" -#include "basic-block.h" -#include "diagnostic-core.h" +#include "memmodel.h" +#include "backend.h" +#include "target.h" +#include "tree.h" +#include "gimple.h" +#include "cfghooks.h" +#include "tree-pass.h" +#include "ssa.h" +#include "cgraph.h" #include "coverage.h" -#include "tree.h" -#include "tree-flow.h" -#include "tree-dump.h" -#include "tree-pass.h" -#include "timevar.h" +#include "diagnostic-core.h" +#include "fold-const.h" +#include "varasm.h" +#include "tree-nested.h" +#include "gimplify.h" +#include "gimple-iterator.h" +#include "gimplify-me.h" +#include "tree-cfg.h" +#include "tree-into-ssa.h" #include "value-prof.h" -#include "cgraph.h" +#include "profile.h" +#include "tree-cfgcleanup.h" +#include "params.h" +#include "stringpool.h" +#include "attribs.h" static GTY(()) tree gcov_type_node; -static GTY(()) tree gcov_type_tmp_var; 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_average_profiler_fn; static GTY(()) tree tree_ior_profiler_fn; - +static GTY(()) tree tree_time_profiler_counter; + static GTY(()) tree ic_void_ptr_var; static GTY(()) tree ic_gcov_type_ptr_var; @@ -61,42 +70,54 @@ /* Do initialization work for the edge profiler. */ /* Add code: - static gcov* __gcov_indirect_call_counters; // pointer to actual counter - static void* __gcov_indirect_call_callee; // actual callee address + __thread gcov* __gcov_indirect_call_counters; // pointer to actual counter + __thread void* __gcov_indirect_call_callee; // actual callee address + __thread int __gcov_function_counter; // time profiler function counter */ static void init_ic_make_global_vars (void) { - tree gcov_type_ptr; + tree gcov_type_ptr; ptr_void = build_pointer_type (void_type_node); ic_void_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, - get_identifier ("__gcov_indirect_call_callee"), + get_identifier ( + (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) ? + "__gcov_indirect_call_topn_callee" : + "__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; - TREE_PUBLIC (ic_void_ptr_var) = 0; DECL_ARTIFICIAL (ic_void_ptr_var) = 1; DECL_INITIAL (ic_void_ptr_var) = NULL; - varpool_finalize_decl (ic_void_ptr_var); - varpool_mark_needed_node (varpool_node (ic_void_ptr_var)); + if (targetm.have_tls) + set_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_counters"), + get_identifier ( + (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) ? + "__gcov_indirect_call_topn_counters" : + "__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; - TREE_PUBLIC (ic_gcov_type_ptr_var) = 0; DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1; DECL_INITIAL (ic_gcov_type_ptr_var) = NULL; - varpool_finalize_decl (ic_gcov_type_ptr_var); - varpool_mark_needed_node (varpool_node (ic_gcov_type_ptr_var)); + if (targetm.have_tls) + set_decl_tls_model (ic_gcov_type_ptr_var, decl_default_tls_model (ic_gcov_type_ptr_var)); } +/* Create the type and function decls for the interface with gcov. */ + void -gimple_init_edge_profiler (void) +gimple_init_gcov_profiler (void) { tree interval_profiler_fn_type; tree pow2_profiler_fn_type; @@ -104,9 +125,14 @@ tree gcov_type_ptr; tree ic_profiler_fn_type; tree average_profiler_fn_type; + const char *profiler_fn_name; + const char *fn_name; if (!gcov_type_node) { + const char *fn_suffix + = flag_profile_update == PROFILE_UPDATE_ATOMIC ? "_atomic" : ""; + gcov_type_node = get_gcov_type (); gcov_type_ptr = build_pointer_type (gcov_type_node); @@ -116,9 +142,10 @@ 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); + fn_name = concat ("__gcov_interval_profiler", fn_suffix, NULL); + tree_interval_profiler_fn = build_fn_decl (fn_name, + interval_profiler_fn_type); + free (CONST_CAST (char *, fn_name)); TREE_NOTHROW (tree_interval_profiler_fn) = 1; DECL_ATTRIBUTES (tree_interval_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, @@ -129,8 +156,9 @@ = 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); + fn_name = concat ("__gcov_pow2_profiler", fn_suffix, NULL); + tree_pow2_profiler_fn = build_fn_decl (fn_name, pow2_profiler_fn_type); + free (CONST_CAST (char *, fn_name)); TREE_NOTHROW (tree_pow2_profiler_fn) = 1; DECL_ATTRIBUTES (tree_pow2_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, @@ -141,9 +169,10 @@ = 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); + fn_name = concat ("__gcov_one_value_profiler", fn_suffix, NULL); + tree_one_value_profiler_fn = build_fn_decl (fn_name, + one_value_profiler_fn_type); + free (CONST_CAST (char *, fn_name)); TREE_NOTHROW (tree_one_value_profiler_fn) = 1; DECL_ATTRIBUTES (tree_one_value_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, @@ -151,34 +180,49 @@ init_ic_make_global_vars (); - /* void (*) (gcov_type *, gcov_type, void *, void *) */ + /* void (*) (gcov_type, void *) */ ic_profiler_fn_type = build_function_type_list (void_type_node, - gcov_type_ptr, gcov_type_node, + gcov_type_node, ptr_void, - ptr_void, NULL_TREE); + NULL_TREE); + profiler_fn_name = "__gcov_indirect_call_profiler_v2"; + if (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE)) + profiler_fn_name = "__gcov_indirect_call_topn_profiler"; + tree_indirect_call_profiler_fn - = build_fn_decl ("__gcov_indirect_call_profiler", - ic_profiler_fn_type); + = build_fn_decl (profiler_fn_name, 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)); + tree_time_profiler_counter + = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("__gcov_time_profiler_counter"), + get_gcov_type ()); + TREE_PUBLIC (tree_time_profiler_counter) = 1; + DECL_EXTERNAL (tree_time_profiler_counter) = 1; + TREE_STATIC (tree_time_profiler_counter) = 1; + DECL_ARTIFICIAL (tree_time_profiler_counter) = 1; + DECL_INITIAL (tree_time_profiler_counter) = NULL; + /* 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); + fn_name = concat ("__gcov_average_profiler", fn_suffix, NULL); + tree_average_profiler_fn = build_fn_decl (fn_name, + average_profiler_fn_type); + free (CONST_CAST (char *, fn_name)); 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); + fn_name = concat ("__gcov_ior_profiler", fn_suffix, NULL); + tree_ior_profiler_fn = build_fn_decl (fn_name, average_profiler_fn_type); + free (CONST_CAST (char *, fn_name)); TREE_NOTHROW (tree_ior_profiler_fn) = 1; DECL_ATTRIBUTES (tree_ior_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, @@ -202,24 +246,38 @@ void gimple_gen_edge_profiler (int edgeno, edge e) { - tree ref, one; - gimple stmt1, stmt2, stmt3; + tree one; - /* We share one temporary variable declaration per function. This - gets re-set in tree_profiling. */ - if (gcov_type_tmp_var == NULL_TREE) - gcov_type_tmp_var = create_tmp_reg (gcov_type_node, "PROF_edge_counter"); - ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno); one = build_int_cst (gcov_type_node, 1); - stmt1 = gimple_build_assign (gcov_type_tmp_var, ref); - gimple_assign_set_lhs (stmt1, make_ssa_name (gcov_type_tmp_var, stmt1)); - stmt2 = gimple_build_assign_with_ops (PLUS_EXPR, gcov_type_tmp_var, - gimple_assign_lhs (stmt1), one); - gimple_assign_set_lhs (stmt2, make_ssa_name (gcov_type_tmp_var, stmt2)); - 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); + + if (flag_profile_update == PROFILE_UPDATE_ATOMIC) + { + /* __atomic_fetch_add (&counter, 1, MEMMODEL_RELAXED); */ + tree addr = tree_coverage_counter_addr (GCOV_COUNTER_ARCS, edgeno); + tree f = builtin_decl_explicit (LONG_LONG_TYPE_SIZE > 32 + ? BUILT_IN_ATOMIC_FETCH_ADD_8: + BUILT_IN_ATOMIC_FETCH_ADD_4); + gcall *stmt = gimple_build_call (f, 3, addr, one, + build_int_cst (integer_type_node, + MEMMODEL_RELAXED)); + gsi_insert_on_edge (e, stmt); + } + else + { + tree ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno); + tree gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node, + NULL, "PROF_edge_counter"); + gassign *stmt1 = gimple_build_assign (gcov_type_tmp_var, ref); + gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node, + NULL, "PROF_edge_counter"); + gassign *stmt2 = gimple_build_assign (gcov_type_tmp_var, PLUS_EXPR, + gimple_assign_lhs (stmt1), one); + gassign *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 @@ -230,7 +288,8 @@ { tree val = value->hvalue.value; if (POINTER_TYPE_P (TREE_TYPE (val))) - val = fold_convert (sizetype, 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); } @@ -242,10 +301,10 @@ void gimple_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base) { - gimple stmt = value->hvalue.stmt; + 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; + gcall *call; tree val; tree start = build_int_cst_type (integer_type_node, value->hdata.intvl.int_start); @@ -253,7 +312,7 @@ value->hdata.intvl.steps); ref_ptr = force_gimple_operand_gsi (&gsi, - build_addr (ref, current_function_decl), + build_addr (ref), true, NULL_TREE, true, GSI_SAME_STMT); val = prepare_instrumented_value (&gsi, value); call = gimple_build_call (tree_interval_profiler_fn, 4, @@ -268,10 +327,10 @@ void gimple_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base) { - gimple stmt = value->hvalue.stmt; + gimple *stmt = value->hvalue.stmt; gimple_stmt_iterator gsi = gsi_for_stmt (stmt); tree ref_ptr = tree_coverage_counter_addr (tag, base); - gimple call; + gcall *call; tree val; ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, @@ -288,10 +347,10 @@ void gimple_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base) { - gimple stmt = value->hvalue.stmt; + gimple *stmt = value->hvalue.stmt; gimple_stmt_iterator gsi = gsi_for_stmt (stmt); tree ref_ptr = tree_coverage_counter_addr (tag, base); - gimple call; + gcall *call; tree val; ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, @@ -312,24 +371,37 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base) { tree tmp1; - gimple stmt1, stmt2, stmt3; - gimple stmt = value->hvalue.stmt; + gassign *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); + if ( (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) && + tag == GCOV_COUNTER_V_INDIR) || + (!PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) && + tag == GCOV_COUNTER_ICALL_TOPNV)) + return; + ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, true, NULL_TREE, true, GSI_SAME_STMT); /* Insert code: - __gcov_indirect_call_counters = get_relevant_counter_ptr (); - __gcov_indirect_call_callee = (void *) indirect call argument; + stmt1: __gcov_indirect_call_counters = get_relevant_counter_ptr (); + stmt2: tmp1 = (void *) (indirect call argument value) + stmt3: __gcov_indirect_call_callee = tmp1; + + Example: + f_1 = foo; + __gcov_indirect_call_counters = &__gcov4.main[0]; + PROF_9 = f_1; + __gcov_indirect_call_callee = PROF_9; + _4 = f_1 (); */ - tmp1 = create_tmp_reg (ptr_void, "PROF"); 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)); - gimple_assign_set_lhs (stmt2, make_ssa_name (tmp1, stmt2)); stmt3 = gimple_build_assign (ic_void_ptr_var, gimple_assign_lhs (stmt2)); gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); @@ -346,57 +418,146 @@ void gimple_gen_ic_func_profiler (void) { - struct cgraph_node * c_node = cgraph_node (current_function_decl); - gimple_stmt_iterator gsi; - gimple stmt1, stmt2; - tree tree_uid, cur_func, counter_ptr, ptr_var, void0; + struct cgraph_node * c_node = cgraph_node::get (current_function_decl); + gcall *stmt1; + tree tree_uid, cur_func, void0; - if (cgraph_only_called_directly_p (c_node)) + if (c_node->only_called_directly_p ()) return; - gimple_init_edge_profiler (); + gimple_init_gcov_profiler (); + + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + basic_block cond_bb = split_edge (single_succ_edge (entry)); + basic_block update_bb = split_edge (single_succ_edge (cond_bb)); + + /* We need to do an extra split in order to not create an input + for a possible PHI node. */ + split_edge (single_succ_edge (update_bb)); + + edge true_edge = single_succ_edge (cond_bb); + true_edge->flags = EDGE_TRUE_VALUE; + + profile_probability probability; + if (DECL_VIRTUAL_P (current_function_decl)) + probability = profile_probability::very_likely (); + else + probability = profile_probability::unlikely (); - gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR)); + true_edge->probability = probability; + edge e = make_edge (cond_bb, single_succ_edge (update_bb)->dest, + EDGE_FALSE_VALUE); + e->probability = true_edge->probability.invert (); + + /* Insert code: + + if (__gcov_indirect_call_callee != NULL) + __gcov_indirect_call_profiler_v2 (profile_id, ¤t_function_decl); + + The function __gcov_indirect_call_profiler_v2 is responsible for + resetting __gcov_indirect_call_callee to NULL. */ + + gimple_stmt_iterator gsi = gsi_start_bb (cond_bb); + void0 = build_int_cst (build_pointer_type (void_type_node), 0); + + tree ref = force_gimple_operand_gsi (&gsi, ic_void_ptr_var, true, NULL_TREE, + true, GSI_SAME_STMT); + + gcond *cond = gimple_build_cond (NE_EXPR, ref, + void0, NULL, NULL); + gsi_insert_before (&gsi, cond, GSI_NEW_STMT); + + gsi = gsi_after_labels (update_bb); cur_func = force_gimple_operand_gsi (&gsi, - build_addr (current_function_decl, - current_function_decl), + build_addr (current_function_decl), true, NULL_TREE, true, GSI_SAME_STMT); - counter_ptr = force_gimple_operand_gsi (&gsi, ic_gcov_type_ptr_var, - true, NULL_TREE, true, - GSI_SAME_STMT); - ptr_var = force_gimple_operand_gsi (&gsi, ic_void_ptr_var, - true, NULL_TREE, true, - GSI_SAME_STMT); - tree_uid = build_int_cst (gcov_type_node, c_node->pid); - stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 4, - counter_ptr, tree_uid, cur_func, ptr_var); + tree_uid = build_int_cst + (gcov_type_node, + cgraph_node::get (current_function_decl)->profile_id); + stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 2, + tree_uid, cur_func); gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); - - /* Set __gcov_indirect_call_callee to 0, - 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 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. */ +/* 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_const_delta_profiler (histogram_value value ATTRIBUTE_UNUSED, - unsigned tag ATTRIBUTE_UNUSED, - unsigned base ATTRIBUTE_UNUSED) +gimple_gen_time_profiler (unsigned tag, unsigned base) { - /* FIXME implement this. */ -#ifdef ENABLE_CHECKING - internal_error ("unimplemented functionality"); -#endif - gcc_unreachable (); + tree type = get_gcov_type (); + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + basic_block cond_bb = split_edge (single_succ_edge (entry)); + basic_block update_bb = split_edge (single_succ_edge (cond_bb)); + + /* We need to do an extra split in order to not create an input + for a possible PHI node. */ + split_edge (single_succ_edge (update_bb)); + + edge true_edge = single_succ_edge (cond_bb); + true_edge->flags = EDGE_TRUE_VALUE; + true_edge->probability = profile_probability::unlikely (); + edge e + = make_edge (cond_bb, single_succ_edge (update_bb)->dest, EDGE_FALSE_VALUE); + e->probability = true_edge->probability.invert (); + + gimple_stmt_iterator gsi = gsi_start_bb (cond_bb); + tree original_ref = tree_coverage_counter_ref (tag, base); + tree ref = force_gimple_operand_gsi (&gsi, original_ref, true, NULL_TREE, + true, GSI_SAME_STMT); + tree one = build_int_cst (type, 1); + + /* Emit: if (counters[0] != 0). */ + gcond *cond = gimple_build_cond (EQ_EXPR, ref, build_int_cst (type, 0), + NULL, NULL); + gsi_insert_before (&gsi, cond, GSI_NEW_STMT); + + gsi = gsi_start_bb (update_bb); + + /* Emit: counters[0] = ++__gcov_time_profiler_counter. */ + if (flag_profile_update == PROFILE_UPDATE_ATOMIC) + { + tree ptr = make_temp_ssa_name (build_pointer_type (type), NULL, + "time_profiler_counter_ptr"); + tree addr = build1 (ADDR_EXPR, TREE_TYPE (ptr), + tree_time_profiler_counter); + gassign *assign = gimple_build_assign (ptr, NOP_EXPR, addr); + gsi_insert_before (&gsi, assign, GSI_NEW_STMT); + tree f = builtin_decl_explicit (LONG_LONG_TYPE_SIZE > 32 + ? BUILT_IN_ATOMIC_ADD_FETCH_8: + BUILT_IN_ATOMIC_ADD_FETCH_4); + gcall *stmt = gimple_build_call (f, 3, ptr, one, + build_int_cst (integer_type_node, + MEMMODEL_RELAXED)); + tree result_type = TREE_TYPE (TREE_TYPE (f)); + tree tmp = make_temp_ssa_name (result_type, NULL, "time_profile"); + gimple_set_lhs (stmt, tmp); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + tmp = make_temp_ssa_name (type, NULL, "time_profile"); + assign = gimple_build_assign (tmp, NOP_EXPR, + gimple_call_lhs (stmt)); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + assign = gimple_build_assign (original_ref, tmp); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + } + else + { + tree tmp = make_temp_ssa_name (type, NULL, "time_profile"); + gassign *assign = gimple_build_assign (tmp, tree_time_profiler_counter); + gsi_insert_before (&gsi, assign, GSI_NEW_STMT); + + tmp = make_temp_ssa_name (type, NULL, "time_profile"); + assign = gimple_build_assign (tmp, PLUS_EXPR, gimple_assign_lhs (assign), + one); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + assign = gimple_build_assign (original_ref, tmp); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + assign = gimple_build_assign (tree_time_profiler_counter, tmp); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + } } /* Output instructions as GIMPLE trees to increment the average histogram @@ -406,10 +567,10 @@ void gimple_gen_average_profiler (histogram_value value, unsigned tag, unsigned base) { - gimple stmt = value->hvalue.stmt; + gimple *stmt = value->hvalue.stmt; gimple_stmt_iterator gsi = gsi_for_stmt (stmt); tree ref_ptr = tree_coverage_counter_addr (tag, base); - gimple call; + gcall *call; tree val; ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, @@ -427,10 +588,10 @@ void gimple_gen_ior_profiler (histogram_value value, unsigned tag, unsigned base) { - gimple stmt = value->hvalue.stmt; + gimple *stmt = value->hvalue.stmt; gimple_stmt_iterator gsi = gsi_for_stmt (stmt); tree ref_ptr = tree_coverage_counter_addr (tag, base); - gimple call; + gcall *call; tree val; ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, @@ -440,6 +601,20 @@ gsi_insert_before (&gsi, call, GSI_NEW_STMT); } +#ifndef HAVE_sync_compare_and_swapsi +#define HAVE_sync_compare_and_swapsi 0 +#endif +#ifndef HAVE_atomic_compare_and_swapsi +#define HAVE_atomic_compare_and_swapsi 0 +#endif + +#ifndef HAVE_sync_compare_and_swapdi +#define HAVE_sync_compare_and_swapdi 0 +#endif +#ifndef HAVE_atomic_compare_and_swapdi +#define HAVE_atomic_compare_and_swapdi 0 +#endif + /* Profile all functions in the callgraph. */ static unsigned int @@ -447,30 +622,58 @@ { struct cgraph_node *node; - /* Don't profile functions produced at destruction time, particularly - the gcov datastructure initializer. Don't profile if it has been - already instrumented either (when OpenMP expansion creates - child function from already instrumented body). */ - if (cgraph_state == CGRAPH_STATE_FINISHED) - return 0; + /* Verify whether we can utilize atomic update operations. */ + bool can_support_atomic = false; + unsigned HOST_WIDE_INT gcov_type_size + = tree_to_uhwi (TYPE_SIZE_UNIT (get_gcov_type ())); + if (gcov_type_size == 4) + can_support_atomic + = HAVE_sync_compare_and_swapsi || HAVE_atomic_compare_and_swapsi; + else if (gcov_type_size == 8) + can_support_atomic + = HAVE_sync_compare_and_swapdi || HAVE_atomic_compare_and_swapdi; - for (node = cgraph_nodes; node; node = node->next) + if (flag_profile_update == PROFILE_UPDATE_ATOMIC + && !can_support_atomic) { - if (!node->analyzed - || !gimple_has_body_p (node->decl) - || !(!node->clone_of || node->decl != node->clone_of->decl)) + warning (0, "target does not support atomic profile update, " + "single mode is selected"); + flag_profile_update = PROFILE_UPDATE_SINGLE; + } + else if (flag_profile_update == PROFILE_UPDATE_PREFER_ATOMIC) + flag_profile_update = can_support_atomic + ? PROFILE_UPDATE_ATOMIC : PROFILE_UPDATE_SINGLE; + + /* 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 - || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile) + if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION) + continue; + + if (lookup_attribute ("no_profile_instrument_function", + DECL_ATTRIBUTES (node->decl))) + 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)); - current_function_decl = node->decl; - /* Re-set global shared temporary variable for edge-counters. */ - gcov_type_tmp_var = NULL_TREE; + /* Local pure-const may imply need to fixup the cfg. */ + if (execute_fixup_cfg () & TODO_cleanup_cfg) + cleanup_tree_cfg (); branch_prob (); @@ -488,96 +691,113 @@ easy to adjust it, if and when there is some. */ free_dominance_info (CDI_DOMINATORS); free_dominance_info (CDI_POST_DOMINATORS); - - current_function_decl = NULL; pop_cfun (); } /* Drop pure/const flags from instrumented functions. */ - for (node = cgraph_nodes; node; node = node->next) + if (profile_arc_flag || flag_test_coverage) + 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) { - if (!node->analyzed - || !gimple_has_body_p (node->decl) - || !(!node->clone_of || node->decl != node->clone_of->decl)) + 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 - || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile) - continue; - - cgraph_set_const_flag (node, false, false); - cgraph_set_pure_flag (node, false, false); - } - - /* Update call statements and rebuild the cgraph. */ - for (node = cgraph_nodes; node; node = node->next) - { - basic_block bb; - - if (!node->analyzed - || !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 - || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile) + if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION) continue; push_cfun (DECL_STRUCT_FUNCTION (node->decl)); - current_function_decl = node->decl; - FOR_EACH_BB (bb) + 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); + gimple *stmt = gsi_stmt (gsi); if (is_gimple_call (stmt)) update_stmt (stmt); } } - cfun->after_tree_profile = 1; + /* re-merge split blocks. */ + cleanup_tree_cfg (); update_ssa (TODO_update_ssa); - rebuild_cgraph_edges (); + cgraph_edge::rebuild_edges (); - current_function_decl = NULL; pop_cfun (); } + handle_missing_profiles (); + + del_node_map (); return 0; } -/* When profile instrumentation, use or test coverage shall be performed. */ +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 */ + TODO_dump_symtab, /* todo_flags_finish */ +}; -static bool -gate_tree_profile_ipa (void) +class pass_ipa_tree_profile : public simple_ipa_opt_pass { - return (!in_lto_p +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. + But for AutoFDO, this there is no instrumentation, thus this pass is + diabled. */ + return (!in_lto_p && !flag_auto_profile && (flag_branch_probabilities || flag_test_coverage || profile_arc_flag)); } -struct simple_ipa_opt_pass pass_ipa_tree_profile = +} // anon namespace + +simple_ipa_opt_pass * +make_pass_ipa_tree_profile (gcc::context *ctxt) { - { - SIMPLE_IPA_PASS, - "tree_profile_ipa", /* name */ - gate_tree_profile_ipa, /* gate */ - tree_profiling, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_IPA_PROFILE, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ - } -}; + return new pass_ipa_tree_profile (ctxt); +} #include "gt-tree-profile.h"