Commit 0127c169 by Jan Hubicka Committed by Jan Hubicka

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
parent 2add94cd
2014-10-04 Jan Hubicka <hubicka@ucw.cz> 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 * ipa-prop.c (ipa_compute_jump_functions_for_edge): Call
get_dynamic_type; drop TODO. get_dynamic_type; drop TODO.
* ipa-polymorphic-call.c * ipa-polymorphic-call.c
...@@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags, ...@@ -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 = cgraph_allocate_init_indirect_info ();
edge->indirect_info->ecf_flags = ecf_flags; edge->indirect_info->ecf_flags = ecf_flags;
edge->indirect_info->vptr_changed = true;
/* Record polymorphic call info. */ /* Record polymorphic call info. */
if (compute_indirect_info if (compute_indirect_info
...@@ -1988,6 +1989,8 @@ cgraph_node::dump (FILE *f) ...@@ -1988,6 +1989,8 @@ cgraph_node::dump (FILE *f)
edge->indirect_info->member_ptr ? "member ptr" : "aggregate", edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
edge->indirect_info->by_ref ? "passed by reference":"", edge->indirect_info->by_ref ? "passed by reference":"",
(int)edge->indirect_info->offset); (int)edge->indirect_info->offset);
if (edge->indirect_info->vptr_changed)
fprintf (f, " (vptr maybe changed)");
} }
fprintf (f, "\n"); fprintf (f, "\n");
if (edge->indirect_info->polymorphic) if (edge->indirect_info->polymorphic)
......
...@@ -1393,6 +1393,9 @@ struct GTY(()) cgraph_indirect_call_info ...@@ -1393,6 +1393,9 @@ struct GTY(()) cgraph_indirect_call_info
/* When the previous bit is set, this one determines whether the destination /* When the previous bit is set, this one determines whether the destination
is loaded from a parameter passed by reference. */ is loaded from a parameter passed by reference. */
unsigned by_ref : 1; 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 { 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, ...@@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
t = NULL; t = NULL;
/* Try to work out value of virtual table pointer value in replacemnets. */ /* 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) while (agg_reps)
{ {
...@@ -1578,7 +1579,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, ...@@ -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 /* Try to work out value of virtual table pointer value in known
aggregate values. */ aggregate values. */
if (!t && known_aggs.length () > (unsigned int) param_index 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; struct ipa_agg_jump_function *agg;
agg = known_aggs[param_index]; agg = known_aggs[param_index];
......
...@@ -760,11 +760,37 @@ walk_ssa_copies (tree op) ...@@ -760,11 +760,37 @@ walk_ssa_copies (tree op)
while (TREE_CODE (op) == SSA_NAME while (TREE_CODE (op) == SSA_NAME
&& !SSA_NAME_IS_DEFAULT_DEF (op) && !SSA_NAME_IS_DEFAULT_DEF (op)
&& SSA_NAME_DEF_STMT (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))) /* Special case
return op; if (ptr == 0)
op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op)); 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); STRIP_NOPS (op);
} }
return op; return op;
...@@ -1371,6 +1397,8 @@ check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data) ...@@ -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 is set), try to walk memory writes and find the actual construction of the
instance. instance.
Return true if memory is unchanged from function entry.
We do not include this analysis in the context analysis itself, because 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. 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. */ 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, ...@@ -1615,7 +1643,7 @@ ipa_polymorphic_call_context::get_dynamic_type (tree instance,
function_entry_reached ? " (multiple types encountered)" : ""); 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 /* 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) ...@@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
gcc_checking_assert (cs->indirect_info->otr_token gcc_checking_assert (cs->indirect_info->otr_token
== tree_to_shwi (OBJ_TYPE_REF_TOKEN (target))); == tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
if (context.get_dynamic_type (instance, context.get_dynamic_type (instance,
OBJ_TYPE_REF_OBJECT (target), OBJ_TYPE_REF_OBJECT (target),
obj_type_ref_class (target), call)) obj_type_ref_class (target), call);
cs->indirect_info->context = context; cs->indirect_info->context = context;
} }
if (TREE_CODE (target) == SSA_NAME) if (TREE_CODE (target) == SSA_NAME)
...@@ -2878,6 +2878,38 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, ...@@ -2878,6 +2878,38 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
callee = cgraph_node::get_create (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)) if (!dbg_cnt (devirt))
return NULL; return NULL;
...@@ -3127,17 +3159,17 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, ...@@ -3127,17 +3159,17 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
ctx.offset_by (ie->indirect_info->offset); ctx.offset_by (ie->indirect_info->offset);
/* TODO: We want to record if type change happens. if (ie->indirect_info->vptr_changed)
Old code did not do that that seems like a bug. */ ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor, ie->indirect_info->otr_type);
ie->indirect_info->otr_type);
updated = ie->indirect_info->context.combine_with updated = ie->indirect_info->context.combine_with
(ctx, ie->indirect_info->otr_type); (ctx, ie->indirect_info->otr_type);
} }
/* Try to do lookup via known virtual table pointer value. */ /* 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; tree vtable;
unsigned HOST_WIDE_INT offset; unsigned HOST_WIDE_INT offset;
...@@ -3146,16 +3178,24 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, ...@@ -3146,16 +3178,24 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
true); true);
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset)) 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); vtable, offset);
if (target) if (t)
{ {
if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
&& DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE) && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
|| !possible_polymorphic_call_target_p || !possible_polymorphic_call_target_p
(ie, cgraph_node::get (target))) (ie, cgraph_node::get (t)))
target = ipa_impossible_devirt_target (ie, target); {
return ipa_make_edge_direct_to_target (ie, target); /* 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, ...@@ -3188,7 +3228,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
else else
target = ipa_impossible_devirt_target (ie, NULL_TREE); 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 ()) && !ie->speculative && ie->maybe_hot_p ())
{ {
cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type, 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, ...@@ -3222,7 +3262,11 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
if (target) if (target)
{ {
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (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); return ipa_make_edge_direct_to_target (ie, target, speculative);
} }
else else
...@@ -4801,6 +4845,7 @@ ipa_write_indirect_edge_info (struct output_block *ob, ...@@ -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->agg_contents, 1);
bp_pack_value (&bp, ii->member_ptr, 1); bp_pack_value (&bp, ii->member_ptr, 1);
bp_pack_value (&bp, ii->by_ref, 1); bp_pack_value (&bp, ii->by_ref, 1);
bp_pack_value (&bp, ii->vptr_changed, 1);
streamer_write_bitpack (&bp); streamer_write_bitpack (&bp);
if (ii->agg_contents || ii->polymorphic) if (ii->agg_contents || ii->polymorphic)
streamer_write_hwi (ob, ii->offset); streamer_write_hwi (ob, ii->offset);
...@@ -4832,6 +4877,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib, ...@@ -4832,6 +4877,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib,
ii->agg_contents = bp_unpack_value (&bp, 1); ii->agg_contents = bp_unpack_value (&bp, 1);
ii->member_ptr = bp_unpack_value (&bp, 1); ii->member_ptr = bp_unpack_value (&bp, 1);
ii->by_ref = 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) if (ii->agg_contents || ii->polymorphic)
ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib); ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
else else
......
2014-10-04 Jan Hubicka <hubicka@ucw.cz> 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 PR ipa/61144
* gcc.dg/tree-ssa/pr61144.c: New testcase. * gcc.dg/tree-ssa/pr61144.c: New testcase.
......
/* { 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" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment