This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PR59319] output friends in debug info


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]