ipa-prop.h (enum ipa_lattice_type): Changed comments.

2010-08-05  Martin Jambor  <mjambor@suse.cz>

	* 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
This commit is contained in:
Martin Jambor 2010-08-05 15:23:07 +02:00 committed by Martin Jambor
parent 4caa21a13b
commit 3949c4a710
16 changed files with 762 additions and 74 deletions

View File

@ -1,3 +1,46 @@
2010-08-05 Martin Jambor <mjambor@suse.cz>
* 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 <ubizjak@gmail.com> 2010-08-05 Uros Bizjak <ubizjak@gmail.com>
* expmed.c (expand_mult_const) <case alg_shift>: Expand shift into * expmed.c (expand_mult_const) <case alg_shift>: Expand shift into

View File

@ -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) \ 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) $(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 \ 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_PASS_H) $(FLAGS_H) $(TIMEVAR_H) $(DIAGNOSTIC_H) $(TREE_DUMP_H) \
$(TREE_INLINE_H) $(FIBHEAP_H) $(PARAMS_H) tree-pretty-print.h $(TREE_INLINE_H) $(FIBHEAP_H) $(PARAMS_H) tree-pretty-print.h
ipa-split.o : ipa-split.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ ipa-split.o : ipa-split.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \

View File

@ -2362,14 +2362,18 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
struct cgraph_node *node; struct cgraph_node *node;
#endif #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. */ /* 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; return e->call_stmt;
#ifdef ENABLE_CHECKING #ifdef ENABLE_CHECKING
if (decl)
{
node = cgraph_get_node (decl); node = cgraph_get_node (decl);
gcc_assert (!node || !node->clone.combined_args_to_skip); gcc_assert (!node || !node->clone.combined_args_to_skip);
}
#endif #endif
if (cgraph_dump_file) if (cgraph_dump_file)

View File

@ -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} length can be changed using the @option{loop-block-tile-size}
parameter. The default value is 51 iterations. 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
@end table @end table

View File

@ -70,7 +70,7 @@ along with GCC; see the file COPYING3. If not see
modified_flags are defined in ipa_node_params structure modified_flags are defined in ipa_node_params structure
(defined in ipa_prop.h and pointed to by cgraph_edge->aux). (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 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. -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" #include "config.h"
@ -124,6 +135,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h" #include "coretypes.h"
#include "tree.h" #include "tree.h"
#include "target.h" #include "target.h"
#include "gimple.h"
#include "cgraph.h" #include "cgraph.h"
#include "ipa-prop.h" #include "ipa-prop.h"
#include "tree-flow.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)), print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (cst, 0)),
0); 0);
} }
fprintf (f, "\n");
} }
else if (lat->type == IPA_TOP) else if (lat->type == IPA_TOP)
fprintf (f, "type is TOP\n"); fprintf (f, "type is TOP");
else 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; 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 /* Initialize ipcp_lattices array. The lattices corresponding to supported
types (integers, real types and Fortran constants defined as const_decls) types (integers, real types and Fortran constants defined as const_decls)
are initialized to IPA_TOP, the rest of them to IPA_BOTTOM. */ 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; type = IPA_BOTTOM;
for (i = 0; i < ipa_get_param_count (info) ; i++) 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. /* 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); 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 /* 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 the whole compilation unit). Change their values to IPA_BOTTOM, since they
most probably get their values from outside of this compilation unit. */ 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; 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; 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 /* Interprocedural analysis. The algorithm propagates constants from the
caller's parameters to the callee's arguments. */ caller's parameters to the callee's arguments. */
static void static void
@ -701,6 +852,9 @@ ipcp_propagate_stage (void)
dest_lat->constant = new_lat.constant; dest_lat->constant = new_lat.constant;
ipa_push_func_to_list (&wl, cs->callee); 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; struct ipa_node_params *orig_callee_info;
int i, count; int i, count;
struct ipa_jump_func *jump_func;
struct cgraph_node *node = cs->callee, *orig; struct cgraph_node *node = cs->callee, *orig;
if (!n_cloning_candidates) if (!n_cloning_candidates)
@ -868,13 +1021,17 @@ ipcp_need_redirect_p (struct cgraph_edge *cs)
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_lattice (orig_callee_info, i); struct ipcp_lattice *lat = ipcp_get_lattice (orig_callee_info, i);
if (ipcp_lat_is_const (lat)) struct ipa_jump_func *jump_func;
{
jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i); jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
if (jump_func->type != IPA_JF_CONST) 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 true;
} }
}
return false; return false;
} }
@ -912,9 +1069,17 @@ ipcp_update_callgraph (void)
{ {
next = cs->next_caller; next = cs->next_caller;
if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs)) if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs))
{
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); cgraph_redirect_edge_callee (cs, orig_node);
} }
} }
}
} }
/* Update profiling info for versioned functions and the functions they were /* Update profiling info for versioned functions and the functions they were
@ -1031,6 +1196,57 @@ ipcp_estimate_cloning_cost (struct cgraph_node *node)
return cost + 1; 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. */ /* Return number of live constant parameters. */
static int static int
ipcp_const_param_count (struct cgraph_node *node) 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++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_lattice (info, 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. */ /* 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++; const_param++;
} }
return 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 = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS);
max_new_size = max_new_size * PARAM_VALUE (PARAM_IPCP_UNIT_GROWTH) / 100 + 1; 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 (); heap = fibheap_new ();
for (node = cgraph_nodes; node; node = node->next) for (node = cgraph_nodes; node; node = node->next)
{ {
@ -1099,7 +1318,8 @@ ipcp_insert_stage (void)
if (ipa_is_called_with_var_arguments (info)) if (ipa_is_called_with_var_arguments (info))
continue; continue;
if (ipcp_const_param_count (node)) 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 /* Now clone in priority order until code size growth limits are met or
@ -1183,6 +1403,8 @@ ipcp_insert_stage (void)
if (node1 == NULL) if (node1 == NULL)
continue; continue;
ipcp_process_devirtualization_opportunities (node1);
if (dump_file) if (dump_file)
fprintf (dump_file, "versioned function %s with growth %i, overall %i\n", fprintf (dump_file, "versioned function %s with growth %i, overall %i\n",
cgraph_node_name (node), (int)growth, (int)new_size); cgraph_node_name (node), (int)growth, (int)new_size);
@ -1245,18 +1467,30 @@ ipcp_driver (void)
return 0; 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 static void
ipcp_generate_summary (void) ipcp_generate_summary (void)
{ {
struct cgraph_node *node;
if (dump_file) if (dump_file)
fprintf (dump_file, "\nIPA constant propagation start:\n"); fprintf (dump_file, "\nIPA constant propagation start:\n");
ipa_check_create_node_params (); ipa_check_create_node_params ();
ipa_check_create_edge_args (); ipa_check_create_edge_args ();
ipa_register_cgraph_hooks (); ipa_register_cgraph_hooks ();
/* 1. Call the init stage to initialize
the ipa_node_params and ipa_edge_args structures. */ for (node = cgraph_nodes; node; node = node->next)
ipcp_init_stage (); 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. */ /* Write ipcp summary for nodes in SET. */

View File

@ -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 /* 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. */ of an indirect edge IE and return the edge. Otherwise, return NULL. */
static struct cgraph_edge * struct cgraph_edge *
make_edge_direct_to_target (struct cgraph_edge *ie, tree target) ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
{ {
struct cgraph_node *callee; struct cgraph_node *callee;
@ -1484,7 +1484,7 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
else else
return NULL; 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 /* 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; return NULL;
if (target) if (target)
return make_edge_direct_to_target (ie, target); return ipa_make_edge_direct_to_target (ie, target);
else else
return NULL; return NULL;
} }
@ -1794,7 +1794,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
__attribute__((unused)) void *data) __attribute__((unused)) void *data)
{ {
struct ipa_node_params *old_info, *new_info; struct ipa_node_params *old_info, *new_info;
int param_count; int param_count, i;
ipa_check_create_node_params (); ipa_check_create_node_params ();
old_info = IPA_NODE_REF (src); 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 *) new_info->params = (struct ipa_param_descriptor *)
duplicate_array (old_info->params, duplicate_array (old_info->params,
sizeof (struct ipa_param_descriptor) * param_count); 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->ipcp_orig_node = old_info->ipcp_orig_node;
new_info->count_scale = old_info->count_scale; 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. */ /* Register our cgraph hooks if they are not already there. */

View File

@ -133,11 +133,12 @@ struct GTY (()) ipa_jump_func
computed by the interprocedural stage of IPCP. computed by the interprocedural stage of IPCP.
There are three main values of the lattice: There are three main values of the lattice:
IPA_TOP - unknown, IPA_TOP - unknown,
IPA_BOTTOM - non constant, IPA_BOTTOM - variable,
IPA_CONST_VALUE - simple scalar constant, 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. We also use this type to propagate types accross the call graph for the
Integer and real constants are represented as IPA_CONST_VALUE. */ purpose of devirtualization. In that case, IPA_CONST_VALUE denotes a known
type, rather than a constant. */
enum ipa_lattice_type enum ipa_lattice_type
{ {
IPA_BOTTOM, IPA_BOTTOM,
@ -161,8 +162,14 @@ struct ipa_param_descriptor
struct ipcp_lattice ipcp_lattice; struct ipcp_lattice ipcp_lattice;
/* PARAM_DECL of this parameter. */ /* PARAM_DECL of this parameter. */
tree decl; 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. */ /* The parameter is used. */
unsigned used : 1; 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 /* 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 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. */ /* Flag this node as having callers with variable number of arguments. */
static inline void 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, bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
VEC (cgraph_edge_p, heap) **new_edges); 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. */ /* Debugging interface. */
void ipa_print_node_params (FILE *, struct cgraph_node *node); void ipa_print_node_params (FILE *, struct cgraph_node *node);
void ipa_print_all_params (FILE *); void ipa_print_all_params (FILE *);

View File

@ -832,6 +832,12 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTOR,
"a pointer to an aggregate with", "a pointer to an aggregate with",
2, 0, 0) 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: Local variables:
mode:c mode:c

View File

@ -1,3 +1,12 @@
2010-08-05 Martin Jambor <mjambor@suse.cz>
* 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 <jie@codesourcery.com> 2010-08-05 Jie Zhang <jie@codesourcery.com>
PR tree-optimization/45144 PR tree-optimization/45144

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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;
}

View File

@ -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. */ /* If STMT is a GIMPLE_CALL, replace it with its inline expansion. */
static bool static bool
@ -3753,12 +3739,8 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
/* First, see if we can figure out what function is being called. /* First, see if we can figure out what function is being called.
If we cannot, then there is no hope of inlining the function. */ If we cannot, then there is no hope of inlining the function. */
fn = gimple_call_fndecl (stmt); fn = gimple_call_fndecl (stmt);
if (!fn)
{
fn = get_indirect_callee_fndecl (id->dst_node, stmt);
if (!fn) if (!fn)
goto egress; goto egress;
}
/* Turn forward declarations into real ones. */ /* Turn forward declarations into real ones. */
fn = cgraph_node (fn)->decl; fn = cgraph_node (fn)->decl;