From patchwork Wed Aug 4 19:06:12 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 60884 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id E4169B6F0D for ; Thu, 5 Aug 2010 05:06:44 +1000 (EST) Received: (qmail 16287 invoked by alias); 4 Aug 2010 19:06:41 -0000 Received: (qmail 15481 invoked by uid 22791); 4 Aug 2010 19:06:29 -0000 X-SWARE-Spam-Status: No, hits=-3.7 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from cantor2.suse.de (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 04 Aug 2010 19:06:17 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.221.2]) by mx2.suse.de (Postfix) with ESMTP id AD31988B6C; Wed, 4 Aug 2010 21:06:13 +0200 (CEST) Date: Wed, 4 Aug 2010 21:06:12 +0200 From: Martin Jambor To: GCC Patches Cc: Jan Hubicka Subject: [PATCH] Devirtualization within ipa-cp Message-ID: <20100804190612.GA6199@virgil.arch.suse.de> Mail-Followup-To: GCC Patches , Jan Hubicka MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, this patch adds devirtualization capabilities to the IPA_CP pass. In addition to gathering standard constant-propagation lattices it also keeps a list of all possible types an argument can have whenever we can track all of them and if their number is smaller than a newly introduced parameter devirt-type-list-size. The pass then goes over the (outgoing) indirect call graph edges and if the type information can make them direct - it is a virtual call, the object is a parameter of known types and the called method is the same for all of them - it is made direct. Finally, cgraph_redirect_edge_call_stmt_to_callee in cgraphunit.c is taught to change the function declarations in call statements accordingly. The default value of devirt-type-list-size is 8. This is so far only a rather wild guess, I intend to change it after I looked at how big the lists get when compiling firefox with whopr. Bootstrapped and tested on x86_64-linux without any problems. I'm sure there will be comments and requests for changes, nevertheless after I address those, I'd like to commit it to trunk. Thanks, Martin 2010-08-03 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. * 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. Index: icln/gcc/ipa-cp.c =================================================================== --- icln.orig/gcc/ipa-cp.c +++ icln/gcc/ipa-cp.c @@ -70,7 +70,7 @@ along with GCC; see the file COPYING3. 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. -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. #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"); + 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, "type is BOTTOM\n"); + fprintf (f, "\n"); } } } @@ -523,6 +540,19 @@ ipcp_cloning_candidate_p (struct cgraph_ return true; } +/* Mark parameter with index I of function described by INFO as unsuitable for + devirtualization. */ + +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 = 1; + 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 cg 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_n 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 { 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 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 cgrap 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_no 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. */ Index: icln/gcc/ipa-prop.h =================================================================== --- icln.orig/gcc/ipa-prop.h +++ icln/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_param 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 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 *); Index: icln/gcc/cgraphunit.c =================================================================== --- icln.orig/gcc/cgraphunit.c +++ icln/gcc/cgraphunit.c @@ -2362,14 +2362,18 @@ cgraph_redirect_edge_call_stmt_to_callee 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) Index: icln/gcc/ipa-prop.c =================================================================== --- icln.orig/gcc/ipa-prop.c +++ icln/gcc/ipa-prop.c @@ -1430,8 +1430,8 @@ update_jump_functions_after_inlining (st /* 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 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 (struc 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 __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 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. */ Index: icln/gcc/testsuite/g++.dg/ipa/devirt-1.C =================================================================== --- /dev/null +++ icln/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" } } */ Index: icln/gcc/testsuite/g++.dg/ipa/devirt-2.C =================================================================== --- /dev/null +++ icln/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" } } */ Index: icln/gcc/testsuite/g++.dg/ipa/devirt-3.C =================================================================== --- /dev/null +++ icln/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" } } */ Index: icln/gcc/testsuite/g++.dg/ipa/devirt-4.C =================================================================== --- /dev/null +++ icln/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" } } */ Index: icln/gcc/testsuite/g++.dg/ipa/devirt-5.C =================================================================== --- /dev/null +++ icln/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" } } */ Index: icln/gcc/testsuite/gcc.dg/ipa/iinline-3.c =================================================================== --- /dev/null +++ icln/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; +} Index: icln/gcc/tree-inline.c =================================================================== --- icln.orig/gcc/tree-inline.c +++ icln/gcc/tree-inline.c @@ -3707,20 +3707,6 @@ add_local_variables (struct function *ca } } -/* 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, gimp 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; Index: icln/gcc/Makefile.in =================================================================== --- icln.orig/gcc/Makefile.in +++ icln/gcc/Makefile.in @@ -3016,7 +3016,7 @@ ipa-cp.o : ipa-cp.c $(CONFIG_H) $(SYSTEM $(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 \ - $(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) matrix-reorg.o : matrix-reorg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ Index: icln/gcc/params.def =================================================================== --- icln.orig/gcc/params.def +++ icln/gcc/params.def @@ -832,6 +832,12 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTO "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 Index: icln/gcc/doc/invoke.texi =================================================================== --- icln.orig/gcc/doc/invoke.texi +++ icln/gcc/doc/invoke.texi @@ -8708,6 +8708,12 @@ loop in the loop nest by a given number 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