ipa-polymorphic-call.c (possible_placement_new): Fix condition on size.
* ipa-polymorphic-call.c (possible_placement_new): Fix condition on size. (ipa_polymorphic_call_context::restrict_to_inner_type): Do not walk into vptr pointer. (ipa_polymorphic_call_context::dump): Fix formating. (walk_ssa_copies): Add logic avoiding loops; update uses. * ipa-prop.c (ipa_analyze_call_uses): Compute vptr_changed. * g++.dg/ipa/devirt-42.C: Update template. * g++.dg/ipa/devirt-44.C: Update template. * g++.dg/ipa/devirt-45.C: Update template. * g++.dg/ipa/devirt-46.C: Update template. * g++.dg/ipa/devirt-47.C: Update template. * g++.dg/ipa/devirt-48.C: New testcase. From-SVN: r215902
This commit is contained in:
parent
f50592234c
commit
29c43c83ef
|
@ -1,3 +1,13 @@
|
||||||
|
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||||
|
|
||||||
|
* ipa-polymorphic-call.c (possible_placement_new): Fix condition
|
||||||
|
on size.
|
||||||
|
(ipa_polymorphic_call_context::restrict_to_inner_type): Do not walk
|
||||||
|
into vptr pointer.
|
||||||
|
(ipa_polymorphic_call_context::dump): Fix formating.
|
||||||
|
(walk_ssa_copies): Add logic avoiding loops; update uses.
|
||||||
|
* ipa-prop.c (ipa_analyze_call_uses): Compute vptr_changed.
|
||||||
|
|
||||||
2014-10-02 Mark Wielaard <mjw@redhat.com>
|
2014-10-02 Mark Wielaard <mjw@redhat.com>
|
||||||
|
|
||||||
PR debug/63239
|
PR debug/63239
|
||||||
|
|
|
@ -97,7 +97,7 @@ possible_placement_new (tree type, tree expected_type,
|
||||||
|| !tree_fits_shwi_p (TYPE_SIZE (type))
|
|| !tree_fits_shwi_p (TYPE_SIZE (type))
|
||||||
|| (cur_offset
|
|| (cur_offset
|
||||||
+ (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
|
+ (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
|
||||||
: 1)
|
: GET_MODE_BITSIZE (Pmode))
|
||||||
<= tree_to_uhwi (TYPE_SIZE (type)))));
|
<= tree_to_uhwi (TYPE_SIZE (type)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,6 +278,14 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree otr_type,
|
||||||
pos = int_bit_position (fld);
|
pos = int_bit_position (fld);
|
||||||
if (pos > (unsigned HOST_WIDE_INT)cur_offset)
|
if (pos > (unsigned HOST_WIDE_INT)cur_offset)
|
||||||
continue;
|
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)))
|
if (!DECL_SIZE (fld) || !tree_fits_uhwi_p (DECL_SIZE (fld)))
|
||||||
goto no_useful_type_info;
|
goto no_useful_type_info;
|
||||||
size = tree_to_uhwi (DECL_SIZE (fld));
|
size = tree_to_uhwi (DECL_SIZE (fld));
|
||||||
|
@ -583,7 +591,7 @@ ipa_polymorphic_call_context::dump (FILE *f) const
|
||||||
{
|
{
|
||||||
fprintf (f, " ");
|
fprintf (f, " ");
|
||||||
if (invalid)
|
if (invalid)
|
||||||
fprintf (f, "Call is known to be undefined\n");
|
fprintf (f, "Call is known to be undefined");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (useless_p ())
|
if (useless_p ())
|
||||||
|
@ -751,11 +759,14 @@ ipa_polymorphic_call_context::set_by_invariant (tree cst,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if OP is SSA name initialized as a copy or by single assignment.
|
/* See if OP is SSA name initialized as a copy or by single assignment.
|
||||||
If so, walk the SSA graph up. */
|
If so, walk the SSA graph up. Because simple PHI conditional is considered
|
||||||
|
copy, GLOBAL_VISITED may be used to avoid infinite loop walking the SSA
|
||||||
|
graph. */
|
||||||
|
|
||||||
static tree
|
static tree
|
||||||
walk_ssa_copies (tree op)
|
walk_ssa_copies (tree op, hash_set<tree> **global_visited = NULL)
|
||||||
{
|
{
|
||||||
|
hash_set <tree> *visited = NULL;
|
||||||
STRIP_NOPS (op);
|
STRIP_NOPS (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)
|
||||||
|
@ -763,6 +774,20 @@ walk_ssa_copies (tree 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))
|
|| gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI))
|
||||||
{
|
{
|
||||||
|
if (global_visited)
|
||||||
|
{
|
||||||
|
if (!*global_visited)
|
||||||
|
*global_visited = new hash_set<tree>;
|
||||||
|
if ((*global_visited)->add (op))
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!visited)
|
||||||
|
visited = new hash_set<tree>;
|
||||||
|
if (visited->add (op))
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
/* Special case
|
/* Special case
|
||||||
if (ptr == 0)
|
if (ptr == 0)
|
||||||
ptr = 0;
|
ptr = 0;
|
||||||
|
@ -776,23 +801,28 @@ walk_ssa_copies (tree op)
|
||||||
{
|
{
|
||||||
gimple phi = SSA_NAME_DEF_STMT (op);
|
gimple phi = SSA_NAME_DEF_STMT (op);
|
||||||
|
|
||||||
if (gimple_phi_num_args (phi) != 2)
|
if (gimple_phi_num_args (phi) > 2)
|
||||||
return op;
|
goto done;
|
||||||
if (integer_zerop (gimple_phi_arg_def (phi, 0)))
|
if (gimple_phi_num_args (phi) == 1)
|
||||||
|
op = gimple_phi_arg_def (phi, 0);
|
||||||
|
else if (integer_zerop (gimple_phi_arg_def (phi, 0)))
|
||||||
op = gimple_phi_arg_def (phi, 1);
|
op = gimple_phi_arg_def (phi, 1);
|
||||||
else if (integer_zerop (gimple_phi_arg_def (phi, 1)))
|
else if (integer_zerop (gimple_phi_arg_def (phi, 1)))
|
||||||
op = gimple_phi_arg_def (phi, 0);
|
op = gimple_phi_arg_def (phi, 0);
|
||||||
else
|
else
|
||||||
return op;
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
|
if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
|
||||||
return op;
|
goto done;
|
||||||
op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
|
op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
|
||||||
}
|
}
|
||||||
STRIP_NOPS (op);
|
STRIP_NOPS (op);
|
||||||
}
|
}
|
||||||
|
done:
|
||||||
|
if (visited)
|
||||||
|
delete (visited);
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,6 +850,7 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
|
||||||
{
|
{
|
||||||
tree otr_type = NULL;
|
tree otr_type = NULL;
|
||||||
tree base_pointer;
|
tree base_pointer;
|
||||||
|
hash_set <tree> *visited = NULL;
|
||||||
|
|
||||||
if (TREE_CODE (ref) == OBJ_TYPE_REF)
|
if (TREE_CODE (ref) == OBJ_TYPE_REF)
|
||||||
{
|
{
|
||||||
|
@ -835,9 +866,9 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
|
||||||
invalid = false;
|
invalid = false;
|
||||||
|
|
||||||
/* Walk SSA for outer object. */
|
/* Walk SSA for outer object. */
|
||||||
do
|
while (true)
|
||||||
{
|
{
|
||||||
base_pointer = walk_ssa_copies (base_pointer);
|
base_pointer = walk_ssa_copies (base_pointer, &visited);
|
||||||
if (TREE_CODE (base_pointer) == ADDR_EXPR)
|
if (TREE_CODE (base_pointer) == ADDR_EXPR)
|
||||||
{
|
{
|
||||||
HOST_WIDE_INT size, max_size;
|
HOST_WIDE_INT size, max_size;
|
||||||
|
@ -869,6 +900,8 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
|
||||||
is known. */
|
is known. */
|
||||||
else if (DECL_P (base))
|
else if (DECL_P (base))
|
||||||
{
|
{
|
||||||
|
if (visited)
|
||||||
|
delete (visited);
|
||||||
/* Only type inconsistent programs can have otr_type that is
|
/* Only type inconsistent programs can have otr_type that is
|
||||||
not part of outer type. */
|
not part of outer type. */
|
||||||
if (otr_type
|
if (otr_type
|
||||||
|
@ -907,7 +940,9 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while (true);
|
|
||||||
|
if (visited)
|
||||||
|
delete (visited);
|
||||||
|
|
||||||
/* Try to determine type of the outer object. */
|
/* Try to determine type of the outer object. */
|
||||||
if (TREE_CODE (base_pointer) == SSA_NAME
|
if (TREE_CODE (base_pointer) == SSA_NAME
|
||||||
|
|
|
@ -2371,9 +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)));
|
||||||
|
|
||||||
context.get_dynamic_type (instance,
|
cs->indirect_info->vptr_changed
|
||||||
OBJ_TYPE_REF_OBJECT (target),
|
= !context.get_dynamic_type (instance,
|
||||||
obj_type_ref_class (target), call);
|
OBJ_TYPE_REF_OBJECT (target),
|
||||||
|
obj_type_ref_class (target), call);
|
||||||
cs->indirect_info->context = context;
|
cs->indirect_info->context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3263,7 +3264,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
|
||||||
{
|
{
|
||||||
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
|
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
|
||||||
{
|
{
|
||||||
if (!speculative)
|
if (speculative)
|
||||||
return NULL;
|
return NULL;
|
||||||
target = ipa_impossible_devirt_target (ie, target);
|
target = ipa_impossible_devirt_target (ie, target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
2014-10-04 Jan Hubicka <hubicka@ucw.cz>
|
||||||
|
|
||||||
|
* g++.dg/ipa/devirt-42.C: Update template.
|
||||||
|
* g++.dg/ipa/devirt-44.C: Update template.
|
||||||
|
* g++.dg/ipa/devirt-45.C: Update template.
|
||||||
|
* g++.dg/ipa/devirt-46.C: Update template.
|
||||||
|
* g++.dg/ipa/devirt-47.C: Update template.
|
||||||
|
* g++.dg/ipa/devirt-48.C: New testcase.
|
||||||
|
|
||||||
2014-10-02 Mark Wielaard <mjw@redhat.com>
|
2014-10-02 Mark Wielaard <mjw@redhat.com>
|
||||||
|
|
||||||
PR debug/63239
|
PR debug/63239
|
||||||
|
|
|
@ -26,13 +26,9 @@ main()
|
||||||
/* Inlining everything into main makes type clear from type of variable b.
|
/* Inlining everything into main makes type clear from type of variable b.
|
||||||
However devirtualization is also possible for offline copy of A::barbar. Invoking
|
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
|
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.
|
namespace type and therefore we know it has no derivations. */
|
||||||
FIXME: Currently we devirtualize speculatively only because we do not track
|
/* { dg-final { scan-ipa-dump-times "First type is base of second" 3 "inline" } } */
|
||||||
dynamic type changes well. */
|
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "inline" } } */
|
||||||
/* { 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" } } */
|
|
||||||
|
|
||||||
/* Verify that speculation is optimized by late optimizers. */
|
/* Verify that speculation is optimized by late optimizers. */
|
||||||
/* { dg-final { scan-ipa-dump-times "return 2" 2 "optimized" } } */
|
/* { dg-final { scan-ipa-dump-times "return 2" 2 "optimized" } } */
|
||||||
|
|
|
@ -27,7 +27,6 @@ main()
|
||||||
Check that we handle that. */
|
Check that we handle that. */
|
||||||
|
|
||||||
/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */
|
/* { 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\]*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 { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
|
||||||
/* { dg-final { cleanup-ipa-dump "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() */
|
/* 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\]*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 { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
|
||||||
/* { dg-final { cleanup-ipa-dump "inline" } } */
|
/* { dg-final { cleanup-ipa-dump "inline" } } */
|
||||||
|
|
|
@ -20,7 +20,7 @@ m()
|
||||||
return 0;
|
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 "OBJ_TYPE_REF" "optimized" } } */
|
||||||
/* { dg-final { scan-ipa-dump-not "abort" "optimized" } } */
|
/* { dg-final { scan-ipa-dump-not "abort" "optimized" } } */
|
||||||
/* { dg-final { cleanup-ipa-dump "inline" } } */
|
/* { dg-final { cleanup-ipa-dump "inline" } } */
|
||||||
|
|
|
@ -23,7 +23,7 @@ m()
|
||||||
return 0;
|
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" } } */
|
/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized" } } */
|
||||||
/* FIXME: We ought to inline thunk. */
|
/* FIXME: We ought to inline thunk. */
|
||||||
/* { dg-final { scan-ipa-dump "C::_ZThn" "optimized" } } */
|
/* { dg-final { scan-ipa-dump "C::_ZThn" "optimized" } } */
|
||||||
|
|
|
@ -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" } } */
|
Loading…
Reference in New Issue