diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-04-17 02:43:53 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2014-04-17 02:43:53 +0000 |
commit | afb441656c670b6e22014c30859c8c21f15fbb10 (patch) | |
tree | d8e52d8022602d98ec6b0fde5b40d561be3e613b /gcc/ipa-devirt.c | |
parent | 6c5f20b680f656069b0fb41a1912fae1d06818dc (diff) | |
download | gcc-afb441656c670b6e22014c30859c8c21f15fbb10.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@209461 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-devirt.c')
-rw-r--r-- | gcc/ipa-devirt.c | 173 |
1 files changed, 146 insertions, 27 deletions
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c index ce724a5147a..eab7ecdb8c7 100644 --- a/gcc/ipa-devirt.c +++ b/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; - - record_target_from_binfo (nodes, binfo, otr_type, type_binfos, otr_token, - outer_type, offset, - inserted, matched_vtables, - type->anonymous_namespace, completep); + bool possibly_instantiated = type_possibly_instantiated_p (type->type); + + /* 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) + 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) - record_targets_from_bases (otr_type, otr_token, outer_type->type, - context.offset, nodes, inserted, - matched_vtables, &complete); + { + 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; |