From 3949c4a710360edb924d4c88a8974ed0bbfa9f20 Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Thu, 5 Aug 2010 15:23:07 +0200 Subject: [PATCH] ipa-prop.h (enum ipa_lattice_type): Changed comments. 2010-08-05 Martin Jambor * ipa-prop.h (enum ipa_lattice_type): Changed comments. (struct ipa_param_descriptor): New fields types and cannot_devirtualize. (ipa_param_cannot_devirtualize_p): New function. (ipa_param_types_vec_empty): Likewise. (ipa_make_edge_direct_to_target): Declare. * ipa-cp.c: Fixed first stage driver name in initial comment, described devirtualization there too. (ipcp_analyze_node): Call ipa_analyze_params_uses. (ipcp_print_all_lattices): Print devirtualization info. (ipa_set_param_cannot_devirtualize): New function. (ipcp_initialize_node_lattices): Set cannot_devirtualize when setting lattice to BOTTOM. (ipcp_init_stage): Merged into... (ipcp_generate_summary): ...its caller. (ipcp_change_tops_to_bottom): Also process type lists. (ipcp_add_param_type): New function. (ipcp_copy_types): Likewise. (ipcp_propagate_types): Likewise. (ipcp_propagate_stage): Also propagate types. (ipcp_need_redirect_p): Variable jump_func moved to its scope block. Also return true if propagated types require it. (ipcp_update_callgraph): Dump redirection info. (ipcp_process_devirtualization_opportunities): New function. (ipcp_const_param_count): Include known type information. (ipcp_insert_stage): Call ipcp_process_devirtualization_opportunities on new node. Fixed formatting. * ipa-prop.c (make_edge_direct_to_target): Renamed to ipa_make_edge_direct_to_target and changed all callers. Made externally visible. (ipa_node_duplication_hook): Duplicate types vector. * cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Also try to redirect outgoing calls for which we can't get a decl from the statement. Check that we can get a decl from the call statement. * ipa-inline.c (inline_indirect_intraprocedural_analysis): Call ipa_analyze_params_uses only when ipa-cp is disabled. * tree-inline.c (get_indirect_callee_fndecl): Removed. (expand_call_inline): Do not call get_indirect_callee_fndecl. * params.def (PARAM_DEVIRT_TYPE_LIST_SIZE): New parameter. * Makefile.in (ipa-cp.o): Add gimple.h to dependencies. * testsuite/g++.dg/ipa/devirt-1.C: New test. * testsuite/g++.dg/ipa/devirt-2.C: Likewise. * testsuite/g++.dg/ipa/devirt-3.C: Likewise. * testsuite/g++.dg/ipa/devirt-4.C: Likewise. * testsuite/g++.dg/ipa/devirt-5.C: Likewise. * testsuite/gcc.dg/ipa/iinline-3.c: Likewise. From-SVN: r162911 --- gcc/ChangeLog | 43 ++++ gcc/Makefile.in | 2 +- gcc/cgraphunit.c | 12 +- gcc/doc/invoke.texi | 6 + gcc/ipa-cp.c | 316 +++++++++++++++++++++++---- gcc/ipa-prop.c | 17 +- gcc/ipa-prop.h | 38 +++- gcc/params.def | 6 + gcc/testsuite/ChangeLog | 9 + gcc/testsuite/g++.dg/ipa/devirt-1.C | 62 ++++++ gcc/testsuite/g++.dg/ipa/devirt-2.C | 62 ++++++ gcc/testsuite/g++.dg/ipa/devirt-3.C | 63 ++++++ gcc/testsuite/g++.dg/ipa/devirt-4.C | 68 ++++++ gcc/testsuite/g++.dg/ipa/devirt-5.C | 79 +++++++ gcc/testsuite/gcc.dg/ipa/iinline-3.c | 33 +++ gcc/tree-inline.c | 20 +- 16 files changed, 762 insertions(+), 74 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-1.C create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-2.C create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-3.C create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-4.C create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-5.C create mode 100644 gcc/testsuite/gcc.dg/ipa/iinline-3.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0b0d4181057..a3ea02be2f0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,46 @@ +2010-08-05 Martin Jambor + + * ipa-prop.h (enum ipa_lattice_type): Changed comments. + (struct ipa_param_descriptor): New fields types and + cannot_devirtualize. + (ipa_param_cannot_devirtualize_p): New function. + (ipa_param_types_vec_empty): Likewise. + (ipa_make_edge_direct_to_target): Declare. + * ipa-cp.c: Fixed first stage driver name in initial comment, + described devirtualization there too. + (ipcp_analyze_node): Call ipa_analyze_params_uses. + (ipcp_print_all_lattices): Print devirtualization info. + (ipa_set_param_cannot_devirtualize): New function. + (ipcp_initialize_node_lattices): Set cannot_devirtualize when setting + lattice to BOTTOM. + (ipcp_init_stage): Merged into... + (ipcp_generate_summary): ...its caller. + (ipcp_change_tops_to_bottom): Also process type lists. + (ipcp_add_param_type): New function. + (ipcp_copy_types): Likewise. + (ipcp_propagate_types): Likewise. + (ipcp_propagate_stage): Also propagate types. + (ipcp_need_redirect_p): Variable jump_func moved to its scope block. + Also return true if propagated types require it. + (ipcp_update_callgraph): Dump redirection info. + (ipcp_process_devirtualization_opportunities): New function. + (ipcp_const_param_count): Include known type information. + (ipcp_insert_stage): Call ipcp_process_devirtualization_opportunities + on new node. Fixed formatting. + * ipa-prop.c (make_edge_direct_to_target): Renamed to + ipa_make_edge_direct_to_target and changed all callers. Made + externally visible. + (ipa_node_duplication_hook): Duplicate types vector. + * cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Also try to + redirect outgoing calls for which we can't get a decl from the + statement. Check that we can get a decl from the call statement. + * ipa-inline.c (inline_indirect_intraprocedural_analysis): Call + ipa_analyze_params_uses only when ipa-cp is disabled. + * tree-inline.c (get_indirect_callee_fndecl): Removed. + (expand_call_inline): Do not call get_indirect_callee_fndecl. + * params.def (PARAM_DEVIRT_TYPE_LIST_SIZE): New parameter. + * Makefile.in (ipa-cp.o): Add gimple.h to dependencies. + 2010-08-05 Uros Bizjak * expmed.c (expand_mult_const) : Expand shift into diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 030b9c06dcc..d87d4e2f66d 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -3013,7 +3013,7 @@ ipa-ref.o : ipa-ref.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ langhooks.h $(GGC_H) $(TARGET_H) $(CGRAPH_H) $(TREE_H) $(TARGET_H) \ $(TREE_FLOW_H) $(TM_H) $(TREE_PASS_H) $(FLAGS_H) $(TREE_H) $(GGC_H) ipa-cp.o : ipa-cp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ - $(TREE_H) $(TARGET_H) $(CGRAPH_H) $(IPA_PROP_H) $(TREE_FLOW_H) \ + $(TREE_H) $(TARGET_H) $(GIMPLE_H) $(CGRAPH_H) $(IPA_PROP_H) $(TREE_FLOW_H) \ $(TREE_PASS_H) $(FLAGS_H) $(TIMEVAR_H) $(DIAGNOSTIC_H) $(TREE_DUMP_H) \ $(TREE_INLINE_H) $(FIBHEAP_H) $(PARAMS_H) tree-pretty-print.h ipa-split.o : ipa-split.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 0f3a6e23d00..4d02057420a 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -2362,14 +2362,18 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e) struct cgraph_node *node; #endif - if (!decl || decl == e->callee->decl + if (e->indirect_unknown_callee + || decl == e->callee->decl /* Don't update call from same body alias to the real function. */ - || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)) + || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl))) return e->call_stmt; #ifdef ENABLE_CHECKING - node = cgraph_get_node (decl); - gcc_assert (!node || !node->clone.combined_args_to_skip); + if (decl) + { + node = cgraph_get_node (decl); + gcc_assert (!node || !node->clone.combined_args_to_skip); + } #endif if (cgraph_dump_file) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index bd317aa6c3e..fb33b728e93 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -8731,6 +8731,12 @@ loop in the loop nest by a given number of iterations. The strip length can be changed using the @option{loop-block-tile-size} parameter. The default value is 51 iterations. +@item devirt-type-list-size +IPA-CP attempts to track all possible types passed to a function's +parameter in order to perform devirtualization. +@option{devirt-type-list-size} is the maximum number of types it +stores per a single formal parameter of a function. + @end table @end table diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 6918273cba6..354a404d36f 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -70,7 +70,7 @@ along with GCC; see the file COPYING3. If not see modified_flags are defined in ipa_node_params structure (defined in ipa_prop.h and pointed to by cgraph_edge->aux). - -ipcp_init_stage() is the first stage driver. + -ipcp_generate_summary() is the first stage driver. Second stage - interprocedural analysis ======================================== @@ -117,6 +117,17 @@ along with GCC; see the file COPYING3. If not see -ipcp_insert_stage() is the third phase driver. + + This pass also performs devirtualization - turns virtual calls into direct + ones if it can prove that all invocations of the function call the same + callee. This is achieved by building a list of all base types (actually, + their BINFOs) that individual parameters can have in an iterative matter + just like propagating scalar constants and then examining whether virtual + calls which take a parameter as their object fold to the same target for all + these types. If we cannot enumerate all types or there is a type which does + not have any BINFO associated with it, cannot_devirtualize of the associated + parameter descriptor is set which is an equivalent of BOTTOM lattice value + in standard IPA constant propagation. */ #include "config.h" @@ -124,6 +135,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "tree.h" #include "target.h" +#include "gimple.h" #include "cgraph.h" #include "ipa-prop.h" #include "tree-flow.h" @@ -393,12 +405,17 @@ ipcp_print_all_lattices (FILE * f) print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (cst, 0)), 0); } - fprintf (f, "\n"); } else if (lat->type == IPA_TOP) - fprintf (f, "type is TOP\n"); + fprintf (f, "type is TOP"); else - fprintf (f, "type is BOTTOM\n"); + fprintf (f, "type is BOTTOM"); + if (ipa_param_cannot_devirtualize_p (info, i)) + fprintf (f, " - cannot_devirtualize set\n"); + else if (ipa_param_types_vec_empty (info, i)) + fprintf (f, " - type list empty\n"); + else + fprintf (f, "\n"); } } } @@ -523,6 +540,19 @@ ipcp_cloning_candidate_p (struct cgraph_node *node) return true; } +/* Mark parameter with index I of function described by INFO as unsuitable for + devirtualization. Return true if it has already been marked so. */ + +static bool +ipa_set_param_cannot_devirtualize (struct ipa_node_params *info, int i) +{ + bool ret = info->params[i].cannot_devirtualize; + info->params[i].cannot_devirtualize = true; + if (info->params[i].types) + VEC_free (tree, heap, info->params[i].types); + return ret; +} + /* Initialize ipcp_lattices array. The lattices corresponding to supported types (integers, real types and Fortran constants defined as const_decls) are initialized to IPA_TOP, the rest of them to IPA_BOTTOM. */ @@ -545,7 +575,11 @@ ipcp_initialize_node_lattices (struct cgraph_node *node) type = IPA_BOTTOM; for (i = 0; i < ipa_get_param_count (info) ; i++) - ipcp_get_lattice (info, i)->type = type; + { + ipcp_get_lattice (info, i)->type = type; + if (type == IPA_BOTTOM) + ipa_set_param_cannot_devirtualize (info, i); + } } /* build INTEGER_CST tree with type TREE_TYPE and value according to LAT. @@ -599,26 +633,6 @@ ipcp_compute_node_scale (struct cgraph_node *node) ipcp_set_node_scale (node, sum * REG_BR_PROB_BASE / node->count); } -/* Initialization and computation of IPCP data structures. This is the initial - intraprocedural analysis of functions, which gathers information to be - propagated later on. */ - -static void -ipcp_init_stage (void) -{ - struct cgraph_node *node; - - for (node = cgraph_nodes; node; node = node->next) - if (node->analyzed) - { - /* Unreachable nodes should have been eliminated before ipcp. */ - gcc_assert (node->needed || node->reachable); - - node->local.versionable = tree_versionable_function_p (node->decl); - ipa_analyze_node (node); - } -} - /* Return true if there are some formal parameters whose value is IPA_TOP (in the whole compilation unit). Change their values to IPA_BOTTOM, since they most probably get their values from outside of this compilation unit. */ @@ -649,11 +663,148 @@ ipcp_change_tops_to_bottom (void) } lat->type = IPA_BOTTOM; } + if (!ipa_param_cannot_devirtualize_p (info, i) + && ipa_param_types_vec_empty (info, i)) + { + prop_again = true; + ipa_set_param_cannot_devirtualize (info, i); + if (dump_file) + { + fprintf (dump_file, "Marking param "); + print_generic_expr (dump_file, ipa_get_param (info, i), 0); + fprintf (dump_file, " of node %s as unusable for " + "devirtualization.\n", + cgraph_node_name (node)); + } + } } } return prop_again; } +/* Insert BINFO to the list of known types of parameter number I of the + function described by CALLEE_INFO. Return true iff the type information + associated with the callee parameter changed in any way. */ + +static bool +ipcp_add_param_type (struct ipa_node_params *callee_info, int i, tree binfo) +{ + int j, count; + + if (ipa_param_cannot_devirtualize_p (callee_info, i)) + return false; + + if (callee_info->params[i].types) + { + count = VEC_length (tree, callee_info->params[i].types); + for (j = 0; j < count; j++) + if (VEC_index (tree, callee_info->params[i].types, j) == binfo) + return false; + } + + if (VEC_length (tree, callee_info->params[i].types) + == (unsigned) PARAM_VALUE (PARAM_DEVIRT_TYPE_LIST_SIZE)) + return !ipa_set_param_cannot_devirtualize (callee_info, i); + + VEC_safe_push (tree, heap, callee_info->params[i].types, binfo); + return true; +} + +/* Copy known types information for parameter number CALLEE_IDX of CALLEE_INFO + from a parameter of CALLER_INFO as described by JF. Return true iff the + type information changed in any way. JF must be a pass-through or an + ancestor jump function. */ + +static bool +ipcp_copy_types (struct ipa_node_params *caller_info, + struct ipa_node_params *callee_info, + int callee_idx, struct ipa_jump_func *jf) +{ + int caller_idx, j, count; + bool res; + + if (ipa_param_cannot_devirtualize_p (callee_info, callee_idx)) + return false; + + if (jf->type == IPA_JF_PASS_THROUGH) + { + if (jf->value.pass_through.operation != NOP_EXPR) + { + ipa_set_param_cannot_devirtualize (callee_info, callee_idx); + return true; + } + caller_idx = jf->value.pass_through.formal_id; + } + else + caller_idx = jf->value.ancestor.formal_id; + + if (ipa_param_cannot_devirtualize_p (caller_info, caller_idx)) + { + ipa_set_param_cannot_devirtualize (callee_info, callee_idx); + return true; + } + + if (!caller_info->params[caller_idx].types) + return false; + + res = false; + count = VEC_length (tree, caller_info->params[caller_idx].types); + for (j = 0; j < count; j++) + { + tree binfo = VEC_index (tree, caller_info->params[caller_idx].types, j); + if (jf->type == IPA_JF_ANCESTOR) + { + binfo = get_binfo_at_offset (binfo, jf->value.ancestor.offset, + jf->value.ancestor.type); + if (!binfo) + { + ipa_set_param_cannot_devirtualize (callee_info, callee_idx); + return true; + } + } + res |= ipcp_add_param_type (callee_info, callee_idx, binfo); + } + return res; +} + +/* Propagate type information for parameter of CALLEE_INFO number I as + described by JF. CALLER_INFO describes the caller. Return true iff the + type information changed in any way. */ + +static bool +ipcp_propagate_types (struct ipa_node_params *caller_info, + struct ipa_node_params *callee_info, + struct ipa_jump_func *jf, int i) +{ + tree cst, binfo; + + switch (jf->type) + { + case IPA_JF_UNKNOWN: + case IPA_JF_CONST_MEMBER_PTR: + break; + + case IPA_JF_KNOWN_TYPE: + return ipcp_add_param_type (callee_info, i, jf->value.base_binfo); + + case IPA_JF_CONST: + cst = jf->value.constant; + if (TREE_CODE (cst) != ADDR_EXPR) + break; + binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE); + if (!binfo) + break; + return ipcp_add_param_type (callee_info, i, binfo); + + case IPA_JF_PASS_THROUGH: + case IPA_JF_ANCESTOR: + return ipcp_copy_types (caller_info, callee_info, i, jf); + } + + /* If we reach this we cannot use this parameter for devirtualization. */ + return !ipa_set_param_cannot_devirtualize (callee_info, i); +} + /* Interprocedural analysis. The algorithm propagates constants from the caller's parameters to the callee's arguments. */ static void @@ -701,6 +852,9 @@ ipcp_propagate_stage (void) dest_lat->constant = new_lat.constant; ipa_push_func_to_list (&wl, cs->callee); } + + if (ipcp_propagate_types (info, callee_info, jump_func, i)) + ipa_push_func_to_list (&wl, cs->callee); } } } @@ -852,7 +1006,6 @@ ipcp_need_redirect_p (struct cgraph_edge *cs) { struct ipa_node_params *orig_callee_info; int i, count; - struct ipa_jump_func *jump_func; struct cgraph_node *node = cs->callee, *orig; if (!n_cloning_candidates) @@ -868,12 +1021,16 @@ ipcp_need_redirect_p (struct cgraph_edge *cs) for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (orig_callee_info, i); - if (ipcp_lat_is_const (lat)) - { - jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i); - if (jump_func->type != IPA_JF_CONST) - return true; - } + struct ipa_jump_func *jump_func; + + jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i); + if ((ipcp_lat_is_const (lat) + && jump_func->type != IPA_JF_CONST) + || (!ipa_param_cannot_devirtualize_p (orig_callee_info, i) + && !ipa_param_types_vec_empty (orig_callee_info, i) + && jump_func->type != IPA_JF_CONST + && jump_func->type != IPA_JF_KNOWN_TYPE)) + return true; } return false; @@ -912,7 +1069,15 @@ ipcp_update_callgraph (void) { next = cs->next_caller; if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs)) - cgraph_redirect_edge_callee (cs, orig_node); + { + if (dump_file) + fprintf (dump_file, "Redirecting edge %s/%i -> %s/%i " + "back to %s/%i.", + cgraph_node_name (cs->caller), cs->caller->uid, + cgraph_node_name (cs->callee), cs->callee->uid, + cgraph_node_name (orig_node), orig_node->uid); + cgraph_redirect_edge_callee (cs, orig_node); + } } } } @@ -1031,6 +1196,57 @@ ipcp_estimate_cloning_cost (struct cgraph_node *node) return cost + 1; } +/* Walk indirect calls of NODE and if any polymorphic can be turned into a + direct one now, do so. */ + +static void +ipcp_process_devirtualization_opportunities (struct cgraph_node *node) +{ + struct ipa_node_params *info = IPA_NODE_REF (node); + struct cgraph_edge *ie, *next_ie; + + for (ie = node->indirect_calls; ie; ie = next_ie) + { + int param_index, types_count, j; + HOST_WIDE_INT token; + tree target; + + next_ie = ie->next_callee; + if (!ie->indirect_info->polymorphic) + continue; + param_index = ie->indirect_info->param_index; + if (param_index == -1 + || ipa_param_cannot_devirtualize_p (info, param_index) + || ipa_param_types_vec_empty (info, param_index)) + continue; + + token = ie->indirect_info->otr_token; + target = NULL_TREE; + types_count = VEC_length (tree, info->params[param_index].types); + for (j = 0; j < types_count; j++) + { + tree binfo = VEC_index (tree, info->params[param_index].types, j); + tree t = gimple_fold_obj_type_ref_known_binfo (token, binfo); + + if (!t) + { + target = NULL_TREE; + break; + } + else if (!target) + target = t; + else if (target != t) + { + target = NULL_TREE; + break; + } + } + + if (target) + ipa_make_edge_direct_to_target (ie, target); + } +} + /* Return number of live constant parameters. */ static int ipcp_const_param_count (struct cgraph_node *node) @@ -1043,9 +1259,11 @@ ipcp_const_param_count (struct cgraph_node *node) for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); - if (ipcp_lat_is_insertable (lat) + if ((ipcp_lat_is_insertable (lat) /* Do not count obviously unused arguments. */ - && ipa_is_param_used (info, i)) + && ipa_is_param_used (info, i)) + || (!ipa_param_cannot_devirtualize_p (info, i) + && !ipa_param_types_vec_empty (info, i))) const_param++; } return const_param; @@ -1087,7 +1305,8 @@ ipcp_insert_stage (void) max_new_size = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS); max_new_size = max_new_size * PARAM_VALUE (PARAM_IPCP_UNIT_GROWTH) / 100 + 1; - /* First collect all functions we proved to have constant arguments to heap. */ + /* First collect all functions we proved to have constant arguments to + heap. */ heap = fibheap_new (); for (node = cgraph_nodes; node; node = node->next) { @@ -1099,7 +1318,8 @@ ipcp_insert_stage (void) if (ipa_is_called_with_var_arguments (info)) continue; if (ipcp_const_param_count (node)) - node->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (node), node); + node->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (node), + node); } /* Now clone in priority order until code size growth limits are met or @@ -1183,6 +1403,8 @@ ipcp_insert_stage (void) if (node1 == NULL) continue; + ipcp_process_devirtualization_opportunities (node1); + if (dump_file) fprintf (dump_file, "versioned function %s with growth %i, overall %i\n", cgraph_node_name (node), (int)growth, (int)new_size); @@ -1245,18 +1467,30 @@ ipcp_driver (void) return 0; } -/* Note function body size. */ +/* Initialization and computation of IPCP data structures. This is the initial + intraprocedural analysis of functions, which gathers information to be + propagated later on. */ + static void ipcp_generate_summary (void) { + struct cgraph_node *node; + if (dump_file) fprintf (dump_file, "\nIPA constant propagation start:\n"); ipa_check_create_node_params (); ipa_check_create_edge_args (); ipa_register_cgraph_hooks (); - /* 1. Call the init stage to initialize - the ipa_node_params and ipa_edge_args structures. */ - ipcp_init_stage (); + + for (node = cgraph_nodes; node; node = node->next) + if (node->analyzed) + { + /* Unreachable nodes should have been eliminated before ipcp. */ + gcc_assert (node->needed || node->reachable); + + node->local.versionable = tree_versionable_function_p (node->decl); + ipa_analyze_node (node); + } } /* Write ipcp summary for nodes in SET. */ diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index c5da8f84fdb..9d7df075292 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -1430,8 +1430,8 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, /* If TARGET is an addr_expr of a function declaration, make it the destination of an indirect edge IE and return the edge. Otherwise, return NULL. */ -static struct cgraph_edge * -make_edge_direct_to_target (struct cgraph_edge *ie, tree target) +struct cgraph_edge * +ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target) { struct cgraph_node *callee; @@ -1484,7 +1484,7 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, else return NULL; - return make_edge_direct_to_target (ie, target); + return ipa_make_edge_direct_to_target (ie, target); } /* Try to find a destination for indirect edge IE that corresponds to a @@ -1525,7 +1525,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, return NULL; if (target) - return make_edge_direct_to_target (ie, target); + return ipa_make_edge_direct_to_target (ie, target); else return NULL; } @@ -1794,7 +1794,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, __attribute__((unused)) void *data) { struct ipa_node_params *old_info, *new_info; - int param_count; + int param_count, i; ipa_check_create_node_params (); old_info = IPA_NODE_REF (src); @@ -1805,8 +1805,15 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, new_info->params = (struct ipa_param_descriptor *) duplicate_array (old_info->params, sizeof (struct ipa_param_descriptor) * param_count); + for (i = 0; i < param_count; i++) + new_info->params[i].types = VEC_copy (tree, heap, + old_info->params[i].types); new_info->ipcp_orig_node = old_info->ipcp_orig_node; new_info->count_scale = old_info->count_scale; + + new_info->called_with_var_arguments = old_info->called_with_var_arguments; + new_info->uses_analysis_done = old_info->uses_analysis_done; + new_info->node_enqueued = old_info->node_enqueued; } /* Register our cgraph hooks if they are not already there. */ diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 2a28aa3ce38..8244a7f79e3 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -133,11 +133,12 @@ struct GTY (()) ipa_jump_func computed by the interprocedural stage of IPCP. There are three main values of the lattice: IPA_TOP - unknown, - IPA_BOTTOM - non constant, + IPA_BOTTOM - variable, IPA_CONST_VALUE - simple scalar constant, - Cval of formal f will have a constant value if all callsites to this - function have the same constant value passed to f. - Integer and real constants are represented as IPA_CONST_VALUE. */ + + We also use this type to propagate types accross the call graph for the + purpose of devirtualization. In that case, IPA_CONST_VALUE denotes a known + type, rather than a constant. */ enum ipa_lattice_type { IPA_BOTTOM, @@ -161,8 +162,14 @@ struct ipa_param_descriptor struct ipcp_lattice ipcp_lattice; /* PARAM_DECL of this parameter. */ tree decl; + /* Vector of BINFOs of types that this argument might encounter. NULL + basically means a top value, bottom is marked by the cannot_devirtualize + flag below.*/ + VEC (tree, heap) *types; /* The parameter is used. */ unsigned used : 1; + /* Set when parameter type cannot be used for devirtualization. */ + unsigned cannot_devirtualize : 1; }; /* ipa_node_params stores information related to formal parameters of functions @@ -232,6 +239,25 @@ ipa_is_param_used (struct ipa_node_params *info, int i) return info->params[i].used; } +/* Return the cannot_devirtualize flag corresponding to the Ith formal + parameter of the function associated with INFO. The corresponding function + to set the flag is ipa_set_param_cannot_devirtualize. */ + +static inline bool +ipa_param_cannot_devirtualize_p (struct ipa_node_params *info, int i) +{ + return info->params[i].cannot_devirtualize; +} + +/* Return true iff the vector of possible types of the Ith formal parameter of + the function associated with INFO is empty. */ + +static inline bool +ipa_param_types_vec_empty (struct ipa_node_params *info, int i) +{ + return info->params[i].types == NULL; +} + /* Flag this node as having callers with variable number of arguments. */ static inline void @@ -402,6 +428,10 @@ void ipa_initialize_node_params (struct cgraph_node *node); bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs, VEC (cgraph_edge_p, heap) **new_edges); +/* Indirect edge and binfo processing. */ +struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree); + + /* Debugging interface. */ void ipa_print_node_params (FILE *, struct cgraph_node *node); void ipa_print_all_params (FILE *); diff --git a/gcc/params.def b/gcc/params.def index 1d4e6872259..d470539c9c4 100644 --- a/gcc/params.def +++ b/gcc/params.def @@ -832,6 +832,12 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTOR, "a pointer to an aggregate with", 2, 0, 0) +DEFPARAM (PARAM_DEVIRT_TYPE_LIST_SIZE, + "devirt-type-list-size", + "Maximum size of a type list associated with each parameter for " + "devirtualization", + 8, 0, 0) + /* Local variables: mode:c diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6c37a88dd06..59bde3f29df 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2010-08-05 Martin Jambor + + * g++.dg/ipa/devirt-1.C: New test. + * g++.dg/ipa/devirt-2.C: Likewise. + * g++.dg/ipa/devirt-3.C: Likewise. + * g++.dg/ipa/devirt-4.C: Likewise. + * g++.dg/ipa/devirt-5.C: Likewise. + * gcc.dg/ipa/iinline-3.c: Likewise. + 2010-08-05 Jie Zhang PR tree-optimization/45144 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-1.C b/gcc/testsuite/g++.dg/ipa/devirt-1.C new file mode 100644 index 00000000000..de0f665c451 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-1.C @@ -0,0 +1,62 @@ +/* Verify that simple virtual calls are converted to direct calls by ipa-cp. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static int middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +int main (int argc, char *argv[]) +{ + class B b; + if (middleman (&b, get_input ()) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */ +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C new file mode 100644 index 00000000000..d3181e4c0f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-2.C @@ -0,0 +1,62 @@ +/* Verify that simple virtual calls using this pointer are converted + to direct calls by ipa-cp. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + virtual int foo (int i); + int middleman (int i) + { + return foo (i); + } +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +int main (int argc, char *argv[]) +{ + class B b; + int i; + for (i = 0; i < get_input(); i++) + if (b.middleman (get_input ()) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-3.C b/gcc/testsuite/g++.dg/ipa/devirt-3.C new file mode 100644 index 00000000000..2d7bb0ab04e --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-3.C @@ -0,0 +1,63 @@ +/* Verify that simple virtual calls on an object refrence are + converted to simple calls by ipa-cp. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static int middleman (class A &obj, int i) +{ + return obj.foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +int main (int argc, char *argv[]) +{ + class B b; + if (middleman (b, get_input ()) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */ +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-4.C b/gcc/testsuite/g++.dg/ipa/devirt-4.C new file mode 100644 index 00000000000..72c883b3f6a --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-4.C @@ -0,0 +1,68 @@ +/* Verify that ipa-co can convert virtual calls to direct ones even + when a typecast to an ancestor is involved along the way. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +static int middleman_1 (class A *obj, int i) +{ + return obj->foo (i); +} + +static int middleman_2 (class B *obj, int i) +{ + return middleman_1 (obj, i); +} + +int main (int argc, char *argv[]) +{ + class B b; + if (middleman_2 (&b, get_input ()) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */ +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-5.C b/gcc/testsuite/g++.dg/ipa/devirt-5.C new file mode 100644 index 00000000000..575bc0271e6 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-5.C @@ -0,0 +1,79 @@ +/* Verify that ipa-cp can convert simple virtual calls to a direct + ones even when a typecast to an ancestor is involved along the way + and that ancestor is not the first one with virtual functions. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */ + +extern "C" void abort (void); + +class Distraction +{ +public: + float f; + double d; + Distraction () + { + f = 8.3; + d = 10.2; + } + virtual float bar (float z); +}; + +class A +{ +public: + int data; + virtual int foo (int i); +}; + + +class B : public Distraction, public A +{ +public: + virtual int foo (int i); +}; + +float Distraction::bar (float z) +{ + f += z; + return f/2; +} + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +static int middleman_1 (class A *obj, int i) +{ + return obj->foo (i); +} + +static int middleman_2 (class B *obj, int i) +{ + return middleman_1 (obj, i); +} + +int main (int argc, char *argv[]) +{ + class B b; + + if (middleman_2 (&b, get_input ()) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */ +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-3.c b/gcc/testsuite/gcc.dg/ipa/iinline-3.c new file mode 100644 index 00000000000..b39957162ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-3.c @@ -0,0 +1,33 @@ +/* Verify that call declarations are not redirected according to indirect + inlining edges too early. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining" } */ + +extern void abort (void); + +int bar (int k) +{ + return k+2; +} + +int baz (int k) +{ + return k+1; +} + +static int foo (int (*p)(int), int i) +{ + return p (i+1); +} + +int (*g)(int) = baz; + +int main (int argc, char *argv[]) +{ + if (foo (bar, 0) != 3) + abort (); + if (foo (g, 1) != 3) + abort (); + + return 0; +} diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 81d6cfb4c50..a4785d800ff 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -3707,20 +3707,6 @@ add_local_variables (struct function *callee, struct function *caller, } } -/* Fetch callee declaration from the call graph edge going from NODE and - associated with STMR call statement. Return NULL_TREE if not found. */ -static tree -get_indirect_callee_fndecl (struct cgraph_node *node, gimple stmt) -{ - struct cgraph_edge *cs; - - cs = cgraph_edge (node, stmt); - if (cs && !cs->indirect_unknown_callee) - return cs->callee->decl; - - return NULL_TREE; -} - /* If STMT is a GIMPLE_CALL, replace it with its inline expansion. */ static bool @@ -3754,11 +3740,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) If we cannot, then there is no hope of inlining the function. */ fn = gimple_call_fndecl (stmt); if (!fn) - { - fn = get_indirect_callee_fndecl (id->dst_node, stmt); - if (!fn) - goto egress; - } + goto egress; /* Turn forward declarations into real ones. */ fn = cgraph_node (fn)->decl;