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:
Jan Hubicka 2014-10-05 07:02:19 +02:00 committed by Jan Hubicka
parent 2add94cd44
commit 0127c169d1
8 changed files with 161 additions and 25 deletions

View File

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

View File

@ -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)

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

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