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