Message ID | or8tvsa95w.fsf@livre.home |
---|---|
State | New |
Headers | show |
On Fri, Aug 19, 2016 at 8:46 PM, Alexandre Oliva <aoliva@redhat.com> wrote: > This is not a finished patch. There are two issues I'd like feedback > on before a final submission. See them below. First, a general > description. > > Handling non-template friends is kind of easy, but it required a bit > of infrastructure in dwarf2out to avoid (i) forcing debug info for > unused types or functions: DW_TAG_friend DIEs are only emitted if > their DW_AT_friend DIE is emitted, and (ii) creating DIEs for such > types or functions just to have them discarded at the end. To this > end, I introduced a list (vec, actually) of types with friends, > processed at the end of the translation unit, and a list of > DW_TAG_friend DIEs that, when we're pruning unused types, reference > DIEs that are still not known to be used, revisited after we finish > deciding all other DIEs, so that we prune DIEs that would have > referenced pruned types or functions. > > Handlig template friends turned out to be trickier: there's no > representation in DWARF for templates. I decided to give debuggers as > much information as possible, enumerating all specializations of > friend templates and outputting DW_TAG_friend DIEs referencing them as > well, but marking them as DW_AT_artificial to indicate they're not > explicitly stated in the source code. This attribute is not valid for > DW_TAG_friend, so it's only emitted in non-strict mode. The greatest > challenge was to enumerate all specializations of a template. It > looked trivial at first, given DECL_TEMPLATE_INSTANTIATIONS, but in > some of the testcases, cases it wouldn't list any specializations, and > in others it would list only some of them. I couldn't figure out the > logic behind that, and it seemed clear from the documentation of this > macro that at least in some cases it wouldn't hold the list, so I > ended up writing code to look for specializations in the hashtables of > decl or type specializations. That worked fine, but it's not exactly > an efficient way to obtain the desired information, at least in some > cases. > > > > - should we output specializations of friend templates as friends even > in strict mode? Currently we output them with DW_AT_artificial in > non-strict mode, and without the artificial mark in strict mode. > > - is there any way we can use DECL_TEMPLATE_INSTANTIATIONS reliably to > enumerate the specializations of a friend template, or at least tell > when it can be used? > > - I haven't used local_specializations, should I? I was a bit > confused about the apparently unused local_specialization_stack, > too. > > I haven't covered partial and explicit specializations in the > testcases yet. > > > for gcc/ChangeLog > > PR debug/59319 > * dwarf2out.c (class_types_with_friends): New. > (gen_friend_tags_for_type, gen_friend_tags): New. > (gen_member_die): Record class types with friends. > (deferred_marks): New. > (prune_unused_types_defer_undecided_mark_p): New. > (prune_unused_types_defer_mark): New. > (prune_unused_types_deferred_walk): New. > (prune_unused_types_walk): Defer DW_TAG_friend. > (prune_unused_types): Check deferred marks is empty on entry, > empty it after processing. > (dwarf2out_finish): Generate friend tags. Just throwing in a wrench from the side ... you should do this in dwarf2out_early_finish as late there will be no frontend around anymore. Yeah, prune_unused_types is still done in dwarf2out_finish on trunk but I am moving it early for LTO early debug (I didn't try doing this in isolation on trunk now but you may ...;)). In fact I am moving almost all type related post-processing from dwarf2out_finish to dwarf2out_early_finish. Richard. > * langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New. > (LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it. > * langhooks.h (lang_hooks_for_types): Add get_friends. > > for gcc/cp/ChangeLog > > PR debug/59319 > * cp-objcp-common.c (cp_get_friends): New. > * cp-objcp-common.h (cp_get_friends): Declare. > (LANG_HOOKS_GET_FRIENDS): Override. > * cp-tree.h (enumerate_friend_specializations): Declare. > * pt.c (enumerate_friend_specializations): New. > > for gcc/testsuite/ChangeLog > > PR debug/59319 > * g++.dg/debug/dwarf2/friend-1.C: New. > * g++.dg/debug/dwarf2/friend-2.C: New. > * g++.dg/debug/dwarf2/friend-3.C: New. > * g++.dg/debug/dwarf2/friend-4.C: New. > * g++.dg/debug/dwarf2/friend-5.C: New. > * g++.dg/debug/dwarf2/friend-6.C: New. > * g++.dg/debug/dwarf2/friend-7.C: New. > * g++.dg/debug/dwarf2/friend-8.C: New. > * g++.dg/debug/dwarf2/friend-9.C: New. > * g++.dg/debug/dwarf2/friend-10.C: New. > * g++.dg/debug/dwarf2/friend-11.C: New. > * g++.dg/debug/dwarf2/friend-12.C: New. > * g++.dg/debug/dwarf2/friend-13.C: New. > --- > gcc/cp/cp-objcp-common.c | 103 ++++++++++++++++ > gcc/cp/cp-objcp-common.h | 3 > gcc/cp/cp-tree.h | 1 > gcc/cp/pt.c | 73 +++++++++++ > gcc/dwarf2out.c | 161 +++++++++++++++++++++++++ > gcc/langhooks-def.h | 4 - > gcc/langhooks.h | 19 +++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C | 10 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C | 13 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C | 13 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C | 15 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C | 11 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C | 9 + > gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C | 12 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C | 10 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C | 11 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C | 11 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C | 13 ++ > gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C | 13 ++ > 19 files changed, 503 insertions(+), 2 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C > create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C > > diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c > index e9f9a63..d35632c 100644 > --- a/gcc/cp/cp-objcp-common.c > +++ b/gcc/cp/cp-objcp-common.c > @@ -167,6 +167,109 @@ cp_get_ptrmemfn_type (const_tree type, int selector) > } > } > > +/* At DETAIL level 0, returns non-NULL if the named class TYPE has any > + friends, NULL otherwise. At higher detail levels, return a tree > + list with the friends of the named class type. Each TREE_VALUE > + contains one friend type or function decl. For non-template > + friends, TREE_PURPOSE is NULL. For template friend declarations, > + the returned entries depend on the DETAIL level. At level 1, and > + only at level 1, an entry with NULL TREE_VALUE and non-NULL > + TREE_PURPOSE will START the returned list to indicate the named > + class TYPE has at least one template friend. At level 2, each > + template friend will be in an entry with NULL TREE_VALUE, and with > + the TEMPLATE_DECL in TREE_PURPOSE. At level 3, instead of a NULL > + TREE_VALUE, we add one entry for each instantiation or > + specialization of the template that fits the template friend > + declaration, as long as there is at least one instantiation or > + specialization; if there isn't any, an entry with NULL TREE_VALUE > + is created. A negative detail level will omit non-template friends > + from the returned list. */ > + > +tree > +cp_get_friends (const_tree type, int detail) > +{ > + tree list = NULL_TREE; > + tree typedecl = TYPE_MAIN_DECL (type); > + bool has_templates = false; > + bool non_templates = true; > + > + if (detail == 0) > + { > + if (DECL_FRIENDLIST (typedecl) > + || CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl))) > + return integer_one_node; > + else > + return NULL_TREE; > + } > + else if (detail < 0) > + { > + detail = -detail; > + non_templates = false; > + } > + > + gcc_assert (detail <= 3); > + > + for (tree fnlist = DECL_FRIENDLIST (typedecl); fnlist; > + fnlist = TREE_CHAIN (fnlist)) > + for (tree fns = FRIEND_DECLS (fnlist); fns; fns = TREE_CHAIN (fns)) > + { > + tree fn = TREE_VALUE (fns); > + if (TREE_CODE (fn) == FUNCTION_DECL > + && (!DECL_TEMPLATE_INFO (fn) || DECL_USE_TEMPLATE (fn))) > + { > + if (non_templates) > + list = tree_cons (NULL_TREE, fn, list); > + continue; > + } > + > + has_templates = true; > + > + if (detail == 2) > + list = tree_cons (fn, NULL_TREE, list); > + > + if (detail <= 2) > + continue; > + > + tree new_list = enumerate_friend_specializations (fn); > + if (new_list) > + list = chainon (new_list, list); > + else > + list = tree_cons (fn, NULL_TREE, list); > + } > + > + for (tree cllist = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl)); > + cllist; cllist = TREE_CHAIN (cllist)) > + { > + tree cl = TREE_VALUE (cllist); > + > + if (TREE_CODE (cl) == RECORD_TYPE) > + { > + if (non_templates) > + list = tree_cons (NULL_TREE, cl, list); > + continue; > + } > + > + has_templates = true; > + > + if (detail == 2) > + list = tree_cons (cl, NULL_TREE, list); > + > + if (detail <= 2) > + continue; > + > + tree new_list = enumerate_friend_specializations (cl); > + if (new_list) > + list = chainon (new_list, list); > + else > + list = tree_cons (cl, NULL_TREE, list); > + } > + > + if (has_templates && detail == 1) > + list = tree_cons (integer_one_node, NULL_TREE, list); > + > + return list; > +} > + > /* Return true if DECL is explicit member function. */ > > bool > diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h > index 00780c7..d85d357 100644 > --- a/gcc/cp/cp-objcp-common.h > +++ b/gcc/cp/cp-objcp-common.h > @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see > > extern int cp_get_ref_qualifier (const_tree); > extern tree cp_get_ptrmemfn_type (const_tree, int); > +extern tree cp_get_friends (const_tree, int); > > extern tree objcp_tsubst_copy_and_build (tree, tree, tsubst_flags_t, > tree, bool); > @@ -134,6 +135,8 @@ extern void cp_common_init_ts (void); > #define LANG_HOOKS_GET_REF_QUALIFIER cp_get_ref_qualifier > #undef LANG_HOOKS_GET_PTRMEMFN_TYPE > #define LANG_HOOKS_GET_PTRMEMFN_TYPE cp_get_ptrmemfn_type > +#undef LANG_HOOKS_GET_FRIENDS > +#define LANG_HOOKS_GET_FRIENDS cp_get_friends > #undef LANG_HOOKS_TO_TARGET_CHARSET > #define LANG_HOOKS_TO_TARGET_CHARSET c_common_to_target_charset > #undef LANG_HOOKS_GIMPLIFY_EXPR > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 8a32f17..66106b5 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -6114,6 +6114,7 @@ extern vec<qualified_typedef_usage_t, va_gc> *get_types_needing_access_check (tr > extern int template_class_depth (tree); > extern int is_specialization_of (tree, tree); > extern bool is_specialization_of_friend (tree, tree); > +extern tree enumerate_friend_specializations (tree); > extern tree get_pattern_parm (tree, tree); > extern int comp_template_args (tree, tree, tree * = NULL, > tree * = NULL); > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 1ee5fd4..f0bd40b 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -1469,6 +1469,79 @@ is_specialization_of_friend (tree decl, tree friend_decl) > return false; > } > > +/* Return a list of instantiations/specializations that match > + FRIEND_DECL. */ > + > +tree > +enumerate_friend_specializations (tree friend_decl) > +{ > + if (TREE_CODE (friend_decl) != TEMPLATE_DECL) > + friend_decl = DECL_TI_TEMPLATE (friend_decl); > + > + tree opt_decl = friend_decl; > + while (optimize_specialization_lookup_p (opt_decl)) > + opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl)); > + > + gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL); > + > + /* FIXME: This would be much preferred, but it doesn't always list > + all specializations. */ > + if (0 && DECL_TEMPLATE_INSTANTIATIONS (opt_decl)) > + { > + tree list = NULL_TREE; > + for (tree speclist = DECL_TEMPLATE_INSTANTIATIONS (opt_decl); > + speclist; speclist = TREE_CHAIN (speclist)) > + { > + tree spec = TREE_VALUE (speclist); > + if (opt_decl != friend_decl) > + spec = retrieve_specialization > + (friend_decl, CLASSTYPE_TI_ARGS (spec), 0); > + if (TREE_CODE (spec) == TYPE_DECL) > + spec = TREE_TYPE (spec); > + list = tree_cons (friend_decl, spec, list); > + } > + return list; > + } > + > + typedef hash_table<spec_hasher> specs_t; > + specs_t *specializations; > + tree_code code; > + > + if (DECL_CLASS_TEMPLATE_P (opt_decl)) > + { > + specializations = type_specializations; > + code = RECORD_TYPE; > + } > + else > + { > + specializations = decl_specializations; > + code = FUNCTION_DECL; > + } > + > + tree list = NULL_TREE; > + > + for (specs_t::iterator iter = specializations->begin(), > + end = specializations->end(); > + iter != end; ++iter) > + { > + tree spec = (*iter)->spec; > + if (TREE_CODE (spec) != code) > + continue; > + if (TREE_CODE (spec) == RECORD_TYPE) > + spec = TYPE_NAME (spec); > + if (is_specialization_of_friend (spec, opt_decl)) > + { > + if (opt_decl != friend_decl) > + spec = retrieve_specialization (friend_decl, (*iter)->args, 0); > + if (TREE_CODE (spec) == TYPE_DECL) > + spec = TREE_TYPE (spec); > + list = tree_cons (friend_decl, spec, list); > + } > + } > + > + return list; > +} > + > /* Register the specialization SPEC as a specialization of TMPL with > the indicated ARGS. IS_FRIEND indicates whether the specialization > is actually just a friend declaration. Returns SPEC, or an > diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c > index f40f759..43aa5a0 100644 > --- a/gcc/dwarf2out.c > +++ b/gcc/dwarf2out.c > @@ -22474,6 +22474,57 @@ gen_variant_part (tree variant_part_decl, struct vlr_context *vlr_ctx, > free (discr_lists); > } > > +/* Types that have friends have to be revisited, because we want to > + emit friend attributes for them once we know what types and decls > + have DIEs, and we want to emit friend tags for specializations of > + template friends. We could create DIEs in limbo ourselves, but we > + can't know the specializations before we've seen the entire > + translation unit. */ > + > +static vec<tree> class_types_with_friends; > + > +/* Add any friend tags corresponding to the named TYPE. */ > + > +static void > +gen_friend_tags_for_type (tree type) > +{ > + dw_die_ref context_die = lookup_type_die (type); > + gcc_assert (context_die); > + > + for (tree friends = lang_hooks.types.get_friends (type, 3); friends; > + friends = TREE_CHAIN (friends)) > + { > + tree t = TREE_VALUE (friends); > + dw_die_ref die = NULL; > + if (!t) > + /* If it's a friend template without any specializations, we > + can't refer to it in debug information. */ > + continue; > + else if (TYPE_P (t)) > + die = lookup_type_die (t); > + else if (DECL_P (t)) > + die = lookup_decl_die (t); > + else > + gcc_unreachable (); > + if (!die) > + continue; > + dw_die_ref child = new_die (DW_TAG_friend, context_die, type); > + add_AT_die_ref (child, DW_AT_friend, die); > + if (!dwarf_strict && TREE_PURPOSE (friends)) > + add_AT_flag (child, DW_AT_artificial, 1); > + } > +} > + > +/* Add any friend tags corresponding to class TYPEs that were found to > + have friend declarations. */ > + > +static void > +gen_friend_tags () > +{ > + while (!class_types_with_friends.is_empty ()) > + gen_friend_tags_for_type (class_types_with_friends.pop ()); > +} > + > /* Generate a DIE for a class member. */ > > static void > @@ -22559,6 +22610,9 @@ gen_member_die (tree type, dw_die_ref context_die) > else > gen_decl_die (member, NULL, NULL, context_die); > } > + > + if (lang_hooks.types.get_friends (type, 0)) > + class_types_with_friends.safe_push (type); > } > > /* Generate a DIE for a structure or union type. If TYPE_DECL_SUPPRESS_DEBUG > @@ -26031,6 +26085,97 @@ prune_unused_types_walk_local_classes (dw_die_ref die) > FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c)); > } > > +/* Nodes to revisit after marking everything else, to decide whether > + or not they can/should be emitted. */ > + > +static vec<dw_die_ref> deferred_marks; > + > +/* Return true if the mark is already decided, false otherwise. */ > + > +static bool > +prune_unused_types_defer_undecided_mark_p (dw_die_ref die) > +{ > + gcc_assert (!die->die_mark); > + > + dw_attr_node *a; > + unsigned ix; > + bool can_mark_now = true; > + > + FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a) > + switch (AT_class (a)) > + { > + case dw_val_class_loc: > + case dw_val_class_loc_list: > + /* We don't support attributes of this type now. Deferred > + walking of the location expressions might mark DIEs that > + we've already decided not to output, and we may have > + already based other decisions on it. This is not > + insurmountable, but we don't need to tackle that right > + away. */ > + gcc_unreachable (); > + > + case dw_val_class_die_ref: > + if (!a->dw_attr_val.v.val_die_ref.die->die_mark) > + can_mark_now = false; > + break; > + > + case dw_val_class_str: > + default: > + break; > + } > + > + return !can_mark_now; > +} > + > +/* Return true if we've deferred the decision on whether to mark DIE. > + It must not have children or attributes with location expressions > + or lists. Attributes with strings and other DIEs are ok. If any > + of the DIEs referenced by attributes is not marked, we defer the > + decision to give it a chance to be marked so that we output the > + present DIE too. In this case, we return TRUE, to indicate the > + decision was deferred. If they are all marked already, then we > + know we can output this one as well, so we return FALSE to indicate > + it was NOT deferred. */ > + > +static bool > +prune_unused_types_defer_mark (dw_die_ref die) > +{ > + gcc_assert (die->die_parent->die_mark); > + > + /* We use this for friend DIEs only, and they have no children, so > + don't make things more complicated than needed. */ > + gcc_assert (!die->die_child); > + > + if (die->die_mark || !prune_unused_types_defer_undecided_mark_p (die)) > + return false; > + > + deferred_marks.safe_push (die); > + > + return true; > +} > + > +/* This function revisits a deferred DIE, and marks it iff each DIE > + its attributes reference is also marked. */ > + > +static void > +prune_unused_types_deferred_walk (dw_die_ref die) > +{ > + /* If we're marked, we're done. Otherwise, if referenced DIEs > + remain unmarked, then we don't mark this one either. */ > + if (die->die_mark > + || prune_unused_types_defer_undecided_mark_p (die)) > + return; > + > + gcc_assert (!die->die_mark); > + die->die_mark = 1; > + /* This should do no more than resetting the refcount of > + strings. */ > + prune_unused_types_walk_attribs (die); > + die->die_mark = 2; > + > + /* We don't mark children because we know we have none. */ > +} > + > /* Walk the tree DIE and mark types that we actually use. */ > > static void > @@ -26066,6 +26211,13 @@ prune_unused_types_walk (dw_die_ref die) > /* It's a type node --- don't mark it. */ > return; > > + case DW_TAG_friend: > + if (die->die_perennial_p > + || !prune_unused_types_defer_mark (die)) > + break; > + > + return; > + > case DW_TAG_const_type: > case DW_TAG_packed_type: > case DW_TAG_pointer_type: > @@ -26075,7 +26227,6 @@ prune_unused_types_walk (dw_die_ref die) > case DW_TAG_typedef: > case DW_TAG_array_type: > case DW_TAG_interface_type: > - case DW_TAG_friend: > case DW_TAG_enumeration_type: > case DW_TAG_subroutine_type: > case DW_TAG_string_type: > @@ -26191,6 +26342,8 @@ prune_unused_types (void) > pubname_entry *pub; > dw_die_ref base_type; > > + gcc_assert (deferred_marks.is_empty ()); > + > #if ENABLE_ASSERT_CHECKING > /* All the marks should already be clear. */ > verify_marks_clear (comp_unit_die ()); > @@ -26223,6 +26376,10 @@ prune_unused_types (void) > for (i = 0; base_types.iterate (i, &base_type); i++) > prune_unused_types_mark (base_type, 1); > > + while (!deferred_marks.is_empty ()) > + prune_unused_types_deferred_walk (deferred_marks.pop ()); > + deferred_marks.release (); > + > if (debug_str_hash) > debug_str_hash->empty (); > if (skeleton_debug_str_hash) > @@ -27569,6 +27726,8 @@ dwarf2out_finish (const char *filename) > > gen_remaining_tmpl_value_param_die_attribute (); > > + gen_friend_tags (); > + > /* Add the name for the main input file now. We delayed this from > dwarf2out_init to avoid complications with PCH. > For LTO produced units use a fixed artificial name to avoid > diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h > index 5961c42..b11384f 100644 > --- a/gcc/langhooks-def.h > +++ b/gcc/langhooks-def.h > @@ -182,6 +182,7 @@ extern tree lhd_make_node (enum tree_code); > #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO NULL > #define LANG_HOOKS_GET_REF_QUALIFIER hook_int_const_tree_0 > #define LANG_HOOKS_GET_PTRMEMFN_TYPE hook_tree_const_tree_int_null > +#define LANG_HOOKS_GET_FRIENDS hook_tree_const_tree_int_null > > #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \ > LANG_HOOKS_MAKE_TYPE, \ > @@ -206,7 +207,8 @@ extern tree lhd_make_node (enum tree_code); > LANG_HOOKS_GET_DEBUG_TYPE, \ > LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \ > LANG_HOOKS_GET_REF_QUALIFIER, \ > - LANG_HOOKS_GET_PTRMEMFN_TYPE \ > + LANG_HOOKS_GET_PTRMEMFN_TYPE, \ > + LANG_HOOKS_GET_FRIENDS \ > } > > /* Declaration hooks. */ > diff --git a/gcc/langhooks.h b/gcc/langhooks.h > index cf8b550..73b7bb8 100644 > --- a/gcc/langhooks.h > +++ b/gcc/langhooks.h > @@ -170,6 +170,25 @@ struct lang_hooks_for_types > Otherwise, return the class type when the selector is 0, or the > member function type when the selector is 1. */ > tree (*get_ptrmemfn_type) (const_tree, int); > + > + /* At DETAIL level 0, returns non-NULL if the named class TYPE has > + any friends, NULL otherwise. At higher detail levels, return a > + tree list with the friends of the named class type. Each > + TREE_VALUE contains one friend type or function decl. For > + non-template friends, TREE_PURPOSE is NULL. For template friend > + declarations, the returned entries depend on the DETAIL level. > + At level 1, and only at level 1, an entry with NULL TREE_VALUE > + and non-NULL TREE_PURPOSE will START the returned list to > + indicate the named class TYPE has at least one template friend. > + At level 2, each template friend will be in an entry with NULL > + TREE_VALUE, and with the TEMPLATE_DECL in TREE_PURPOSE. At level > + 3, instead of a NULL TREE_VALUE, we add one entry for each > + instantiation or specialization of the template that fits the > + template friend declaration, as long as there is at least one > + instantiation or specialization; if there isn't any, an entry > + with NULL TREE_VALUE is created. A negative detail level will > + omit non-template friends from the returned list. */ > + tree (*get_friends) (const_tree, int); > }; > > /* Language hooks related to decls and the symbol table. */ > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C > new file mode 100644 > index 0000000..df06f6f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C > @@ -0,0 +1,10 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } > + > +class foo {}; > +class bar { > + friend class foo; > +}; > +bar t; > +foo l; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C > new file mode 100644 > index 0000000..dcff721 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C > @@ -0,0 +1,13 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> struct foo { > + template <typename> void f () {} > +}; > +class bar { > + template <typename T> template <typename> friend void foo<T>::f (); > +}; > +bar t; > +template void foo<int>::f<bar> (); > +template void foo<bar>::f<int> (); > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C > new file mode 100644 > index 0000000..24382c8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C > @@ -0,0 +1,13 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> struct foo { > + struct f {}; > +}; > +class bar { > + template <typename T> friend struct foo<T>::f; > +}; > +bar t; > +foo<int>::f i; > +foo<bar>::f b; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C > new file mode 100644 > index 0000000..0c172a7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C > @@ -0,0 +1,15 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 5 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> struct foo { > + template <typename> struct f {}; > +}; > +class bar { > + template <typename T> template <typename> friend struct foo<T>::f; > +}; > +bar t; > +foo<int>::f<int> i; > +foo<bar>::f<bar> b; > +foo<int>::f<bar> ib; > +foo<bar>::f<int> bi; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C > new file mode 100644 > index 0000000..b4cd04d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C > @@ -0,0 +1,11 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-not " DW_AT_friend" { xfail { powerpc-ibm-aix* } } } } > +// class foo is unused, so we do NOT output the friend tag. > + > +class foo {}; > +class bar { > + friend class foo; > +}; > +bar t; > +// foo l; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C > new file mode 100644 > index 0000000..05cdde1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C > @@ -0,0 +1,9 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } > + > +int f() {} > +class bar { > + friend int f(); > +}; > +bar t; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C > new file mode 100644 > index 0000000..4a67516 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C > @@ -0,0 +1,12 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } > + > +struct foo { > + int f(); > +}; > +class bar { > + friend int foo::f(); > +}; > +int foo::f() {} > +bar t; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C > new file mode 100644 > index 0000000..3ccfd6d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C > @@ -0,0 +1,10 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> class foo {}; > +class bar { > + friend class foo<int>; > +}; > +bar t; > +foo<int> l; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C > new file mode 100644 > index 0000000..34dd458 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C > @@ -0,0 +1,11 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> class foo {}; > +class bar { > + template <typename> friend class foo; > +}; > +bar t; > +foo<int> l; > +foo<bar> b; > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C > new file mode 100644 > index 0000000..3a6554c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C > @@ -0,0 +1,11 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> void f () {} > +class bar { > + template <typename> friend void f (); > +}; > +bar t; > +template void f<int> (); > +template void f<bar> (); > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C > new file mode 100644 > index 0000000..4ede43c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C > @@ -0,0 +1,13 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +struct foo { > + template <typename> void f () {} > +}; > +class bar { > + template <typename> friend void foo::f (); > +}; > +bar t; > +template void foo::f<int> (); > +template void foo::f<bar> (); > diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C > new file mode 100644 > index 0000000..fe6b0ac > --- /dev/null > +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C > @@ -0,0 +1,13 @@ > +// { dg-do compile } > +// { dg-options "-O -g -dA" } > +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } > + > +template <typename> struct foo { > + void f () {} > +}; > +class bar { > + template <typename T> friend void foo<T>::f (); > +}; > +bar t; > +template void foo<int>::f (); > +template void foo<bar>::f (); > > -- > Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ > You must be the change you wish to see in the world. -- Gandhi > Be Free! -- http://FSFLA.org/ FSF Latin America board member > Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer
On Aug 22, 2016, Richard Biener <richard.guenther@gmail.com> wrote: > Just throwing in a wrench from the side ... you should do this in > dwarf2out_early_finish as late there will be no frontend around anymore. Thanks for the heads up, I've adjusted my local copy to do so. > Yeah, prune_unused_types is still done in dwarf2out_finish on trunk That doesn't matter much, the DW_TAG_friend pruning doesn't look at the types, only the class_types_with_friends users did, and those run much earlier (now at dwarf2out_early_finish too).
On Fri, Aug 26, 2016 at 7:25 AM, Alexandre Oliva <aoliva@redhat.com> wrote: > On Aug 22, 2016, Richard Biener <richard.guenther@gmail.com> wrote: > >> Just throwing in a wrench from the side ... you should do this in >> dwarf2out_early_finish as late there will be no frontend around anymore. > > Thanks for the heads up, I've adjusted my local copy to do so. > >> Yeah, prune_unused_types is still done in dwarf2out_finish on trunk > > That doesn't matter much, the DW_TAG_friend pruning doesn't look at the > types, only the class_types_with_friends users did, and those run much > earlier (now at dwarf2out_early_finish too). Thanks! Richard. > -- > Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ > You must be the change you wish to see in the world. -- Gandhi > Be Free! -- http://FSFLA.org/ FSF Latin America board member > Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer
On Fri, Aug 19, 2016 at 2:46 PM, Alexandre Oliva <aoliva@redhat.com> wrote: > Handling non-template friends is kind of easy, but it required a bit > of infrastructure in dwarf2out to avoid (i) forcing debug info for > unused types or functions: DW_TAG_friend DIEs are only emitted if > their DW_AT_friend DIE is emitted, and (ii) creating DIEs for such > types or functions just to have them discarded at the end. To this > end, I introduced a list (vec, actually) of types with friends, > processed at the end of the translation unit, and a list of > DW_TAG_friend DIEs that, when we're pruning unused types, reference > DIEs that are still not known to be used, revisited after we finish > deciding all other DIEs, so that we prune DIEs that would have > referenced pruned types or functions. > > Handlig template friends turned out to be trickier: there's no > representation in DWARF for templates. I decided to give debuggers as > much information as possible, enumerating all specializations of > friend templates and outputting DW_TAG_friend DIEs referencing them as > well, This makes sense, though I'm concerned about the impact on DWARF optimizers. I suppose we can teach dwz to use the maximal set of friends... > but marking them as DW_AT_artificial to indicate they're not > explicitly stated in the source code. This seems unnecessary; there is no semantic difference for a particular specialization depending on whether it became a friend directly or from its template. > This attribute is not valid for > DW_TAG_friend, so it's only emitted in non-strict mode. The greatest > challenge was to enumerate all specializations of a template. It > looked trivial at first, given DECL_TEMPLATE_INSTANTIATIONS, but in > some of the testcases, cases it wouldn't list any specializations, and > in others it would list only some of them. Hmm, I would expect it to work where it's documented to be meaningful: namespace-scope functions and classes. But looking more closely I see that for functions, it is only maintained before the function template is defined. That should be simple enough to change. > I couldn't figure out the > logic behind that, and it seemed clear from the documentation of this > macro that at least in some cases it wouldn't hold the list, so I > ended up writing code to look for specializations in the hashtables of > decl or type specializations. That worked fine, but it's not exactly > an efficient way to obtain the desired information, at least in some > cases. > - should we output specializations of friend templates as friends even > in strict mode? Currently we output them with DW_AT_artificial in > non-strict mode, and without the artificial mark in strict mode. > > - is there any way we can use DECL_TEMPLATE_INSTANTIATIONS reliably to > enumerate the specializations of a friend template, or at least tell > when it can be used? > > - I haven't used local_specializations, should I? I was a bit > confused about the apparently unused local_specialization_stack, > too. No, local_specializations is just for function-local decls. Jason
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index e9f9a63..d35632c 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -167,6 +167,109 @@ cp_get_ptrmemfn_type (const_tree type, int selector) } } +/* At DETAIL level 0, returns non-NULL if the named class TYPE has any + friends, NULL otherwise. At higher detail levels, return a tree + list with the friends of the named class type. Each TREE_VALUE + contains one friend type or function decl. For non-template + friends, TREE_PURPOSE is NULL. For template friend declarations, + the returned entries depend on the DETAIL level. At level 1, and + only at level 1, an entry with NULL TREE_VALUE and non-NULL + TREE_PURPOSE will START the returned list to indicate the named + class TYPE has at least one template friend. At level 2, each + template friend will be in an entry with NULL TREE_VALUE, and with + the TEMPLATE_DECL in TREE_PURPOSE. At level 3, instead of a NULL + TREE_VALUE, we add one entry for each instantiation or + specialization of the template that fits the template friend + declaration, as long as there is at least one instantiation or + specialization; if there isn't any, an entry with NULL TREE_VALUE + is created. A negative detail level will omit non-template friends + from the returned list. */ + +tree +cp_get_friends (const_tree type, int detail) +{ + tree list = NULL_TREE; + tree typedecl = TYPE_MAIN_DECL (type); + bool has_templates = false; + bool non_templates = true; + + if (detail == 0) + { + if (DECL_FRIENDLIST (typedecl) + || CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl))) + return integer_one_node; + else + return NULL_TREE; + } + else if (detail < 0) + { + detail = -detail; + non_templates = false; + } + + gcc_assert (detail <= 3); + + for (tree fnlist = DECL_FRIENDLIST (typedecl); fnlist; + fnlist = TREE_CHAIN (fnlist)) + for (tree fns = FRIEND_DECLS (fnlist); fns; fns = TREE_CHAIN (fns)) + { + tree fn = TREE_VALUE (fns); + if (TREE_CODE (fn) == FUNCTION_DECL + && (!DECL_TEMPLATE_INFO (fn) || DECL_USE_TEMPLATE (fn))) + { + if (non_templates) + list = tree_cons (NULL_TREE, fn, list); + continue; + } + + has_templates = true; + + if (detail == 2) + list = tree_cons (fn, NULL_TREE, list); + + if (detail <= 2) + continue; + + tree new_list = enumerate_friend_specializations (fn); + if (new_list) + list = chainon (new_list, list); + else + list = tree_cons (fn, NULL_TREE, list); + } + + for (tree cllist = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl)); + cllist; cllist = TREE_CHAIN (cllist)) + { + tree cl = TREE_VALUE (cllist); + + if (TREE_CODE (cl) == RECORD_TYPE) + { + if (non_templates) + list = tree_cons (NULL_TREE, cl, list); + continue; + } + + has_templates = true; + + if (detail == 2) + list = tree_cons (cl, NULL_TREE, list); + + if (detail <= 2) + continue; + + tree new_list = enumerate_friend_specializations (cl); + if (new_list) + list = chainon (new_list, list); + else + list = tree_cons (cl, NULL_TREE, list); + } + + if (has_templates && detail == 1) + list = tree_cons (integer_one_node, NULL_TREE, list); + + return list; +} + /* Return true if DECL is explicit member function. */ bool diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index 00780c7..d85d357 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see extern int cp_get_ref_qualifier (const_tree); extern tree cp_get_ptrmemfn_type (const_tree, int); +extern tree cp_get_friends (const_tree, int); extern tree objcp_tsubst_copy_and_build (tree, tree, tsubst_flags_t, tree, bool); @@ -134,6 +135,8 @@ extern void cp_common_init_ts (void); #define LANG_HOOKS_GET_REF_QUALIFIER cp_get_ref_qualifier #undef LANG_HOOKS_GET_PTRMEMFN_TYPE #define LANG_HOOKS_GET_PTRMEMFN_TYPE cp_get_ptrmemfn_type +#undef LANG_HOOKS_GET_FRIENDS +#define LANG_HOOKS_GET_FRIENDS cp_get_friends #undef LANG_HOOKS_TO_TARGET_CHARSET #define LANG_HOOKS_TO_TARGET_CHARSET c_common_to_target_charset #undef LANG_HOOKS_GIMPLIFY_EXPR diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8a32f17..66106b5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6114,6 +6114,7 @@ extern vec<qualified_typedef_usage_t, va_gc> *get_types_needing_access_check (tr extern int template_class_depth (tree); extern int is_specialization_of (tree, tree); extern bool is_specialization_of_friend (tree, tree); +extern tree enumerate_friend_specializations (tree); extern tree get_pattern_parm (tree, tree); extern int comp_template_args (tree, tree, tree * = NULL, tree * = NULL); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 1ee5fd4..f0bd40b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -1469,6 +1469,79 @@ is_specialization_of_friend (tree decl, tree friend_decl) return false; } +/* Return a list of instantiations/specializations that match + FRIEND_DECL. */ + +tree +enumerate_friend_specializations (tree friend_decl) +{ + if (TREE_CODE (friend_decl) != TEMPLATE_DECL) + friend_decl = DECL_TI_TEMPLATE (friend_decl); + + tree opt_decl = friend_decl; + while (optimize_specialization_lookup_p (opt_decl)) + opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl)); + + gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL); + + /* FIXME: This would be much preferred, but it doesn't always list + all specializations. */ + if (0 && DECL_TEMPLATE_INSTANTIATIONS (opt_decl)) + { + tree list = NULL_TREE; + for (tree speclist = DECL_TEMPLATE_INSTANTIATIONS (opt_decl); + speclist; speclist = TREE_CHAIN (speclist)) + { + tree spec = TREE_VALUE (speclist); + if (opt_decl != friend_decl) + spec = retrieve_specialization + (friend_decl, CLASSTYPE_TI_ARGS (spec), 0); + if (TREE_CODE (spec) == TYPE_DECL) + spec = TREE_TYPE (spec); + list = tree_cons (friend_decl, spec, list); + } + return list; + } + + typedef hash_table<spec_hasher> specs_t; + specs_t *specializations; + tree_code code; + + if (DECL_CLASS_TEMPLATE_P (opt_decl)) + { + specializations = type_specializations; + code = RECORD_TYPE; + } + else + { + specializations = decl_specializations; + code = FUNCTION_DECL; + } + + tree list = NULL_TREE; + + for (specs_t::iterator iter = specializations->begin(), + end = specializations->end(); + iter != end; ++iter) + { + tree spec = (*iter)->spec; + if (TREE_CODE (spec) != code) + continue; + if (TREE_CODE (spec) == RECORD_TYPE) + spec = TYPE_NAME (spec); + if (is_specialization_of_friend (spec, opt_decl)) + { + if (opt_decl != friend_decl) + spec = retrieve_specialization (friend_decl, (*iter)->args, 0); + if (TREE_CODE (spec) == TYPE_DECL) + spec = TREE_TYPE (spec); + list = tree_cons (friend_decl, spec, list); + } + } + + return list; +} + /* Register the specialization SPEC as a specialization of TMPL with the indicated ARGS. IS_FRIEND indicates whether the specialization is actually just a friend declaration. Returns SPEC, or an diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index f40f759..43aa5a0 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -22474,6 +22474,57 @@ gen_variant_part (tree variant_part_decl, struct vlr_context *vlr_ctx, free (discr_lists); } +/* Types that have friends have to be revisited, because we want to + emit friend attributes for them once we know what types and decls + have DIEs, and we want to emit friend tags for specializations of + template friends. We could create DIEs in limbo ourselves, but we + can't know the specializations before we've seen the entire + translation unit. */ + +static vec<tree> class_types_with_friends; + +/* Add any friend tags corresponding to the named TYPE. */ + +static void +gen_friend_tags_for_type (tree type) +{ + dw_die_ref context_die = lookup_type_die (type); + gcc_assert (context_die); + + for (tree friends = lang_hooks.types.get_friends (type, 3); friends; + friends = TREE_CHAIN (friends)) + { + tree t = TREE_VALUE (friends); + dw_die_ref die = NULL; + if (!t) + /* If it's a friend template without any specializations, we + can't refer to it in debug information. */ + continue; + else if (TYPE_P (t)) + die = lookup_type_die (t); + else if (DECL_P (t)) + die = lookup_decl_die (t); + else + gcc_unreachable (); + if (!die) + continue; + dw_die_ref child = new_die (DW_TAG_friend, context_die, type); + add_AT_die_ref (child, DW_AT_friend, die); + if (!dwarf_strict && TREE_PURPOSE (friends)) + add_AT_flag (child, DW_AT_artificial, 1); + } +} + +/* Add any friend tags corresponding to class TYPEs that were found to + have friend declarations. */ + +static void +gen_friend_tags () +{ + while (!class_types_with_friends.is_empty ()) + gen_friend_tags_for_type (class_types_with_friends.pop ()); +} + /* Generate a DIE for a class member. */ static void @@ -22559,6 +22610,9 @@ gen_member_die (tree type, dw_die_ref context_die) else gen_decl_die (member, NULL, NULL, context_die); } + + if (lang_hooks.types.get_friends (type, 0)) + class_types_with_friends.safe_push (type); } /* Generate a DIE for a structure or union type. If TYPE_DECL_SUPPRESS_DEBUG @@ -26031,6 +26085,97 @@ prune_unused_types_walk_local_classes (dw_die_ref die) FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c)); } +/* Nodes to revisit after marking everything else, to decide whether + or not they can/should be emitted. */ + +static vec<dw_die_ref> deferred_marks; + +/* Return true if the mark is already decided, false otherwise. */ + +static bool +prune_unused_types_defer_undecided_mark_p (dw_die_ref die) +{ + gcc_assert (!die->die_mark); + + dw_attr_node *a; + unsigned ix; + bool can_mark_now = true; + + FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a) + switch (AT_class (a)) + { + case dw_val_class_loc: + case dw_val_class_loc_list: + /* We don't support attributes of this type now. Deferred + walking of the location expressions might mark DIEs that + we've already decided not to output, and we may have + already based other decisions on it. This is not + insurmountable, but we don't need to tackle that right + away. */ + gcc_unreachable (); + + case dw_val_class_die_ref: + if (!a->dw_attr_val.v.val_die_ref.die->die_mark) + can_mark_now = false; + break; + + case dw_val_class_str: + default: + break; + } + + return !can_mark_now; +} + +/* Return true if we've deferred the decision on whether to mark DIE. + It must not have children or attributes with location expressions + or lists. Attributes with strings and other DIEs are ok. If any + of the DIEs referenced by attributes is not marked, we defer the + decision to give it a chance to be marked so that we output the + present DIE too. In this case, we return TRUE, to indicate the + decision was deferred. If they are all marked already, then we + know we can output this one as well, so we return FALSE to indicate + it was NOT deferred. */ + +static bool +prune_unused_types_defer_mark (dw_die_ref die) +{ + gcc_assert (die->die_parent->die_mark); + + /* We use this for friend DIEs only, and they have no children, so + don't make things more complicated than needed. */ + gcc_assert (!die->die_child); + + if (die->die_mark || !prune_unused_types_defer_undecided_mark_p (die)) + return false; + + deferred_marks.safe_push (die); + + return true; +} + +/* This function revisits a deferred DIE, and marks it iff each DIE + its attributes reference is also marked. */ + +static void +prune_unused_types_deferred_walk (dw_die_ref die) +{ + /* If we're marked, we're done. Otherwise, if referenced DIEs + remain unmarked, then we don't mark this one either. */ + if (die->die_mark + || prune_unused_types_defer_undecided_mark_p (die)) + return; + + gcc_assert (!die->die_mark); + die->die_mark = 1; + /* This should do no more than resetting the refcount of + strings. */ + prune_unused_types_walk_attribs (die); + die->die_mark = 2; + + /* We don't mark children because we know we have none. */ +} + /* Walk the tree DIE and mark types that we actually use. */ static void @@ -26066,6 +26211,13 @@ prune_unused_types_walk (dw_die_ref die) /* It's a type node --- don't mark it. */ return; + case DW_TAG_friend: + if (die->die_perennial_p + || !prune_unused_types_defer_mark (die)) + break; + + return; + case DW_TAG_const_type: case DW_TAG_packed_type: case DW_TAG_pointer_type: @@ -26075,7 +26227,6 @@ prune_unused_types_walk (dw_die_ref die) case DW_TAG_typedef: case DW_TAG_array_type: case DW_TAG_interface_type: - case DW_TAG_friend: case DW_TAG_enumeration_type: case DW_TAG_subroutine_type: case DW_TAG_string_type: @@ -26191,6 +26342,8 @@ prune_unused_types (void) pubname_entry *pub; dw_die_ref base_type; + gcc_assert (deferred_marks.is_empty ()); + #if ENABLE_ASSERT_CHECKING /* All the marks should already be clear. */ verify_marks_clear (comp_unit_die ()); @@ -26223,6 +26376,10 @@ prune_unused_types (void) for (i = 0; base_types.iterate (i, &base_type); i++) prune_unused_types_mark (base_type, 1); + while (!deferred_marks.is_empty ()) + prune_unused_types_deferred_walk (deferred_marks.pop ()); + deferred_marks.release (); + if (debug_str_hash) debug_str_hash->empty (); if (skeleton_debug_str_hash) @@ -27569,6 +27726,8 @@ dwarf2out_finish (const char *filename) gen_remaining_tmpl_value_param_die_attribute (); + gen_friend_tags (); + /* Add the name for the main input file now. We delayed this from dwarf2out_init to avoid complications with PCH. For LTO produced units use a fixed artificial name to avoid diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 5961c42..b11384f 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -182,6 +182,7 @@ extern tree lhd_make_node (enum tree_code); #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO NULL #define LANG_HOOKS_GET_REF_QUALIFIER hook_int_const_tree_0 #define LANG_HOOKS_GET_PTRMEMFN_TYPE hook_tree_const_tree_int_null +#define LANG_HOOKS_GET_FRIENDS hook_tree_const_tree_int_null #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \ LANG_HOOKS_MAKE_TYPE, \ @@ -206,7 +207,8 @@ extern tree lhd_make_node (enum tree_code); LANG_HOOKS_GET_DEBUG_TYPE, \ LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \ LANG_HOOKS_GET_REF_QUALIFIER, \ - LANG_HOOKS_GET_PTRMEMFN_TYPE \ + LANG_HOOKS_GET_PTRMEMFN_TYPE, \ + LANG_HOOKS_GET_FRIENDS \ } /* Declaration hooks. */ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index cf8b550..73b7bb8 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -170,6 +170,25 @@ struct lang_hooks_for_types Otherwise, return the class type when the selector is 0, or the member function type when the selector is 1. */ tree (*get_ptrmemfn_type) (const_tree, int); + + /* At DETAIL level 0, returns non-NULL if the named class TYPE has + any friends, NULL otherwise. At higher detail levels, return a + tree list with the friends of the named class type. Each + TREE_VALUE contains one friend type or function decl. For + non-template friends, TREE_PURPOSE is NULL. For template friend + declarations, the returned entries depend on the DETAIL level. + At level 1, and only at level 1, an entry with NULL TREE_VALUE + and non-NULL TREE_PURPOSE will START the returned list to + indicate the named class TYPE has at least one template friend. + At level 2, each template friend will be in an entry with NULL + TREE_VALUE, and with the TEMPLATE_DECL in TREE_PURPOSE. At level + 3, instead of a NULL TREE_VALUE, we add one entry for each + instantiation or specialization of the template that fits the + template friend declaration, as long as there is at least one + instantiation or specialization; if there isn't any, an entry + with NULL TREE_VALUE is created. A negative detail level will + omit non-template friends from the returned list. */ + tree (*get_friends) (const_tree, int); }; /* Language hooks related to decls and the symbol table. */ diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C new file mode 100644 index 0000000..df06f6f --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C @@ -0,0 +1,10 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } + +class foo {}; +class bar { + friend class foo; +}; +bar t; +foo l; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C new file mode 100644 index 0000000..dcff721 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +template <typename> struct foo { + template <typename> void f () {} +}; +class bar { + template <typename T> template <typename> friend void foo<T>::f (); +}; +bar t; +template void foo<int>::f<bar> (); +template void foo<bar>::f<int> (); diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C new file mode 100644 index 0000000..24382c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +template <typename> struct foo { + struct f {}; +}; +class bar { + template <typename T> friend struct foo<T>::f; +}; +bar t; +foo<int>::f i; +foo<bar>::f b; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C new file mode 100644 index 0000000..0c172a7 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 5 { xfail { powerpc-ibm-aix* } } } } + +template <typename> struct foo { + template <typename> struct f {}; +}; +class bar { + template <typename T> template <typename> friend struct foo<T>::f; +}; +bar t; +foo<int>::f<int> i; +foo<bar>::f<bar> b; +foo<int>::f<bar> ib; +foo<bar>::f<int> bi; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C new file mode 100644 index 0000000..b4cd04d --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C @@ -0,0 +1,11 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-not " DW_AT_friend" { xfail { powerpc-ibm-aix* } } } } +// class foo is unused, so we do NOT output the friend tag. + +class foo {}; +class bar { + friend class foo; +}; +bar t; +// foo l; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C new file mode 100644 index 0000000..05cdde1 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C @@ -0,0 +1,9 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } + +int f() {} +class bar { + friend int f(); +}; +bar t; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C new file mode 100644 index 0000000..4a67516 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C @@ -0,0 +1,12 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } + +struct foo { + int f(); +}; +class bar { + friend int foo::f(); +}; +int foo::f() {} +bar t; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C new file mode 100644 index 0000000..3ccfd6d --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C @@ -0,0 +1,10 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } } + +template <typename> class foo {}; +class bar { + friend class foo<int>; +}; +bar t; +foo<int> l; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C new file mode 100644 index 0000000..34dd458 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C @@ -0,0 +1,11 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +template <typename> class foo {}; +class bar { + template <typename> friend class foo; +}; +bar t; +foo<int> l; +foo<bar> b; diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C new file mode 100644 index 0000000..3a6554c --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C @@ -0,0 +1,11 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +template <typename> void f () {} +class bar { + template <typename> friend void f (); +}; +bar t; +template void f<int> (); +template void f<bar> (); diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C new file mode 100644 index 0000000..4ede43c --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +struct foo { + template <typename> void f () {} +}; +class bar { + template <typename> friend void foo::f (); +}; +bar t; +template void foo::f<int> (); +template void foo::f<bar> (); diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C new file mode 100644 index 0000000..fe6b0ac --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O -g -dA" } +// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } } + +template <typename> struct foo { + void f () {} +}; +class bar { + template <typename T> friend void foo<T>::f (); +}; +bar t; +template void foo<int>::f (); +template void foo<bar>::f ();