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);
>  }
>  

Reply via email to