diff mbox series

free_lang_data fixes (PR lto/89692)

Message ID 20190320230204.GQ7611@tucnak
State New
Headers show
Series free_lang_data fixes (PR lto/89692) | expand

Commit Message

Jakub Jelinek March 20, 2019, 11:02 p.m. UTC
Hi!

As mentioned in the PR, if e.g. build_aligned_type creates some specially
aligned variant of some TYPE_MAIN_VARIANT, that type doesn't appear in the
IL during free_lang_data and later on we call build_aligned_type with the
same arguments as before, we'll get the previously created aligned variant
which has not been free_lang_data processed and during LTO streaming we ICE
because of that.

The following patch prunes unmarked types from the type variant chain, so
that we don't find them later on and instead create new type variants from
the free_lang_data processed types.

Another change is just to be sure, marking TYPE_CANONICAL if it is not
already marked, so that we don't get weird behavior if we'd remove it.
In the usual case TYPE_CANONICAL will be the TYPE_MAIN_VARIANT type we mark
already anyway.

The rest of the changes is to make sure the new types we create during
free_lang_data get marked in the pset, so that we will not try to purge
them.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-03-20  Jan Hubicka  <hubicka@ucw.cz>
	    Jakub Jelinek  <jakub@redhat.com>

	PR lto/89692
	* tree.c (fld_type_variant): Call fld->pset.add.
	(fld_incomplete_type_of): Likewise and don't call add_tree_to_fld_list
	if it returns true.
	(fld_process_array_type): Likewise.
	(free_lang_data_in_type): Purge non-marked types from TYPE_NEXT_VARIANT
	list.
	(find_decls_types_r): Call fld_worklist_push for TYPE_CANONICAL (t).

	* g++.dg/other/pr89692.C: New test.


	Jakub

Comments

Richard Biener March 21, 2019, 8:35 a.m. UTC | #1
On Thu, 21 Mar 2019, Jakub Jelinek wrote:

> Hi!
> 
> As mentioned in the PR, if e.g. build_aligned_type creates some specially
> aligned variant of some TYPE_MAIN_VARIANT, that type doesn't appear in the
> IL during free_lang_data and later on we call build_aligned_type with the
> same arguments as before, we'll get the previously created aligned variant
> which has not been free_lang_data processed and during LTO streaming we ICE
> because of that.
> 
> The following patch prunes unmarked types from the type variant chain, so
> that we don't find them later on and instead create new type variants from
> the free_lang_data processed types.
> 
> Another change is just to be sure, marking TYPE_CANONICAL if it is not
> already marked, so that we don't get weird behavior if we'd remove it.
> In the usual case TYPE_CANONICAL will be the TYPE_MAIN_VARIANT type we mark
> already anyway.
> 
> The rest of the changes is to make sure the new types we create during
> free_lang_data get marked in the pset, so that we will not try to purge
> them.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

LGTM - any reason you only sometimes conditionalize add_tree_to_fld_list
on the fld->pset.add () result?  IMHO consistency should win over
micro-optimizing the case you know the type will not be in the set.

Thanks,
Richard.

> 2019-03-20  Jan Hubicka  <hubicka@ucw.cz>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/89692
> 	* tree.c (fld_type_variant): Call fld->pset.add.
> 	(fld_incomplete_type_of): Likewise and don't call add_tree_to_fld_list
> 	if it returns true.
> 	(fld_process_array_type): Likewise.
> 	(free_lang_data_in_type): Purge non-marked types from TYPE_NEXT_VARIANT
> 	list.
> 	(find_decls_types_r): Call fld_worklist_push for TYPE_CANONICAL (t).
> 
> 	* g++.dg/other/pr89692.C: New test.
> 
> --- gcc/tree.c.jj	2019-03-20 12:24:56.443322228 +0100
> +++ gcc/tree.c	2019-03-20 20:16:12.277091827 +0100
> @@ -5215,6 +5215,7 @@ fld_type_variant (tree first, tree t, st
>    if (inner_type)
>      TREE_TYPE (v) = inner_type;
>    gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
> +  fld->pset.add (v);
>    add_tree_to_fld_list (v, fld);
>    return v;
>  }
> @@ -5253,7 +5254,8 @@ fld_process_array_type (tree t, tree t2,
>        array = build_array_type_1 (t2, TYPE_DOMAIN (t),
>  				  TYPE_TYPELESS_STORAGE (t), false);
>        TYPE_CANONICAL (array) = TYPE_CANONICAL (t);
> -      add_tree_to_fld_list (array, fld);
> +      if (!fld->pset.add (array))
> +	add_tree_to_fld_list (array, fld);
>      }
>    return array;
>  }
> @@ -5298,7 +5300,8 @@ fld_incomplete_type_of (tree t, struct f
>  						TYPE_REF_CAN_ALIAS_ALL (t));
>  	  gcc_assert (TYPE_CANONICAL (t2) != t2
>  		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
> -	  add_tree_to_fld_list (first, fld);
> +	  if (!fld->pset.add (first))
> +	    add_tree_to_fld_list (first, fld);
>  	  return fld_type_variant (first, t, fld);
>  	}
>        return t;
> @@ -5321,6 +5324,7 @@ fld_incomplete_type_of (tree t, struct f
>  	  copy = build_distinct_type_copy (t);
>  
>  	  /* It is possible that type was not seen by free_lang_data yet.  */
> +	  fld->pset.add (copy);
>  	  add_tree_to_fld_list (copy, fld);
>  	  TYPE_SIZE (copy) = NULL;
>  	  TYPE_USER_ALIGN (copy) = 0;
> @@ -5445,6 +5449,18 @@ free_lang_data_in_type (tree type, struc
>  
>    TYPE_NEEDS_CONSTRUCTING (type) = 0;
>  
> +  /* Purge non-marked variants from the variants chain, so that they
> +     don't reappear in the IL after free_lang_data.  */
> +  while (TYPE_NEXT_VARIANT (type)
> +	 && !fld->pset.contains (TYPE_NEXT_VARIANT (type)))
> +    {
> +      tree t = TYPE_NEXT_VARIANT (type);
> +      TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t);
> +      /* Turn the removed types into distinct types.  */
> +      TYPE_MAIN_VARIANT (t) = t;
> +      TYPE_NEXT_VARIANT (t) = NULL_TREE;
> +    }
> +
>    if (TREE_CODE (type) == FUNCTION_TYPE)
>      {
>        TREE_TYPE (type) = fld_simplified_type (TREE_TYPE (type), fld);
> @@ -5464,6 +5480,7 @@ free_lang_data_in_type (tree type, struc
>  			  & ~TYPE_QUAL_CONST
>  			  & ~TYPE_QUAL_VOLATILE;
>  	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
> +	      fld->pset.add (TREE_VALUE (p));
>  	      free_lang_data_in_type (TREE_VALUE (p), fld);
>  	    }
>  	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
> @@ -5886,8 +5903,7 @@ find_decls_types_r (tree *tp, int *ws, v
>  	    ctx = BLOCK_SUPERCONTEXT (ctx);
>  	  fld_worklist_push (ctx, fld);
>  	}
> -      /* Do not walk TYPE_CANONICAL.  We do not stream it and thus do not
> -	 and want not to reach unused types this way.  */
> +      fld_worklist_push (TYPE_CANONICAL (t), fld);
>  
>        if (RECORD_OR_UNION_TYPE_P (t) && TYPE_BINFO (t))
>  	{
> --- gcc/testsuite/g++.dg/other/pr89692.C.jj	2019-03-20 20:20:10.233278697 +0100
> +++ gcc/testsuite/g++.dg/other/pr89692.C	2019-03-20 20:19:53.193551778 +0100
> @@ -0,0 +1,20 @@
> +// PR lto/89692
> +// { dg-do compile }
> +// { dg-require-effective-target lto }
> +// { dg-options "-flto -O2" }
> +
> +struct S {
> +  short int a, b;
> +  unsigned char c : 1;
> +};
> +
> +bool
> +foo (void)
> +{
> +  unsigned char d[sizeof (S)] = { 0 };
> +  S e;
> +
> +  __builtin_memcpy (&e, d, sizeof (d));
> +
> +  return e.c == d[0];
> +}
> 
> 	Jakub
>
Jakub Jelinek March 21, 2019, 8:45 a.m. UTC | #2
On Thu, Mar 21, 2019 at 09:35:07AM +0100, Richard Biener wrote:
> LGTM - any reason you only sometimes conditionalize add_tree_to_fld_list
> on the fld->pset.add () result?  IMHO consistency should win over
> micro-optimizing the case you know the type will not be in the set.

Yeah, it was a optimization for the most common case, when
find_decls_types_r calls add_tree_to_fld_list (t, fld); for DECL_P as well
as TYPE_P, because in that case the fld->pset.add check has been done
already by walk_tree.  If we have hundreds thousands of elts in the
hash_set, it could be more than a microoptimization, but if you think
strongly that it should be all thrown away and add_tree_to_fld_list should
start with if (fld->pset.add (t)) return; then I can do that too.

> > 2019-03-20  Jan Hubicka  <hubicka@ucw.cz>
> > 	    Jakub Jelinek  <jakub@redhat.com>
> > 
> > 	PR lto/89692
> > 	* tree.c (fld_type_variant): Call fld->pset.add.
> > 	(fld_incomplete_type_of): Likewise and don't call add_tree_to_fld_list
> > 	if it returns true.
> > 	(fld_process_array_type): Likewise.
> > 	(free_lang_data_in_type): Purge non-marked types from TYPE_NEXT_VARIANT
> > 	list.
> > 	(find_decls_types_r): Call fld_worklist_push for TYPE_CANONICAL (t).
> > 
> > 	* g++.dg/other/pr89692.C: New test.

	Jakub
Richard Biener March 21, 2019, 9:11 a.m. UTC | #3
On Thu, 21 Mar 2019, Jakub Jelinek wrote:

> On Thu, Mar 21, 2019 at 09:35:07AM +0100, Richard Biener wrote:
> > LGTM - any reason you only sometimes conditionalize add_tree_to_fld_list
> > on the fld->pset.add () result?  IMHO consistency should win over
> > micro-optimizing the case you know the type will not be in the set.
> 
> Yeah, it was a optimization for the most common case, when
> find_decls_types_r calls add_tree_to_fld_list (t, fld); for DECL_P as well
> as TYPE_P, because in that case the fld->pset.add check has been done
> already by walk_tree.  If we have hundreds thousands of elts in the
> hash_set, it could be more than a microoptimization, but if you think
> strongly that it should be all thrown away and add_tree_to_fld_list should
> start with if (fld->pset.add (t)) return; then I can do that too.

Yes, I think that would be better.

Richard.
Jakub Jelinek March 21, 2019, 9:19 a.m. UTC | #4
On Thu, Mar 21, 2019 at 10:11:00AM +0100, Richard Biener wrote:
> On Thu, 21 Mar 2019, Jakub Jelinek wrote:
> 
> > On Thu, Mar 21, 2019 at 09:35:07AM +0100, Richard Biener wrote:
> > > LGTM - any reason you only sometimes conditionalize add_tree_to_fld_list
> > > on the fld->pset.add () result?  IMHO consistency should win over
> > > micro-optimizing the case you know the type will not be in the set.
> > 
> > Yeah, it was a optimization for the most common case, when
> > find_decls_types_r calls add_tree_to_fld_list (t, fld); for DECL_P as well
> > as TYPE_P, because in that case the fld->pset.add check has been done
> > already by walk_tree.  If we have hundreds thousands of elts in the
> > hash_set, it could be more than a microoptimization, but if you think
> > strongly that it should be all thrown away and add_tree_to_fld_list should
> > start with if (fld->pset.add (t)) return; then I can do that too.
> 
> Yes, I think that would be better.

Actually sorry, that is not going to work, because in the two
find_decls_types_r cases it is already set in the pset by walk_tree and thus
fld->pset.add (t) will return true because it is the second addition.
And just doing fld->pset.add (t) and not testing the result is undesirable
too for the other cases; in the patch there was some set of cases where I
was sure that the type isn't already in the pset (e.g. immediately after
build_variant_type_copy or build_distinct_type_copy), but in several other
cases it is unclear if it is already in or not and I think we don't want to
add it again in those cases.

Would you prefer to have say add_tree_to_fld_list that does what it does now
and has just 2 callers in find_decls_types_r and a new
maybe_add_tree_to_fld_list that would do
  if (!fld->pset.add (t))
    add_tree_to_fld_list (t, fld);
and change all the other add_tree_to_fld_list callers?

	Jakub
Richard Biener March 21, 2019, 10:13 a.m. UTC | #5
On Thu, 21 Mar 2019, Jakub Jelinek wrote:

> On Thu, Mar 21, 2019 at 10:11:00AM +0100, Richard Biener wrote:
> > On Thu, 21 Mar 2019, Jakub Jelinek wrote:
> > 
> > > On Thu, Mar 21, 2019 at 09:35:07AM +0100, Richard Biener wrote:
> > > > LGTM - any reason you only sometimes conditionalize add_tree_to_fld_list
> > > > on the fld->pset.add () result?  IMHO consistency should win over
> > > > micro-optimizing the case you know the type will not be in the set.
> > > 
> > > Yeah, it was a optimization for the most common case, when
> > > find_decls_types_r calls add_tree_to_fld_list (t, fld); for DECL_P as well
> > > as TYPE_P, because in that case the fld->pset.add check has been done
> > > already by walk_tree.  If we have hundreds thousands of elts in the
> > > hash_set, it could be more than a microoptimization, but if you think
> > > strongly that it should be all thrown away and add_tree_to_fld_list should
> > > start with if (fld->pset.add (t)) return; then I can do that too.
> > 
> > Yes, I think that would be better.
> 
> Actually sorry, that is not going to work, because in the two
> find_decls_types_r cases it is already set in the pset by walk_tree and thus
> fld->pset.add (t) will return true because it is the second addition.

Oh.  I was mostly looking at the cases you add, find_decls_types_r
is indeed special.

> And just doing fld->pset.add (t) and not testing the result is undesirable
> too for the other cases; in the patch there was some set of cases where I
> was sure that the type isn't already in the pset (e.g. immediately after
> build_variant_type_copy or build_distinct_type_copy), but in several other
> cases it is unclear if it is already in or not and I think we don't want to
> add it again in those cases.

Yeah, I think there's no point to add it again.  Or is there a case
I am missing - Honza?

> Would you prefer to have say add_tree_to_fld_list that does what it does now
> and has just 2 callers in find_decls_types_r and a new
> maybe_add_tree_to_fld_list that would do
>   if (!fld->pset.add (t))
>     add_tree_to_fld_list (t, fld);
> and change all the other add_tree_to_fld_list callers?

Not sure if that's needed, if you leave find_decls_types_r alone then
always doing the check should work, no?

Richard.
Jakub Jelinek March 21, 2019, 10:23 a.m. UTC | #6
On Thu, Mar 21, 2019 at 11:13:41AM +0100, Richard Biener wrote:
> > Would you prefer to have say add_tree_to_fld_list that does what it does now
> > and has just 2 callers in find_decls_types_r and a new
> > maybe_add_tree_to_fld_list that would do
> >   if (!fld->pset.add (t))
> >     add_tree_to_fld_list (t, fld);
> > and change all the other add_tree_to_fld_list callers?
> 
> Not sure if that's needed, if you leave find_decls_types_r alone then
> always doing the check should work, no?

Well, find_decls_types_r uses add_tree_to_fld_list, so the if (fld->pset.add
(t)) return; can't be in that function.  If we want to simplify all the
other callers, so that they don't need to do it manually, then the only
options I see is call some other function in those spots that does this
additional check, or call another function that doesn't do that in the
two spots of find_decls_types_r, or add another argument to the function
say with default argument and test that argument before the fld->pset.add.
E.g. , bool do_add = true and if (do_add && fld->pset.add (t)) return;
and adjust the two calls in find_decls_types_r to call
add_tree_to_fld_list (t, fld, false);
Or keep the patch as is, do the tests before each but the two
add_tree_to_fld_list calls.

Or do you have yet another variant, or what from the above do you prefer?

	Jakub
Richard Biener March 21, 2019, 10:25 a.m. UTC | #7
On Thu, 21 Mar 2019, Jakub Jelinek wrote:

> On Thu, Mar 21, 2019 at 11:13:41AM +0100, Richard Biener wrote:
> > > Would you prefer to have say add_tree_to_fld_list that does what it does now
> > > and has just 2 callers in find_decls_types_r and a new
> > > maybe_add_tree_to_fld_list that would do
> > >   if (!fld->pset.add (t))
> > >     add_tree_to_fld_list (t, fld);
> > > and change all the other add_tree_to_fld_list callers?
> > 
> > Not sure if that's needed, if you leave find_decls_types_r alone then
> > always doing the check should work, no?
> 
> Well, find_decls_types_r uses add_tree_to_fld_list, so the if (fld->pset.add
> (t)) return; can't be in that function.  If we want to simplify all the
> other callers, so that they don't need to do it manually, then the only
> options I see is call some other function in those spots that does this
> additional check, or call another function that doesn't do that in the
> two spots of find_decls_types_r, or add another argument to the function
> say with default argument and test that argument before the fld->pset.add.
> E.g. , bool do_add = true and if (do_add && fld->pset.add (t)) return;
> and adjust the two calls in find_decls_types_r to call
> add_tree_to_fld_list (t, fld, false);
> Or keep the patch as is, do the tests before each but the two
> add_tree_to_fld_list calls.

I'd have kept the patch but made the add_tree_to_fld_list following
the new fld->pset.add (t) calls conditional on the result.

Richard.
Jakub Jelinek March 21, 2019, 10:36 a.m. UTC | #8
On Thu, Mar 21, 2019 at 11:25:38AM +0100, Richard Biener wrote:
> On Thu, 21 Mar 2019, Jakub Jelinek wrote:
> 
> > On Thu, Mar 21, 2019 at 11:13:41AM +0100, Richard Biener wrote:
> > > > Would you prefer to have say add_tree_to_fld_list that does what it does now
> > > > and has just 2 callers in find_decls_types_r and a new
> > > > maybe_add_tree_to_fld_list that would do
> > > >   if (!fld->pset.add (t))
> > > >     add_tree_to_fld_list (t, fld);
> > > > and change all the other add_tree_to_fld_list callers?
> > > 
> > > Not sure if that's needed, if you leave find_decls_types_r alone then
> > > always doing the check should work, no?
> > 
> > Well, find_decls_types_r uses add_tree_to_fld_list, so the if (fld->pset.add
> > (t)) return; can't be in that function.  If we want to simplify all the
> > other callers, so that they don't need to do it manually, then the only
> > options I see is call some other function in those spots that does this
> > additional check, or call another function that doesn't do that in the
> > two spots of find_decls_types_r, or add another argument to the function
> > say with default argument and test that argument before the fld->pset.add.
> > E.g. , bool do_add = true and if (do_add && fld->pset.add (t)) return;
> > and adjust the two calls in find_decls_types_r to call
> > add_tree_to_fld_list (t, fld, false);
> > Or keep the patch as is, do the tests before each but the two
> > add_tree_to_fld_list calls.
> 
> I'd have kept the patch but made the add_tree_to_fld_list following
> the new fld->pset.add (t) calls conditional on the result.

Ok, so incremental diff:
--- gcc/tree.c	2019-03-20 20:16:12.277091827 +0100
+++ gcc/tree.c	2019-03-21 11:33:17.008936452 +0100
@@ -5215,8 +5215,8 @@
   if (inner_type)
     TREE_TYPE (v) = inner_type;
   gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
-  fld->pset.add (v);
-  add_tree_to_fld_list (v, fld);
+  if (!fld->pset.add (v))
+    add_tree_to_fld_list (v, fld);
   return v;
 }
 
@@ -5324,8 +5324,8 @@
 	  copy = build_distinct_type_copy (t);
 
 	  /* It is possible that type was not seen by free_lang_data yet.  */
-	  fld->pset.add (copy);
-	  add_tree_to_fld_list (copy, fld);
+	  if (!fld->pset.add (copy))
+	    add_tree_to_fld_list (copy, fld);
 	  TYPE_SIZE (copy) = NULL;
 	  TYPE_USER_ALIGN (copy) = 0;
 	  TYPE_SIZE_UNIT (copy) = NULL;
@@ -5480,8 +5480,8 @@
 			  & ~TYPE_QUAL_CONST
 			  & ~TYPE_QUAL_VOLATILE;
 	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
-	      fld->pset.add (TREE_VALUE (p));
-	      free_lang_data_in_type (TREE_VALUE (p), fld);
+	      if (!fld->pset.add (TREE_VALUE (p)))
+		free_lang_data_in_type (TREE_VALUE (p), fld);
 	    }
 	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
 	  TREE_PURPOSE (p) = NULL;

and full patch below?  Ok for trunk if it passes bootstrap/regtest?

2019-03-21  Jan Hubicka  <hubicka@ucw.cz>
	    Jakub Jelinek  <jakub@redhat.com>

	PR lto/89692
	* tree.c (fld_type_variant, fld_incomplete_type_of,
	fld_process_array_type): Call fld->pset.add and don't call
	add_tree_to_fld_list if it returns true.
	(free_lang_data_in_type): Likewise.  Purge non-marked types from
	TYPE_NEXT_VARIANT list.
	(find_decls_types_r): Call fld_worklist_push for TYPE_CANONICAL (t).

	* g++.dg/other/pr89692.C: New test.

--- gcc/tree.c.jj	2019-03-20 12:24:56.443322228 +0100
+++ gcc/tree.c	2019-03-21 11:33:17.008936452 +0100
@@ -5215,7 +5215,8 @@ fld_type_variant (tree first, tree t, st
   if (inner_type)
     TREE_TYPE (v) = inner_type;
   gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
-  add_tree_to_fld_list (v, fld);
+  if (!fld->pset.add (v))
+    add_tree_to_fld_list (v, fld);
   return v;
 }
 
@@ -5253,7 +5254,8 @@ fld_process_array_type (tree t, tree t2,
       array = build_array_type_1 (t2, TYPE_DOMAIN (t),
 				  TYPE_TYPELESS_STORAGE (t), false);
       TYPE_CANONICAL (array) = TYPE_CANONICAL (t);
-      add_tree_to_fld_list (array, fld);
+      if (!fld->pset.add (array))
+	add_tree_to_fld_list (array, fld);
     }
   return array;
 }
@@ -5298,7 +5300,8 @@ fld_incomplete_type_of (tree t, struct f
 						TYPE_REF_CAN_ALIAS_ALL (t));
 	  gcc_assert (TYPE_CANONICAL (t2) != t2
 		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
-	  add_tree_to_fld_list (first, fld);
+	  if (!fld->pset.add (first))
+	    add_tree_to_fld_list (first, fld);
 	  return fld_type_variant (first, t, fld);
 	}
       return t;
@@ -5321,7 +5324,8 @@ fld_incomplete_type_of (tree t, struct f
 	  copy = build_distinct_type_copy (t);
 
 	  /* It is possible that type was not seen by free_lang_data yet.  */
-	  add_tree_to_fld_list (copy, fld);
+	  if (!fld->pset.add (copy))
+	    add_tree_to_fld_list (copy, fld);
 	  TYPE_SIZE (copy) = NULL;
 	  TYPE_USER_ALIGN (copy) = 0;
 	  TYPE_SIZE_UNIT (copy) = NULL;
@@ -5445,6 +5449,18 @@ free_lang_data_in_type (tree type, struc
 
   TYPE_NEEDS_CONSTRUCTING (type) = 0;
 
+  /* Purge non-marked variants from the variants chain, so that they
+     don't reappear in the IL after free_lang_data.  */
+  while (TYPE_NEXT_VARIANT (type)
+	 && !fld->pset.contains (TYPE_NEXT_VARIANT (type)))
+    {
+      tree t = TYPE_NEXT_VARIANT (type);
+      TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t);
+      /* Turn the removed types into distinct types.  */
+      TYPE_MAIN_VARIANT (t) = t;
+      TYPE_NEXT_VARIANT (t) = NULL_TREE;
+    }
+
   if (TREE_CODE (type) == FUNCTION_TYPE)
     {
       TREE_TYPE (type) = fld_simplified_type (TREE_TYPE (type), fld);
@@ -5464,7 +5480,8 @@ free_lang_data_in_type (tree type, struc
 			  & ~TYPE_QUAL_CONST
 			  & ~TYPE_QUAL_VOLATILE;
 	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
-	      free_lang_data_in_type (TREE_VALUE (p), fld);
+	      if (!fld->pset.add (TREE_VALUE (p)))
+		free_lang_data_in_type (TREE_VALUE (p), fld);
 	    }
 	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
 	  TREE_PURPOSE (p) = NULL;
@@ -5886,8 +5903,7 @@ find_decls_types_r (tree *tp, int *ws, v
 	    ctx = BLOCK_SUPERCONTEXT (ctx);
 	  fld_worklist_push (ctx, fld);
 	}
-      /* Do not walk TYPE_CANONICAL.  We do not stream it and thus do not
-	 and want not to reach unused types this way.  */
+      fld_worklist_push (TYPE_CANONICAL (t), fld);
 
       if (RECORD_OR_UNION_TYPE_P (t) && TYPE_BINFO (t))
 	{
--- gcc/testsuite/g++.dg/other/pr89692.C.jj	2019-03-20 20:20:10.233278697 +0100
+++ gcc/testsuite/g++.dg/other/pr89692.C	2019-03-20 20:19:53.193551778 +0100
@@ -0,0 +1,20 @@
+// PR lto/89692
+// { dg-do compile }
+// { dg-require-effective-target lto }
+// { dg-options "-flto -O2" }
+
+struct S {
+  short int a, b;
+  unsigned char c : 1;
+};
+
+bool
+foo (void)
+{
+  unsigned char d[sizeof (S)] = { 0 };
+  S e;
+
+  __builtin_memcpy (&e, d, sizeof (d));
+
+  return e.c == d[0];
+}


	Jakub
Richard Biener March 21, 2019, 11:02 a.m. UTC | #9
On Thu, 21 Mar 2019, Jakub Jelinek wrote:

> On Thu, Mar 21, 2019 at 11:25:38AM +0100, Richard Biener wrote:
> > On Thu, 21 Mar 2019, Jakub Jelinek wrote:
> > 
> > > On Thu, Mar 21, 2019 at 11:13:41AM +0100, Richard Biener wrote:
> > > > > Would you prefer to have say add_tree_to_fld_list that does what it does now
> > > > > and has just 2 callers in find_decls_types_r and a new
> > > > > maybe_add_tree_to_fld_list that would do
> > > > >   if (!fld->pset.add (t))
> > > > >     add_tree_to_fld_list (t, fld);
> > > > > and change all the other add_tree_to_fld_list callers?
> > > > 
> > > > Not sure if that's needed, if you leave find_decls_types_r alone then
> > > > always doing the check should work, no?
> > > 
> > > Well, find_decls_types_r uses add_tree_to_fld_list, so the if (fld->pset.add
> > > (t)) return; can't be in that function.  If we want to simplify all the
> > > other callers, so that they don't need to do it manually, then the only
> > > options I see is call some other function in those spots that does this
> > > additional check, or call another function that doesn't do that in the
> > > two spots of find_decls_types_r, or add another argument to the function
> > > say with default argument and test that argument before the fld->pset.add.
> > > E.g. , bool do_add = true and if (do_add && fld->pset.add (t)) return;
> > > and adjust the two calls in find_decls_types_r to call
> > > add_tree_to_fld_list (t, fld, false);
> > > Or keep the patch as is, do the tests before each but the two
> > > add_tree_to_fld_list calls.
> > 
> > I'd have kept the patch but made the add_tree_to_fld_list following
> > the new fld->pset.add (t) calls conditional on the result.
> 
> Ok, so incremental diff:
> --- gcc/tree.c	2019-03-20 20:16:12.277091827 +0100
> +++ gcc/tree.c	2019-03-21 11:33:17.008936452 +0100
> @@ -5215,8 +5215,8 @@
>    if (inner_type)
>      TREE_TYPE (v) = inner_type;
>    gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
> -  fld->pset.add (v);
> -  add_tree_to_fld_list (v, fld);
> +  if (!fld->pset.add (v))
> +    add_tree_to_fld_list (v, fld);
>    return v;
>  }
>  
> @@ -5324,8 +5324,8 @@
>  	  copy = build_distinct_type_copy (t);
>  
>  	  /* It is possible that type was not seen by free_lang_data yet.  */
> -	  fld->pset.add (copy);
> -	  add_tree_to_fld_list (copy, fld);
> +	  if (!fld->pset.add (copy))
> +	    add_tree_to_fld_list (copy, fld);
>  	  TYPE_SIZE (copy) = NULL;
>  	  TYPE_USER_ALIGN (copy) = 0;
>  	  TYPE_SIZE_UNIT (copy) = NULL;
> @@ -5480,8 +5480,8 @@
>  			  & ~TYPE_QUAL_CONST
>  			  & ~TYPE_QUAL_VOLATILE;
>  	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
> -	      fld->pset.add (TREE_VALUE (p));
> -	      free_lang_data_in_type (TREE_VALUE (p), fld);
> +	      if (!fld->pset.add (TREE_VALUE (p)))
> +		free_lang_data_in_type (TREE_VALUE (p), fld);
>  	    }
>  	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
>  	  TREE_PURPOSE (p) = NULL;
> 
> and full patch below?  Ok for trunk if it passes bootstrap/regtest?

OK.

Thanks,
Richard.

> 2019-03-21  Jan Hubicka  <hubicka@ucw.cz>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/89692
> 	* tree.c (fld_type_variant, fld_incomplete_type_of,
> 	fld_process_array_type): Call fld->pset.add and don't call
> 	add_tree_to_fld_list if it returns true.
> 	(free_lang_data_in_type): Likewise.  Purge non-marked types from
> 	TYPE_NEXT_VARIANT list.
> 	(find_decls_types_r): Call fld_worklist_push for TYPE_CANONICAL (t).
> 
> 	* g++.dg/other/pr89692.C: New test.
> 
> --- gcc/tree.c.jj	2019-03-20 12:24:56.443322228 +0100
> +++ gcc/tree.c	2019-03-21 11:33:17.008936452 +0100
> @@ -5215,7 +5215,8 @@ fld_type_variant (tree first, tree t, st
>    if (inner_type)
>      TREE_TYPE (v) = inner_type;
>    gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
> -  add_tree_to_fld_list (v, fld);
> +  if (!fld->pset.add (v))
> +    add_tree_to_fld_list (v, fld);
>    return v;
>  }
>  
> @@ -5253,7 +5254,8 @@ fld_process_array_type (tree t, tree t2,
>        array = build_array_type_1 (t2, TYPE_DOMAIN (t),
>  				  TYPE_TYPELESS_STORAGE (t), false);
>        TYPE_CANONICAL (array) = TYPE_CANONICAL (t);
> -      add_tree_to_fld_list (array, fld);
> +      if (!fld->pset.add (array))
> +	add_tree_to_fld_list (array, fld);
>      }
>    return array;
>  }
> @@ -5298,7 +5300,8 @@ fld_incomplete_type_of (tree t, struct f
>  						TYPE_REF_CAN_ALIAS_ALL (t));
>  	  gcc_assert (TYPE_CANONICAL (t2) != t2
>  		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
> -	  add_tree_to_fld_list (first, fld);
> +	  if (!fld->pset.add (first))
> +	    add_tree_to_fld_list (first, fld);
>  	  return fld_type_variant (first, t, fld);
>  	}
>        return t;
> @@ -5321,7 +5324,8 @@ fld_incomplete_type_of (tree t, struct f
>  	  copy = build_distinct_type_copy (t);
>  
>  	  /* It is possible that type was not seen by free_lang_data yet.  */
> -	  add_tree_to_fld_list (copy, fld);
> +	  if (!fld->pset.add (copy))
> +	    add_tree_to_fld_list (copy, fld);
>  	  TYPE_SIZE (copy) = NULL;
>  	  TYPE_USER_ALIGN (copy) = 0;
>  	  TYPE_SIZE_UNIT (copy) = NULL;
> @@ -5445,6 +5449,18 @@ free_lang_data_in_type (tree type, struc
>  
>    TYPE_NEEDS_CONSTRUCTING (type) = 0;
>  
> +  /* Purge non-marked variants from the variants chain, so that they
> +     don't reappear in the IL after free_lang_data.  */
> +  while (TYPE_NEXT_VARIANT (type)
> +	 && !fld->pset.contains (TYPE_NEXT_VARIANT (type)))
> +    {
> +      tree t = TYPE_NEXT_VARIANT (type);
> +      TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t);
> +      /* Turn the removed types into distinct types.  */
> +      TYPE_MAIN_VARIANT (t) = t;
> +      TYPE_NEXT_VARIANT (t) = NULL_TREE;
> +    }
> +
>    if (TREE_CODE (type) == FUNCTION_TYPE)
>      {
>        TREE_TYPE (type) = fld_simplified_type (TREE_TYPE (type), fld);
> @@ -5464,7 +5480,8 @@ free_lang_data_in_type (tree type, struc
>  			  & ~TYPE_QUAL_CONST
>  			  & ~TYPE_QUAL_VOLATILE;
>  	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
> -	      free_lang_data_in_type (TREE_VALUE (p), fld);
> +	      if (!fld->pset.add (TREE_VALUE (p)))
> +		free_lang_data_in_type (TREE_VALUE (p), fld);
>  	    }
>  	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
>  	  TREE_PURPOSE (p) = NULL;
> @@ -5886,8 +5903,7 @@ find_decls_types_r (tree *tp, int *ws, v
>  	    ctx = BLOCK_SUPERCONTEXT (ctx);
>  	  fld_worklist_push (ctx, fld);
>  	}
> -      /* Do not walk TYPE_CANONICAL.  We do not stream it and thus do not
> -	 and want not to reach unused types this way.  */
> +      fld_worklist_push (TYPE_CANONICAL (t), fld);
>  
>        if (RECORD_OR_UNION_TYPE_P (t) && TYPE_BINFO (t))
>  	{
> --- gcc/testsuite/g++.dg/other/pr89692.C.jj	2019-03-20 20:20:10.233278697 +0100
> +++ gcc/testsuite/g++.dg/other/pr89692.C	2019-03-20 20:19:53.193551778 +0100
> @@ -0,0 +1,20 @@
> +// PR lto/89692
> +// { dg-do compile }
> +// { dg-require-effective-target lto }
> +// { dg-options "-flto -O2" }
> +
> +struct S {
> +  short int a, b;
> +  unsigned char c : 1;
> +};
> +
> +bool
> +foo (void)
> +{
> +  unsigned char d[sizeof (S)] = { 0 };
> +  S e;
> +
> +  __builtin_memcpy (&e, d, sizeof (d));
> +
> +  return e.c == d[0];
> +}
> 
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/tree.c.jj	2019-03-20 12:24:56.443322228 +0100
+++ gcc/tree.c	2019-03-20 20:16:12.277091827 +0100
@@ -5215,6 +5215,7 @@  fld_type_variant (tree first, tree t, st
   if (inner_type)
     TREE_TYPE (v) = inner_type;
   gcc_checking_assert (fld_type_variant_equal_p (t,v, inner_type));
+  fld->pset.add (v);
   add_tree_to_fld_list (v, fld);
   return v;
 }
@@ -5253,7 +5254,8 @@  fld_process_array_type (tree t, tree t2,
       array = build_array_type_1 (t2, TYPE_DOMAIN (t),
 				  TYPE_TYPELESS_STORAGE (t), false);
       TYPE_CANONICAL (array) = TYPE_CANONICAL (t);
-      add_tree_to_fld_list (array, fld);
+      if (!fld->pset.add (array))
+	add_tree_to_fld_list (array, fld);
     }
   return array;
 }
@@ -5298,7 +5300,8 @@  fld_incomplete_type_of (tree t, struct f
 						TYPE_REF_CAN_ALIAS_ALL (t));
 	  gcc_assert (TYPE_CANONICAL (t2) != t2
 		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
-	  add_tree_to_fld_list (first, fld);
+	  if (!fld->pset.add (first))
+	    add_tree_to_fld_list (first, fld);
 	  return fld_type_variant (first, t, fld);
 	}
       return t;
@@ -5321,6 +5324,7 @@  fld_incomplete_type_of (tree t, struct f
 	  copy = build_distinct_type_copy (t);
 
 	  /* It is possible that type was not seen by free_lang_data yet.  */
+	  fld->pset.add (copy);
 	  add_tree_to_fld_list (copy, fld);
 	  TYPE_SIZE (copy) = NULL;
 	  TYPE_USER_ALIGN (copy) = 0;
@@ -5445,6 +5449,18 @@  free_lang_data_in_type (tree type, struc
 
   TYPE_NEEDS_CONSTRUCTING (type) = 0;
 
+  /* Purge non-marked variants from the variants chain, so that they
+     don't reappear in the IL after free_lang_data.  */
+  while (TYPE_NEXT_VARIANT (type)
+	 && !fld->pset.contains (TYPE_NEXT_VARIANT (type)))
+    {
+      tree t = TYPE_NEXT_VARIANT (type);
+      TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t);
+      /* Turn the removed types into distinct types.  */
+      TYPE_MAIN_VARIANT (t) = t;
+      TYPE_NEXT_VARIANT (t) = NULL_TREE;
+    }
+
   if (TREE_CODE (type) == FUNCTION_TYPE)
     {
       TREE_TYPE (type) = fld_simplified_type (TREE_TYPE (type), fld);
@@ -5464,6 +5480,7 @@  free_lang_data_in_type (tree type, struc
 			  & ~TYPE_QUAL_CONST
 			  & ~TYPE_QUAL_VOLATILE;
 	      TREE_VALUE (p) = build_qualified_type (arg_type, quals);
+	      fld->pset.add (TREE_VALUE (p));
 	      free_lang_data_in_type (TREE_VALUE (p), fld);
 	    }
 	  /* C++ FE uses TREE_PURPOSE to store initial values.  */
@@ -5886,8 +5903,7 @@  find_decls_types_r (tree *tp, int *ws, v
 	    ctx = BLOCK_SUPERCONTEXT (ctx);
 	  fld_worklist_push (ctx, fld);
 	}
-      /* Do not walk TYPE_CANONICAL.  We do not stream it and thus do not
-	 and want not to reach unused types this way.  */
+      fld_worklist_push (TYPE_CANONICAL (t), fld);
 
       if (RECORD_OR_UNION_TYPE_P (t) && TYPE_BINFO (t))
 	{
--- gcc/testsuite/g++.dg/other/pr89692.C.jj	2019-03-20 20:20:10.233278697 +0100
+++ gcc/testsuite/g++.dg/other/pr89692.C	2019-03-20 20:19:53.193551778 +0100
@@ -0,0 +1,20 @@ 
+// PR lto/89692
+// { dg-do compile }
+// { dg-require-effective-target lto }
+// { dg-options "-flto -O2" }
+
+struct S {
+  short int a, b;
+  unsigned char c : 1;
+};
+
+bool
+foo (void)
+{
+  unsigned char d[sizeof (S)] = { 0 };
+  S e;
+
+  __builtin_memcpy (&e, d, sizeof (d));
+
+  return e.c == d[0];
+}