This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PR59319] output friends in debug info
- From: Richard Biener <richard dot guenther at gmail dot com>
- To: Alexandre Oliva <aoliva at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Mon, 22 Aug 2016 13:35:45 +0200
- Subject: Re: [PR59319] output friends in debug info
- Authentication-results: sourceware.org; auth=none
- References: <or8tvsa95w.fsf@livre.home>
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