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