On Fri, Apr 13, 2018, 5:19 PM Alexandre Oliva <aol...@redhat.com> 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 >