mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-16 18:40:57 +08:00
ipa-polymorphic-call.c (walk_ssa_copies): Recognize NULL pointer checks.
* ipa-polymorphic-call.c (walk_ssa_copies): Recognize NULL pointer checks. (ipa_polymorphic_call_context::get_dynamic_type): Return true if type doesn't change. * cgraph.h (cgraph_indirect_call_info): New flag. * cgraph.c (cgraph_node::create_indirect_edge): Initialize it. (cgraph_node::dump): Dump it. * ipa-prop.c (ipa_analyze_call_uses): Ignore return valud of context.get_dynamic_type. (ipa_make_edge_direct_to_target): Do not speculate edge that is already speuclative. (try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not speculate to __builtin_unreachable (ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream vptr_changed. * ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed. * g++.dg/ipa/devirt-47.C: New testcase. From-SVN: r215898
This commit is contained in:
parent
2add94cd44
commit
0127c169d1
@ -1,3 +1,22 @@
|
||||
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* ipa-polymorphic-call.c (walk_ssa_copies): Recognize
|
||||
NULL pointer checks.
|
||||
(ipa_polymorphic_call_context::get_dynamic_type): Return true
|
||||
if type doesn't change.
|
||||
* cgraph.h (cgraph_indirect_call_info): New flag.
|
||||
* cgraph.c (cgraph_node::create_indirect_edge): Initialize it.
|
||||
(cgraph_node::dump): Dump it.
|
||||
* ipa-prop.c (ipa_analyze_call_uses): Ignore return valud
|
||||
of context.get_dynamic_type.
|
||||
(ipa_make_edge_direct_to_target): Do not speculate
|
||||
edge that is already speuclative.
|
||||
(try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not
|
||||
speculate to __builtin_unreachable
|
||||
(ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream
|
||||
vptr_changed.
|
||||
* ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed.
|
||||
|
||||
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* ipa-prop.c (ipa_compute_jump_functions_for_edge): Call
|
||||
|
@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags,
|
||||
|
||||
edge->indirect_info = cgraph_allocate_init_indirect_info ();
|
||||
edge->indirect_info->ecf_flags = ecf_flags;
|
||||
edge->indirect_info->vptr_changed = true;
|
||||
|
||||
/* Record polymorphic call info. */
|
||||
if (compute_indirect_info
|
||||
@ -1988,6 +1989,8 @@ cgraph_node::dump (FILE *f)
|
||||
edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
|
||||
edge->indirect_info->by_ref ? "passed by reference":"",
|
||||
(int)edge->indirect_info->offset);
|
||||
if (edge->indirect_info->vptr_changed)
|
||||
fprintf (f, " (vptr maybe changed)");
|
||||
}
|
||||
fprintf (f, "\n");
|
||||
if (edge->indirect_info->polymorphic)
|
||||
|
@ -1393,6 +1393,9 @@ struct GTY(()) cgraph_indirect_call_info
|
||||
/* When the previous bit is set, this one determines whether the destination
|
||||
is loaded from a parameter passed by reference. */
|
||||
unsigned by_ref : 1;
|
||||
/* For polymorphic calls this specify whether the virtual table pointer
|
||||
may have changed in between function entry and the call. */
|
||||
unsigned vptr_changed : 1;
|
||||
};
|
||||
|
||||
struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
|
||||
|
@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
|
||||
t = NULL;
|
||||
|
||||
/* Try to work out value of virtual table pointer value in replacemnets. */
|
||||
if (!t && agg_reps && !ie->indirect_info->by_ref)
|
||||
if (!t && agg_reps && !ie->indirect_info->by_ref
|
||||
&& !ie->indirect_info->vptr_changed)
|
||||
{
|
||||
while (agg_reps)
|
||||
{
|
||||
@ -1578,7 +1579,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
|
||||
/* Try to work out value of virtual table pointer value in known
|
||||
aggregate values. */
|
||||
if (!t && known_aggs.length () > (unsigned int) param_index
|
||||
&& !ie->indirect_info->by_ref)
|
||||
&& !ie->indirect_info->by_ref
|
||||
&& !ie->indirect_info->vptr_changed)
|
||||
{
|
||||
struct ipa_agg_jump_function *agg;
|
||||
agg = known_aggs[param_index];
|
||||
|
@ -760,11 +760,37 @@ walk_ssa_copies (tree op)
|
||||
while (TREE_CODE (op) == SSA_NAME
|
||||
&& !SSA_NAME_IS_DEFAULT_DEF (op)
|
||||
&& SSA_NAME_DEF_STMT (op)
|
||||
&& gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
|
||||
&& (gimple_assign_single_p (SSA_NAME_DEF_STMT (op))
|
||||
|| gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI))
|
||||
{
|
||||
if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
|
||||
return op;
|
||||
op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
|
||||
/* Special case
|
||||
if (ptr == 0)
|
||||
ptr = 0;
|
||||
else
|
||||
ptr = ptr.foo;
|
||||
This pattern is implicitly produced for casts to non-primary
|
||||
bases. When doing context analysis, we do not really care
|
||||
about the case pointer is NULL, becuase the call will be
|
||||
undefined anyway. */
|
||||
if (gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI)
|
||||
{
|
||||
gimple phi = SSA_NAME_DEF_STMT (op);
|
||||
|
||||
if (gimple_phi_num_args (phi) != 2)
|
||||
return op;
|
||||
if (integer_zerop (gimple_phi_arg_def (phi, 0)))
|
||||
op = gimple_phi_arg_def (phi, 1);
|
||||
else if (integer_zerop (gimple_phi_arg_def (phi, 1)))
|
||||
op = gimple_phi_arg_def (phi, 0);
|
||||
else
|
||||
return op;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
|
||||
return op;
|
||||
op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
|
||||
}
|
||||
STRIP_NOPS (op);
|
||||
}
|
||||
return op;
|
||||
@ -1371,6 +1397,8 @@ check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data)
|
||||
is set), try to walk memory writes and find the actual construction of the
|
||||
instance.
|
||||
|
||||
Return true if memory is unchanged from function entry.
|
||||
|
||||
We do not include this analysis in the context analysis itself, because
|
||||
it needs memory SSA to be fully built and the walk may be expensive.
|
||||
So it is not suitable for use withing fold_stmt and similar uses. */
|
||||
@ -1615,7 +1643,7 @@ ipa_polymorphic_call_context::get_dynamic_type (tree instance,
|
||||
function_entry_reached ? " (multiple types encountered)" : "");
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See if speculation given by SPEC_OUTER_TYPE, SPEC_OFFSET and SPEC_MAYBE_DERIVED_TYPE
|
||||
|
@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
|
||||
gcc_checking_assert (cs->indirect_info->otr_token
|
||||
== tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
|
||||
|
||||
if (context.get_dynamic_type (instance,
|
||||
OBJ_TYPE_REF_OBJECT (target),
|
||||
obj_type_ref_class (target), call))
|
||||
cs->indirect_info->context = context;
|
||||
context.get_dynamic_type (instance,
|
||||
OBJ_TYPE_REF_OBJECT (target),
|
||||
obj_type_ref_class (target), call);
|
||||
cs->indirect_info->context = context;
|
||||
}
|
||||
|
||||
if (TREE_CODE (target) == SSA_NAME)
|
||||
@ -2878,6 +2878,38 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
|
||||
callee = cgraph_node::get_create (target);
|
||||
}
|
||||
|
||||
/* If the edge is already speculated. */
|
||||
if (speculative && ie->speculative)
|
||||
{
|
||||
struct cgraph_edge *e2;
|
||||
struct ipa_ref *ref;
|
||||
ie->speculative_call_info (e2, ie, ref);
|
||||
if (e2->callee->ultimate_alias_target ()
|
||||
!= callee->ultimate_alias_target ())
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
|
||||
"(%s/%i -> %s/%i) but the call is already speculated to %s/%i. Giving up.\n",
|
||||
xstrdup (ie->caller->name ()),
|
||||
ie->caller->order,
|
||||
xstrdup (callee->name ()),
|
||||
callee->order,
|
||||
xstrdup (e2->callee->name ()),
|
||||
e2->callee->order);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
|
||||
"(%s/%i -> %s/%i) this agree with previous speculation.\n",
|
||||
xstrdup (ie->caller->name ()),
|
||||
ie->caller->order,
|
||||
xstrdup (callee->name ()),
|
||||
callee->order);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dbg_cnt (devirt))
|
||||
return NULL;
|
||||
|
||||
@ -3127,17 +3159,17 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
|
||||
|
||||
ctx.offset_by (ie->indirect_info->offset);
|
||||
|
||||
/* TODO: We want to record if type change happens.
|
||||
Old code did not do that that seems like a bug. */
|
||||
ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
|
||||
ie->indirect_info->otr_type);
|
||||
if (ie->indirect_info->vptr_changed)
|
||||
ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
|
||||
ie->indirect_info->otr_type);
|
||||
|
||||
updated = ie->indirect_info->context.combine_with
|
||||
(ctx, ie->indirect_info->otr_type);
|
||||
}
|
||||
|
||||
/* Try to do lookup via known virtual table pointer value. */
|
||||
if (!ie->indirect_info->by_ref)
|
||||
if (!ie->indirect_info->by_ref
|
||||
&& (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively))
|
||||
{
|
||||
tree vtable;
|
||||
unsigned HOST_WIDE_INT offset;
|
||||
@ -3146,16 +3178,24 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
|
||||
true);
|
||||
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
|
||||
{
|
||||
target = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
|
||||
t = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
|
||||
vtable, offset);
|
||||
if (target)
|
||||
if (t)
|
||||
{
|
||||
if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
|
||||
&& DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
|
||||
if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
|
||||
&& DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
|
||||
|| !possible_polymorphic_call_target_p
|
||||
(ie, cgraph_node::get (target)))
|
||||
target = ipa_impossible_devirt_target (ie, target);
|
||||
return ipa_make_edge_direct_to_target (ie, target);
|
||||
(ie, cgraph_node::get (t)))
|
||||
{
|
||||
/* Do not speculate builtin_unreachable, it is stpid! */
|
||||
if (!ie->indirect_info->vptr_changed)
|
||||
target = ipa_impossible_devirt_target (ie, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = t;
|
||||
speculative = ie->indirect_info->vptr_changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3188,7 +3228,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
|
||||
else
|
||||
target = ipa_impossible_devirt_target (ie, NULL_TREE);
|
||||
}
|
||||
else if (flag_devirtualize_speculatively
|
||||
else if (!target && flag_devirtualize_speculatively
|
||||
&& !ie->speculative && ie->maybe_hot_p ())
|
||||
{
|
||||
cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type,
|
||||
@ -3222,7 +3262,11 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
|
||||
if (target)
|
||||
{
|
||||
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
|
||||
target = ipa_impossible_devirt_target (ie, target);
|
||||
{
|
||||
if (!speculative)
|
||||
return NULL;
|
||||
target = ipa_impossible_devirt_target (ie, target);
|
||||
}
|
||||
return ipa_make_edge_direct_to_target (ie, target, speculative);
|
||||
}
|
||||
else
|
||||
@ -4801,6 +4845,7 @@ ipa_write_indirect_edge_info (struct output_block *ob,
|
||||
bp_pack_value (&bp, ii->agg_contents, 1);
|
||||
bp_pack_value (&bp, ii->member_ptr, 1);
|
||||
bp_pack_value (&bp, ii->by_ref, 1);
|
||||
bp_pack_value (&bp, ii->vptr_changed, 1);
|
||||
streamer_write_bitpack (&bp);
|
||||
if (ii->agg_contents || ii->polymorphic)
|
||||
streamer_write_hwi (ob, ii->offset);
|
||||
@ -4832,6 +4877,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib,
|
||||
ii->agg_contents = bp_unpack_value (&bp, 1);
|
||||
ii->member_ptr = bp_unpack_value (&bp, 1);
|
||||
ii->by_ref = bp_unpack_value (&bp, 1);
|
||||
ii->vptr_changed = bp_unpack_value (&bp, 1);
|
||||
if (ii->agg_contents || ii->polymorphic)
|
||||
ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
|
||||
else
|
||||
|
@ -1,3 +1,7 @@
|
||||
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* g++.dg/ipa/devirt-47.C: New testcase.
|
||||
|
||||
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
PR ipa/61144
|
||||
|
31
gcc/testsuite/g++.dg/ipa/devirt-47.C
Normal file
31
gcc/testsuite/g++.dg/ipa/devirt-47.C
Normal file
@ -0,0 +1,31 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining -fdump-tree-optimized" } */
|
||||
struct A {
|
||||
virtual int foo(){return 1;}
|
||||
};
|
||||
struct B {
|
||||
virtual int bar(){return 4;}
|
||||
};
|
||||
struct C:B,A {
|
||||
virtual int foo(){return 2;}
|
||||
};
|
||||
static void
|
||||
test (struct A *a)
|
||||
{
|
||||
if (a->foo() != 2)
|
||||
__builtin_abort ();
|
||||
}
|
||||
int
|
||||
m()
|
||||
{
|
||||
struct A *a = new C;
|
||||
test (a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target\[^\\n\]*C::_ZTh" 1 "inline" } } */
|
||||
/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized" } } */
|
||||
/* FIXME: We ought to inline thunk. */
|
||||
/* { dg-final { scan-ipa-dump "C::_ZThn" "optimized" } } */
|
||||
/* { dg-final { cleanup-ipa-dump "inline" } } */
|
||||
/* { dg-final { cleanup-ipa-dump "optimized" } } */
|
Loading…
x
Reference in New Issue
Block a user