On Fri, Apr 13, 2018, 5:19 PM Alexandre Oliva <[email protected]> wrote:
> 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%.
>
Great!
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 <tinst_level *>(this)->to_list ();
> + }
>
This looks like a dangerous violation of const correctness, since it does
in fact modify the object.
+ /* 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 <typename T>
> +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<T> (); }
> +
> + /* 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<tree_node>: 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<tree_node>::next (tree obj) {
> + return TREE_CHAIN (obj);
> +}
> +template <>
> +inline tree
> +freelist<tree_node>::anew () {
> + return build_tree_list (NULL, NULL);
> +}
> +template <>
> +inline void
> +freelist<tree_node>::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<tree_node>::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_node>
> +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>
> +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>
> +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 <typename T>
> +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<pending_template> ();
> + 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<tinst_level> ();
> - 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<ARGS>; 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<tinst_level *> 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<resetter>();
> +
> 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;
>
>
> --
> 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
>