From 962e88a9f5a19736412f8b17b24464e2b700f002 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Tue, 1 Jan 2019 13:49:18 +0100 Subject: [PATCH] coverage.c (get_coverage_counts): Use current_function_decl. * coverage.c (get_coverage_counts): Use current_function_decl. * profile.c (read_thunk_profile): New function. (branch_prob): Add THUNK parameter. * tree-profile.c (tree_profiling): Handle thunks. * value-prof.c (init_node_map): Handle thunks. * value-prof.h (branch_prob): Upate prototype. (read_thunk_profile): Declare. * g++.dg/tree-prof/devirt.C: Update testcase. From-SVN: r267495 --- gcc/ChangeLog | 10 + gcc/coverage.c | 2 +- gcc/profile.c | 250 ++++++++++++++---------- gcc/testsuite/ChangeLog | 4 + gcc/testsuite/g++.dg/tree-prof/devirt.C | 4 +- gcc/tree-profile.c | 28 ++- gcc/value-prof.c | 2 +- gcc/value-prof.h | 3 +- 8 files changed, 191 insertions(+), 112 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 53e546f0707..f7c7109a89f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2019-01-01 Jan Hubicka + + * coverage.c (get_coverage_counts): Use current_function_decl. + * profile.c (read_thunk_profile): New function. + (branch_prob): Add THUNK parameter. + * tree-profile.c (tree_profiling): Handle thunks. + * value-prof.c (init_node_map): Handle thunks. + * value-prof.h (branch_prob): Upate prototype. + (read_thunk_profile): Declare. + 2019-01-01 Jakub Jelinek Update copyright years. diff --git a/gcc/coverage.c b/gcc/coverage.c index 0e1873b9589..a34c5da49bf 100644 --- a/gcc/coverage.c +++ b/gcc/coverage.c @@ -329,7 +329,7 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum, else { gcc_assert (coverage_node_map_initialized_p ()); - elt.ident = cgraph_node::get (cfun->decl)->profile_id; + elt.ident = cgraph_node::get (current_function_decl)->profile_id; } elt.ctr = counter; entry = counts_hash->find (&elt); diff --git a/gcc/profile.c b/gcc/profile.c index 14b35ff465c..7374b0d6883 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -963,6 +963,25 @@ compare_freqs (const void *p1, const void *p2) return e2->dest->index - e1->dest->index; } +/* Only read execution count for thunks. */ + +void +read_thunk_profile (struct cgraph_node *node) +{ + tree old = current_function_decl; + current_function_decl = node->decl; + gcov_type *counts = get_coverage_counts (GCOV_COUNTER_ARCS, 0, 0, 1); + if (counts) + { + node->callees->count = node->count + = profile_count::from_gcov_type (counts[0]); + free (counts); + } + current_function_decl = old; + return; +} + + /* Instrument and/or analyze program behavior based on program the CFG. This function creates a representation of the control flow graph (of @@ -983,7 +1002,7 @@ compare_freqs (const void *p1, const void *p2) Main entry point of this file. */ void -branch_prob (void) +branch_prob (bool thunk) { basic_block bb; unsigned i; @@ -1000,118 +1019,121 @@ branch_prob (void) hash_set streamed_locations; - /* We can't handle cyclic regions constructed using abnormal edges. - To avoid these we replace every source of abnormal edge by a fake - edge from entry node and every destination by fake edge to exit. - This keeps graph acyclic and our calculation exact for all normal - edges except for exit and entrance ones. - - We also add fake exit edges for each call and asm statement in the - basic, since it may not return. */ - - FOR_EACH_BB_FN (bb, cfun) + if (!thunk) { - int need_exit_edge = 0, need_entry_edge = 0; - int have_exit_edge = 0, have_entry_edge = 0; - edge e; - edge_iterator ei; + /* We can't handle cyclic regions constructed using abnormal edges. + To avoid these we replace every source of abnormal edge by a fake + edge from entry node and every destination by fake edge to exit. + This keeps graph acyclic and our calculation exact for all normal + edges except for exit and entrance ones. - /* Functions returning multiple times are not handled by extra edges. - Instead we simply allow negative counts on edges from exit to the - block past call and corresponding probabilities. We can't go - with the extra edges because that would result in flowgraph that - needs to have fake edges outside the spanning tree. */ + We also add fake exit edges for each call and asm statement in the + basic, since it may not return. */ - FOR_EACH_EDGE (e, ei, bb->succs) + FOR_EACH_BB_FN (bb, cfun) { - gimple_stmt_iterator gsi; - gimple *last = NULL; - - /* It may happen that there are compiler generated statements - without a locus at all. Go through the basic block from the - last to the first statement looking for a locus. */ - for (gsi = gsi_last_nondebug_bb (bb); - !gsi_end_p (gsi); - gsi_prev_nondebug (&gsi)) + int need_exit_edge = 0, need_entry_edge = 0; + int have_exit_edge = 0, have_entry_edge = 0; + edge e; + edge_iterator ei; + + /* Functions returning multiple times are not handled by extra edges. + Instead we simply allow negative counts on edges from exit to the + block past call and corresponding probabilities. We can't go + with the extra edges because that would result in flowgraph that + needs to have fake edges outside the spanning tree. */ + + FOR_EACH_EDGE (e, ei, bb->succs) { - last = gsi_stmt (gsi); - if (!RESERVED_LOCATION_P (gimple_location (last))) - break; - } + gimple_stmt_iterator gsi; + gimple *last = NULL; + + /* It may happen that there are compiler generated statements + without a locus at all. Go through the basic block from the + last to the first statement looking for a locus. */ + for (gsi = gsi_last_nondebug_bb (bb); + !gsi_end_p (gsi); + gsi_prev_nondebug (&gsi)) + { + last = gsi_stmt (gsi); + if (!RESERVED_LOCATION_P (gimple_location (last))) + break; + } - /* Edge with goto locus might get wrong coverage info unless - it is the only edge out of BB. - Don't do that when the locuses match, so - if (blah) goto something; - is not computed twice. */ - if (last - && gimple_has_location (last) - && !RESERVED_LOCATION_P (e->goto_locus) - && !single_succ_p (bb) - && (LOCATION_FILE (e->goto_locus) - != LOCATION_FILE (gimple_location (last)) - || (LOCATION_LINE (e->goto_locus) - != LOCATION_LINE (gimple_location (last))))) + /* Edge with goto locus might get wrong coverage info unless + it is the only edge out of BB. + Don't do that when the locuses match, so + if (blah) goto something; + is not computed twice. */ + if (last + && gimple_has_location (last) + && !RESERVED_LOCATION_P (e->goto_locus) + && !single_succ_p (bb) + && (LOCATION_FILE (e->goto_locus) + != LOCATION_FILE (gimple_location (last)) + || (LOCATION_LINE (e->goto_locus) + != LOCATION_LINE (gimple_location (last))))) + { + basic_block new_bb = split_edge (e); + edge ne = single_succ_edge (new_bb); + ne->goto_locus = e->goto_locus; + } + if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL)) + && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)) + need_exit_edge = 1; + if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun)) + have_exit_edge = 1; + } + FOR_EACH_EDGE (e, ei, bb->preds) { - basic_block new_bb = split_edge (e); - edge ne = single_succ_edge (new_bb); - ne->goto_locus = e->goto_locus; + if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL)) + && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun)) + need_entry_edge = 1; + if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun)) + have_entry_edge = 1; } - if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL)) - && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)) - need_exit_edge = 1; - if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun)) - have_exit_edge = 1; - } - FOR_EACH_EDGE (e, ei, bb->preds) - { - if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL)) - && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun)) - need_entry_edge = 1; - if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun)) - have_entry_edge = 1; - } - if (need_exit_edge && !have_exit_edge) - { - if (dump_file) - fprintf (dump_file, "Adding fake exit edge to bb %i\n", - bb->index); - make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_FAKE); - } - if (need_entry_edge && !have_entry_edge) - { - if (dump_file) - fprintf (dump_file, "Adding fake entry edge to bb %i\n", - bb->index); - make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FAKE); - /* Avoid bbs that have both fake entry edge and also some - exit edge. One of those edges wouldn't be added to the - spanning tree, but we can't instrument any of them. */ - if (have_exit_edge || need_exit_edge) + if (need_exit_edge && !have_exit_edge) { - gimple_stmt_iterator gsi; - gimple *first; - - gsi = gsi_start_nondebug_after_labels_bb (bb); - gcc_checking_assert (!gsi_end_p (gsi)); - first = gsi_stmt (gsi); - /* Don't split the bbs containing __builtin_setjmp_receiver - or ABNORMAL_DISPATCHER calls. These are very - special and don't expect anything to be inserted before - them. */ - if (is_gimple_call (first) - && (gimple_call_builtin_p (first, BUILT_IN_SETJMP_RECEIVER) - || (gimple_call_flags (first) & ECF_RETURNS_TWICE) - || (gimple_call_internal_p (first) - && (gimple_call_internal_fn (first) - == IFN_ABNORMAL_DISPATCHER)))) - continue; - if (dump_file) - fprintf (dump_file, "Splitting bb %i after labels\n", + fprintf (dump_file, "Adding fake exit edge to bb %i\n", bb->index); - split_block_after_labels (bb); + make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_FAKE); + } + if (need_entry_edge && !have_entry_edge) + { + if (dump_file) + fprintf (dump_file, "Adding fake entry edge to bb %i\n", + bb->index); + make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FAKE); + /* Avoid bbs that have both fake entry edge and also some + exit edge. One of those edges wouldn't be added to the + spanning tree, but we can't instrument any of them. */ + if (have_exit_edge || need_exit_edge) + { + gimple_stmt_iterator gsi; + gimple *first; + + gsi = gsi_start_nondebug_after_labels_bb (bb); + gcc_checking_assert (!gsi_end_p (gsi)); + first = gsi_stmt (gsi); + /* Don't split the bbs containing __builtin_setjmp_receiver + or ABNORMAL_DISPATCHER calls. These are very + special and don't expect anything to be inserted before + them. */ + if (is_gimple_call (first) + && (gimple_call_builtin_p (first, BUILT_IN_SETJMP_RECEIVER) + || (gimple_call_flags (first) & ECF_RETURNS_TWICE) + || (gimple_call_internal_p (first) + && (gimple_call_internal_fn (first) + == IFN_ABNORMAL_DISPATCHER)))) + continue; + + if (dump_file) + fprintf (dump_file, "Splitting bb %i after labels\n", + bb->index); + split_block_after_labels (bb); + } } } } @@ -1143,7 +1165,18 @@ branch_prob (void) on the spanning tree. We insert as many abnormal and critical edges as possible to minimize number of edge splits necessary. */ - find_spanning_tree (el); + if (!thunk) + find_spanning_tree (el); + else + { + edge e; + edge_iterator ei; + /* Keep only edge from entry block to be instrumented. */ + FOR_EACH_BB_FN (bb, cfun) + FOR_EACH_EDGE (e, ei, bb->succs) + EDGE_INFO (e)->ignore = true; + } + /* Fake edges that are not on the tree will not be instrumented, so mark them ignored. */ @@ -1183,8 +1216,17 @@ branch_prob (void) the checksum in only once place, since it depends on the shape of the control flow which can change during various transformations. */ - cfg_checksum = coverage_compute_cfg_checksum (cfun); - lineno_checksum = coverage_compute_lineno_checksum (); + if (thunk) + { + /* At stream in time we do not have CFG, so we can not do checksums. */ + cfg_checksum = 0; + lineno_checksum = 0; + } + else + { + cfg_checksum = coverage_compute_cfg_checksum (cfun); + lineno_checksum = coverage_compute_lineno_checksum (); + } /* Write the data from which gcov can reconstruct the basic block graph and function line numbers (the gcno file). */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4c9083a857b..0f75fffb2f8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2019-01-01 Jan Hubicka + + * g++.dg/tree-prof/devirt.C: Update testcase. + 2019-01-01 Jakub Jelinek Update copyright years. diff --git a/gcc/testsuite/g++.dg/tree-prof/devirt.C b/gcc/testsuite/g++.dg/tree-prof/devirt.C index 86cba41452e..7d6797dd226 100644 --- a/gcc/testsuite/g++.dg/tree-prof/devirt.C +++ b/gcc/testsuite/g++.dg/tree-prof/devirt.C @@ -119,5 +119,5 @@ main () __builtin_abort (); } -/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::_ZThn16" 3 "dom3" } } */ -/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::AddRef" 3 "dom3" } } */ +/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::_ZThn16" 1 "dom3" } } */ +/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::AddRef" 1 "dom3" } } */ diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c index ab4a439a5e3..30fadde86cf 100644 --- a/gcc/tree-profile.c +++ b/gcc/tree-profile.c @@ -739,7 +739,8 @@ tree_profiling (void) FOR_EACH_DEFINED_FUNCTION (node) { - if (!gimple_has_body_p (node->decl)) + bool thunk = false; + if (!gimple_has_body_p (node->decl) && !node->thunk.thunk_p) continue; /* Don't profile functions produced for builtin stuff. */ @@ -760,22 +761,43 @@ tree_profiling (void) if (!include_source_file_for_profile (file)) continue; + if (node->thunk.thunk_p) + { + /* We can not expand variadic thunks to Gimple. */ + if (stdarg_p (TREE_TYPE (node->decl))) + continue; + thunk = true; + /* When generate profile, expand thunk to gimple so it can be + instrumented same way as other functions. */ + if (profile_arc_flag) + node->expand_thunk (false, true); + /* Read cgraph profile but keep function as thunk at profile-use + time. */ + else + { + read_thunk_profile (node); + continue; + } + } + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); if (dump_file) dump_function_header (dump_file, cfun->decl, dump_flags); /* Local pure-const may imply need to fixup the cfg. */ - if (execute_fixup_cfg () & TODO_cleanup_cfg) + if (gimple_has_body_p (node->decl) + && (execute_fixup_cfg () & TODO_cleanup_cfg)) cleanup_tree_cfg (); - branch_prob (); + branch_prob (thunk); if (! flag_branch_probabilities && flag_profile_values) gimple_gen_ic_func_profiler (); if (flag_branch_probabilities + && !thunk && flag_profile_values && flag_value_profile_transformations) gimple_value_profile_transformations (); diff --git a/gcc/value-prof.c b/gcc/value-prof.c index fbca6524a36..5013956cf86 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -1188,7 +1188,7 @@ init_node_map (bool local) cgraph_node_map = new hash_map; FOR_EACH_DEFINED_FUNCTION (n) - if (n->has_gimple_body_p ()) + if (n->has_gimple_body_p () || n->thunk.thunk_p) { cgraph_node **val; if (local) diff --git a/gcc/value-prof.h b/gcc/value-prof.h index 3e0e0580f93..1251fa95e31 100644 --- a/gcc/value-prof.h +++ b/gcc/value-prof.h @@ -112,7 +112,8 @@ extern struct cgraph_node* find_func_by_profile_id (int func_id); /* In profile.c. */ extern void init_branch_prob (void); -extern void branch_prob (void); +extern void branch_prob (bool); +extern void read_thunk_profile (struct cgraph_node *); extern void end_branch_prob (void); #endif /* GCC_VALUE_PROF_H */ -- 2.30.2