public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PR59319] output friends in debug info
@ 2016-08-19 18:47 Alexandre Oliva
  2016-08-22 11:36 ` Richard Biener
  2016-08-26 16:13 ` Jason Merrill
  0 siblings, 2 replies; 17+ messages in thread
From: Alexandre Oliva @ 2016-08-19 18:47 UTC (permalink / raw)
  To: gcc-patches

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.
	* 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-19 18:47 [PR59319] output friends in debug info Alexandre Oliva
@ 2016-08-22 11:36 ` Richard Biener
  2016-08-26  5:26   ` Alexandre Oliva
  2016-08-26 16:13 ` Jason Merrill
  1 sibling, 1 reply; 17+ messages in thread
From: Richard Biener @ 2016-08-22 11:36 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: GCC Patches

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-22 11:36 ` Richard Biener
@ 2016-08-26  5:26   ` Alexandre Oliva
  2016-08-26  8:59     ` Richard Biener
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2016-08-26  5:26 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

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).

-- 
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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-26  5:26   ` Alexandre Oliva
@ 2016-08-26  8:59     ` Richard Biener
  0 siblings, 0 replies; 17+ messages in thread
From: Richard Biener @ 2016-08-26  8:59 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: GCC Patches

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-19 18:47 [PR59319] output friends in debug info Alexandre Oliva
  2016-08-22 11:36 ` Richard Biener
@ 2016-08-26 16:13 ` Jason Merrill
  2016-08-30 23:13   ` Alexandre Oliva
  1 sibling, 1 reply; 17+ messages in thread
From: Jason Merrill @ 2016-08-26 16:13 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches List

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-26 16:13 ` Jason Merrill
@ 2016-08-30 23:13   ` Alexandre Oliva
  2016-09-24  2:39     ` Alexandre Oliva
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2016-08-30 23:13 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener; +Cc: gcc-patches List

On Aug 26, 2016, Jason Merrill <jason@redhat.com> wrote:

> I suppose we can teach dwz to use the maximal set of friends...

*nod*

> This seems unnecessary; there is no semantic difference for a
> particular specialization depending on whether it became a friend
> directly or from its template.

'k, I took that out.  If we change our minds, it's easy enough to put it
back in.

> 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.

Done.

>> - 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.

Thanks.


Here's the updated patch.



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.

Handling 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.  I considered marking those as DW_AT_artificial, to indicate
they're not explicitly stated in the source code, but in the end we
decided that was not useful.  The greatest challenge was to enumerate
all specializations of a template.  It looked trivial at first, given
DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
class-scoped functions and of nested templates.  For other templates,
I ended up writing code to look for specializations in the hashtables
of decl or type specializations.  That's not exactly efficient, but it
gets the job done.


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.
	(dwarf2out_early_finish): Likewise.
	* 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 (optimize_friend_specialization_lookup_p): New.
	(retrieve_friend_specialization): New.
	(enumerate_friend_specializations): New.
	(register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
	for functions, even after definition, if we are emitting debug
	info.

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.
	* g++.dg/debug/dwarf2/friend-14.C: New.
	* g++.dg/debug/dwarf2/friend-15.C: New.
	* g++.dg/debug/dwarf2/friend-16.C: New.
	* g++.dg/debug/dwarf2/friend-17.C: New.
	* g++.dg/debug/dwarf2/friend-18.C: New.
---
 gcc/cp/cp-objcp-common.c                      |  106 ++++++++++++++
 gcc/cp/cp-objcp-common.h                      |    3 
 gcc/cp/cp-tree.h                              |    1 
 gcc/cp/pt.c                                   |  194 +++++++++++++++++++++++++
 gcc/dwarf2out.c                               |  165 +++++++++++++++++++++
 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-13.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C |   12 ++
 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 ++
 25 files changed, 726 insertions(+), 3 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-13.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.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..7dad303 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -167,6 +167,112 @@ 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 (!typedecl)
+    return NULL_TREE;
+
+  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
+	    && !uses_template_parms (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..bfb55ef 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1469,6 +1469,192 @@ is_specialization_of_friend (tree decl, tree friend_decl)
   return false;
 }
 
+/* Return TRUE if TMPL is not a template itself, but rather a member
+   of a [template] class, so that, in order to catch all friends,
+   including corresponding members of partial and explicit
+   specializations, we should look for specializations of the
+   enclosing class.  */
+
+static bool
+optimize_friend_specialization_lookup_p (tree tmpl)
+{
+  return (optimize_specialization_lookup_p (tmpl)
+	  || ((TREE_CODE (tmpl) == FUNCTION_DECL
+	       || (DECL_CLASS_TEMPLATE_P (tmpl)
+		   && !DECL_MEMBER_TEMPLATE_P (tmpl)))
+	      && DECL_CLASS_SCOPE_P (tmpl)
+	      && CLASS_TYPE_P (DECL_CONTEXT (tmpl))
+	      && !CLASSTYPE_TEMPLATE_SPECIALIZATION (DECL_CONTEXT (tmpl))));
+}
+
+/* Retrieve the specialization of TMPL that is a member of CTX with
+   ARGS.  If retrieve_specialization can't find such a specialization,
+   or it yields a specialization that is not a member of CTX (say,
+   because CTX is a partial specialization, and ARGS has the arguments
+   for the partial specialization), look up TMPL's name in CTX, and
+   see which result, if any, is a friend specialization of TMPL.  */
+
+static tree
+retrieve_friend_specialization (tree tmpl, tree args, tree ctx)
+{
+  tree ret;
+
+  if (TREE_CODE (tmpl) == FUNCTION_DECL)
+    {
+      ret = retrieve_specialization (DECL_TI_TEMPLATE (tmpl), args, 0);
+      if (TREE_CODE (ctx) == FUNCTION_DECL)
+	{
+	  gcc_assert (ctx == ret);
+	  ctx = DECL_CONTEXT (ret);
+	}
+    }
+  else
+    ret = retrieve_specialization (tmpl, args, 0);
+
+  /* We have to test the context because ARGS could be from a partial
+     specialization, and using that in tmpl might get us a different
+     specialization.  We could improve this with a reverse
+     get_partial_spec_bindings, but this is simple and cheap
+     enough.  */
+  if (ret && (DECL_P (ret) ? DECL_CONTEXT (ret) : TYPE_CONTEXT (ret)) == ctx)
+    {
+    found:
+      tree testme = ret;
+      if (!DECL_P (testme))
+	testme = TYPE_NAME (ret);
+      if (is_specialization_of_friend (testme, tmpl))
+	return ret;
+      else
+	return NULL_TREE;
+    }
+
+  if (!optimize_friend_specialization_lookup_p (tmpl))
+    return NULL_TREE;
+
+  if (DECL_CLASS_TEMPLATE_P (tmpl))
+    {
+      ret = lookup_member (ctx, DECL_NAME (tmpl), 0, true, tf_none);
+      if (!ret)
+	return NULL_TREE;
+
+      goto found;
+    }
+
+  ret = lookup_fnfields (ctx, DECL_NAME (tmpl), 0);
+  for (tree fns = ret, prev_found = NULL_TREE; fns; fns = OVL_NEXT (fns))
+    {
+      tree fn = OVL_CURRENT (fns);
+
+      if (!is_specialization_of_friend (fn, tmpl))
+	continue;
+
+      if (prev_found)
+	return NULL_TREE;
+
+      ret = prev_found = fn;
+    }
+
+  return ret;
+}
+
+/* Return a list of instantiations/specializations that match
+   FRIEND_DECL.  */
+
+tree
+enumerate_friend_specializations (tree friend_decl)
+{
+  tree opt_decl = friend_decl;
+  if (optimize_friend_specialization_lookup_p (opt_decl))
+    opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl));
+
+  if (TREE_CODE (opt_decl) == FUNCTION_DECL)
+    {
+      gcc_assert (uses_template_parms (opt_decl));
+      opt_decl = DECL_TI_TEMPLATE (opt_decl);
+    }
+
+  gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL);
+
+  /* For nested template classes, we might be able to enumerate the
+     enclosing template classes, and then locate their member
+     templates, but it's not clear it's worth the effort.  If we were
+     to use their DECL_TEMPLATE_INSTANTIATIONS, we'd only get partial
+     specializations (try g++.dg/debug/dwarf2/friend-12.C), so we
+     don't.  So we only use DECL_TEMPLATE_INSTANTIATIONS for template
+     classes in classes that are not templates, or that are
+     fully-specialized template, and for primary template functions in
+     namespace scopes (DECL_TEMPLATE_INSTANTIATIONS is not even
+     defined for other template functions).  */
+  if (DECL_CLASS_TEMPLATE_P (opt_decl)
+      ? (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 || !uses_template_parms (DECL_CONTEXT (opt_decl)))
+      : (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 && PRIMARY_TEMPLATE_P (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_friend_specialization
+		(friend_decl, TREE_PURPOSE (speclist), spec);
+	      if (!spec)
+		continue;
+	    }
+	  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 ospec = (*iter)->spec;
+      if (TREE_CODE (ospec) != code)
+	continue;
+      tree spec = ospec;
+      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_friend_specialization
+		(friend_decl, (*iter)->args, ospec);
+	      if (!spec)
+		continue;
+	    }
+	  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
@@ -1623,12 +1809,18 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
       *slot = entry;
       if ((TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
 	   && PRIMARY_TEMPLATE_P (tmpl)
-	   && DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
+	   && (DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE
+	       || debug_info_level != DINFO_LEVEL_NONE))
 	  || variable_template_p (tmpl))
 	/* If TMPL is a forward declaration of a template function, keep a list
 	   of all specializations in case we need to reassign them to a friend
 	   template later in tsubst_friend_function.
 
+	   We also use DECL_TEMPLATE_INSTANTIATIONS to enumerate the
+	   specializations of a friend function template in debug
+	   information, so if we are emitting debug information, add
+	   specializations to the list.
+
 	   Also keep a list of all variable template instantiations so that
 	   process_partial_specialization can check whether a later partial
 	   specialization would have used it.  */
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index f3928e0..3b7ac4f 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -22527,6 +22527,61 @@ 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 GTY (()) vec<tree, va_gc> *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);
+      /* We don't distinguish between actual friend declarations, and
+	 template specializations of template friend declarations.  We
+	 could test TREE_PURPOSE at this point to that end.  */
+    }
+}
+
+/* Add any friend tags corresponding to class TYPEs that were found to
+   have friend declarations.  */
+
+static void
+gen_friend_tags ()
+{
+  if (!class_types_with_friends)
+    return;
+
+  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
@@ -22612,6 +22667,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))
+    vec_safe_push (class_types_with_friends, type);
 }
 
 /* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
@@ -26088,6 +26146,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
@@ -26123,6 +26272,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:
@@ -26132,7 +26288,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:
@@ -26248,6 +26403,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 ());
@@ -26280,6 +26437,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)
@@ -27632,6 +27793,7 @@ dwarf2out_finish (const char *filename)
   producer->dw_attr_val.v.val_str = find_AT_string (producer_string);
 
   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.
@@ -27970,6 +28132,7 @@ dwarf2out_early_finish (void)
 
   gen_scheduled_generic_parms_dies ();
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
   /* Add DW_AT_linkage_name for all deferred DIEs.  */
   for (limbo_die_node *node = deferred_asm_name; node; node = node->next)
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..55d3fe2
--- /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" 1 { 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..0e6e4ad
--- /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" 2 { 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..f8d01db
--- /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" 2 { 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..cb92cfb
--- /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" 4 { 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-13.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
new file mode 100644
index 0000000..4127447
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA -Wno-non-template-friend" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+void f(int) {}
+void f(long) {}
+void f(char) {}
+template <typename T> struct foo {
+  friend void f(T);
+};
+foo<int> i;
+foo<long> l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
new file mode 100644
index 0000000..ab3b297
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; };
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; };
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
new file mode 100644
index 0000000..f0bbcf9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
new file mode 100644
index 0000000..65ef4df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.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* } } } }
+
+template <typename T> class bar {
+  friend inline bool operator==(const bar&, const bar&) { return true; }
+};
+bar<int> i;
+bar<void*> vp;
+bool f() {
+  return i == i && vp == vp;
+}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
new file mode 100644
index 0000000..2d2b96d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T*>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int*>::bar::baz ip;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
new file mode 100644
index 0000000..9afa9a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T, typename U> void f (T, U) {}
+template <typename T> void f (T, T) {}
+class bar {
+  template <typename T> friend void f (T, T);
+};
+bar t;
+template void f<> (int, long);
+template void f<> (int, int);
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..d02c47f
--- /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" 1 { 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..bf59f3d
--- /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" 1 { 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..e84e838
--- /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" 1 { 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..9ba8207
--- /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" 2 { 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..b909e9b
--- /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" 2 { 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..ea44ed1c
--- /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" 2 { 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..64b3485
--- /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" 2 { 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-08-30 23:13   ` Alexandre Oliva
@ 2016-09-24  2:39     ` Alexandre Oliva
  2016-10-19 10:17       ` Alexandre Oliva
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2016-09-24  2:39 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Aug 30, 2016, 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.

> Handling 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.  I considered marking those as DW_AT_artificial, to indicate
> they're not explicitly stated in the source code, but in the end we
> decided that was not useful.  The greatest challenge was to enumerate
> all specializations of a template.  It looked trivial at first, given
> DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
> class-scoped functions and of nested templates.  For other templates,
> I ended up writing code to look for specializations in the hashtables
> of decl or type specializations.  That's not exactly efficient, but it
> gets the job done.

Regstrapped on x86_64-linux-gnu and i686-linux-gnu, I'd failed to
mention.

Ping?

https://gcc.gnu.org/ml/gcc-patches/2016-08/msg02092.html

-- 
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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-09-24  2:39     ` Alexandre Oliva
@ 2016-10-19 10:17       ` Alexandre Oliva
  2017-01-27  6:27         ` Alexandre Oliva
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2016-10-19 10:17 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>> Handling non-template friends is kind of easy, [...]

> Regstrapped on x86_64-linux-gnu and i686-linux-gnu, I'd failed to
> mention.

> Ping?

Ping?  (conflicts resolved, patch refreshed and retested)


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.

Handling 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.  I considered marking those as DW_AT_artificial, to indicate
they're not explicitly stated in the source code, but in the end we
decided that was not useful.  The greatest challenge was to enumerate
all specializations of a template.  It looked trivial at first, given
DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
class-scoped functions and of nested templates.  For other templates,
I ended up writing code to look for specializations in the hashtables
of decl or type specializations.  That's not exactly efficient, but it
gets the job done.


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.
	(dwarf2out_early_finish): Likewise.
	* 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 (optimize_friend_specialization_lookup_p): New.
	(retrieve_friend_specialization): New.
	(enumerate_friend_specializations): New.
	(register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
	for functions, even after definition, if we are emitting debug
	info.

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.
	* g++.dg/debug/dwarf2/friend-14.C: New.
	* g++.dg/debug/dwarf2/friend-15.C: New.
	* g++.dg/debug/dwarf2/friend-16.C: New.
	* g++.dg/debug/dwarf2/friend-17.C: New.
	* g++.dg/debug/dwarf2/friend-18.C: New.
---
 gcc/cp/cp-objcp-common.c                      |  106 ++++++++++++++
 gcc/cp/cp-objcp-common.h                      |    3 
 gcc/cp/cp-tree.h                              |    1 
 gcc/cp/pt.c                                   |  194 +++++++++++++++++++++++++
 gcc/dwarf2out.c                               |  165 +++++++++++++++++++++
 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-13.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C |   12 ++
 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 ++
 25 files changed, 726 insertions(+), 3 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-13.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.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 78ffe8c..e82334b 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -168,6 +168,112 @@ 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 (!typedecl)
+    return NULL_TREE;
+
+  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
+	    && !uses_template_parms (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 -1 if dwarf ATTR shouldn't be added for DECL, or the attribute
    value otherwise.  */
 int
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 5cf0170..2eece80 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);
@@ -132,6 +133,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 f4a8985..25a04cd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6122,6 +6122,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 028025d..6f860a6 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1468,6 +1468,192 @@ is_specialization_of_friend (tree decl, tree friend_decl)
   return false;
 }
 
+/* Return TRUE if TMPL is not a template itself, but rather a member
+   of a [template] class, so that, in order to catch all friends,
+   including corresponding members of partial and explicit
+   specializations, we should look for specializations of the
+   enclosing class.  */
+
+static bool
+optimize_friend_specialization_lookup_p (tree tmpl)
+{
+  return (optimize_specialization_lookup_p (tmpl)
+	  || ((TREE_CODE (tmpl) == FUNCTION_DECL
+	       || (DECL_CLASS_TEMPLATE_P (tmpl)
+		   && !DECL_MEMBER_TEMPLATE_P (tmpl)))
+	      && DECL_CLASS_SCOPE_P (tmpl)
+	      && CLASS_TYPE_P (DECL_CONTEXT (tmpl))
+	      && !CLASSTYPE_TEMPLATE_SPECIALIZATION (DECL_CONTEXT (tmpl))));
+}
+
+/* Retrieve the specialization of TMPL that is a member of CTX with
+   ARGS.  If retrieve_specialization can't find such a specialization,
+   or it yields a specialization that is not a member of CTX (say,
+   because CTX is a partial specialization, and ARGS has the arguments
+   for the partial specialization), look up TMPL's name in CTX, and
+   see which result, if any, is a friend specialization of TMPL.  */
+
+static tree
+retrieve_friend_specialization (tree tmpl, tree args, tree ctx)
+{
+  tree ret;
+
+  if (TREE_CODE (tmpl) == FUNCTION_DECL)
+    {
+      ret = retrieve_specialization (DECL_TI_TEMPLATE (tmpl), args, 0);
+      if (TREE_CODE (ctx) == FUNCTION_DECL)
+	{
+	  gcc_assert (ctx == ret);
+	  ctx = DECL_CONTEXT (ret);
+	}
+    }
+  else
+    ret = retrieve_specialization (tmpl, args, 0);
+
+  /* We have to test the context because ARGS could be from a partial
+     specialization, and using that in tmpl might get us a different
+     specialization.  We could improve this with a reverse
+     get_partial_spec_bindings, but this is simple and cheap
+     enough.  */
+  if (ret && (DECL_P (ret) ? DECL_CONTEXT (ret) : TYPE_CONTEXT (ret)) == ctx)
+    {
+    found:
+      tree testme = ret;
+      if (!DECL_P (testme))
+	testme = TYPE_NAME (ret);
+      if (is_specialization_of_friend (testme, tmpl))
+	return ret;
+      else
+	return NULL_TREE;
+    }
+
+  if (!optimize_friend_specialization_lookup_p (tmpl))
+    return NULL_TREE;
+
+  if (DECL_CLASS_TEMPLATE_P (tmpl))
+    {
+      ret = lookup_member (ctx, DECL_NAME (tmpl), 0, true, tf_none);
+      if (!ret)
+	return NULL_TREE;
+
+      goto found;
+    }
+
+  ret = lookup_fnfields (ctx, DECL_NAME (tmpl), 0);
+  for (tree fns = ret, prev_found = NULL_TREE; fns; fns = OVL_NEXT (fns))
+    {
+      tree fn = OVL_CURRENT (fns);
+
+      if (!is_specialization_of_friend (fn, tmpl))
+	continue;
+
+      if (prev_found)
+	return NULL_TREE;
+
+      ret = prev_found = fn;
+    }
+
+  return ret;
+}
+
+/* Return a list of instantiations/specializations that match
+   FRIEND_DECL.  */
+
+tree
+enumerate_friend_specializations (tree friend_decl)
+{
+  tree opt_decl = friend_decl;
+  if (optimize_friend_specialization_lookup_p (opt_decl))
+    opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl));
+
+  if (TREE_CODE (opt_decl) == FUNCTION_DECL)
+    {
+      gcc_assert (uses_template_parms (opt_decl));
+      opt_decl = DECL_TI_TEMPLATE (opt_decl);
+    }
+
+  gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL);
+
+  /* For nested template classes, we might be able to enumerate the
+     enclosing template classes, and then locate their member
+     templates, but it's not clear it's worth the effort.  If we were
+     to use their DECL_TEMPLATE_INSTANTIATIONS, we'd only get partial
+     specializations (try g++.dg/debug/dwarf2/friend-12.C), so we
+     don't.  So we only use DECL_TEMPLATE_INSTANTIATIONS for template
+     classes in classes that are not templates, or that are
+     fully-specialized template, and for primary template functions in
+     namespace scopes (DECL_TEMPLATE_INSTANTIATIONS is not even
+     defined for other template functions).  */
+  if (DECL_CLASS_TEMPLATE_P (opt_decl)
+      ? (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 || !uses_template_parms (DECL_CONTEXT (opt_decl)))
+      : (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 && PRIMARY_TEMPLATE_P (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_friend_specialization
+		(friend_decl, TREE_PURPOSE (speclist), spec);
+	      if (!spec)
+		continue;
+	    }
+	  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 ospec = (*iter)->spec;
+      if (TREE_CODE (ospec) != code)
+	continue;
+      tree spec = ospec;
+      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_friend_specialization
+		(friend_decl, (*iter)->args, ospec);
+	      if (!spec)
+		continue;
+	    }
+	  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
@@ -1622,12 +1808,18 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
       *slot = entry;
       if ((TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
 	   && PRIMARY_TEMPLATE_P (tmpl)
-	   && DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
+	   && (DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE
+	       || debug_info_level != DINFO_LEVEL_NONE))
 	  || variable_template_p (tmpl))
 	/* If TMPL is a forward declaration of a template function, keep a list
 	   of all specializations in case we need to reassign them to a friend
 	   template later in tsubst_friend_function.
 
+	   We also use DECL_TEMPLATE_INSTANTIATIONS to enumerate the
+	   specializations of a friend function template in debug
+	   information, so if we are emitting debug information, add
+	   specializations to the list.
+
 	   Also keep a list of all variable template instantiations so that
 	   process_partial_specialization can check whether a later partial
 	   specialization would have used it.  */
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index afe7bfd..ea785c1 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -22673,6 +22673,61 @@ 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 GTY (()) vec<tree, va_gc> *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);
+      /* We don't distinguish between actual friend declarations, and
+	 template specializations of template friend declarations.  We
+	 could test TREE_PURPOSE at this point to that end.  */
+    }
+}
+
+/* Add any friend tags corresponding to class TYPEs that were found to
+   have friend declarations.  */
+
+static void
+gen_friend_tags ()
+{
+  if (!class_types_with_friends)
+    return;
+
+  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
@@ -22769,6 +22824,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))
+    vec_safe_push (class_types_with_friends, type);
 }
 
 /* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
@@ -26290,6 +26348,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
@@ -26325,6 +26474,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:
@@ -26334,7 +26490,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:
@@ -26468,6 +26623,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 ());
@@ -26500,6 +26657,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)
@@ -28058,6 +28219,7 @@ dwarf2out_finish (const char *)
   gcc_assert (deferred_asm_name == NULL);
 
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
 #if ENABLE_ASSERT_CHECKING
   {
@@ -28389,6 +28551,7 @@ dwarf2out_early_finish (const char *filename)
 
   gen_scheduled_generic_parms_dies ();
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
   /* Add DW_AT_linkage_name for all deferred DIEs.  */
   for (limbo_die_node *node = deferred_asm_name; node; node = node->next)
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index a2e8d10..1ff51b9a 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -188,6 +188,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, \
@@ -212,7 +213,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 916c060..e2d2ba7 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -172,6 +172,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..55d3fe2
--- /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" 1 { 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..0e6e4ad
--- /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" 2 { 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..f8d01db
--- /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" 2 { 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..cb92cfb
--- /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" 4 { 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-13.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
new file mode 100644
index 0000000..4127447
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA -Wno-non-template-friend" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+void f(int) {}
+void f(long) {}
+void f(char) {}
+template <typename T> struct foo {
+  friend void f(T);
+};
+foo<int> i;
+foo<long> l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
new file mode 100644
index 0000000..ab3b297
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; };
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; };
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
new file mode 100644
index 0000000..f0bbcf9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
new file mode 100644
index 0000000..65ef4df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.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* } } } }
+
+template <typename T> class bar {
+  friend inline bool operator==(const bar&, const bar&) { return true; }
+};
+bar<int> i;
+bar<void*> vp;
+bool f() {
+  return i == i && vp == vp;
+}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
new file mode 100644
index 0000000..2d2b96d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T*>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int*>::bar::baz ip;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
new file mode 100644
index 0000000..9afa9a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T, typename U> void f (T, U) {}
+template <typename T> void f (T, T) {}
+class bar {
+  template <typename T> friend void f (T, T);
+};
+bar t;
+template void f<> (int, long);
+template void f<> (int, int);
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..d02c47f
--- /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" 1 { 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..bf59f3d
--- /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" 1 { 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..e84e838
--- /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" 1 { 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..9ba8207
--- /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" 2 { 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..b909e9b
--- /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" 2 { 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..ea44ed1c
--- /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" 2 { 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..64b3485
--- /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" 2 { 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2016-10-19 10:17       ` Alexandre Oliva
@ 2017-01-27  6:27         ` Alexandre Oliva
  2017-03-21 18:35           ` Alexandre Oliva
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2017-01-27  6:27 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> Handling non-template friends is kind of easy, [...]

>> Regstrapped on x86_64-linux-gnu and i686-linux-gnu, I'd failed to
>> mention.

>> Ping?

> Ping?  (conflicts resolved, patch refreshed and retested)

Ping?  (trivial conflicts resolved)

[PR59319] output friends in debug info

From: Alexandre Oliva <aoliva@redhat.com>

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.

Handling 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.  I considered marking those as DW_AT_artificial, to indicate
they're not explicitly stated in the source code, but in the end we
decided that was not useful.  The greatest challenge was to enumerate
all specializations of a template.  It looked trivial at first, given
DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
class-scoped functions and of nested templates.  For other templates,
I ended up writing code to look for specializations in the hashtables
of decl or type specializations.  That's not exactly efficient, but it
gets the job done.


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.
	(dwarf2out_early_finish): Likewise.
	* langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New.
	(LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it.
	* langhooks.h (lang_hooks_for_types): Add get_friends.
	* hooks.c (hook_tree_const_tree_int_null): New.
	* hooks.h (hook_tree_const_tree_int_null): Declare.

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 (optimize_friend_specialization_lookup_p): New.
	(retrieve_friend_specialization): New.
	(enumerate_friend_specializations): New.
	(register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
	for functions, even after definition, if we are emitting debug
	info.

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.
	* g++.dg/debug/dwarf2/friend-14.C: New.
	* g++.dg/debug/dwarf2/friend-15.C: New.
	* g++.dg/debug/dwarf2/friend-16.C: New.
	* g++.dg/debug/dwarf2/friend-17.C: New.
	* g++.dg/debug/dwarf2/friend-18.C: New.
---
 gcc/cp/cp-objcp-common.c                      |  106 ++++++++++++++
 gcc/cp/cp-objcp-common.h                      |    4 +
 gcc/cp/cp-tree.h                              |    1 
 gcc/cp/pt.c                                   |  194 +++++++++++++++++++++++++
 gcc/dwarf2out.c                               |  165 +++++++++++++++++++++
 gcc/hooks.c                                   |    7 +
 gcc/hooks.h                                   |    1 
 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-13.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C |   12 ++
 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 ++
 27 files changed, 735 insertions(+), 3 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-13.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.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 2c93252..721e88f 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -144,6 +144,112 @@ cp_get_debug_type (const_tree type)
   return NULL_TREE;
 }
 
+/* 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 (!typedecl)
+    return NULL_TREE;
+
+  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
+	    && !uses_template_parms (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 -1 if dwarf ATTR shouldn't be added for DECL, or the attribute
    value otherwise.  */
 int
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 6692ca8..97023e7 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -24,6 +24,8 @@ along with GCC; see the file COPYING3.  If not see
 /* In cp/objcp-common.c, cp/cp-lang.c and objcp/objcp-lang.c.  */
 
 extern tree cp_get_debug_type (const_tree);
+extern tree cp_get_friends (const_tree, int);
+
 extern tree objcp_tsubst_copy_and_build (tree, tree, tsubst_flags_t,
 					 tree, bool);
 
@@ -130,6 +132,8 @@ extern tree cp_unit_size_without_reusable_padding (tree);
 #define LANG_HOOKS_RECONSTRUCT_COMPLEX_TYPE cp_reconstruct_complex_type
 #undef LANG_HOOKS_GET_DEBUG_TYPE
 #define LANG_HOOKS_GET_DEBUG_TYPE cp_get_debug_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 f91b830..fb859ef 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6196,6 +6196,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 57334b4..caab131 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1471,6 +1471,192 @@ is_specialization_of_friend (tree decl, tree friend_decl)
   return false;
 }
 
+/* Return TRUE if TMPL is not a template itself, but rather a member
+   of a [template] class, so that, in order to catch all friends,
+   including corresponding members of partial and explicit
+   specializations, we should look for specializations of the
+   enclosing class.  */
+
+static bool
+optimize_friend_specialization_lookup_p (tree tmpl)
+{
+  return (optimize_specialization_lookup_p (tmpl)
+	  || ((TREE_CODE (tmpl) == FUNCTION_DECL
+	       || (DECL_CLASS_TEMPLATE_P (tmpl)
+		   && !DECL_MEMBER_TEMPLATE_P (tmpl)))
+	      && DECL_CLASS_SCOPE_P (tmpl)
+	      && CLASS_TYPE_P (DECL_CONTEXT (tmpl))
+	      && !CLASSTYPE_TEMPLATE_SPECIALIZATION (DECL_CONTEXT (tmpl))));
+}
+
+/* Retrieve the specialization of TMPL that is a member of CTX with
+   ARGS.  If retrieve_specialization can't find such a specialization,
+   or it yields a specialization that is not a member of CTX (say,
+   because CTX is a partial specialization, and ARGS has the arguments
+   for the partial specialization), look up TMPL's name in CTX, and
+   see which result, if any, is a friend specialization of TMPL.  */
+
+static tree
+retrieve_friend_specialization (tree tmpl, tree args, tree ctx)
+{
+  tree ret;
+
+  if (TREE_CODE (tmpl) == FUNCTION_DECL)
+    {
+      ret = retrieve_specialization (DECL_TI_TEMPLATE (tmpl), args, 0);
+      if (TREE_CODE (ctx) == FUNCTION_DECL)
+	{
+	  gcc_assert (ctx == ret);
+	  ctx = DECL_CONTEXT (ret);
+	}
+    }
+  else
+    ret = retrieve_specialization (tmpl, args, 0);
+
+  /* We have to test the context because ARGS could be from a partial
+     specialization, and using that in tmpl might get us a different
+     specialization.  We could improve this with a reverse
+     get_partial_spec_bindings, but this is simple and cheap
+     enough.  */
+  if (ret && (DECL_P (ret) ? DECL_CONTEXT (ret) : TYPE_CONTEXT (ret)) == ctx)
+    {
+    found:
+      tree testme = ret;
+      if (!DECL_P (testme))
+	testme = TYPE_NAME (ret);
+      if (is_specialization_of_friend (testme, tmpl))
+	return ret;
+      else
+	return NULL_TREE;
+    }
+
+  if (!optimize_friend_specialization_lookup_p (tmpl))
+    return NULL_TREE;
+
+  if (DECL_CLASS_TEMPLATE_P (tmpl))
+    {
+      ret = lookup_member (ctx, DECL_NAME (tmpl), 0, true, tf_none);
+      if (!ret)
+	return NULL_TREE;
+
+      goto found;
+    }
+
+  ret = lookup_fnfields (ctx, DECL_NAME (tmpl), 0);
+  for (tree fns = ret, prev_found = NULL_TREE; fns; fns = OVL_NEXT (fns))
+    {
+      tree fn = OVL_CURRENT (fns);
+
+      if (!is_specialization_of_friend (fn, tmpl))
+	continue;
+
+      if (prev_found)
+	return NULL_TREE;
+
+      ret = prev_found = fn;
+    }
+
+  return ret;
+}
+
+/* Return a list of instantiations/specializations that match
+   FRIEND_DECL.  */
+
+tree
+enumerate_friend_specializations (tree friend_decl)
+{
+  tree opt_decl = friend_decl;
+  if (optimize_friend_specialization_lookup_p (opt_decl))
+    opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl));
+
+  if (TREE_CODE (opt_decl) == FUNCTION_DECL)
+    {
+      gcc_assert (uses_template_parms (opt_decl));
+      opt_decl = DECL_TI_TEMPLATE (opt_decl);
+    }
+
+  gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL);
+
+  /* For nested template classes, we might be able to enumerate the
+     enclosing template classes, and then locate their member
+     templates, but it's not clear it's worth the effort.  If we were
+     to use their DECL_TEMPLATE_INSTANTIATIONS, we'd only get partial
+     specializations (try g++.dg/debug/dwarf2/friend-12.C), so we
+     don't.  So we only use DECL_TEMPLATE_INSTANTIATIONS for template
+     classes in classes that are not templates, or that are
+     fully-specialized template, and for primary template functions in
+     namespace scopes (DECL_TEMPLATE_INSTANTIATIONS is not even
+     defined for other template functions).  */
+  if (DECL_CLASS_TEMPLATE_P (opt_decl)
+      ? (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 || !uses_template_parms (DECL_CONTEXT (opt_decl)))
+      : (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 && PRIMARY_TEMPLATE_P (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_friend_specialization
+		(friend_decl, TREE_PURPOSE (speclist), spec);
+	      if (!spec)
+		continue;
+	    }
+	  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 ospec = (*iter)->spec;
+      if (TREE_CODE (ospec) != code)
+	continue;
+      tree spec = ospec;
+      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_friend_specialization
+		(friend_decl, (*iter)->args, ospec);
+	      if (!spec)
+		continue;
+	    }
+	  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
@@ -1625,12 +1811,18 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
       *slot = entry;
       if ((TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
 	   && PRIMARY_TEMPLATE_P (tmpl)
-	   && DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
+	   && (DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE
+	       || debug_info_level != DINFO_LEVEL_NONE))
 	  || variable_template_p (tmpl))
 	/* If TMPL is a forward declaration of a template function, keep a list
 	   of all specializations in case we need to reassign them to a friend
 	   template later in tsubst_friend_function.
 
+	   We also use DECL_TEMPLATE_INSTANTIATIONS to enumerate the
+	   specializations of a friend function template in debug
+	   information, so if we are emitting debug information, add
+	   specializations to the list.
+
 	   Also keep a list of all variable template instantiations so that
 	   process_partial_specialization can check whether a later partial
 	   specialization would have used it.  */
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index f8fe4c1..252fd3d 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -24012,6 +24012,61 @@ 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 GTY (()) vec<tree, va_gc> *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);
+      /* We don't distinguish between actual friend declarations, and
+	 template specializations of template friend declarations.  We
+	 could test TREE_PURPOSE at this point to that end.  */
+    }
+}
+
+/* Add any friend tags corresponding to class TYPEs that were found to
+   have friend declarations.  */
+
+static void
+gen_friend_tags ()
+{
+  if (!class_types_with_friends)
+    return;
+
+  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
@@ -24122,6 +24177,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))
+    vec_safe_push (class_types_with_friends, type);
 }
 
 /* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
@@ -27654,6 +27712,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
@@ -27689,6 +27838,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:
@@ -27698,7 +27854,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:
@@ -27832,6 +27987,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 ());
@@ -27883,6 +28040,10 @@ prune_unused_types (void)
 	    }
       }
 
+  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)
@@ -29473,6 +29634,7 @@ dwarf2out_finish (const char *)
   gcc_assert (deferred_asm_name == NULL);
 
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
 #if ENABLE_ASSERT_CHECKING
   {
@@ -29887,6 +30049,7 @@ dwarf2out_early_finish (const char *filename)
 
   gen_scheduled_generic_parms_dies ();
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
   /* Add DW_AT_linkage_name for all deferred DIEs.  */
   for (limbo_die_node *node = deferred_asm_name; node; node = node->next)
diff --git a/gcc/hooks.c b/gcc/hooks.c
index f4591dc..dfd3edd 100644
--- a/gcc/hooks.c
+++ b/gcc/hooks.c
@@ -422,6 +422,13 @@ hook_constcharptr_int_const_tree_const_tree_null (int, const_tree, const_tree)
   return NULL;
 }
 
+/* Generic hook that takes a const_tree and an int, and returns NULL_TREE.  */
+tree
+hook_tree_const_tree_int_null (const_tree, int)
+{
+  return NULL;
+}
+
 /* Generic hook that takes a const_tree and returns NULL_TREE.  */
 tree
 hook_tree_const_tree_null (const_tree)
diff --git a/gcc/hooks.h b/gcc/hooks.h
index 95f7810..2fb148d 100644
--- a/gcc/hooks.h
+++ b/gcc/hooks.h
@@ -89,6 +89,7 @@ extern int hook_int_rtx_bool_0 (rtx, bool);
 extern int hook_int_rtx_mode_as_bool_0 (rtx, machine_mode, addr_space_t,
 					bool);
 
+extern tree hook_tree_const_tree_int_null (const_tree, int);
 extern tree hook_tree_const_tree_null (const_tree);
 extern tree hook_tree_void_null (void);
 
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index eb68084..ef2d0e3 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -194,6 +194,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
 #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO NULL
 #define LANG_HOOKS_TYPE_DWARF_ATTRIBUTE	lhd_type_dwarf_attribute
 #define LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING lhd_unit_size_without_reusable_padding
+#define LANG_HOOKS_GET_FRIENDS		hook_tree_const_tree_int_null
 
 #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \
   LANG_HOOKS_MAKE_TYPE, \
@@ -218,7 +219,8 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
   LANG_HOOKS_GET_DEBUG_TYPE, \
   LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \
   LANG_HOOKS_TYPE_DWARF_ATTRIBUTE, \
-  LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING \
+  LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING, \
+  LANG_HOOKS_GET_FRIENDS \
 }
 
 /* Declaration hooks.  */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 2a2ef8a..c5512552 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -170,6 +170,25 @@ struct lang_hooks_for_types
   /* Returns a tree for the unit size of T excluding tail padding that
      might be used by objects inheriting from T.  */
   tree (*unit_size_without_reusable_padding) (tree);
+
+  /* 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..55d3fe2
--- /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" 1 { 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..0e6e4ad
--- /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" 2 { 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..f8d01db
--- /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" 2 { 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..cb92cfb
--- /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" 4 { 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-13.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
new file mode 100644
index 0000000..4127447
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA -Wno-non-template-friend" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+void f(int) {}
+void f(long) {}
+void f(char) {}
+template <typename T> struct foo {
+  friend void f(T);
+};
+foo<int> i;
+foo<long> l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
new file mode 100644
index 0000000..ab3b297
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; };
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; };
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
new file mode 100644
index 0000000..f0bbcf9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
new file mode 100644
index 0000000..65ef4df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.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* } } } }
+
+template <typename T> class bar {
+  friend inline bool operator==(const bar&, const bar&) { return true; }
+};
+bar<int> i;
+bar<void*> vp;
+bool f() {
+  return i == i && vp == vp;
+}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
new file mode 100644
index 0000000..2d2b96d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T*>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int*>::bar::baz ip;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
new file mode 100644
index 0000000..9afa9a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T, typename U> void f (T, U) {}
+template <typename T> void f (T, T) {}
+class bar {
+  template <typename T> friend void f (T, T);
+};
+bar t;
+template void f<> (int, long);
+template void f<> (int, int);
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..d02c47f
--- /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" 1 { 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..bf59f3d
--- /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" 1 { 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..e84e838
--- /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" 1 { 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..9ba8207
--- /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" 2 { 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..b909e9b
--- /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" 2 { 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..ea44ed1c
--- /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" 2 { 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..64b3485
--- /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" 2 { 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-01-27  6:27         ` Alexandre Oliva
@ 2017-03-21 18:35           ` Alexandre Oliva
  2017-04-07 18:32             ` Alexandre Oliva
  2017-04-12 22:06             ` Jeff Law
  0 siblings, 2 replies; 17+ messages in thread
From: Alexandre Oliva @ 2017-03-21 18:35 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Jan 27, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>> Handling non-template friends is kind of easy, [...]

>>> Regstrapped on x86_64-linux-gnu and i686-linux-gnu, I'd failed to
>>> mention.

>>> Ping?

>> Ping?  (conflicts resolved, patch refreshed and retested)

> Ping?  (trivial conflicts resolved)

Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html


> 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.

> Handling 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.  I considered marking those as DW_AT_artificial, to indicate
> they're not explicitly stated in the source code, but in the end we
> decided that was not useful.  The greatest challenge was to enumerate
> all specializations of a template.  It looked trivial at first, given
> DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
> class-scoped functions and of nested templates.  For other templates,
> I ended up writing code to look for specializations in the hashtables
> of decl or type specializations.  That's not exactly efficient, but it
> gets the job done.


> 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.
> 	(dwarf2out_early_finish): Likewise.
> 	* langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New.
> 	(LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it.
> 	* langhooks.h (lang_hooks_for_types): Add get_friends.
> 	* hooks.c (hook_tree_const_tree_int_null): New.
> 	* hooks.h (hook_tree_const_tree_int_null): Declare.

> 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 (optimize_friend_specialization_lookup_p): New.
> 	(retrieve_friend_specialization): New.
> 	(enumerate_friend_specializations): New.
> 	(register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
> 	for functions, even after definition, if we are emitting debug
> 	info.

> 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.
> 	* g++.dg/debug/dwarf2/friend-14.C: New.
> 	* g++.dg/debug/dwarf2/friend-15.C: New.
> 	* g++.dg/debug/dwarf2/friend-16.C: New.
> 	* g++.dg/debug/dwarf2/friend-17.C: New.
> 	* g++.dg/debug/dwarf2/friend-18.C: New.

-- 
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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-03-21 18:35           ` Alexandre Oliva
@ 2017-04-07 18:32             ` Alexandre Oliva
  2017-04-10 16:24               ` Mike Stump
  2017-12-07 21:04               ` Alexandre Oliva
  2017-04-12 22:06             ` Jeff Law
  1 sibling, 2 replies; 17+ messages in thread
From: Alexandre Oliva @ 2017-04-07 18:32 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Mar 21, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
> On Jan 27, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>> On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>> Handling non-template friends is kind of easy, [...]
>>>> Ping?
>>> Ping?  (conflicts resolved, patch refreshed and retested)
>> Ping?  (trivial conflicts resolved)
> Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html
Ping?

>> 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.

>> Handling 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.  I considered marking those as DW_AT_artificial, to indicate
>> they're not explicitly stated in the source code, but in the end we
>> decided that was not useful.  The greatest challenge was to enumerate
>> all specializations of a template.  It looked trivial at first, given
>> DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
>> class-scoped functions and of nested templates.  For other templates,
>> I ended up writing code to look for specializations in the hashtables
>> of decl or type specializations.  That's not exactly efficient, but it
>> gets the job done.


>> 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.
>> (dwarf2out_early_finish): Likewise.
>> * langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New.
>> (LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it.
>> * langhooks.h (lang_hooks_for_types): Add get_friends.
>> * hooks.c (hook_tree_const_tree_int_null): New.
>> * hooks.h (hook_tree_const_tree_int_null): Declare.

>> 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 (optimize_friend_specialization_lookup_p): New.
>> (retrieve_friend_specialization): New.
>> (enumerate_friend_specializations): New.
>> (register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
>> for functions, even after definition, if we are emitting debug
>> info.

>> 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.
>> * g++.dg/debug/dwarf2/friend-14.C: New.
>> * g++.dg/debug/dwarf2/friend-15.C: New.
>> * g++.dg/debug/dwarf2/friend-16.C: New.
>> * g++.dg/debug/dwarf2/friend-17.C: New.
>> * g++.dg/debug/dwarf2/friend-18.C: New.

-- 
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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-04-07 18:32             ` Alexandre Oliva
@ 2017-04-10 16:24               ` Mike Stump
  2017-12-07 21:04               ` Alexandre Oliva
  1 sibling, 0 replies; 17+ messages in thread
From: Mike Stump @ 2017-04-10 16:24 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Jason Merrill, Richard Biener, gcc-patches List, ccoutant

On Apr 7, 2017, at 11:32 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
> 
> On Mar 21, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>> 
>> Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html
> Ping?

The Objective-C/C++ parts look fine.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-03-21 18:35           ` Alexandre Oliva
  2017-04-07 18:32             ` Alexandre Oliva
@ 2017-04-12 22:06             ` Jeff Law
  1 sibling, 0 replies; 17+ messages in thread
From: Jeff Law @ 2017-04-12 22:06 UTC (permalink / raw)
  To: Alexandre Oliva, Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On 03/21/2017 12:34 PM, Alexandre Oliva wrote:
> On Jan 27, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>
>> On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>> Handling non-template friends is kind of easy, [...]
>
>>>> Regstrapped on x86_64-linux-gnu and i686-linux-gnu, I'd failed to
>>>> mention.
>
>>>> Ping?
>
>>> Ping?  (conflicts resolved, patch refreshed and retested)
>
>> Ping?  (trivial conflicts resolved)
>
> Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html
Going to punt to gcc-8.  Sorry, but we're just getting late in the 
release process..

jeff

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-04-07 18:32             ` Alexandre Oliva
  2017-04-10 16:24               ` Mike Stump
@ 2017-12-07 21:04               ` Alexandre Oliva
  2017-12-14 18:48                 ` Jason Merrill
  1 sibling, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2017-12-07 21:04 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Apr  7, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Mar 21, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>> On Jan 27, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>>> Handling non-template friends is kind of easy, [...]
>>>>> Ping?
>>>> Ping?  (conflicts resolved, patch refreshed and retested)
>>> Ping?  (trivial conflicts resolved)
>> Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html
> Ping?
Ping? (refreshed, retested)

[PR59319] output friends in debug info

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.

Handling 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.  I considered marking those as DW_AT_artificial, to indicate
they're not explicitly stated in the source code, but in the end we
decided that was not useful.  The greatest challenge was to enumerate
all specializations of a template.  It looked trivial at first, given
DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
class-scoped functions and of nested templates.  For other templates,
I ended up writing code to look for specializations in the hashtables
of decl or type specializations.  That's not exactly efficient, but it
gets the job done.


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.
	(dwarf2out_early_finish): Likewise.
	* langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New.
	(LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it.
	* langhooks.h (lang_hooks_for_types): Add get_friends.
	* hooks.c (hook_tree_const_tree_int_null): New.
	* hooks.h (hook_tree_const_tree_int_null): Declare.

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 (optimize_friend_specialization_lookup_p): New.
	(retrieve_friend_specialization): New.
	(enumerate_friend_specializations): New.
	(register_specialization): Update DECL_TEMPLATE_INSTANTIATIONS
	for functions, even after definition, if we are emitting debug
	info.

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.
	* g++.dg/debug/dwarf2/friend-14.C: New.
	* g++.dg/debug/dwarf2/friend-15.C: New.
	* g++.dg/debug/dwarf2/friend-16.C: New.
	* g++.dg/debug/dwarf2/friend-17.C: New.
	* g++.dg/debug/dwarf2/friend-18.C: New.
---
 gcc/cp/cp-objcp-common.c                      |  106 ++++++++++++++
 gcc/cp/cp-objcp-common.h                      |    4 +
 gcc/cp/cp-tree.h                              |    1 
 gcc/cp/pt.c                                   |  195 +++++++++++++++++++++++++
 gcc/dwarf2out.c                               |  165 +++++++++++++++++++++
 gcc/hooks.c                                   |    7 +
 gcc/hooks.h                                   |    1 
 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-13.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C |   20 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C |   12 ++
 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 ++
 27 files changed, 736 insertions(+), 3 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-13.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-18.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 dc558eed1a50..a0ddbc1f1d7d 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -155,6 +155,112 @@ cp_get_debug_type (const_tree type)
   return dtype;
 }
 
+/* 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 (!typedecl)
+    return NULL_TREE;
+
+  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
+	    && !uses_template_parms (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 -1 if dwarf ATTR shouldn't be added for DECL, or the attribute
    value otherwise.  */
 int
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 8d3bc8767589..f9555ae4ce7b 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -24,6 +24,8 @@ along with GCC; see the file COPYING3.  If not see
 /* In cp/objcp-common.c, cp/cp-lang.c and objcp/objcp-lang.c.  */
 
 extern tree cp_get_debug_type (const_tree);
+extern tree cp_get_friends (const_tree, int);
+
 extern tree objcp_tsubst_copy_and_build (tree, tree, tsubst_flags_t,
 					 tree, bool);
 
@@ -141,6 +143,8 @@ extern void cp_register_dumps (gcc::dump_manager *);
 #define LANG_HOOKS_RECONSTRUCT_COMPLEX_TYPE cp_reconstruct_complex_type
 #undef LANG_HOOKS_GET_DEBUG_TYPE
 #define LANG_HOOKS_GET_DEBUG_TYPE cp_get_debug_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 4780df4dbf63..13723a416760 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6508,6 +6508,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, bool = false);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 795c53e65895..40b042ef5013 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1469,6 +1469,193 @@ is_specialization_of_friend (tree decl, tree friend_decl)
   return false;
 }
 
+/* Return TRUE if TMPL is not a template itself, but rather a member
+   of a [template] class, so that, in order to catch all friends,
+   including corresponding members of partial and explicit
+   specializations, we should look for specializations of the
+   enclosing class.  */
+
+static bool
+optimize_friend_specialization_lookup_p (tree tmpl)
+{
+  return (optimize_specialization_lookup_p (tmpl)
+	  || ((TREE_CODE (tmpl) == FUNCTION_DECL
+	       || (DECL_CLASS_TEMPLATE_P (tmpl)
+		   && !DECL_MEMBER_TEMPLATE_P (tmpl)))
+	      && DECL_CLASS_SCOPE_P (tmpl)
+	      && CLASS_TYPE_P (DECL_CONTEXT (tmpl))
+	      && !CLASSTYPE_TEMPLATE_SPECIALIZATION (DECL_CONTEXT (tmpl))));
+}
+
+/* Retrieve the specialization of TMPL that is a member of CTX with
+   ARGS.  If retrieve_specialization can't find such a specialization,
+   or it yields a specialization that is not a member of CTX (say,
+   because CTX is a partial specialization, and ARGS has the arguments
+   for the partial specialization), look up TMPL's name in CTX, and
+   see which result, if any, is a friend specialization of TMPL.  */
+
+static tree
+retrieve_friend_specialization (tree tmpl, tree args, tree ctx)
+{
+  tree ret;
+
+  if (TREE_CODE (tmpl) == FUNCTION_DECL)
+    {
+      ret = retrieve_specialization (DECL_TI_TEMPLATE (tmpl), args, 0);
+      if (TREE_CODE (ctx) == FUNCTION_DECL)
+	{
+	  gcc_assert (ctx == ret);
+	  ctx = DECL_CONTEXT (ret);
+	}
+    }
+  else
+    ret = retrieve_specialization (tmpl, args, 0);
+
+  /* We have to test the context because ARGS could be from a partial
+     specialization, and using that in tmpl might get us a different
+     specialization.  We could improve this with a reverse
+     get_partial_spec_bindings, but this is simple and cheap
+     enough.  */
+  if (ret && (DECL_P (ret) ? DECL_CONTEXT (ret) : TYPE_CONTEXT (ret)) == ctx)
+    {
+    found:
+      tree testme = ret;
+      if (!DECL_P (testme))
+	testme = TYPE_NAME (ret);
+      if (is_specialization_of_friend (testme, tmpl))
+	return ret;
+      else
+	return NULL_TREE;
+    }
+
+  if (!optimize_friend_specialization_lookup_p (tmpl))
+    return NULL_TREE;
+
+  if (DECL_CLASS_TEMPLATE_P (tmpl))
+    {
+      ret = lookup_member (ctx, DECL_NAME (tmpl), 0, true, tf_none);
+      if (!ret)
+	return NULL_TREE;
+
+      goto found;
+    }
+
+  ret = NULL_TREE;
+  for (lkp_iterator iter (lookup_fnfields (ctx, DECL_NAME (tmpl), 0));
+       iter; ++iter)
+    {
+      tree fn = *iter;
+
+      if (!is_specialization_of_friend (fn, tmpl))
+	continue;
+
+      if (ret)
+	return NULL_TREE;
+
+      ret = fn;
+    }
+
+  return ret;
+}
+
+/* Return a list of instantiations/specializations that match
+   FRIEND_DECL.  */
+
+tree
+enumerate_friend_specializations (tree friend_decl)
+{
+  tree opt_decl = friend_decl;
+  if (optimize_friend_specialization_lookup_p (opt_decl))
+    opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl));
+
+  if (TREE_CODE (opt_decl) == FUNCTION_DECL)
+    {
+      gcc_assert (uses_template_parms (opt_decl));
+      opt_decl = DECL_TI_TEMPLATE (opt_decl);
+    }
+
+  gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL);
+
+  /* For nested template classes, we might be able to enumerate the
+     enclosing template classes, and then locate their member
+     templates, but it's not clear it's worth the effort.  If we were
+     to use their DECL_TEMPLATE_INSTANTIATIONS, we'd only get partial
+     specializations (try g++.dg/debug/dwarf2/friend-12.C), so we
+     don't.  So we only use DECL_TEMPLATE_INSTANTIATIONS for template
+     classes in classes that are not templates, or that are
+     fully-specialized template, and for primary template functions in
+     namespace scopes (DECL_TEMPLATE_INSTANTIATIONS is not even
+     defined for other template functions).  */
+  if (DECL_CLASS_TEMPLATE_P (opt_decl)
+      ? (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 || !uses_template_parms (DECL_CONTEXT (opt_decl)))
+      : (DECL_NAMESPACE_SCOPE_P (opt_decl)
+	 && PRIMARY_TEMPLATE_P (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_friend_specialization
+		(friend_decl, TREE_PURPOSE (speclist), spec);
+	      if (!spec)
+		continue;
+	    }
+	  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 ospec = (*iter)->spec;
+      if (TREE_CODE (ospec) != code)
+	continue;
+      tree spec = ospec;
+      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_friend_specialization
+		(friend_decl, (*iter)->args, ospec);
+	      if (!spec)
+		continue;
+	    }
+	  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
@@ -1628,12 +1815,18 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
       *slot = entry;
       if ((TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
 	   && PRIMARY_TEMPLATE_P (tmpl)
-	   && DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
+	   && (DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE
+	       || debug_info_level != DINFO_LEVEL_NONE))
 	  || variable_template_p (tmpl))
 	/* If TMPL is a forward declaration of a template function, keep a list
 	   of all specializations in case we need to reassign them to a friend
 	   template later in tsubst_friend_function.
 
+	   We also use DECL_TEMPLATE_INSTANTIATIONS to enumerate the
+	   specializations of a friend function template in debug
+	   information, so if we are emitting debug information, add
+	   specializations to the list.
+
 	   Also keep a list of all variable template instantiations so that
 	   process_partial_specialization can check whether a later partial
 	   specialization would have used it.  */
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index ebc100437263..32cdf457d5aa 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -24575,6 +24575,61 @@ 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 GTY (()) vec<tree, va_gc> *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);
+      /* We don't distinguish between actual friend declarations, and
+	 template specializations of template friend declarations.  We
+	 could test TREE_PURPOSE at this point to that end.  */
+    }
+}
+
+/* Add any friend tags corresponding to class TYPEs that were found to
+   have friend declarations.  */
+
+static void
+gen_friend_tags ()
+{
+  if (!class_types_with_friends)
+    return;
+
+  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
@@ -24696,6 +24751,9 @@ gen_member_die (tree type, dw_die_ref context_die)
 	  DECL_EXTERNAL (member) = old_extern;
 	}
     }
+
+  if (lang_hooks.types.get_friends (type, 0))
+    vec_safe_push (class_types_with_friends, type);
 }
 
 /* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
@@ -28643,6 +28701,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
@@ -28678,6 +28827,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:
@@ -28687,7 +28843,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:
@@ -28821,6 +28976,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 ());
@@ -28872,6 +29029,10 @@ prune_unused_types (void)
 	    }
       }
 
+  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)
@@ -30600,6 +30761,7 @@ dwarf2out_finish (const char *)
   gcc_assert (deferred_asm_name == NULL);
 
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
   if (flag_generate_lto || flag_generate_offload)
     {
@@ -31324,6 +31486,7 @@ dwarf2out_early_finish (const char *filename)
 
   gen_scheduled_generic_parms_dies ();
   gen_remaining_tmpl_value_param_die_attribute ();
+  gen_friend_tags ();
 
   /* Add DW_AT_linkage_name for all deferred DIEs.  */
   for (limbo_die_node *node = deferred_asm_name; node; node = node->next)
diff --git a/gcc/hooks.c b/gcc/hooks.c
index 61ff890b45b3..c7dc98233808 100644
--- a/gcc/hooks.c
+++ b/gcc/hooks.c
@@ -456,6 +456,13 @@ hook_constcharptr_int_const_tree_const_tree_null (int, const_tree, const_tree)
   return NULL;
 }
 
+/* Generic hook that takes a const_tree and an int, and returns NULL_TREE.  */
+tree
+hook_tree_const_tree_int_null (const_tree, int)
+{
+  return NULL;
+}
+
 /* Generic hook that takes a const_tree and returns NULL_TREE.  */
 tree
 hook_tree_const_tree_null (const_tree)
diff --git a/gcc/hooks.h b/gcc/hooks.h
index 8dbfd78717f6..45ebaaa8dcf7 100644
--- a/gcc/hooks.h
+++ b/gcc/hooks.h
@@ -100,6 +100,7 @@ extern int hook_int_rtx_mode_as_bool_0 (rtx, machine_mode, addr_space_t,
 
 extern HOST_WIDE_INT hook_hwi_void_0 (void);
 
+extern tree hook_tree_const_tree_int_null (const_tree, int);
 extern tree hook_tree_const_tree_null (const_tree);
 extern tree hook_tree_void_null (void);
 
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 9362d9674ede..cb0dde51237e 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -200,6 +200,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
 #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO NULL
 #define LANG_HOOKS_TYPE_DWARF_ATTRIBUTE	lhd_type_dwarf_attribute
 #define LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING lhd_unit_size_without_reusable_padding
+#define LANG_HOOKS_GET_FRIENDS		hook_tree_const_tree_int_null
 
 #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \
   LANG_HOOKS_MAKE_TYPE, \
@@ -225,7 +226,8 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
   LANG_HOOKS_GET_DEBUG_TYPE, \
   LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \
   LANG_HOOKS_TYPE_DWARF_ATTRIBUTE, \
-  LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING \
+  LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING, \
+  LANG_HOOKS_GET_FRIENDS \
 }
 
 /* Declaration hooks.  */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index dddb0270d498..1a774f8690aa 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -174,6 +174,25 @@ struct lang_hooks_for_types
   /* Returns a tree for the unit size of T excluding tail padding that
      might be used by objects inheriting from T.  */
   tree (*unit_size_without_reusable_padding) (tree);
+
+  /* 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 000000000000..55d3fe23b465
--- /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" 1 { 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 000000000000..0e6e4ade6c58
--- /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" 2 { 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 000000000000..f8d01db5f629
--- /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" 2 { 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 000000000000..cb92cfbad470
--- /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" 4 { 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-13.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
new file mode 100644
index 000000000000..4127447d56c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-13.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA -Wno-non-template-friend" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+void f(int) {}
+void f(long) {}
+void f(char) {}
+template <typename T> struct foo {
+  friend void f(T);
+};
+foo<int> i;
+foo<long> l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
new file mode 100644
index 000000000000..ab3b29791583
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-14.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; };
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; };
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
new file mode 100644
index 000000000000..f0bbcf95ce56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-15.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int>::bar::baz i;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.C
new file mode 100644
index 000000000000..65ef4dffa622
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-16.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* } } } }
+
+template <typename T> class bar {
+  friend inline bool operator==(const bar&, const bar&) { return true; }
+};
+bar<int> i;
+bar<void*> vp;
+bool f() {
+  return i == i && vp == vp;
+}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
new file mode 100644
index 000000000000..2d2b96dac1a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-17.C
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T> struct foo {
+  struct bar { struct baz {}; };
+};
+template <typename T> struct foo<T*> {
+  struct bar { struct baz {}; }; // This baz is not a friend.
+};
+template <> struct foo<int> {
+  struct bar { struct baz {}; }; // Likewise.
+};
+class boo {
+  template <typename T> friend struct foo<T*>::bar::baz;
+};
+foo<void>::bar::baz v;
+foo<void*>::bar::baz vp;
+foo<int*>::bar::baz ip;
+boo m;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
new file mode 100644
index 000000000000..9afa9a6012eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-18.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 1 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename T, typename U> void f (T, U) {}
+template <typename T> void f (T, T) {}
+class bar {
+  template <typename T> friend void f (T, T);
+};
+bar t;
+template void f<> (int, long);
+template void f<> (int, int);
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 000000000000..b4cd04dbedf3
--- /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 000000000000..d02c47fa8ce8
--- /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" 1 { 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 000000000000..bf59f3d0c323
--- /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" 1 { 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 000000000000..e84e83867034
--- /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" 1 { 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 000000000000..9ba8207f7036
--- /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" 2 { 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 000000000000..b909e9b90512
--- /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" 2 { 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 000000000000..ea44ed1c309b
--- /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" 2 { 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 000000000000..64b348519602
--- /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" 2 { 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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-12-07 21:04               ` Alexandre Oliva
@ 2017-12-14 18:48                 ` Jason Merrill
  2017-12-19 21:57                   ` Alexandre Oliva
  0 siblings, 1 reply; 17+ messages in thread
From: Jason Merrill @ 2017-12-14 18:48 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Richard Biener, gcc-patches List, ccoutant

On 12/07/2017 04:04 PM, Alexandre Oliva wrote:
> On Apr  7, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
> 
>> On Mar 21, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> On Jan 27, 2017, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>> On Oct 19, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>> On Sep 23, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>>> On Aug 30, 2016, Alexandre Oliva <aoliva@redhat.com> wrote:
>>>>>>> Handling non-template friends is kind of easy, [...]
>>>>>> Ping?
>>>>> Ping?  (conflicts resolved, patch refreshed and retested)
>>>> Ping?  (trivial conflicts resolved)
>>> Ping?  https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02112.html
>> Ping?
> Ping? (refreshed, retested)
> 
> [PR59319] output friends in debug info
> 
> 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.
> 
> Handling 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.  I considered marking those as DW_AT_artificial, to indicate
> they're not explicitly stated in the source code, but in the end we
> decided that was not useful.  The greatest challenge was to enumerate
> all specializations of a template.  It looked trivial at first, given
> DECL_TEMPLATE_INSTANTIATIONS, but it won't list specializations of
> class-scoped functions and of nested templates.  For other templates,
> I ended up writing code to look for specializations in the hashtables
> of decl or type specializations.  That's not exactly efficient, but it
> gets the job done.

How inefficient is it, exactly?  I'm concerned about the impact on 
compile time of scanning the entire hash table for each friend 
declaration in a template instantiation.  This sounds prohibitive for 
template-heavy code that uses friends.

I wonder about changing register_specialization to fill out 
DECL_TEMPLATE_INSTANTIATIONS for more templates (even more than you 
already do).

> +  /* 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.  */

The calls I see only seem to use details 0 and 3, while I would expect 
level 3 to only be used with -g3.  What is the purpose of the negative 
level?

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-12-14 18:48                 ` Jason Merrill
@ 2017-12-19 21:57                   ` Alexandre Oliva
  2017-12-21 22:37                     ` Jason Merrill
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Oliva @ 2017-12-19 21:57 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Richard Biener, gcc-patches List, ccoutant

On Dec 14, 2017, Jason Merrill <jason@redhat.com> wrote:

> On 12/07/2017 04:04 PM, Alexandre Oliva wrote:

>> For other templates, I ended up writing code to look for
>> specializations in the hashtables of decl or type specializations.
>> That's not exactly efficient, but it gets the job done.

> How inefficient is it, exactly?

Err...  O(n)? :-)

For the cases we go down this path, we iterate over the hashtable of
specializations of types or functions, testing whether each one fits the
friend template declaration with is_specialization_of_friend().  The
tests there are not particularly expensive, but I suppose they could add
up given tons of template instantiations.  I assume the test is already
made efficient, but perhaps a different version could be tuned to this
use, or something, or we could try to come up with a completely
different algorithm that, I don't know, grouped instantiations and
friend declarations by identifiers first, or somesuch.

Now, perhaps it is possible to locate instantiations more immediately in
cases I haven't been able to figure out.  It's not like I know my way
around the internal representation of templates ;-)  If you have any
suggestions and directions, I'm all ears.

> I wonder about changing register_specialization to fill out
> DECL_TEMPLATE_INSTANTIATIONS for more templates (even more than you
> already do).

If spending "permanent" memory to this end is better than gathering it
when needed makes it more efficient, sure.

>> +  /* 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.  */

> The calls I see only seem to use details 0 and 3, while I would expect
> level 3 to only be used with -g3.  What is the purpose of the negative
> level?

I designed the API so as to give dwarf2out some flexibility in getting
to the information in different ways, allowing queries that wouldn't
waste much memory building lists just to throw them away right away.  My
goal was always to list all befriended instantiations at -g, but e.g. if
we've already got the data for non-template friends in an earlier query,
I wanted to avoid duplicating them in a subsequent query for template
friends.  That's where the negative levels would come handy.  The code
could be structured so as to query at level 1 at first, emit the
non-template friend attributes, and then issue a level 2 or 3 query to
deal with template ones only if the special entry is returned in the
first query.  But that subsequent query would duplicate template
entries, whereas with a negative level they would be left out, saving
the memory the list would spend on them.

Now, regardless of what I set out to do, I figured we might want to vary
the amount of detail put in debug info, and that there might be
discussion about it, so the detail levels would help in that regard too.
I envisioned command line options to control some of that, but I didn't
go down this path further before discussion as to what's desirable,
costly, etc.

-- 
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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PR59319] output friends in debug info
  2017-12-19 21:57                   ` Alexandre Oliva
@ 2017-12-21 22:37                     ` Jason Merrill
  0 siblings, 0 replies; 17+ messages in thread
From: Jason Merrill @ 2017-12-21 22:37 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Richard Biener, gcc-patches List, Cary Coutant

On Tue, Dec 19, 2017 at 4:56 PM, Alexandre Oliva <aoliva@redhat.com> wrote:
> On Dec 14, 2017, Jason Merrill <jason@redhat.com> wrote:
>
>> On 12/07/2017 04:04 PM, Alexandre Oliva wrote:
>
>>> For other templates, I ended up writing code to look for
>>> specializations in the hashtables of decl or type specializations.
>>> That's not exactly efficient, but it gets the job done.
>
>> How inefficient is it, exactly?
>
> Err...  O(n)? :-)

For a single friend, but more generally O(M*N) where M is the number
of template friends (including instantiations of class templates
containing template friends) and N is the total number of
instantiations of all (decl) templates.  Right?

> For the cases we go down this path, we iterate over the hashtable of
> specializations of types or functions, testing whether each one fits the
> friend template declaration with is_specialization_of_friend().  The
> tests there are not particularly expensive, but I suppose they could add
> up given tons of template instantiations.  I assume the test is already
> made efficient, but perhaps a different version could be tuned to this
> use, or something, or we could try to come up with a completely
> different algorithm that, I don't know, grouped instantiations and
> friend declarations by identifiers first, or somesuch.
>
> Now, perhaps it is possible to locate instantiations more immediately in
> cases I haven't been able to figure out.  It's not like I know my way
> around the internal representation of templates ;-)  If you have any
> suggestions and directions, I'm all ears.

No, currently instantiations of most templates are only hashed, not
enumerated.  Which was why...

>> I wonder about changing register_specialization to fill out
>> DECL_TEMPLATE_INSTANTIATIONS for more templates (even more than you
>> already do).

...although this would only work for template friends that befriend a
specific template; for friend declarations that befriend a member of a
class template, we'd still need to do the enumeration the way you do
it now, since those declarations also befriend matching members of an
explicit specialization of the class template.

>>> +  /* 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.  */
>
>> The calls I see only seem to use details 0 and 3, while I would expect
>> level 3 to only be used with -g3.  What is the purpose of the negative
>> level?
>
> I designed the API so as to give dwarf2out some flexibility in getting
> to the information in different ways, allowing queries that wouldn't
> waste much memory building lists just to throw them away right away.  My
> goal was always to list all befriended instantiations at -g, but e.g. if
> we've already got the data for non-template friends in an earlier query,
> I wanted to avoid duplicating them in a subsequent query for template
> friends.  That's where the negative levels would come handy.  The code
> could be structured so as to query at level 1 at first, emit the
> non-template friend attributes, and then issue a level 2 or 3 query to
> deal with template ones only if the special entry is returned in the
> first query.  But that subsequent query would duplicate template
> entries, whereas with a negative level they would be left out, saving
> the memory the list would spend on them.
>
> Now, regardless of what I set out to do, I figured we might want to vary
> the amount of detail put in debug info, and that there might be
> discussion about it, so the detail levels would help in that regard too.
> I envisioned command line options to control some of that, but I didn't
> go down this path further before discussion as to what's desirable,
> costly, etc.

My inclination is not to enumerate specializations of friend templates
at -g1; it seems expensive both in compile time and debug info size.

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-12-21 22:37 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-19 18:47 [PR59319] output friends in debug info Alexandre Oliva
2016-08-22 11:36 ` Richard Biener
2016-08-26  5:26   ` Alexandre Oliva
2016-08-26  8:59     ` Richard Biener
2016-08-26 16:13 ` Jason Merrill
2016-08-30 23:13   ` Alexandre Oliva
2016-09-24  2:39     ` Alexandre Oliva
2016-10-19 10:17       ` Alexandre Oliva
2017-01-27  6:27         ` Alexandre Oliva
2017-03-21 18:35           ` Alexandre Oliva
2017-04-07 18:32             ` Alexandre Oliva
2017-04-10 16:24               ` Mike Stump
2017-12-07 21:04               ` Alexandre Oliva
2017-12-14 18:48                 ` Jason Merrill
2017-12-19 21:57                   ` Alexandre Oliva
2017-12-21 22:37                     ` Jason Merrill
2017-04-12 22:06             ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).