From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 103083 invoked by alias); 27 Jan 2017 06:24:25 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 97417 invoked by uid 89); 27 Jan 2017 06:22:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.2 required=5.0 tests=BAYES_40,KAM_LAZY_DOMAIN_SECURITY,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=1328, excluding, lookup_decl_die, dw_die_ref X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 27 Jan 2017 06:21:52 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 21D4FC04B95B; Fri, 27 Jan 2017 06:21:52 +0000 (UTC) Received: from freie.home (ovpn04.gateway.prod.ext.phx2.redhat.com [10.5.9.4]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v0R6Lm3Q020264 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Fri, 27 Jan 2017 01:21:50 -0500 Received: from livre (livre.home [172.31.160.2]) by freie.home (8.15.2/8.15.2) with ESMTP id v0R6KtKY026558; Fri, 27 Jan 2017 04:20:57 -0200 From: Alexandre Oliva To: Jason Merrill Cc: Richard Biener , gcc-patches List , ccoutant@gmail.com Subject: Re: [PR59319] output friends in debug info References: Date: Fri, 27 Jan 2017 06:27:00 -0000 In-Reply-To: (Alexandre Oliva's message of "Wed, 19 Oct 2016 08:16:40 -0200") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-SW-Source: 2017-01/txt/msg02113.txt.bz2 On Oct 19, 2016, Alexandre Oliva wrote: > On Sep 23, 2016, Alexandre Oliva wrote: >> On Aug 30, 2016, Alexandre Oliva 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 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 *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 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 *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 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 struct foo { + template void f () {} +}; +class bar { + template template friend void foo::f (); +}; +bar t; +template void foo::f (); +template void foo::f (); 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 struct foo { + struct f {}; +}; +class bar { + template friend struct foo::f; +}; +bar t; +foo::f i; +foo::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 struct foo { + template struct f {}; +}; +class bar { + template template friend struct foo::f; +}; +bar t; +foo::f i; +foo::f b; +foo::f ib; +foo::f 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 struct foo { + friend void f(T); +}; +foo i; +foo 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 struct foo { + struct bar { struct baz {}; }; +}; +template struct foo { + struct bar { struct baz {}; }; +}; +template <> struct foo { + struct bar { struct baz {}; }; +}; +class boo { + template friend struct foo::bar; +}; +foo::bar::baz v; +foo::bar::baz vp; +foo::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 struct foo { + struct bar { struct baz {}; }; +}; +template struct foo { + struct bar { struct baz {}; }; // This baz is not a friend. +}; +template <> struct foo { + struct bar { struct baz {}; }; // Likewise. +}; +class boo { + template friend struct foo::bar::baz; +}; +foo::bar::baz v; +foo::bar::baz vp; +foo::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 class bar { + friend inline bool operator==(const bar&, const bar&) { return true; } +}; +bar i; +bar 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 struct foo { + struct bar { struct baz {}; }; +}; +template struct foo { + struct bar { struct baz {}; }; // This baz is not a friend. +}; +template <> struct foo { + struct bar { struct baz {}; }; // Likewise. +}; +class boo { + template friend struct foo::bar::baz; +}; +foo::bar::baz v; +foo::bar::baz vp; +foo::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 void f (T, U) {} +template void f (T, T) {} +class bar { + template 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 class foo {}; +class bar { + friend class foo; +}; +bar t; +foo 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 class foo {}; +class bar { + template friend class foo; +}; +bar t; +foo l; +foo 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 void f () {} +class bar { + template friend void f (); +}; +bar t; +template void f (); +template void f (); 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 void f () {} +}; +class bar { + template friend void foo::f (); +}; +bar t; +template void foo::f (); +template void foo::f (); 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 struct foo { + void f () {} +}; +class bar { + template friend void foo::f (); +}; +bar t; +template void foo::f (); +template void foo::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