ipa-devirt.c (odr_type_d): Add field all_derivations_known.
* ipa-devirt.c (odr_type_d): Add field all_derivations_known. (type_all_derivations_known_p): New predicate. (type_all_ctors_visible_p): New predicate. (type_possibly_instantiated_p): New predicate. (get_odr_type): Compute all_derivations_known. (dump_odr_type): Dump the flag. (maybe_record_type): Cleanup. (record_target_from_binfo): Add bases_to_consider array; record bases for types w/o instances and skip CXX destructor. (possible_polymorphic_call_targets_1): Add bases_to_consider and consider_construction parameters; check if type may have instance. (get_polymorphic_call_info): Set maybe_in_construction to true when we know nothing. (record_targets_from_bases): Skip CXX destructors; they are never called for types in construction. (possible_polymorphic_call_targets): Do not record target when type may not have instance. * g++.dg/ipa/devirt-31.C: New testcase. From-SVN: r209461
This commit is contained in:
parent
7c1b1692e1
commit
2d1644bf5b
@ -1,3 +1,24 @@
|
||||
2014-04-16 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* ipa-devirt.c (odr_type_d): Add field all_derivations_known.
|
||||
(type_all_derivations_known_p): New predicate.
|
||||
(type_all_ctors_visible_p): New predicate.
|
||||
(type_possibly_instantiated_p): New predicate.
|
||||
(get_odr_type): Compute all_derivations_known.
|
||||
(dump_odr_type): Dump the flag.
|
||||
(maybe_record_type): Cleanup.
|
||||
(record_target_from_binfo): Add bases_to_consider array;
|
||||
record bases for types w/o instances and skip CXX destructor.
|
||||
(possible_polymorphic_call_targets_1): Add bases_to_consider
|
||||
and consider_construction parameters; check if type may
|
||||
have instance.
|
||||
(get_polymorphic_call_info): Set maybe_in_construction to true
|
||||
when we know nothing.
|
||||
(record_targets_from_bases): Skip CXX destructors; they are
|
||||
never called for types in construction.
|
||||
(possible_polymorphic_call_targets): Do not record target when
|
||||
type may not have instance.
|
||||
|
||||
2014-04-16 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
PR ipa/60854
|
||||
|
171
gcc/ipa-devirt.c
171
gcc/ipa-devirt.c
@ -162,6 +162,8 @@ struct GTY(()) odr_type_d
|
||||
int id;
|
||||
/* Is it in anonymous namespace? */
|
||||
bool anonymous_namespace;
|
||||
/* Do we know about all derivations of given type? */
|
||||
bool all_derivations_known;
|
||||
};
|
||||
|
||||
|
||||
@ -180,6 +182,61 @@ polymorphic_type_binfo_p (tree binfo)
|
||||
return BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
|
||||
}
|
||||
|
||||
/* Return TRUE if all derived types of T are known and thus
|
||||
we may consider the walk of derived type complete.
|
||||
|
||||
This is typically true only for final anonymous namespace types and types
|
||||
defined within functions (that may be COMDAT and thus shared across units,
|
||||
but with the same set of derived types). */
|
||||
|
||||
static bool
|
||||
type_all_derivations_known_p (tree t)
|
||||
{
|
||||
if (TYPE_FINAL_P (t))
|
||||
return true;
|
||||
if (flag_ltrans)
|
||||
return false;
|
||||
if (type_in_anonymous_namespace_p (t))
|
||||
return true;
|
||||
return (decl_function_context (TYPE_NAME (t)) != NULL);
|
||||
}
|
||||
|
||||
/* Return TURE if type's constructors are all visible. */
|
||||
|
||||
static bool
|
||||
type_all_ctors_visible_p (tree t)
|
||||
{
|
||||
return !flag_ltrans
|
||||
&& cgraph_state >= CGRAPH_STATE_CONSTRUCTION
|
||||
/* We can not always use type_all_derivations_known_p.
|
||||
For function local types we must assume case where
|
||||
the function is COMDAT and shared in between units.
|
||||
|
||||
TODO: These cases are quite easy to get, but we need
|
||||
to keep track of C++ privatizing via -Wno-weak
|
||||
as well as the IPA privatizing. */
|
||||
&& type_in_anonymous_namespace_p (t);
|
||||
}
|
||||
|
||||
/* Return TRUE if type may have instance. */
|
||||
|
||||
static bool
|
||||
type_possibly_instantiated_p (tree t)
|
||||
{
|
||||
tree vtable;
|
||||
varpool_node *vnode;
|
||||
|
||||
/* TODO: Add abstract types here. */
|
||||
if (!type_all_ctors_visible_p (t))
|
||||
return true;
|
||||
|
||||
vtable = BINFO_VTABLE (TYPE_BINFO (t));
|
||||
if (TREE_CODE (vtable) == POINTER_PLUS_EXPR)
|
||||
vtable = TREE_OPERAND (TREE_OPERAND (vtable, 0), 0);
|
||||
vnode = varpool_get_node (vtable);
|
||||
return vnode && vnode->definition;
|
||||
}
|
||||
|
||||
/* One Definition Rule hashtable helpers. */
|
||||
|
||||
struct odr_hasher
|
||||
@ -439,6 +496,7 @@ get_odr_type (tree type, bool insert)
|
||||
val->bases = vNULL;
|
||||
val->derived_types = vNULL;
|
||||
val->anonymous_namespace = type_in_anonymous_namespace_p (type);
|
||||
val->all_derivations_known = type_all_derivations_known_p (type);
|
||||
*slot = val;
|
||||
for (i = 0; i < BINFO_N_BASE_BINFOS (binfo); i++)
|
||||
/* For now record only polymorphic types. other are
|
||||
@ -469,7 +527,8 @@ dump_odr_type (FILE *f, odr_type t, int indent=0)
|
||||
unsigned int i;
|
||||
fprintf (f, "%*s type %i: ", indent * 2, "", t->id);
|
||||
print_generic_expr (f, t->type, TDF_SLIM);
|
||||
fprintf (f, "%s\n", t->anonymous_namespace ? " (anonymous namespace)":"");
|
||||
fprintf (f, "%s", t->anonymous_namespace ? " (anonymous namespace)":"");
|
||||
fprintf (f, "%s\n", t->all_derivations_known ? " (derivations known)":"");
|
||||
if (TYPE_NAME (t->type))
|
||||
{
|
||||
fprintf (f, "%*s defined at: %s:%i\n", indent * 2, "",
|
||||
@ -710,14 +769,16 @@ maybe_record_node (vec <cgraph_node *> &nodes,
|
||||
}
|
||||
}
|
||||
else if (completep
|
||||
&& !type_in_anonymous_namespace_p
|
||||
(method_class_type (TREE_TYPE (target))))
|
||||
&& (!type_in_anonymous_namespace_p
|
||||
(DECL_CONTEXT (target))
|
||||
|| flag_ltrans))
|
||||
*completep = false;
|
||||
}
|
||||
|
||||
/* See if BINFO's type match OUTER_TYPE. If so, lookup
|
||||
BINFO of subtype of OTR_TYPE at OFFSET and in that BINFO find
|
||||
method in vtable and insert method to NODES array.
|
||||
method in vtable and insert method to NODES array
|
||||
or BASES_TO_CONSIDER if this array is non-NULL.
|
||||
Otherwise recurse to base BINFOs.
|
||||
This match what get_binfo_at_offset does, but with offset
|
||||
being unknown.
|
||||
@ -736,6 +797,7 @@ maybe_record_node (vec <cgraph_node *> &nodes,
|
||||
|
||||
static void
|
||||
record_target_from_binfo (vec <cgraph_node *> &nodes,
|
||||
vec <tree> *bases_to_consider,
|
||||
tree binfo,
|
||||
tree otr_type,
|
||||
vec <tree> &type_binfos,
|
||||
@ -795,13 +857,19 @@ record_target_from_binfo (vec <cgraph_node *> &nodes,
|
||||
return;
|
||||
}
|
||||
gcc_assert (inner_binfo);
|
||||
if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (inner_binfo)))
|
||||
if (bases_to_consider
|
||||
? !pointer_set_contains (matched_vtables, BINFO_VTABLE (inner_binfo))
|
||||
: !pointer_set_insert (matched_vtables, BINFO_VTABLE (inner_binfo)))
|
||||
{
|
||||
bool can_refer;
|
||||
tree target = gimple_get_virt_method_for_binfo (otr_token,
|
||||
inner_binfo,
|
||||
&can_refer);
|
||||
maybe_record_node (nodes, target, inserted, can_refer, completep);
|
||||
if (!bases_to_consider)
|
||||
maybe_record_node (nodes, target, inserted, can_refer, completep);
|
||||
/* Destructors are never called via construction vtables. */
|
||||
else if (!target || !DECL_CXX_DESTRUCTOR_P (target))
|
||||
bases_to_consider->safe_push (target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -810,7 +878,7 @@ record_target_from_binfo (vec <cgraph_node *> &nodes,
|
||||
for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
|
||||
/* Walking bases that have no virtual method is pointless excercise. */
|
||||
if (polymorphic_type_binfo_p (base_binfo))
|
||||
record_target_from_binfo (nodes, base_binfo, otr_type,
|
||||
record_target_from_binfo (nodes, bases_to_consider, base_binfo, otr_type,
|
||||
type_binfos,
|
||||
otr_token, outer_type, offset, inserted,
|
||||
matched_vtables, anonymous, completep);
|
||||
@ -822,7 +890,11 @@ record_target_from_binfo (vec <cgraph_node *> &nodes,
|
||||
of TYPE, insert them to NODES, recurse into derived nodes.
|
||||
INSERTED is used to avoid duplicate insertions of methods into NODES.
|
||||
MATCHED_VTABLES are used to avoid duplicate walking vtables.
|
||||
Clear COMPLETEP if unreferable target is found. */
|
||||
Clear COMPLETEP if unreferable target is found.
|
||||
|
||||
If CONSIDER_CONSTURCTION is true, record to BASES_TO_CONSDIER
|
||||
all cases where BASE_SKIPPED is true (because the base is abstract
|
||||
class). */
|
||||
|
||||
static void
|
||||
possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
|
||||
@ -833,23 +905,39 @@ possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
|
||||
HOST_WIDE_INT otr_token,
|
||||
tree outer_type,
|
||||
HOST_WIDE_INT offset,
|
||||
bool *completep)
|
||||
bool *completep,
|
||||
vec <tree> &bases_to_consider,
|
||||
bool consider_construction)
|
||||
{
|
||||
tree binfo = TYPE_BINFO (type->type);
|
||||
unsigned int i;
|
||||
vec <tree> type_binfos = vNULL;
|
||||
bool possibly_instantiated = type_possibly_instantiated_p (type->type);
|
||||
|
||||
record_target_from_binfo (nodes, binfo, otr_type, type_binfos, otr_token,
|
||||
outer_type, offset,
|
||||
inserted, matched_vtables,
|
||||
type->anonymous_namespace, completep);
|
||||
/* We may need to consider types w/o instances because of possible derived
|
||||
types using their methods either directly or via construction vtables.
|
||||
We are safe to skip them when all derivations are known, since we will
|
||||
handle them later.
|
||||
This is done by recording them to BASES_TO_CONSIDER array. */
|
||||
if (possibly_instantiated || consider_construction)
|
||||
{
|
||||
record_target_from_binfo (nodes,
|
||||
(!possibly_instantiated
|
||||
&& type_all_derivations_known_p (type->type))
|
||||
? &bases_to_consider : NULL,
|
||||
binfo, otr_type, type_binfos, otr_token,
|
||||
outer_type, offset,
|
||||
inserted, matched_vtables,
|
||||
type->anonymous_namespace, completep);
|
||||
}
|
||||
type_binfos.release ();
|
||||
for (i = 0; i < type->derived_types.length (); i++)
|
||||
possible_polymorphic_call_targets_1 (nodes, inserted,
|
||||
matched_vtables,
|
||||
otr_type,
|
||||
type->derived_types[i],
|
||||
otr_token, outer_type, offset, completep);
|
||||
otr_token, outer_type, offset, completep,
|
||||
bases_to_consider, consider_construction);
|
||||
}
|
||||
|
||||
/* Cache of queries for polymorphic call targets.
|
||||
@ -1232,7 +1320,7 @@ get_polymorphic_call_info (tree fndecl,
|
||||
context->offset = 0;
|
||||
base_pointer = OBJ_TYPE_REF_OBJECT (ref);
|
||||
context->maybe_derived_type = true;
|
||||
context->maybe_in_construction = false;
|
||||
context->maybe_in_construction = true;
|
||||
|
||||
/* Walk SSA for outer object. */
|
||||
do
|
||||
@ -1433,7 +1521,8 @@ record_targets_from_bases (tree otr_type,
|
||||
tree target = gimple_get_virt_method_for_binfo (otr_token,
|
||||
base_binfo,
|
||||
&can_refer);
|
||||
maybe_record_node (nodes, target, inserted, can_refer, completep);
|
||||
if (!target || ! DECL_CXX_DESTRUCTOR_P (target))
|
||||
maybe_record_node (nodes, target, inserted, can_refer, completep);
|
||||
pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo));
|
||||
}
|
||||
}
|
||||
@ -1487,6 +1576,7 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
pointer_set_t *inserted;
|
||||
pointer_set_t *matched_vtables;
|
||||
vec <cgraph_node *> nodes = vNULL;
|
||||
vec <tree> bases_to_consider = vNULL;
|
||||
odr_type type, outer_type;
|
||||
polymorphic_call_target_d key;
|
||||
polymorphic_call_target_d **slot;
|
||||
@ -1494,6 +1584,7 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
tree binfo, target;
|
||||
bool complete;
|
||||
bool can_refer;
|
||||
bool skipped = false;
|
||||
|
||||
/* If ODR is not initialized, return empty incomplete list. */
|
||||
if (!odr_hash.is_created ())
|
||||
@ -1539,9 +1630,6 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
}
|
||||
/* We need to update our hiearchy if the type does not exist. */
|
||||
outer_type = get_odr_type (context.outer_type, true);
|
||||
/* If outer and inner type match, there are no bases to see. */
|
||||
if (type == outer_type)
|
||||
context.maybe_in_construction = false;
|
||||
/* If the type is complete, there are no derivations. */
|
||||
if (TYPE_FINAL_P (outer_type->type))
|
||||
context.maybe_derived_type = false;
|
||||
@ -1602,7 +1690,10 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
target = NULL;
|
||||
}
|
||||
|
||||
maybe_record_node (nodes, target, inserted, can_refer, &complete);
|
||||
/* Destructors are never called through construction virtual tables,
|
||||
because the type is always known. */
|
||||
if (target && DECL_CXX_DESTRUCTOR_P (target))
|
||||
context.maybe_in_construction = false;
|
||||
|
||||
if (target)
|
||||
{
|
||||
@ -1611,8 +1702,15 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
if (DECL_FINAL_P (target))
|
||||
context.maybe_derived_type = false;
|
||||
}
|
||||
|
||||
/* If OUTER_TYPE is abstract, we know we are not seeing its instance. */
|
||||
if (type_possibly_instantiated_p (outer_type->type))
|
||||
maybe_record_node (nodes, target, inserted, can_refer, &complete);
|
||||
else
|
||||
gcc_assert (!complete);
|
||||
{
|
||||
skipped = true;
|
||||
gcc_assert (in_lto_p || context.maybe_derived_type);
|
||||
}
|
||||
|
||||
pointer_set_insert (matched_vtables, BINFO_VTABLE (binfo));
|
||||
|
||||
@ -1621,7 +1719,7 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
{
|
||||
/* For anonymous namespace types we can attempt to build full type.
|
||||
All derivations must be in this unit (unless we see partial unit). */
|
||||
if (!type->anonymous_namespace || flag_ltrans)
|
||||
if (!type->all_derivations_known)
|
||||
complete = false;
|
||||
for (i = 0; i < outer_type->derived_types.length(); i++)
|
||||
possible_polymorphic_call_targets_1 (nodes, inserted,
|
||||
@ -1629,15 +1727,36 @@ possible_polymorphic_call_targets (tree otr_type,
|
||||
otr_type,
|
||||
outer_type->derived_types[i],
|
||||
otr_token, outer_type->type,
|
||||
context.offset, &complete);
|
||||
context.offset, &complete,
|
||||
bases_to_consider,
|
||||
context.maybe_in_construction);
|
||||
}
|
||||
|
||||
/* Finally walk bases, if asked to. */
|
||||
(*slot)->nonconstruction_targets = nodes.length();
|
||||
|
||||
/* Destructors are never called through construction virtual tables,
|
||||
because the type is always known. One of entries may be cxa_pure_virtual
|
||||
so look to at least two of them. */
|
||||
if (context.maybe_in_construction)
|
||||
record_targets_from_bases (otr_type, otr_token, outer_type->type,
|
||||
context.offset, nodes, inserted,
|
||||
matched_vtables, &complete);
|
||||
for (i =0 ; i < MIN (nodes.length (), 2); i++)
|
||||
if (DECL_CXX_DESTRUCTOR_P (nodes[i]->decl))
|
||||
context.maybe_in_construction = false;
|
||||
if (context.maybe_in_construction)
|
||||
{
|
||||
if (type != outer_type
|
||||
&& (!skipped
|
||||
|| (context.maybe_derived_type
|
||||
&& !type_all_derivations_known_p (outer_type->type))))
|
||||
record_targets_from_bases (otr_type, otr_token, outer_type->type,
|
||||
context.offset, nodes, inserted,
|
||||
matched_vtables, &complete);
|
||||
if (skipped)
|
||||
maybe_record_node (nodes, target, inserted, can_refer, &complete);
|
||||
for (i = 0; i < bases_to_consider.length(); i++)
|
||||
maybe_record_node (nodes, bases_to_consider[i], inserted, can_refer, &complete);
|
||||
}
|
||||
bases_to_consider.release();
|
||||
|
||||
(*slot)->targets = nodes;
|
||||
(*slot)->complete = complete;
|
||||
|
@ -1,3 +1,7 @@
|
||||
2014-04-16 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* g++.dg/ipa/devirt-31.C: New testcase.
|
||||
|
||||
2014-04-16 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
PR lto/60820
|
||||
|
16
gcc/testsuite/g++.dg/ipa/devirt-31.C
Normal file
16
gcc/testsuite/g++.dg/ipa/devirt-31.C
Normal file
@ -0,0 +1,16 @@
|
||||
// { dg-options "-O3 -fdump-tree-ssa" }
|
||||
inline void t()
|
||||
{
|
||||
struct A {virtual void q() {}};
|
||||
static struct A *a;
|
||||
if (!a)
|
||||
a = new(A);
|
||||
a->q();
|
||||
};
|
||||
void
|
||||
m()
|
||||
{
|
||||
t();
|
||||
}
|
||||
// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "ssa" } }
|
||||
// { dg-final { cleanup-tree-dump "ssa" } }
|
Loading…
x
Reference in New Issue
Block a user