From patchwork Fri Apr 13 23:18:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 898120 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-476381-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="QUjrIqQX"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40NDJ76Nyyz9s0t for ; Sat, 14 Apr 2018 09:19:33 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type; q=dns; s=default; b=s0JBU5ev1hdIML7OxbvoI6y49hfI/f2M6wDUalDOKKNGhim0aG 6fyH4BJkmkgJ5aa2AfLmLZMRbnsNEgZdKFstv9CHCsBi0yegO7W4p5rwjPuVq54R Btt4MOKtYlU3E5hfc8joYN/tb1xHQV7SEHsKk9+HNJW2eoxfkIWhl/Qzk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type; s= default; bh=SqWb5IOPV5oQFYbonfqvuTGNxbA=; b=QUjrIqQXhPMbF1FB2/qq CyVpLRxz5ooVXtcz53hFZIQfnvXa5W3uCMbdfteV/q6ogJcXslVGpI01lN2bE/L7 SWPmAisC1HvuXbItUhbaWgrRyohBT1eGEkiqlEYgUFJOA5JhC5sR/8K5iXn9QVzm egQXri4HWOM4exACt1GVy/I= Received: (qmail 961 invoked by alias); 13 Apr 2018 23:19:24 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 945 invoked by uid 89); 13 Apr 2018 23:19:23 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3 autolearn=ham version=3.3.2 spammy=permanently, thousands, NEXT, regarded 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, 13 Apr 2018 23:19:19 +0000 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E6A453136323; Fri, 13 Apr 2018 23:19:17 +0000 (UTC) Received: from free.home (ovpn04.gateway.prod.ext.phx2.redhat.com [10.5.9.4]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0D2E767E5A; Fri, 13 Apr 2018 23:19:16 +0000 (UTC) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTP id w3DNIxPn051407; Fri, 13 Apr 2018 20:19:00 -0300 From: Alexandre Oliva To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, nathan@acm.org Subject: [PATCH] [PR c++/80290] recycle tinst garbage sooner Date: Fri, 13 Apr 2018 20:18:59 -0300 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.91 (gnu/linux) MIME-Version: 1.0 tinst_level objects are created during template instantiation, and they're most often quite short-lived, but since there's no intervening garbage collection, they accumulate throughout the pass while most by far could be recycled after a short while. The original testcase in PR80290, for example, creates almost 55 million tinst_level objects, all but 10 thousand of them without intervening GC, but we don't need more than 284 of them live at a time. Furthermore, in many cases, TREE_LIST objects are created to stand for the decl in tinst_level. In most cases, those can be recycled as soon as the tinst_level object is recycled; in some relatively common cases, they are modified and reused in up to 3 tinst_level objects. In the same testcase, TREE_LISTs are used in all but 3 thousand of the tinst_level objects, and we don't need more than 89 tinst_level objects with TREE_LISTs live at a time. Furthermore, all but 2 thousand of those are created without intervening GC. This patch arranges for tinst_level objects to be refcounted (I've seen as many as 20 live references to a single tinst_level object in my testing), and for pending_template, tinst_level and TREE_LIST objects that can be recycled to be added to freelists; that's much faster than ggc_free()ing them and reallocating them shortly thereafter. In fact, the introduction of such freelists is what makes this entire patch lighter-weight, when it comes to memory use, and faster. With refcounting alone, the total memory footprint was still about the same, and we spent more time. In order to further reduce memory use, I've arranged for us to create TREE_LISTs lazily, only at points that really need them (when printing error messages). This grows tinst_level by an additional pointer, but since a TREE_LIST holds a lot more than an extra pointer, and tinst_levels with TREE_LISTs used to be allocated tens of thousands of times more often than plain decl ones, we still save memory overall. I was still not quite happy about growing memory use in cases that used template classes but not template overload resolution, so I changed the type of the errors field to unsigned short, from int. With that change, in_system_header_p and refcount move into the same word or half-word that used to hold errors, releasing a full word, bringing tinst_level back to its original size, now without any padding. The errors field is only used to test whether we've had any errors since the expansion of some template, to skip the expansion of further templates. If we get 2^16 errors or more, it is probably reasonable to refrain from expanding further templates, even if we would expand them before this change. With these changes, compile time for the original testcase at -O0, with default checking enabled, is cut down by some 3.7%, total GCable memory allocation is cut down by almost 20%, and total memory use (as reported by GNU time as maxresident) is cut down by slightly over 15%. Regstrapped on i686- and x86_64-linux-gnu. Ok to install? (See below for an incremental patch that introduces some statistics collection and reporting, that shows how I collected some of the data above. That one is not meant for inclusion) for gcc/cp/ChangeLog PR c++/80290 * cp-tree.h (struct tinst_level): Split decl into tldcl and targs. Add private split_list_p, tree_list_p, and not_list_p inline const predicates; to_list private member function declaration; list_p, get_node, and get_decl_maybe accessors, and refcount private data member. Narrow errors to unsigned short. * error.c (print_instantiation_full_context): Use new accessors. (print_instantiation_partial_context_line): Likewise. * mangle.c (mangle_decl_string): Likewise. * pt.c (freelist): New template class. (tree_list_freelist_head): New var. (tree_list_freelist): New fn, along with specializations. (tinst_level_freelist_head): New var. (pending_template_freelist_head): Likewise. (tinst_level_freelist, pending_template_freelist): New fns. (tinst_level::to_list): Define. (inc_refcount_use, dec_refcount_use): New fns for tinst_level. (set_refcount_ptr): New template fn. (add_pending_template): Adjust for refcounting, freelists and new accessors. (neglectable_inst_p): Take a NULL d as a non-DECL. (limit_bad_template_recursion): Use new accessors. (push_tinst_level): New overload to create the list. (push_tinst_level_loc): Make it static, split decl into two args, adjust tests and initialization to cope with split lists, use freelist, adjust for refcounting. (push_tinst_level_loc): New wrapper with the old interface. (pop_tinst_level): Adjust for refcounting. (record_last_problematic_instantiation): Likewise. (reopen_tinst_level): Likewise. Use new accessors. (instantiate_alias_template): Adjust for split list. (fn_type_unification): Likewise. (get_partial_spec_bindings): Likewise. (instantiate_pending_templates): Use new accessors. Adjust for refcount. Release pending_template to freelist. (instantiating_current_function_p): Use new accessors. --- gcc/cp/cp-tree.h | 59 ++++++++ gcc/cp/error.c | 10 + gcc/cp/mangle.c | 2 gcc/cp/pt.c | 386 +++++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 379 insertions(+), 78 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d0f87603e98e..e9d9bab879bc 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5870,19 +5870,68 @@ struct GTY((chain_next ("%h.next"))) tinst_level { /* The immediately deeper level in the chain. */ struct tinst_level *next; - /* The original node. Can be either a DECL (for a function or static - data member) or a TYPE (for a class), depending on what we were - asked to instantiate. */ - tree decl; + /* The original node. TLDCL can be a DECL (for a function or static + data member), a TYPE (for a class), depending on what we were + asked to instantiate, or a TREE_LIST with the template as PURPOSE + and the template args as VALUE, if we are substituting for + overload resolution. In all these cases, TARGS is NULL. + However, to avoid creating TREE_LIST objects for substitutions if + we can help, we store PURPOSE and VALUE in TLDCL and TARGS, + respectively. So TLDCL stands for TREE_LIST or DECL (the + template is a DECL too), whereas TARGS stands for the template + arguments. */ + tree tldcl, targs; + + private: + /* Return TRUE iff the original node is a split list. */ + bool split_list_p () const { return targs; } + + /* Return TRUE iff the original node is a TREE_LIST object. */ + bool tree_list_p () const + { + return !split_list_p () && TREE_CODE (tldcl) == TREE_LIST; + } + + /* Return TRUE iff the original node is not a list, split or not. */ + bool not_list_p () const + { + return !split_list_p () && !tree_list_p (); + } + + /* Convert (in place) the original node from a split list to a + TREE_LIST. */ + tree to_list (); + + public: + /* Return TRUE iff the original node is a list, split or not. */ + bool list_p () const { return !not_list_p (); } + + /* Return the original node; if it's a split list, make it a + TREE_LIST first, so that it can be returned as a single tree + object. */ + tree get_node () const { + if (!split_list_p ()) return tldcl; + else return const_cast (this)->to_list (); + } + + /* Return the original node if it's a DECL or a TREE_LIST, but do + NOT convert a split list to a TREE_LIST: return NULL instead. */ + tree get_decl_maybe () const { + if (!split_list_p ()) return tldcl; + else return NULL_TREE; + } /* The location where the template is instantiated. */ location_t locus; /* errorcount+sorrycount when we pushed this level. */ - int errors; + unsigned short errors; /* True if the location is in a system header. */ bool in_system_header_p; + + /* Count references to this object. */ + unsigned char refcount; }; bool decl_spec_seq_has_spec_p (const cp_decl_specifier_seq *, cp_decl_spec); diff --git a/gcc/cp/error.c b/gcc/cp/error.c index f27b700a4349..983ffdfedbb2 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -3457,11 +3457,11 @@ print_instantiation_full_context (diagnostic_context *context) if (p) { pp_verbatim (context->printer, - TREE_CODE (p->decl) == TREE_LIST + p->list_p () ? _("%s: In substitution of %qS:\n") : _("%s: In instantiation of %q#D:\n"), LOCATION_FILE (location), - p->decl); + p->get_node ()); location = p->locus; p = p->next; @@ -3492,18 +3492,18 @@ print_instantiation_partial_context_line (diagnostic_context *context, if (t != NULL) { - if (TREE_CODE (t->decl) == TREE_LIST) + if (t->list_p ()) pp_verbatim (context->printer, recursive_p ? _("recursively required by substitution of %qS\n") : _("required by substitution of %qS\n"), - t->decl); + t->get_node ()); else pp_verbatim (context->printer, recursive_p ? _("recursively required from %q#D\n") : _("required from %q#D\n"), - t->decl); + t->get_node ()); } else { diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index a7f9d686345d..940f7ed87e20 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -3777,7 +3777,7 @@ mangle_decl_string (const tree decl) if (DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (decl)) { struct tinst_level *tl = current_instantiation (); - if ((!tl || tl->decl != decl) + if ((!tl || tl->get_decl_maybe () != decl) && push_tinst_level (decl)) { template_p = true; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index da8a5264d33e..2442f07095ca 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -8731,6 +8731,252 @@ comp_template_args_porder (tree oargs, tree nargs) return comp_template_args (oargs, nargs, NULL, NULL, true); } +/* Implement a freelist interface for objects of type T. + + Head is a separate object, rather than a regular member, so that we + can define it as a GTY deletable pointer, which is highly + desirable. A data member could be declared that way, but then the + containing object would implicitly get GTY((user)), which would + prevent us from instantiating freelists as global objects. + Although this way we can create freelist global objects, they're + such thin wrappers that instantiating temporaries at every use + loses nothing and saves permanent storage for the freelist object. + + Member functions next, anew, poison and reinit have default + implementations that work for most of the types we're interested + in, but if they don't work for some type, they should be explicitly + specialized. See the comments before them for requirements, and + the example specializations for the tree_list_freelist. */ +template +class freelist +{ + /* Return the next object in a chain. We could just do type + punning, but if we access the object with its underlying type, we + avoid strict-aliasing trouble. This needs only work between + poison and reinit. */ + static T *&next (T *obj) { return obj->next; } + + /* Return a newly allocated, uninitialized or minimally-initialized + object of type T. Any initialization performed by anew should + either remain across the life of the object and the execution of + poison, or be redone by reinit. */ + static T *anew () { return ggc_alloc (); } + + /* Optionally scribble all over the bits holding the object, so that + they become (mostly?) uninitialized memory. This is called while + preparing to make the object part of the free list. */ + static void poison (T *obj) { + T *p ATTRIBUTE_UNUSED = obj; + T **q ATTRIBUTE_UNUSED = &next (obj); + +#ifdef ENABLE_GC_CHECKING + /* Poison the data, to indicate the data is garbage. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (p, sizeof (*p))); + memset (p, 0xa5, sizeof (*p)); +#endif + /* Let valgrind know the object is free. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS (p, sizeof (*p))); + + /* Let valgrind know the next portion of the object is available, + but uninitialized. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (q, sizeof (*q))); + } + + /* Bring an object that underwent at least one lifecycle after anew + and before the most recent free and poison, back to a usable + state, reinitializing whatever is needed for it to be + functionally equivalent to an object just allocated and returned + by anew. This may poison or clear the next field, used by + freelist housekeeping after poison was called. */ + static void reinit (T *obj) { + T **q ATTRIBUTE_UNUSED = &next (obj); + +#ifdef ENABLE_GC_CHECKING + memset (q, 0xa5, sizeof (*q)); +#endif + /* Let valgrind know the entire object is available, but + uninitialized. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (obj, sizeof (*obj))); + } + + /* Reference a GTY-deletable pointer that points to the first object + in the free list proper. */ + T *&head; +public: + /* Construct a freelist object chaining objects off of HEAD. */ + freelist (T *&head) : head(head) {} + + /* Add OBJ to the free object list. The former head becomes OBJ's + successor. */ + void free (T *obj) + { + poison (obj); + next (obj) = head; + head = obj; + } + + /* Take an object from the free list, if one is available, or + allocate a new one. Objects taken from the free list should be + regarded as filled with garbage, except for bits that are + configured to be preserved across free and alloc. */ + T *alloc () + { + if (head) + { + T *obj = head; + head = next (head); + reinit (obj); + return obj; + } + else + return anew (); + } +}; + +/* Explicitly specialize the interfaces for freelist: we + want to allocate a TREE_LIST using the usual interface, and ensure + TREE_CHAIN remains functional. Alas, we have to duplicate a bit of + build_tree_list logic in reinit, so this could go out of sync. */ +template <> +inline tree & +freelist::next (tree obj) { + return TREE_CHAIN (obj); +} +template <> +inline tree +freelist::anew () { + return build_tree_list (NULL, NULL); +} +template <> +inline void +freelist::poison (tree obj ATTRIBUTE_UNUSED) { + int size ATTRIBUTE_UNUSED = sizeof (tree_list); + tree p ATTRIBUTE_UNUSED = obj; + tree_base *b ATTRIBUTE_UNUSED = &obj->base; + tree *q ATTRIBUTE_UNUSED = &next (obj); + +#ifdef ENABLE_GC_CHECKING + gcc_checking_assert (TREE_CODE (obj) == TREE_LIST); + + /* Poison the data, to indicate the data is garbage. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (p, size)); + memset (p, 0xa5, size); +#endif + /* Let valgrind know the object is free. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS (p, size)); + /* But we still want to use the TREE_CODE and TREE_CHAIN parts. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (b, sizeof (*b))); + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (q, sizeof (*q))); + +#ifdef ENABLE_GC_CHECKING + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (b, sizeof (*b))); + /* Keep TREE_CHAIN functional. */ + TREE_SET_CODE (obj, TREE_LIST); +#else + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (b, sizeof (*b))); +#endif +} +template <> +inline void +freelist::reinit (tree obj ATTRIBUTE_UNUSED) { + tree_base *b ATTRIBUTE_UNUSED = &obj->base; + +#ifdef ENABLE_GC_CHECKING + gcc_checking_assert (TREE_CODE (obj) == TREE_LIST); + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (obj, sizeof (tree_list))); + memset (obj, 0, sizeof (tree_list)); +#endif + + /* Let valgrind know the entire object is available, but + uninitialized. */ + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (obj, sizeof (tree_list))); + +#ifdef ENABLE_GC_CHECKING + TREE_SET_CODE (obj, TREE_LIST); +#else + VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (b, sizeof (*b))); +#endif +} + +/* Point to the first object in the TREE_LIST freelist. */ +static GTY((deletable)) tree tree_list_freelist_head; +/* Return the/an actual TREE_LIST freelist. */ +static inline freelist +tree_list_freelist () { + return tree_list_freelist_head; +} + +/* Point to the first object in the tinst_level freelist. */ +static GTY((deletable)) tinst_level *tinst_level_freelist_head; +/* Return the/an actual tinst_level freelist. */ +static inline freelist +tinst_level_freelist () { + return tinst_level_freelist_head; +} + +/* Point to the first object in the pending_template freelist. */ +static GTY((deletable)) pending_template *pending_template_freelist_head; +/* Return the/an actual pending_template freelist. */ +static inline freelist +pending_template_freelist () { + return pending_template_freelist_head; +} + +/* Build the TREE_LIST object out of a split list, store it + permanently, and return it. */ +tree +tinst_level::to_list () +{ + gcc_assert (split_list_p ()); + tree ret = tree_list_freelist ().alloc (); + TREE_PURPOSE (ret) = tldcl; + TREE_VALUE (ret) = targs; + tldcl = ret; + targs = NULL; + gcc_assert (tree_list_p ()); + return ret; +} + +/* Increment OBJ's refcount. */ +static tinst_level * +inc_refcount_use (tinst_level *obj) { + if (obj) + { + ++obj->refcount; + gcc_assert (obj->refcount != 0); + } + return obj; +} + +/* Decrement OBJ's refcount. If it reaches zero, release OBJ's DECL + and OBJ, and start over with the tinst_level object that used to be + referenced by OBJ's NEXT. */ +static void +dec_refcount_use (tinst_level *obj) { + while (obj && !--obj->refcount) + { + gcc_assert (obj->refcount+1 != 0); + tinst_level *next = obj->next; + if (obj->list_p () && obj->get_decl_maybe ()) + tree_list_freelist ().free (obj->get_node ()); + tinst_level_freelist ().free (obj); + obj = next; + } +} + +/* Modify PTR so that it points to OBJ, adjusting the refcounts of OBJ + and of the former PTR. Omitting the second argument is equivalent + to passing (T*)NULL; this is allowed because passing the + zero-valued integral constant NULL confuses type deduction and/or + overload resolution. */ +template +static void +set_refcount_ptr (T *& ptr, T *obj = NULL) { + T *save = ptr; + ptr = inc_refcount_use (obj); + dec_refcount_use (save); +} + static void add_pending_template (tree d) { @@ -8746,14 +8992,17 @@ add_pending_template (tree d) /* We are called both from instantiate_decl, where we've already had a tinst_level pushed, and instantiate_template, where we haven't. Compensate. */ - level = !current_tinst_level || current_tinst_level->decl != d; + gcc_assert (TREE_CODE (d) != TREE_LIST); + level = !current_tinst_level + || current_tinst_level->get_decl_maybe () != d; if (level) push_tinst_level (d); - pt = ggc_alloc (); + pt = pending_template_freelist ().alloc (); pt->next = NULL; - pt->tinst = current_tinst_level; + pt->tinst = NULL; + set_refcount_ptr (pt->tinst, current_tinst_level); if (last_pending_template) last_pending_template->next = pt; else @@ -9810,7 +10059,7 @@ uses_outer_template_parms (tree decl) static inline bool neglectable_inst_p (tree d) { - return (DECL_P (d) + return (d && DECL_P (d) && !undeduced_auto_decl (d) && !(TREE_CODE (d) == FUNCTION_DECL ? DECL_DECLARED_CONSTEXPR_P (d) : decl_maybe_constant_var_p (d))); @@ -9828,7 +10077,7 @@ limit_bad_template_recursion (tree decl) return false; for (; lev; lev = lev->next) - if (neglectable_inst_p (lev->decl)) + if (neglectable_inst_p (lev->get_decl_maybe ())) break; return (lev && errs > lev->errors); @@ -9840,20 +10089,11 @@ int depth_reached; static GTY(()) struct tinst_level *last_error_tinst_level; -/* We're starting to instantiate D; record the template instantiation context - for diagnostics and to restore it later. */ - -bool -push_tinst_level (tree d) -{ - return push_tinst_level_loc (d, input_location); -} - /* We're starting to instantiate D; record the template instantiation context at LOC for diagnostics and to restore it later. */ -bool -push_tinst_level_loc (tree d, location_t loc) +static bool +push_tinst_level_loc (tree tldcl, tree targs, location_t loc) { struct tinst_level *new_level; @@ -9871,23 +10111,26 @@ push_tinst_level_loc (tree d, location_t loc) /* If the current instantiation caused problems, don't let it instantiate anything else. Do allow deduction substitution and decls usable in constant expressions. */ - if (limit_bad_template_recursion (d)) + if (!targs && limit_bad_template_recursion (tldcl)) return false; /* When not -quiet, dump template instantiations other than functions, since announce_function will take care of those. */ - if (!quiet_flag - && TREE_CODE (d) != TREE_LIST - && TREE_CODE (d) != FUNCTION_DECL) - fprintf (stderr, " %s", decl_as_string (d, TFF_DECL_SPECIFIERS)); - - new_level = ggc_alloc (); - new_level->decl = d; + if (!quiet_flag && !targs + && TREE_CODE (tldcl) != TREE_LIST + && TREE_CODE (tldcl) != FUNCTION_DECL) + fprintf (stderr, " %s", decl_as_string (tldcl, TFF_DECL_SPECIFIERS)); + + new_level = tinst_level_freelist ().alloc (); + new_level->tldcl = tldcl; + new_level->targs = targs; new_level->locus = loc; new_level->errors = errorcount+sorrycount; new_level->in_system_header_p = in_system_header_at (input_location); - new_level->next = current_tinst_level; - current_tinst_level = new_level; + new_level->next = NULL; + new_level->refcount = 0; + set_refcount_ptr (new_level->next, current_tinst_level); + set_refcount_ptr (current_tinst_level, new_level); ++tinst_depth; if (GATHER_STATISTICS && (tinst_depth > depth_reached)) @@ -9896,6 +10139,34 @@ push_tinst_level_loc (tree d, location_t loc) return true; } +/* We're starting substitution of TMPL; record the template + substitution context for diagnostics and to restore it later. */ + +static bool +push_tinst_level (tree tmpl, tree args) +{ + return push_tinst_level_loc (tmpl, args, input_location); +} + +/* We're starting to instantiate D; record INPUT_LOCATION and the + template instantiation context for diagnostics and to restore it + later. */ + +bool +push_tinst_level (tree d) +{ + return push_tinst_level_loc (d, input_location); +} + +/* Likewise, but record LOC as the program location. */ + +bool +push_tinst_level_loc (tree d, location_t loc) +{ + gcc_assert (TREE_CODE (d) != TREE_LIST); + return push_tinst_level_loc (d, NULL, loc); +} + /* We're done instantiating this template; return to the instantiation context. */ @@ -9905,7 +10176,7 @@ pop_tinst_level (void) /* Restore the filename and line number stashed away when we started this instantiation. */ input_location = current_tinst_level->locus; - current_tinst_level = current_tinst_level->next; + set_refcount_ptr (current_tinst_level, current_tinst_level->next); --tinst_depth; } @@ -9922,11 +10193,11 @@ reopen_tinst_level (struct tinst_level *level) for (t = level; t; t = t->next) ++tinst_depth; - current_tinst_level = level; + set_refcount_ptr (current_tinst_level, level); pop_tinst_level (); if (current_tinst_level) current_tinst_level->errors = errorcount+sorrycount; - return level->decl; + return level->get_decl_maybe (); } /* Returns the TINST_LEVEL which gives the original instantiation @@ -18983,16 +19254,10 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) static tree instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain) { - struct pending_template *old_last_pend = last_pending_template; - struct tinst_level *old_error_tinst = last_error_tinst_level; if (tmpl == error_mark_node || args == error_mark_node) return error_mark_node; - tree tinst = build_tree_list (tmpl, args); - if (!push_tinst_level (tinst)) - { - ggc_free (tinst); - return error_mark_node; - } + if (!push_tinst_level (tmpl, args)) + return error_mark_node; args = coerce_innermost_template_parms (DECL_TEMPLATE_PARMS (tmpl), @@ -19002,11 +19267,6 @@ instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain) tree r = instantiate_template (tmpl, args, complain); pop_tinst_level (); - /* We can't free this if a pending_template entry or last_error_tinst_level - is pointing at it. */ - if (last_pending_template == old_last_pend - && last_error_tinst_level == old_error_tinst) - ggc_free (tinst); return r; } @@ -19096,15 +19356,12 @@ fn_type_unification (tree fn, tsubst_flags_t complain = (explain_p ? tf_warning_or_error : tf_none); bool ok; static int deduction_depth; - struct pending_template *old_last_pend = last_pending_template; - struct tinst_level *old_error_tinst = last_error_tinst_level; tree orig_fn = fn; if (flag_new_inheriting_ctors) fn = strip_inheriting_ctors (fn); tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (fn); - tree tinst; tree r = error_mark_node; tree full_targs = targs; @@ -19130,7 +19387,6 @@ fn_type_unification (tree fn, This is, of course, not reentrant. */ if (excessive_deduction_depth) return error_mark_node; - tinst = build_tree_list (fn, NULL_TREE); ++deduction_depth; gcc_assert (TREE_CODE (fn) == TEMPLATE_DECL); @@ -19223,8 +19479,7 @@ fn_type_unification (tree fn, } } - TREE_VALUE (tinst) = explicit_targs; - if (!push_tinst_level (tinst)) + if (!push_tinst_level (fn, explicit_targs)) { excessive_deduction_depth = true; goto fail; @@ -19279,12 +19534,11 @@ fn_type_unification (tree fn, callers must be ready to deal with unification failures in any event. */ - TREE_VALUE (tinst) = targs; /* If we aren't explaining yet, push tinst context so we can see where any errors (e.g. from class instantiations triggered by instantiation of default template arguments) come from. If we are explaining, this context is redundant. */ - if (!explain_p && !push_tinst_level (tinst)) + if (!explain_p && !push_tinst_level (fn, targs)) { excessive_deduction_depth = true; goto fail; @@ -19340,8 +19594,7 @@ fn_type_unification (tree fn, the corresponding deduced argument values. If the substitution results in an invalid type, as described above, type deduction fails. */ - TREE_VALUE (tinst) = targs; - if (!push_tinst_level (tinst)) + if (!push_tinst_level (fn, targs)) { excessive_deduction_depth = true; goto fail; @@ -19407,12 +19660,6 @@ fn_type_unification (tree fn, excessive_deduction_depth = false; } - /* We can't free this if a pending_template entry or last_error_tinst_level - is pointing at it. */ - if (last_pending_template == old_last_pend - && last_error_tinst_level == old_error_tinst) - ggc_free (tinst); - return r; } @@ -22382,8 +22629,7 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args) return NULL_TREE; } - tree tinst = build_tree_list (spec_tmpl, deduced_args); - if (!push_tinst_level (tinst)) + if (!push_tinst_level (spec_tmpl, deduced_args)) { excessive_deduction_depth = true; return NULL_TREE; @@ -23764,7 +24010,7 @@ instantiate_pending_templates (int retries) to avoid infinite loop. */ if (pending_templates && retries >= max_tinst_depth) { - tree decl = pending_templates->tinst->decl; + tree decl = pending_templates->tinst->get_decl_maybe (); fatal_error (input_location, "template instantiation depth exceeds maximum of %d" @@ -23827,16 +24073,21 @@ instantiate_pending_templates (int retries) } if (complete) - /* If INSTANTIATION has been instantiated, then we don't - need to consider it again in the future. */ - *t = (*t)->next; + { + /* If INSTANTIATION has been instantiated, then we don't + need to consider it again in the future. */ + struct pending_template *drop = *t; + *t = (*t)->next; + set_refcount_ptr (drop->tinst); + pending_template_freelist ().free (drop); + } else { last = *t; t = &(*t)->next; } tinst_depth = 0; - current_tinst_level = NULL; + set_refcount_ptr (current_tinst_level); } last_pending_template = last; } @@ -24084,7 +24335,7 @@ problematic_instantiation_changed (void) void record_last_problematic_instantiation (void) { - last_error_tinst_level = current_tinst_level; + set_refcount_ptr (last_error_tinst_level, current_tinst_level); } struct tinst_level * @@ -24100,7 +24351,8 @@ bool instantiating_current_function_p (void) { return (current_instantiation () - && current_instantiation ()->decl == current_function_decl); + && (current_instantiation ()->get_decl_maybe () + == current_function_decl)); } /* [temp.param] Check that template non-type parm TYPE is of an allowable Just FTR. count tinst_level objs et al --- gcc/cp/cp-tree.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/cp/pt.c | 25 ++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e9d9bab879bc..38de9b600f0d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5865,6 +5865,49 @@ struct cp_declarator { } u; }; + struct consctr { + unsigned ctor, ctorl, dtor, dtorl, maxactive, maxactivel, + lastmark, lastmarkl, longestseq, longestseql, maxcnt, reused; + bool toreport; + void operator++() { + toreport = true; + ctor++; + maxactive = MAX (maxactive, ctor - dtor); + longestseq = MAX (longestseq, ctor - lastmark); + } + void operator--() { toreport = true; dtor++; } + void operator+() { + toreport = true; + ctorl++; + maxactivel = MAX (maxactivel, ctorl - dtorl); + longestseql = MAX (longestseql, ctorl - lastmarkl); + } + void operator-() { + toreport = true; + dtorl++; + } + void operator*() { report(); lastmark = ctor; lastmarkl = ctorl; } + void operator=(unsigned i) { + if (i > maxcnt) + toreport = true; + maxcnt = MAX (maxcnt, i); + } + void operator!() { + toreport = true; + reused++; + } + ~consctr() { report (); } + void report() { + if (toreport) { + printf ("TINST: ctor %u dtor %u max %u end %u long %u LST: ctor %u dtor %u max %u end %u long %u REFS: %u reused: %u\n", + ctor, dtor, maxactive, ctor - dtor, longestseq, + ctorl, dtorl, maxactivel, ctorl - dtorl, longestseql, + maxcnt, reused); + toreport = false; + } + } + }; + /* A level of template instantiation. */ struct GTY((chain_next ("%h.next"))) tinst_level { /* The immediately deeper level in the chain. */ @@ -5932,6 +5975,13 @@ struct GTY((chain_next ("%h.next"))) tinst_level { /* Count references to this object. */ unsigned char refcount; + + static struct consctr consctr; + + static int resetctr () { + *consctr; + return 1; + } }; bool decl_spec_seq_has_spec_p (const cp_decl_specifier_seq *, cp_decl_spec); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 75ed73890c4f..4db3bd49bf3f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -8909,6 +8909,7 @@ tinst_level::to_list () tldcl = ret; targs = NULL; gcc_assert (tree_list_p ()); + +consctr; return ret; } @@ -8917,7 +8918,7 @@ static tinst_level * inc_refcount_use (tinst_level *obj) { if (obj) { - ++obj->refcount; + obj->consctr = ++obj->refcount; gcc_assert (obj->refcount != 0); } return obj; @@ -8930,10 +8931,14 @@ static void dec_refcount_use (tinst_level *obj) { while (obj && !--obj->refcount) { + --obj->consctr; gcc_assert (obj->refcount+1 != 0); tinst_level *next = obj->next; if (obj->list_p () && obj->get_decl_maybe ()) - tree_list_freelist ().free (obj->get_node ()); + { + -obj->consctr; + tree_list_freelist ().free (obj->get_node ()); + } tinst_level_freelist ().free (obj); obj = next; } @@ -10062,7 +10067,14 @@ static int tinst_depth; extern int max_tinst_depth; int depth_reached; -static GTY(()) struct tinst_level *last_error_tinst_level; +static GTY(()) struct tinst_level * last_error_tinst_level; + +struct consctr tinst_level::consctr; +static hash_set used_ptrs; + +struct GTY(()) resetter {}; +static GTY((length ("tinst_level::resetctr ()"))) + struct resetter *tinst_resetter; /* We're starting to instantiate D; record the template instantiation context at LOC for diagnostics and to restore it later. */ @@ -10096,6 +10108,9 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc) && TREE_CODE (tldcl) != FUNCTION_DECL) fprintf (stderr, " %s", decl_as_string (tldcl, TFF_DECL_SPECIFIERS)); + if (!tinst_resetter) + tinst_resetter = ggc_cleared_alloc(); + new_level = tinst_level_freelist ().alloc (); new_level->tldcl = tldcl; new_level->targs = targs; @@ -10104,9 +10119,13 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc) new_level->in_system_header_p = in_system_header_at (input_location); new_level->next = NULL; new_level->refcount = 0; + ++new_level->consctr; set_refcount_ptr (new_level->next, current_tinst_level); set_refcount_ptr (current_tinst_level, new_level); + if (!used_ptrs.add (new_level)) + !tinst_level::consctr; + ++tinst_depth; if (GATHER_STATISTICS && (tinst_depth > depth_reached)) depth_reached = tinst_depth;