On Mon, 6 May 2024, Jason Merrill wrote:

> On 5/3/24 07:17, Nathaniel Shead wrote:
> > On Thu, May 02, 2024 at 02:05:38PM -0400, Jason Merrill wrote:
> > > On 5/1/24 21:34, Nathaniel Shead wrote:
> > > > On Thu, May 02, 2024 at 12:15:44AM +1000, Nathaniel Shead wrote:
> > > > > On Wed, May 01, 2024 at 09:57:38AM -0400, Patrick Palka wrote:
> > > > > > 
> > > > > > On Wed, 1 May 2024, Nathaniel Shead wrote:
> > > > > > 
> > > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk
> > > > > > > (and
> > > > > > > later 14.2)?  I don't think making it a GTY root is necessary but
> > > > > > > I felt
> > > > > > > perhaps better to be safe than sorry.
> > > > > > > 
> > > > > > > Potentially another approach would be to use DECL_UID instead like
> > > > > > > how
> > > > > > > entity_map does; would that be preferable?
> > > > > > > 
> > > > > > > -- >8 --
> > > > > > > 
> > > > > > > I got notified by Linaro CI and by checking testresults that there
> > > > > > > seems
> > > > > > > to be some occasional failures in tpl-friend-4_b.C on some
> > > > > > > architectures
> > > > > > > and standards modes since r15-59-gb5f6a56940e708.  I haven't been
> > > > > > > able
> > > > > > > to reproduce but looking at the backtrace I suspect the issue is
> > > > > > > that
> > > > > > > we're adding to the 'imported_temploid_friend' map a decl that is
> > > > > > > ultimately discarded, which then has its address reused by a later
> > > > > > > decl
> > > > > > > causing a failure in the assert in 'set_originating_module'.
> > > > > > > 
> > > > > > > This patch attempts to fix the issue in two ways: by ensuring that
> > > > > > > we
> > > > > > > only store the decl if we know it's a new decl (and hence won't be
> > > > > > > discarded), and by making the imported_temploid_friends map a GTY
> > > > > > > root
> > > > > > > so that even if the decl does get discarded later the address
> > > > > > > isn't
> > > > > > > reused.
> > > > > > > 
> > > > > > > gcc/cp/ChangeLog:
> > > > > > > 
> > > > > > >   * module.cc (imported_temploid_friends): Mark GTY, and...
> > > > > > >   (init_modules): ...allocate from GGC.
> > > > > > >   (trees_in::decl_value): Only write to
> > > > > > > imported_temploid_friends
> > > > > > >   for new decls.
> > > > > > > 
> > > > > > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > > > > > > ---
> > > > > > >    gcc/cp/module.cc | 7 ++++---
> > > > > > >    1 file changed, 4 insertions(+), 3 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > > > > > > index 5b8ff5bc483..37d38bb9654 100644
> > > > > > > --- a/gcc/cp/module.cc
> > > > > > > +++ b/gcc/cp/module.cc
> > > > > > > @@ -2731,7 +2731,7 @@ static keyed_map_t *keyed_table;
> > > > > > >       need to be attached to the same module as the temploid.
> > > > > > > This maps
> > > > > > >       these decls to the temploid they are instantiated them, as
> > > > > > > there is
> > > > > > >       no other easy way to get this information.  */
> > > > > > > -static hash_map<tree, tree> *imported_temploid_friends;
> > > > > > > +static GTY(()) hash_map<tree, tree> *imported_temploid_friends;
> > > > > > >    
> > > > > > > /********************************************************************/
> > > > > > >    /* Tree streaming.   The tree streaming is very specific to the
> > > > > > > tree
> > > > > > > @@ -8327,7 +8327,8 @@ trees_in::decl_value ()
> > > > > > >      if (TREE_CODE (inner) == FUNCTION_DECL
> > > > > > >          || TREE_CODE (inner) == TYPE_DECL)
> > > > > > >        if (tree owner = tree_node ())
> > > > > > > -      imported_temploid_friends->put (decl, owner);
> > > > > > > +      if (is_new)
> > > > > > > + imported_temploid_friends->put (decl, owner);
> > > > > > 
> > > > > > Hmm, I'm not seeing this code path getting reached for
> > > > > > tpl-friend-4_b.C.
> > > > > > It seems we're instead adding to imported_temploid_friends from
> > > > > > propagate_defining_module, during tsubst_friend_function.
> > > > > > 
> > > > > > What seems to be happening is that we we first
> > > > > > tsubst_friend_function
> > > > > > 'foo' from TPL<int>, and then we tsubst_friend_function 'foo' from
> > > > > > DEF<int>,
> > > > > > which ends up calling duplicate_decls, which ggc_frees this 'foo'
> > > > > > redeclaration that is still present in the imported_temploid_friends
> > > > > > map.
> > > > > > 
> > > > > > So I don't think marking imported_temploid_friends as a GC root
> > > > > > would
> > > > > > help with this situation.  If we want to keep
> > > > > > imported_temploid_friends
> > > > > > as a tree -> tree map, I think we just need to ensure that a decl
> > > > > > is removed from the map upon getting ggc_free'd from e.g.
> > > > > > duplicate_decls.
> > > 
> > > Could we instead move the call to propagate_defining_module down a few
> > > lines, after the pushdecl?
> > 
> > Doing that for tsubst_friend_class seems to work OK with my current test
> > cases, but for tsubst_friend_function doing so causes ICEs in
> > 'module_may_redeclare' within duplicate_decls because the function is
> > already marked as attached but the originating module information hasn't
> > been setup yet.
> 
> It's unfortunate that we need to add a hash table entry in order to make it
> through duplicate_decls, at which point it becomes dead.  Ah, well.
> 
> > I suppose with tsubst_friend_class it works though because we can't ever
> > take the pushdecl branch if an existing type exists that we would call
> > duplicate_decls on.
> > 
> > > > > > But it seems simpler to use DECL_UID as the key instead, since those
> > > > > > never get reused even after the decl gets ggc_free'd IIUC.
> > > 
> > > It still means garbage entries in the hash_map, which is undesirable even
> > > if
> > > it doesn't cause the same kind of breakage.
> > > 
> > > Incidentally, decl_tree_map is preferable to hash_map<tree,tree> when the
> > > key is always a decl.
> > 
> > Ah thanks, didn't know about decl_tree_map.  I feel that I prefer using
> > DECL_UIDs explicitly here though; it's also consistent with the existing
> > usage in entity_map_t, and it looks like decl_tree_map is still perhaps
> > vulnerable to the original issue here (since DECL_UID is only used for
> > hashing and not for equality, it looks like?).
> > 
> > Though that said, 'decl_constraints' in constraints.cc seems to be using
> > it fine (well, with a GTY marking) by using 'remove_constraints' within
> > duplicate_decls to clear this:  Here's a version of the patch that
> > implements in the same way, is something like this preferable?
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu.
> > 
> > -- >8 --
> > 
> > I got notified by Linaro CI and by checking testresults that there seems
> > to be some occasional failures in tpl-friend-4_b.C on some architectures
> > and standards modes since r15-59-gb5f6a56940e708.  I haven't been able
> > to reproduce but looking at the backtrace I suspect the issue is that
> > we're adding to the 'imported_temploid_friend' map a decl that is
> > ultimately discarded, which then has its address reused by a later decl
> > causing a failure in the assert in 'set_originating_module'.
> > 
> > This patch fixes the problem by ensuring 'imported_temploid_friends' is
> > correctly marked as a GTY root, and that 'duplicate_decls' properly
> > removes entries from the map for declarations that it frees.
> 
> Hmm, I think only one or the other of those is necessary: if duplicate_decls
> removes entries, there won't be garbage in the map. Alternatively, if the map
> is marked GTY((cache)), garbage entries will be collected.

If the map is only marked GTY((cache)), isn't there a chance that the
ggc_free'd memory corresponding to a garbage entry gets reused, perhaps
for another decl, before the entry got collected?  And then the garbage
entry would appear live, and incorrectly refer to an unrelated decl.

> 
> The same should be true of decl_constraints.
> 
> The other reason to mark something GTY is so that it's properly dumped to PCH,
> but using PCH with modules would be weird.
> 
> Does using only one or the other affect compilation time measurably?

IIRC in practice with a release compiler the GC doesn't seem to run at
all unless the heap grows to like over 1GB, at least on my machine, so
adding a GC root should be negligible in practice.  The GC runs much
more often with a checking compiler a new GC root might be measurable
there.  But I'd expect the size of imported_temploid_friends to be
pretty small?

> 
> Jason
> 
> 

Reply via email to