diff mbox

Optimize type streaming

Message ID 20140627063910.GA20056@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka June 27, 2014, 6:39 a.m. UTC
Hi,
the most common types of tree nodes streamed are declarations (5.4M for
Firefox) and types (4.2M for Firefox).  This patch makes representation of
types smaller by avoiding saving redundent info about type and its variants.
About 50% of types are variants and overall this saves about 6% of WPA stream:

        -:  797:static void
  4251251:  798:lto_input_ts_type_common_tree_pointers (struct lto_input_block *ib,
        -:  799:                                        struct data_in *data_in, tree expr)
        -:  800:{
  4251251:  801:  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
        -:  802:
        -:  803:  /* Variants share most the properties with the main variant.  */
  4251251:  804:  if (TYPE_MAIN_VARIANT (expr) == expr)
        -:  805:    {
  2511593:  806:      if (COMPLETE_TYPE_P (expr))
        -:  807:        {
  2419254:  808:          TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
  2419254:  809:          TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
        -:  810:        }
  2511593:  811:      TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
        -:  812:    }
  4251251:  822:  TYPE_NAME (expr) = stream_read_tree (ib, data_in);

The patch also adds sanity checking that types actually match that uncovered
at least one bug in the coverage code.


Bootstrapped/regtested x86_64-linux and tested with Firefox build, I got slightly faster
WPA (94->89s) but it bit close to noise factor. OK?

Honza

	* tree-streamer-out.c (pack_ts_type_common_value_fields): Stream if type
	is complete.
	(write_ts_type_common_tree_pointers): Do not stream fields not set for incomplete
	types; do not stream duplicated fields for variants; sanity check that variant
	and type match.
	(write_ts_type_non_common_tree_pointers): Likewise.
	* tree-streamer-in.c (unpack_ts_type_common_value_fields): Mark in TYPE_SIZE whether
	type is complete.
	(lto_input_ts_type_common_tree_pointers): Do same changes as in
	write_ts_type_common_tree_pointers
	(lto_input_ts_type_non_common_tree_pointers): Likewise.

	* lto.c (lto_fixup_prevailing_type): Copy fields shared in between type
	and main variant.
	(compare_tree_sccs_1): Do not compare fields shared in between type
	and variant.
	(lto_read_decls): Fixup types first before inserting into hash.

Comments

Richard Biener June 27, 2014, 9:06 a.m. UTC | #1
On Fri, 27 Jun 2014, Jan Hubicka wrote:

> Hi,
> the most common types of tree nodes streamed are declarations (5.4M for
> Firefox) and types (4.2M for Firefox).  This patch makes representation of
> types smaller by avoiding saving redundent info about type and its variants.
> About 50% of types are variants and overall this saves about 6% of WPA stream:

This is mostly by avoiding the output of references to already written
items (as they are identical as you verify).

>         -:  797:static void
>   4251251:  798:lto_input_ts_type_common_tree_pointers (struct lto_input_block *ib,
>         -:  799:                                        struct data_in *data_in, tree expr)
>         -:  800:{
>   4251251:  801:  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
>         -:  802:
>         -:  803:  /* Variants share most the properties with the main variant.  */
>   4251251:  804:  if (TYPE_MAIN_VARIANT (expr) == expr)
>         -:  805:    {
>   2511593:  806:      if (COMPLETE_TYPE_P (expr))
>         -:  807:        {
>   2419254:  808:          TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
>   2419254:  809:          TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
>         -:  810:        }
>   2511593:  811:      TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
>         -:  812:    }
>   4251251:  822:  TYPE_NAME (expr) = stream_read_tree (ib, data_in);
> 
> The patch also adds sanity checking that types actually match that uncovered
> at least one bug in the coverage code.
> 
> 
> Bootstrapped/regtested x86_64-linux and tested with Firefox build, I got slightly faster
> WPA (94->89s) but it bit close to noise factor. OK?

Minor comments below, ok with those changes.

> Honza
> 
> 	* tree-streamer-out.c (pack_ts_type_common_value_fields): Stream if type
> 	is complete.
> 	(write_ts_type_common_tree_pointers): Do not stream fields not set for incomplete
> 	types; do not stream duplicated fields for variants; sanity check that variant
> 	and type match.
> 	(write_ts_type_non_common_tree_pointers): Likewise.
> 	* tree-streamer-in.c (unpack_ts_type_common_value_fields): Mark in TYPE_SIZE whether
> 	type is complete.
> 	(lto_input_ts_type_common_tree_pointers): Do same changes as in
> 	write_ts_type_common_tree_pointers
> 	(lto_input_ts_type_non_common_tree_pointers): Likewise.
> 
> 	* lto.c (lto_fixup_prevailing_type): Copy fields shared in between type
> 	and main variant.
> 	(compare_tree_sccs_1): Do not compare fields shared in between type
> 	and variant.
> 	(lto_read_decls): Fixup types first before inserting into hash.
> Index: tree-streamer-out.c
> ===================================================================
> --- tree-streamer-out.c	(revision 212058)
> +++ tree-streamer-out.c	(working copy)
> @@ -313,6 +313,7 @@ pack_ts_type_common_value_fields (struct
>    bp_pack_value (bp, TYPE_RESTRICT (expr), 1);
>    bp_pack_value (bp, TYPE_USER_ALIGN (expr), 1);
>    bp_pack_value (bp, TYPE_READONLY (expr), 1);
> +  bp_pack_value (bp, COMPLETE_TYPE_P (expr), 1);
>    bp_pack_var_len_unsigned (bp, TYPE_PRECISION (expr));
>    bp_pack_var_len_unsigned (bp, TYPE_ALIGN (expr));
>    /* Make sure to preserve the fact whether the frontend would assign
> @@ -698,19 +699,34 @@ static void
>  write_ts_type_common_tree_pointers (struct output_block *ob, tree expr,
>  				    bool ref_p)
>  {
> -  stream_write_tree (ob, TYPE_SIZE (expr), ref_p);
> -  stream_write_tree (ob, TYPE_SIZE_UNIT (expr), ref_p);
> -  stream_write_tree (ob, TYPE_ATTRIBUTES (expr), ref_p);
> -  stream_write_tree (ob, TYPE_NAME (expr), ref_p);
> -  /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
> -     reconstructed during fixup.  */
>    /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
>       during fixup.  */
>    stream_write_tree (ob, TYPE_MAIN_VARIANT (expr), ref_p);
> +  if (TYPE_MAIN_VARIANT (expr) == expr)
> +    {
> +      if (COMPLETE_TYPE_P (expr))
> +	{
> +	  stream_write_tree (ob, TYPE_SIZE (expr), ref_p);
> +	  stream_write_tree (ob, TYPE_SIZE_UNIT (expr), ref_p);
> +	}
> +      stream_write_tree (ob, TYPE_ATTRIBUTES (expr), ref_p);
> +    }
> +  else
> +    {
> +      if (COMPLETE_TYPE_P (expr))
> +	{
> +	  gcc_checking_assert (TYPE_SIZE (expr) == TYPE_SIZE (TYPE_MAIN_VARIANT (expr)));
> +	  gcc_checking_assert (TYPE_SIZE_UNIT (expr) == TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (expr)));
> +	}
> +      gcc_checking_assert (TYPE_ATTRIBUTES (expr) == TYPE_ATTRIBUTES (TYPE_MAIN_VARIANT (expr)));
> +    }
> +  stream_write_tree (ob, TYPE_NAME (expr), ref_p);
>    stream_write_tree (ob, TYPE_CONTEXT (expr), ref_p);
> +  stream_write_tree (ob, TYPE_STUB_DECL (expr), ref_p);
> +  /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
> +     reconstructed during fixup.  */
>    /* TYPE_CANONICAL is re-computed during type merging, so no need
>       to stream it here.  */
> -  stream_write_tree (ob, TYPE_STUB_DECL (expr), ref_p);
>  }
>  
>  /* Write all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
> @@ -721,21 +737,61 @@ static void
>  write_ts_type_non_common_tree_pointers (struct output_block *ob, tree expr,
>  					bool ref_p)
>  {
> -  if (TREE_CODE (expr) == ENUMERAL_TYPE)
> -    stream_write_tree (ob, TYPE_VALUES (expr), ref_p);
> -  else if (TREE_CODE (expr) == ARRAY_TYPE)
> -    stream_write_tree (ob, TYPE_DOMAIN (expr), ref_p);
> -  else if (RECORD_OR_UNION_TYPE_P (expr))
> -    streamer_write_chain (ob, TYPE_FIELDS (expr), ref_p);
> -  else if (TREE_CODE (expr) == FUNCTION_TYPE
> -	   || TREE_CODE (expr) == METHOD_TYPE)
> -    stream_write_tree (ob, TYPE_ARG_TYPES (expr), ref_p);
> -
> -  if (!POINTER_TYPE_P (expr))
> -    stream_write_tree (ob, TYPE_MINVAL (expr), ref_p);
> -  stream_write_tree (ob, TYPE_MAXVAL (expr), ref_p);
> -  if (RECORD_OR_UNION_TYPE_P (expr))
> -    stream_write_tree (ob, TYPE_BINFO (expr), ref_p);
> +  if (TYPE_MAIN_VARIANT (expr) == expr)
> +    {
> +      if (TREE_CODE (expr) == ENUMERAL_TYPE && COMPLETE_TYPE_P (expr))
> +	stream_write_tree (ob, TYPE_VALUES (expr), ref_p);
> +      else if (TREE_CODE (expr) == ARRAY_TYPE)
> +	stream_write_tree (ob, TYPE_DOMAIN (expr), ref_p);
> +      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	streamer_write_chain (ob, TYPE_FIELDS (expr), ref_p);
> +
> +      /* TYPE_MINVAL is used for most types, but unused for FUNCTION and METHOD
> +	 TYPES. */

and for POINTER_TYPE_P, so please merge those case here.

> +      if (TREE_CODE (expr) == FUNCTION_TYPE || TREE_CODE (expr) == METHOD_TYPE)
> +	;
> +      else if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	stream_write_tree (ob, TYPE_MINVAL (expr), ref_p);
> +
> +      /* TYPE_MAXVAL is used as TYPE_METHOD_BASETYPE for methods;
> +	 it is unused for functions.  */
> +      if (TREE_CODE (expr) == FUNCTION_TYPE)
> +	;
> +      else if (TREE_CODE (expr) == METHOD_TYPE)
> +        stream_write_tree (ob, TYPE_METHOD_BASETYPE (expr), ref_p);
> +      else if (COMPLETE_TYPE_P (expr))
> +        stream_write_tree (ob, TYPE_MAXVAL (expr), ref_p);
> +
> +      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	stream_write_tree (ob, TYPE_BINFO (expr), ref_p);
> +    }
> +  else
> +    {
> +      tree mv = TYPE_MAIN_VARIANT (expr);
> +
> +      if (TREE_CODE (expr) == ENUMERAL_TYPE)
> +        gcc_checking_assert (TYPE_VALUES (expr) == TYPE_VALUES (mv));
> +      else if (TREE_CODE (expr) == ARRAY_TYPE)
> +        gcc_checking_assert (TYPE_DOMAIN (expr) == TYPE_DOMAIN (mv));
> +      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +        gcc_checking_assert (TYPE_FIELDS (expr) == TYPE_FIELDS (mv));
> +
> +      if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))

The conditionals don't match here.  In general equality should also
hold for !COMPLETE_TYPE_P, no?

> +        gcc_checking_assert (TYPE_MINVAL (expr) == TYPE_MINVAL (mv));
> +
> +      if (TREE_CODE (expr) == FUNCTION_TYPE)
> +	;
> +      else if (TREE_CODE (expr) == METHOD_TYPE)
> +        gcc_checking_assert (TYPE_METHOD_BASETYPE (expr) == TYPE_METHOD_BASETYPE (mv));
> +      else if (COMPLETE_TYPE_P (expr))
> +        gcc_checking_assert (TYPE_MAXVAL (expr) == TYPE_MAXVAL (mv));
> +
> +      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +        gcc_checking_assert (TYPE_BINFO (expr) == TYPE_BINFO (mv));
> +    }
> +   if (TREE_CODE (expr) == FUNCTION_TYPE
> +       || TREE_CODE (expr) == METHOD_TYPE)
> +     stream_write_tree (ob, TYPE_ARG_TYPES (expr), ref_p);
>  }
>  
>  
> Index: lto/lto.c
> ===================================================================
> --- lto/lto.c	(revision 212058)
> +++ lto/lto.c	(working copy)
> @@ -1085,6 +1085,42 @@ lto_fixup_prevailing_type (tree t)
>        TYPE_NEXT_REF_TO (t) = TYPE_REFERENCE_TO (TREE_TYPE (t));
>        TYPE_REFERENCE_TO (TREE_TYPE (t)) = t;
>      }

Please add a comment here that we don't stream those fields for
non-main-variants.

> +  if (TYPE_MAIN_VARIANT (t) != t)
> +    {
> +      tree mv = TYPE_MAIN_VARIANT (t);
> +
> +      if (COMPLETE_TYPE_P (t))
> +	{
> +	  TYPE_SIZE (t) = TYPE_SIZE (mv);
> +	  TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (mv);
> +	}
> +      TYPE_ATTRIBUTES (t) = TYPE_ATTRIBUTES (mv);
> +
> +      if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_TYPE_NON_COMMON))
> +	{
> +	  if (TREE_CODE (t) == ENUMERAL_TYPE && COMPLETE_TYPE_P (t))
> +	    TYPE_VALUES (t) = TYPE_VALUES (mv);
> +	  else if (TREE_CODE (t) == ARRAY_TYPE)
> +	    TYPE_DOMAIN (t) = TYPE_DOMAIN (mv);
> +	  else if (RECORD_OR_UNION_TYPE_P (t) && COMPLETE_TYPE_P (t))
> +	    TYPE_FIELDS (t) = TYPE_FIELDS (mv);
> +
> +	  if (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE)
> +	    ;
> +	  else if (!POINTER_TYPE_P (t) && COMPLETE_TYPE_P (t))
> +	    TYPE_MINVAL (t) = TYPE_MINVAL (mv);
> +
> +	  if (TREE_CODE (t) == FUNCTION_TYPE)
> +	    ;
> +	  else if (TREE_CODE (t) == METHOD_TYPE)
> +	    TYPE_METHOD_BASETYPE (t) = TYPE_METHOD_BASETYPE (mv);
> +	  else if (COMPLETE_TYPE_P (t))
> +	    TYPE_MAXVAL (t) = TYPE_MAXVAL (mv);
> +
> +	  if (RECORD_OR_UNION_TYPE_P (t) && COMPLETE_TYPE_P (t))
> +	    TYPE_BINFO (t) = TYPE_BINFO (mv);
> +	}
> +    }
>  }
>  
>  
> @@ -1546,15 +1582,21 @@ compare_tree_sccs_1 (tree t1, tree t2, t
>  
>    if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
>      {
> -      compare_tree_edges (TYPE_SIZE (t1), TYPE_SIZE (t2));
> -      compare_tree_edges (TYPE_SIZE_UNIT (t1), TYPE_SIZE_UNIT (t2));
> -      compare_tree_edges (TYPE_ATTRIBUTES (t1), TYPE_ATTRIBUTES (t2));
> -      compare_tree_edges (TYPE_NAME (t1), TYPE_NAME (t2));
> +      if (TYPE_MAIN_VARIANT (t1) == t1)
> +	{
> +	  if (TYPE_MAIN_VARIANT (t2) != t2)
> +	    return false;

This asks for a comment.

> +	  compare_tree_edges (TYPE_SIZE (t1), TYPE_SIZE (t2));
> +	  compare_tree_edges (TYPE_SIZE_UNIT (t1), TYPE_SIZE_UNIT (t2));
> +	  compare_tree_edges (TYPE_ATTRIBUTES (t1), TYPE_ATTRIBUTES (t2));
> +	}
> +      else
> +        compare_tree_edges (TYPE_MAIN_VARIANT (t1), TYPE_MAIN_VARIANT (t2));
>        /* Do not compare TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
>  	 reconstructed during fixup.  */
>        /* Do not compare TYPE_NEXT_VARIANT, we reconstruct the variant lists
>  	 during fixup.  */
> -      compare_tree_edges (TYPE_MAIN_VARIANT (t1), TYPE_MAIN_VARIANT (t2));
> +      compare_tree_edges (TYPE_NAME (t1), TYPE_NAME (t2));
>        /* ???  Global types from different TUs have non-matching
>  	 TRANSLATION_UNIT_DECLs.  Still merge them if they are otherwise
>  	 equal.  */
> @@ -1569,25 +1611,28 @@ compare_tree_sccs_1 (tree t1, tree t2, t
>  
>    if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
>      {
> -      if (code == ENUMERAL_TYPE)
> -	compare_tree_edges (TYPE_VALUES (t1), TYPE_VALUES (t2));
> -      else if (code == ARRAY_TYPE)
> -	compare_tree_edges (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2));
> -      else if (RECORD_OR_UNION_TYPE_P (t1))
> +      if (TYPE_MAIN_VARIANT (t1) == t1)
>  	{
> -	  tree f1, f2;
> -	  for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2);
> -	       f1 || f2;
> -	       f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
> -	    compare_tree_edges (f1, f2);
> -	  compare_tree_edges (TYPE_BINFO (t1), TYPE_BINFO (t2));
> +	  if (code == ENUMERAL_TYPE)
> +	    compare_tree_edges (TYPE_VALUES (t1), TYPE_VALUES (t2));
> +	  else if (code == ARRAY_TYPE)
> +	    compare_tree_edges (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2));
> +	  else if (RECORD_OR_UNION_TYPE_P (t1))
> +	    {
> +	      tree f1, f2;
> +	      for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2);
> +		   f1 || f2;
> +		   f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
> +		compare_tree_edges (f1, f2);
> +	      compare_tree_edges (TYPE_BINFO (t1), TYPE_BINFO (t2));
> +	    }
> +	  if (!POINTER_TYPE_P (t1))
> +	    compare_tree_edges (TYPE_MINVAL (t1), TYPE_MINVAL (t2));
> +	  compare_tree_edges (TYPE_MAXVAL (t1), TYPE_MAXVAL (t2));
>  	}
> -      else if (code == FUNCTION_TYPE
> -	       || code == METHOD_TYPE)
> +      if (code == FUNCTION_TYPE
> +	  || code == METHOD_TYPE)
>  	compare_tree_edges (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2));
> -      if (!POINTER_TYPE_P (t1))
> -	compare_tree_edges (TYPE_MINVAL (t1), TYPE_MINVAL (t2));
> -      compare_tree_edges (TYPE_MAXVAL (t1), TYPE_MAXVAL (t2));
>      }

Bah, context diffs are so much easier to parse ...

>    if (CODE_CONTAINS_STRUCT (code, TS_LIST))
> @@ -1908,6 +1953,11 @@ lto_read_decls (struct lto_file_decl_dat
>  		  num_prevailing_types++;
>  		  lto_fixup_prevailing_type (t);
>  		}
> +	    }

Please add a comment why we can't do this in one iteration.

> +	  for (unsigned i = 0; i < len; ++i)
> +	    {
> +	      tree t = streamer_tree_cache_get_tree (data_in->reader_cache,
> +						     from + i);
>  	      /* Compute the canonical type of all types.
>  		 ???  Should be able to assert that !TYPE_CANONICAL.  */
>  	      if (TYPE_P (t) && !TYPE_CANONICAL (t))
> Index: tree-streamer-in.c
> ===================================================================
> --- tree-streamer-in.c	(revision 212058)
> +++ tree-streamer-in.c	(working copy)
> @@ -357,6 +357,10 @@ unpack_ts_type_common_value_fields (stru
>    TYPE_RESTRICT (expr) = (unsigned) bp_unpack_value (bp, 1);
>    TYPE_USER_ALIGN (expr) = (unsigned) bp_unpack_value (bp, 1);
>    TYPE_READONLY (expr) = (unsigned) bp_unpack_value (bp, 1);

Please add a comment here.

> +  if ((unsigned) bp_unpack_value (bp, 1))
> +    TYPE_SIZE (expr) = error_mark_node;
> +  else
> +    TYPE_SIZE (expr) = NULL;

NULL_TREE

>    TYPE_PRECISION (expr) = bp_unpack_var_len_unsigned (bp);
>    TYPE_ALIGN (expr) = bp_unpack_var_len_unsigned (bp);
>    TYPE_ALIAS_SET (expr) = bp_unpack_var_len_int (bp);
> @@ -794,19 +798,27 @@ static void
>  lto_input_ts_type_common_tree_pointers (struct lto_input_block *ib,
>  					struct data_in *data_in, tree expr)
>  {
> -  TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
> -  TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
> -  TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
> +  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
> +
> +  /* Variants share most the properties with the main variant.  */
> +  if (TYPE_MAIN_VARIANT (expr) == expr)
> +    {
> +      if (COMPLETE_TYPE_P (expr))
> +	{
> +	  TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
> +	  TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
> +	}
> +      TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
> +    }
>    TYPE_NAME (expr) = stream_read_tree (ib, data_in);
> +  TYPE_CONTEXT (expr) = stream_read_tree (ib, data_in);
> +  TYPE_STUB_DECL (expr) = stream_read_tree (ib, data_in);
>    /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
>       reconstructed during fixup.  */
>    /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
>       during fixup.  */
> -  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
> -  TYPE_CONTEXT (expr) = stream_read_tree (ib, data_in);
>    /* TYPE_CANONICAL gets re-computed during type merging.  */
>    TYPE_CANONICAL (expr) = NULL_TREE;
> -  TYPE_STUB_DECL (expr) = stream_read_tree (ib, data_in);
>  }
>  
>  /* Read all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
> @@ -818,21 +830,37 @@ lto_input_ts_type_non_common_tree_pointe
>  					    struct data_in *data_in,
>  					    tree expr)
>  {
> -  if (TREE_CODE (expr) == ENUMERAL_TYPE)
> -    TYPE_VALUES (expr) = stream_read_tree (ib, data_in);
> -  else if (TREE_CODE (expr) == ARRAY_TYPE)
> -    TYPE_DOMAIN (expr) = stream_read_tree (ib, data_in);
> -  else if (RECORD_OR_UNION_TYPE_P (expr))
> -    TYPE_FIELDS (expr) = streamer_read_chain (ib, data_in);
> -  else if (TREE_CODE (expr) == FUNCTION_TYPE
> -	   || TREE_CODE (expr) == METHOD_TYPE)
> -    TYPE_ARG_TYPES (expr) = stream_read_tree (ib, data_in);
> +  if (TYPE_MAIN_VARIANT (expr) == expr)
> +    {
> +      if (TREE_CODE (expr) == ENUMERAL_TYPE && COMPLETE_TYPE_P (expr))
> +	TYPE_VALUES (expr) = stream_read_tree (ib, data_in);
> +      else if (TREE_CODE (expr) == ARRAY_TYPE)
> +	TYPE_DOMAIN (expr) = stream_read_tree (ib, data_in);
> +      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	TYPE_FIELDS (expr) = streamer_read_chain (ib, data_in);
> +
> +      /* TYPE_MINVAL is used for most types, but unused for FUNCTION and METHOD
> +	 TYPES. */
> +      if (TREE_CODE (expr) == FUNCTION_TYPE || TREE_CODE (expr) == METHOD_TYPE)
> +	;
> +      else if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	TYPE_MINVAL (expr) = stream_read_tree (ib, data_in);
> +
> +      /* TYPE_MAXVAL is used as TYPE_METHOD_BASETYPE for methods;
> +	 it is unused for functions.  */
> +      if (TREE_CODE (expr) == FUNCTION_TYPE)
> +	;
> +      else if (TREE_CODE (expr) == METHOD_TYPE)
> +        TYPE_METHOD_BASETYPE (expr) = stream_read_tree (ib, data_in);
> +      else if (COMPLETE_TYPE_P (expr))
> +        TYPE_MAXVAL (expr) = stream_read_tree (ib, data_in);
>  
> -  if (!POINTER_TYPE_P (expr))
> -    TYPE_MINVAL (expr) = stream_read_tree (ib, data_in);
> -  TYPE_MAXVAL (expr) = stream_read_tree (ib, data_in);
> -  if (RECORD_OR_UNION_TYPE_P (expr))
> -    TYPE_BINFO (expr) = stream_read_tree (ib, data_in);
> +      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
> +	TYPE_BINFO (expr) = stream_read_tree (ib, data_in);
> +    }
> +  if (TREE_CODE (expr) == FUNCTION_TYPE
> +      || TREE_CODE (expr) == METHOD_TYPE)
> +    TYPE_ARG_TYPES (expr) = stream_read_tree (ib, data_in);
>  }
>
diff mbox

Patch

Index: tree-streamer-out.c
===================================================================
--- tree-streamer-out.c	(revision 212058)
+++ tree-streamer-out.c	(working copy)
@@ -313,6 +313,7 @@  pack_ts_type_common_value_fields (struct
   bp_pack_value (bp, TYPE_RESTRICT (expr), 1);
   bp_pack_value (bp, TYPE_USER_ALIGN (expr), 1);
   bp_pack_value (bp, TYPE_READONLY (expr), 1);
+  bp_pack_value (bp, COMPLETE_TYPE_P (expr), 1);
   bp_pack_var_len_unsigned (bp, TYPE_PRECISION (expr));
   bp_pack_var_len_unsigned (bp, TYPE_ALIGN (expr));
   /* Make sure to preserve the fact whether the frontend would assign
@@ -698,19 +699,34 @@  static void
 write_ts_type_common_tree_pointers (struct output_block *ob, tree expr,
 				    bool ref_p)
 {
-  stream_write_tree (ob, TYPE_SIZE (expr), ref_p);
-  stream_write_tree (ob, TYPE_SIZE_UNIT (expr), ref_p);
-  stream_write_tree (ob, TYPE_ATTRIBUTES (expr), ref_p);
-  stream_write_tree (ob, TYPE_NAME (expr), ref_p);
-  /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
-     reconstructed during fixup.  */
   /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
      during fixup.  */
   stream_write_tree (ob, TYPE_MAIN_VARIANT (expr), ref_p);
+  if (TYPE_MAIN_VARIANT (expr) == expr)
+    {
+      if (COMPLETE_TYPE_P (expr))
+	{
+	  stream_write_tree (ob, TYPE_SIZE (expr), ref_p);
+	  stream_write_tree (ob, TYPE_SIZE_UNIT (expr), ref_p);
+	}
+      stream_write_tree (ob, TYPE_ATTRIBUTES (expr), ref_p);
+    }
+  else
+    {
+      if (COMPLETE_TYPE_P (expr))
+	{
+	  gcc_checking_assert (TYPE_SIZE (expr) == TYPE_SIZE (TYPE_MAIN_VARIANT (expr)));
+	  gcc_checking_assert (TYPE_SIZE_UNIT (expr) == TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (expr)));
+	}
+      gcc_checking_assert (TYPE_ATTRIBUTES (expr) == TYPE_ATTRIBUTES (TYPE_MAIN_VARIANT (expr)));
+    }
+  stream_write_tree (ob, TYPE_NAME (expr), ref_p);
   stream_write_tree (ob, TYPE_CONTEXT (expr), ref_p);
+  stream_write_tree (ob, TYPE_STUB_DECL (expr), ref_p);
+  /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
+     reconstructed during fixup.  */
   /* TYPE_CANONICAL is re-computed during type merging, so no need
      to stream it here.  */
-  stream_write_tree (ob, TYPE_STUB_DECL (expr), ref_p);
 }
 
 /* Write all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
@@ -721,21 +737,61 @@  static void
 write_ts_type_non_common_tree_pointers (struct output_block *ob, tree expr,
 					bool ref_p)
 {
-  if (TREE_CODE (expr) == ENUMERAL_TYPE)
-    stream_write_tree (ob, TYPE_VALUES (expr), ref_p);
-  else if (TREE_CODE (expr) == ARRAY_TYPE)
-    stream_write_tree (ob, TYPE_DOMAIN (expr), ref_p);
-  else if (RECORD_OR_UNION_TYPE_P (expr))
-    streamer_write_chain (ob, TYPE_FIELDS (expr), ref_p);
-  else if (TREE_CODE (expr) == FUNCTION_TYPE
-	   || TREE_CODE (expr) == METHOD_TYPE)
-    stream_write_tree (ob, TYPE_ARG_TYPES (expr), ref_p);
-
-  if (!POINTER_TYPE_P (expr))
-    stream_write_tree (ob, TYPE_MINVAL (expr), ref_p);
-  stream_write_tree (ob, TYPE_MAXVAL (expr), ref_p);
-  if (RECORD_OR_UNION_TYPE_P (expr))
-    stream_write_tree (ob, TYPE_BINFO (expr), ref_p);
+  if (TYPE_MAIN_VARIANT (expr) == expr)
+    {
+      if (TREE_CODE (expr) == ENUMERAL_TYPE && COMPLETE_TYPE_P (expr))
+	stream_write_tree (ob, TYPE_VALUES (expr), ref_p);
+      else if (TREE_CODE (expr) == ARRAY_TYPE)
+	stream_write_tree (ob, TYPE_DOMAIN (expr), ref_p);
+      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	streamer_write_chain (ob, TYPE_FIELDS (expr), ref_p);
+
+      /* TYPE_MINVAL is used for most types, but unused for FUNCTION and METHOD
+	 TYPES. */
+      if (TREE_CODE (expr) == FUNCTION_TYPE || TREE_CODE (expr) == METHOD_TYPE)
+	;
+      else if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	stream_write_tree (ob, TYPE_MINVAL (expr), ref_p);
+
+      /* TYPE_MAXVAL is used as TYPE_METHOD_BASETYPE for methods;
+	 it is unused for functions.  */
+      if (TREE_CODE (expr) == FUNCTION_TYPE)
+	;
+      else if (TREE_CODE (expr) == METHOD_TYPE)
+        stream_write_tree (ob, TYPE_METHOD_BASETYPE (expr), ref_p);
+      else if (COMPLETE_TYPE_P (expr))
+        stream_write_tree (ob, TYPE_MAXVAL (expr), ref_p);
+
+      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	stream_write_tree (ob, TYPE_BINFO (expr), ref_p);
+    }
+  else
+    {
+      tree mv = TYPE_MAIN_VARIANT (expr);
+
+      if (TREE_CODE (expr) == ENUMERAL_TYPE)
+        gcc_checking_assert (TYPE_VALUES (expr) == TYPE_VALUES (mv));
+      else if (TREE_CODE (expr) == ARRAY_TYPE)
+        gcc_checking_assert (TYPE_DOMAIN (expr) == TYPE_DOMAIN (mv));
+      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+        gcc_checking_assert (TYPE_FIELDS (expr) == TYPE_FIELDS (mv));
+
+      if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+        gcc_checking_assert (TYPE_MINVAL (expr) == TYPE_MINVAL (mv));
+
+      if (TREE_CODE (expr) == FUNCTION_TYPE)
+	;
+      else if (TREE_CODE (expr) == METHOD_TYPE)
+        gcc_checking_assert (TYPE_METHOD_BASETYPE (expr) == TYPE_METHOD_BASETYPE (mv));
+      else if (COMPLETE_TYPE_P (expr))
+        gcc_checking_assert (TYPE_MAXVAL (expr) == TYPE_MAXVAL (mv));
+
+      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+        gcc_checking_assert (TYPE_BINFO (expr) == TYPE_BINFO (mv));
+    }
+   if (TREE_CODE (expr) == FUNCTION_TYPE
+       || TREE_CODE (expr) == METHOD_TYPE)
+     stream_write_tree (ob, TYPE_ARG_TYPES (expr), ref_p);
 }
 
 
Index: lto/lto.c
===================================================================
--- lto/lto.c	(revision 212058)
+++ lto/lto.c	(working copy)
@@ -1085,6 +1085,42 @@  lto_fixup_prevailing_type (tree t)
       TYPE_NEXT_REF_TO (t) = TYPE_REFERENCE_TO (TREE_TYPE (t));
       TYPE_REFERENCE_TO (TREE_TYPE (t)) = t;
     }
+  if (TYPE_MAIN_VARIANT (t) != t)
+    {
+      tree mv = TYPE_MAIN_VARIANT (t);
+
+      if (COMPLETE_TYPE_P (t))
+	{
+	  TYPE_SIZE (t) = TYPE_SIZE (mv);
+	  TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (mv);
+	}
+      TYPE_ATTRIBUTES (t) = TYPE_ATTRIBUTES (mv);
+
+      if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_TYPE_NON_COMMON))
+	{
+	  if (TREE_CODE (t) == ENUMERAL_TYPE && COMPLETE_TYPE_P (t))
+	    TYPE_VALUES (t) = TYPE_VALUES (mv);
+	  else if (TREE_CODE (t) == ARRAY_TYPE)
+	    TYPE_DOMAIN (t) = TYPE_DOMAIN (mv);
+	  else if (RECORD_OR_UNION_TYPE_P (t) && COMPLETE_TYPE_P (t))
+	    TYPE_FIELDS (t) = TYPE_FIELDS (mv);
+
+	  if (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE)
+	    ;
+	  else if (!POINTER_TYPE_P (t) && COMPLETE_TYPE_P (t))
+	    TYPE_MINVAL (t) = TYPE_MINVAL (mv);
+
+	  if (TREE_CODE (t) == FUNCTION_TYPE)
+	    ;
+	  else if (TREE_CODE (t) == METHOD_TYPE)
+	    TYPE_METHOD_BASETYPE (t) = TYPE_METHOD_BASETYPE (mv);
+	  else if (COMPLETE_TYPE_P (t))
+	    TYPE_MAXVAL (t) = TYPE_MAXVAL (mv);
+
+	  if (RECORD_OR_UNION_TYPE_P (t) && COMPLETE_TYPE_P (t))
+	    TYPE_BINFO (t) = TYPE_BINFO (mv);
+	}
+    }
 }
 
 
@@ -1546,15 +1582,21 @@  compare_tree_sccs_1 (tree t1, tree t2, t
 
   if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
     {
-      compare_tree_edges (TYPE_SIZE (t1), TYPE_SIZE (t2));
-      compare_tree_edges (TYPE_SIZE_UNIT (t1), TYPE_SIZE_UNIT (t2));
-      compare_tree_edges (TYPE_ATTRIBUTES (t1), TYPE_ATTRIBUTES (t2));
-      compare_tree_edges (TYPE_NAME (t1), TYPE_NAME (t2));
+      if (TYPE_MAIN_VARIANT (t1) == t1)
+	{
+	  if (TYPE_MAIN_VARIANT (t2) != t2)
+	    return false;
+	  compare_tree_edges (TYPE_SIZE (t1), TYPE_SIZE (t2));
+	  compare_tree_edges (TYPE_SIZE_UNIT (t1), TYPE_SIZE_UNIT (t2));
+	  compare_tree_edges (TYPE_ATTRIBUTES (t1), TYPE_ATTRIBUTES (t2));
+	}
+      else
+        compare_tree_edges (TYPE_MAIN_VARIANT (t1), TYPE_MAIN_VARIANT (t2));
       /* Do not compare TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
 	 reconstructed during fixup.  */
       /* Do not compare TYPE_NEXT_VARIANT, we reconstruct the variant lists
 	 during fixup.  */
-      compare_tree_edges (TYPE_MAIN_VARIANT (t1), TYPE_MAIN_VARIANT (t2));
+      compare_tree_edges (TYPE_NAME (t1), TYPE_NAME (t2));
       /* ???  Global types from different TUs have non-matching
 	 TRANSLATION_UNIT_DECLs.  Still merge them if they are otherwise
 	 equal.  */
@@ -1569,25 +1611,28 @@  compare_tree_sccs_1 (tree t1, tree t2, t
 
   if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
     {
-      if (code == ENUMERAL_TYPE)
-	compare_tree_edges (TYPE_VALUES (t1), TYPE_VALUES (t2));
-      else if (code == ARRAY_TYPE)
-	compare_tree_edges (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2));
-      else if (RECORD_OR_UNION_TYPE_P (t1))
+      if (TYPE_MAIN_VARIANT (t1) == t1)
 	{
-	  tree f1, f2;
-	  for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2);
-	       f1 || f2;
-	       f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
-	    compare_tree_edges (f1, f2);
-	  compare_tree_edges (TYPE_BINFO (t1), TYPE_BINFO (t2));
+	  if (code == ENUMERAL_TYPE)
+	    compare_tree_edges (TYPE_VALUES (t1), TYPE_VALUES (t2));
+	  else if (code == ARRAY_TYPE)
+	    compare_tree_edges (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2));
+	  else if (RECORD_OR_UNION_TYPE_P (t1))
+	    {
+	      tree f1, f2;
+	      for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2);
+		   f1 || f2;
+		   f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
+		compare_tree_edges (f1, f2);
+	      compare_tree_edges (TYPE_BINFO (t1), TYPE_BINFO (t2));
+	    }
+	  if (!POINTER_TYPE_P (t1))
+	    compare_tree_edges (TYPE_MINVAL (t1), TYPE_MINVAL (t2));
+	  compare_tree_edges (TYPE_MAXVAL (t1), TYPE_MAXVAL (t2));
 	}
-      else if (code == FUNCTION_TYPE
-	       || code == METHOD_TYPE)
+      if (code == FUNCTION_TYPE
+	  || code == METHOD_TYPE)
 	compare_tree_edges (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2));
-      if (!POINTER_TYPE_P (t1))
-	compare_tree_edges (TYPE_MINVAL (t1), TYPE_MINVAL (t2));
-      compare_tree_edges (TYPE_MAXVAL (t1), TYPE_MAXVAL (t2));
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_LIST))
@@ -1908,6 +1953,11 @@  lto_read_decls (struct lto_file_decl_dat
 		  num_prevailing_types++;
 		  lto_fixup_prevailing_type (t);
 		}
+	    }
+	  for (unsigned i = 0; i < len; ++i)
+	    {
+	      tree t = streamer_tree_cache_get_tree (data_in->reader_cache,
+						     from + i);
 	      /* Compute the canonical type of all types.
 		 ???  Should be able to assert that !TYPE_CANONICAL.  */
 	      if (TYPE_P (t) && !TYPE_CANONICAL (t))
Index: tree-streamer-in.c
===================================================================
--- tree-streamer-in.c	(revision 212058)
+++ tree-streamer-in.c	(working copy)
@@ -357,6 +357,10 @@  unpack_ts_type_common_value_fields (stru
   TYPE_RESTRICT (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_USER_ALIGN (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_READONLY (expr) = (unsigned) bp_unpack_value (bp, 1);
+  if ((unsigned) bp_unpack_value (bp, 1))
+    TYPE_SIZE (expr) = error_mark_node;
+  else
+    TYPE_SIZE (expr) = NULL;
   TYPE_PRECISION (expr) = bp_unpack_var_len_unsigned (bp);
   TYPE_ALIGN (expr) = bp_unpack_var_len_unsigned (bp);
   TYPE_ALIAS_SET (expr) = bp_unpack_var_len_int (bp);
@@ -794,19 +798,27 @@  static void
 lto_input_ts_type_common_tree_pointers (struct lto_input_block *ib,
 					struct data_in *data_in, tree expr)
 {
-  TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
-  TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
-  TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
+  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
+
+  /* Variants share most the properties with the main variant.  */
+  if (TYPE_MAIN_VARIANT (expr) == expr)
+    {
+      if (COMPLETE_TYPE_P (expr))
+	{
+	  TYPE_SIZE (expr) = stream_read_tree (ib, data_in);
+	  TYPE_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
+	}
+      TYPE_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
+    }
   TYPE_NAME (expr) = stream_read_tree (ib, data_in);
+  TYPE_CONTEXT (expr) = stream_read_tree (ib, data_in);
+  TYPE_STUB_DECL (expr) = stream_read_tree (ib, data_in);
   /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
      reconstructed during fixup.  */
   /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
      during fixup.  */
-  TYPE_MAIN_VARIANT (expr) = stream_read_tree (ib, data_in);
-  TYPE_CONTEXT (expr) = stream_read_tree (ib, data_in);
   /* TYPE_CANONICAL gets re-computed during type merging.  */
   TYPE_CANONICAL (expr) = NULL_TREE;
-  TYPE_STUB_DECL (expr) = stream_read_tree (ib, data_in);
 }
 
 /* Read all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
@@ -818,21 +830,37 @@  lto_input_ts_type_non_common_tree_pointe
 					    struct data_in *data_in,
 					    tree expr)
 {
-  if (TREE_CODE (expr) == ENUMERAL_TYPE)
-    TYPE_VALUES (expr) = stream_read_tree (ib, data_in);
-  else if (TREE_CODE (expr) == ARRAY_TYPE)
-    TYPE_DOMAIN (expr) = stream_read_tree (ib, data_in);
-  else if (RECORD_OR_UNION_TYPE_P (expr))
-    TYPE_FIELDS (expr) = streamer_read_chain (ib, data_in);
-  else if (TREE_CODE (expr) == FUNCTION_TYPE
-	   || TREE_CODE (expr) == METHOD_TYPE)
-    TYPE_ARG_TYPES (expr) = stream_read_tree (ib, data_in);
+  if (TYPE_MAIN_VARIANT (expr) == expr)
+    {
+      if (TREE_CODE (expr) == ENUMERAL_TYPE && COMPLETE_TYPE_P (expr))
+	TYPE_VALUES (expr) = stream_read_tree (ib, data_in);
+      else if (TREE_CODE (expr) == ARRAY_TYPE)
+	TYPE_DOMAIN (expr) = stream_read_tree (ib, data_in);
+      else if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	TYPE_FIELDS (expr) = streamer_read_chain (ib, data_in);
+
+      /* TYPE_MINVAL is used for most types, but unused for FUNCTION and METHOD
+	 TYPES. */
+      if (TREE_CODE (expr) == FUNCTION_TYPE || TREE_CODE (expr) == METHOD_TYPE)
+	;
+      else if (!POINTER_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	TYPE_MINVAL (expr) = stream_read_tree (ib, data_in);
+
+      /* TYPE_MAXVAL is used as TYPE_METHOD_BASETYPE for methods;
+	 it is unused for functions.  */
+      if (TREE_CODE (expr) == FUNCTION_TYPE)
+	;
+      else if (TREE_CODE (expr) == METHOD_TYPE)
+        TYPE_METHOD_BASETYPE (expr) = stream_read_tree (ib, data_in);
+      else if (COMPLETE_TYPE_P (expr))
+        TYPE_MAXVAL (expr) = stream_read_tree (ib, data_in);
 
-  if (!POINTER_TYPE_P (expr))
-    TYPE_MINVAL (expr) = stream_read_tree (ib, data_in);
-  TYPE_MAXVAL (expr) = stream_read_tree (ib, data_in);
-  if (RECORD_OR_UNION_TYPE_P (expr))
-    TYPE_BINFO (expr) = stream_read_tree (ib, data_in);
+      if (RECORD_OR_UNION_TYPE_P (expr) && COMPLETE_TYPE_P (expr))
+	TYPE_BINFO (expr) = stream_read_tree (ib, data_in);
+    }
+  if (TREE_CODE (expr) == FUNCTION_TYPE
+      || TREE_CODE (expr) == METHOD_TYPE)
+    TYPE_ARG_TYPES (expr) = stream_read_tree (ib, data_in);
 }