===================================================================
@@ -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 A
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_dynami
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
===================================================================
@@ -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 {
===================================================================
@@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimpl
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)
===================================================================
@@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_
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 c
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 (struc
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 (struc
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 (struc
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 +3264,11 @@ try_make_edge_direct_virtual_call (struc
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 +4847,7 @@ ipa_write_indirect_edge_info (struct out
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 +4879,7 @@ ipa_read_indirect_edge_info (struct lto_
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
===================================================================
@@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct c
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 c
/* 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];
===================================================================
@@ -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" } } */
===================================================================
@@ -97,7 +97,7 @@ possible_placement_new (tree type, tree
|| !tree_fits_shwi_p (TYPE_SIZE (type))
|| (cur_offset
+ (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
- : 1)
+ : GET_MODE_BITSIZE (Pmode))
<= tree_to_uhwi (TYPE_SIZE (type)))));
}
@@ -278,6 +278,14 @@ ipa_polymorphic_call_context::restrict_t
pos = int_bit_position (fld);
if (pos > (unsigned HOST_WIDE_INT)cur_offset)
continue;
+
+ /* Do not consider vptr itself. Not even for placement new. */
+ if (!pos && DECL_ARTIFICIAL (fld)
+ && POINTER_TYPE_P (TREE_TYPE (fld))
+ && TYPE_BINFO (type)
+ && polymorphic_type_binfo_p (TYPE_BINFO (type)))
+ continue;
+
if (!DECL_SIZE (fld) || !tree_fits_uhwi_p (DECL_SIZE (fld)))
goto no_useful_type_info;
size = tree_to_uhwi (DECL_SIZE (fld));
@@ -583,7 +591,7 @@ ipa_polymorphic_call_context::dump (FILE
{
fprintf (f, " ");
if (invalid)
- fprintf (f, "Call is known to be undefined\n");
+ fprintf (f, "Call is known to be undefined");
else
{
if (useless_p ())
===================================================================
@@ -26,13 +26,9 @@ main()
/* Inlining everything into main makes type clear from type of variable b.
However devirtualization is also possible for offline copy of A::barbar. Invoking
B's barbar makes it clear the type is at least B and B is an anonymous
- namespace type and therefore we know it has no derivations.
- FIXME: Currently we devirtualize speculatively only because we do not track
- dynamic type changes well. */
-/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */
-/* { dg-final { scan-ipa-dump-times "Outer types match, merging flags" 2 "inline" } } */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" } } */
+ namespace type and therefore we know it has no derivations. */
+/* { dg-final { scan-ipa-dump-times "First type is base of second" 3 "inline" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "inline" } } */
/* Verify that speculation is optimized by late optimizers. */
/* { dg-final { scan-ipa-dump-times "return 2" 2 "optimized" } } */
===================================================================
@@ -27,7 +27,6 @@ main()
Check that we handle that. */
/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */
-/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
===================================================================
@@ -20,7 +20,7 @@ m()
return 0;
}
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target\[^\\n\]*B::foo" 1 "inline" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized" } } */
/* { dg-final { scan-ipa-dump-not "abort" "optimized" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
===================================================================
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining" } */
+struct A {
+ virtual int foo(){return 1;}
+};
+struct B:A {
+ virtual int foo(){return 2;}
+ int callfoo(){foo();}
+};
+struct C:A {
+ virtual int foo(){return 3;}
+};
+struct D:B {
+ virtual int foo(){return 4;}
+ int callfoo(){foo();}
+};
+static void
+test (struct A *a)
+{
+ if (a->foo() != 2)
+ __builtin_abort ();
+}
+int
+m()
+{
+ struct A *a = new C;
+ static_cast<B*>(a)->callfoo();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*__builtin_unreachable" 1 "inline" } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
===================================================================
@@ -37,7 +37,6 @@ main()
}
/* One invocation is A::foo () other is B::foo () even though the type is destroyed and rebuilt in test() */
-/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
===================================================================
@@ -23,7 +23,7 @@ m()
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-times "Discovered a virtual call to a known 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" } } */
===================================================================
@@ -2371,9 +2371,10 @@ ipa_analyze_call_uses (struct func_body_
gcc_checking_assert (cs->indirect_info->otr_token
== tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
- context.get_dynamic_type (instance,
- OBJ_TYPE_REF_OBJECT (target),
- obj_type_ref_class (target), call);
+ cs->indirect_info->vptr_changed
+ = !context.get_dynamic_type (instance,
+ OBJ_TYPE_REF_OBJECT (target),
+ obj_type_ref_class (target), call);
cs->indirect_info->context = context;
}