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:
parent
4caa21a13b
commit
3949c4a710
16 changed files with 762 additions and 74 deletions
|
@ -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>
|
||||
|
||||
* expmed.c (expand_mult_const) <case alg_shift>: Expand shift into
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
316
gcc/ipa-cp.c
316
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. */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
PR tree-optimization/45144
|
||||
|
|
62
gcc/testsuite/g++.dg/ipa/devirt-1.C
Normal file
62
gcc/testsuite/g++.dg/ipa/devirt-1.C
Normal 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" } } */
|
62
gcc/testsuite/g++.dg/ipa/devirt-2.C
Normal file
62
gcc/testsuite/g++.dg/ipa/devirt-2.C
Normal 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" } } */
|
63
gcc/testsuite/g++.dg/ipa/devirt-3.C
Normal file
63
gcc/testsuite/g++.dg/ipa/devirt-3.C
Normal 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" } } */
|
68
gcc/testsuite/g++.dg/ipa/devirt-4.C
Normal file
68
gcc/testsuite/g++.dg/ipa/devirt-4.C
Normal 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" } } */
|
79
gcc/testsuite/g++.dg/ipa/devirt-5.C
Normal file
79
gcc/testsuite/g++.dg/ipa/devirt-5.C
Normal 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" } } */
|
33
gcc/testsuite/gcc.dg/ipa/iinline-3.c
Normal file
33
gcc/testsuite/gcc.dg/ipa/iinline-3.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue