aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog19
-rw-r--r--gcc/cgraph.c3
-rw-r--r--gcc/cgraph.h3
-rw-r--r--gcc/ipa-cp.c6
-rw-r--r--gcc/ipa-polymorphic-call.c38
-rw-r--r--gcc/ipa-prop.c82
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/g++.dg/ipa/devirt-47.C31
8 files changed, 161 insertions, 25 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d338270..3bf6de1 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,24 @@
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
get_dynamic_type; drop TODO.
* ipa-polymorphic-call.c
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index a46e188..38dc7e6 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -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)
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 5ed078a..20b5c4e 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -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 {
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 657536c..a3be16f 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -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];
diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c
index ecbd78c..74226f2 100644
--- a/gcc/ipa-polymorphic-call.c
+++ b/gcc/ipa-polymorphic-call.c
@@ -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
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index d5ecea4..80acdcc 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 39af721..ce04eac 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,9 @@
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
* gcc.dg/tree-ssa/pr61144.c: New testcase.
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-47.C b/gcc/testsuite/g++.dg/ipa/devirt-47.C
new file mode 100644
index 0000000..85f7b63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/devirt-47.C
@@ -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" } } */