On Wed, Jun 1, 2011 at 5:07 PM, Michael Matz <m...@suse.de> wrote: > Hi, > > On Wed, 1 Jun 2011, Michael Matz wrote: > >> Hi, >> >> so here's something more of my patch queue. It adds the facility to merge >> also other trees than types over compilation unit borders. This specific >> patch only deals with STRING_CST and INTEGER_CST nodes. Originally I used >> that place for merging declarations, hence the naming of some variables. >> That needs to wait for adjustments of the cgraph/varpool streamer. >> >> On building cc1 the overall numbers of trees that stay around after all >> global sections are streamed in goes down from 620818 to 598843 trees, >> overall saving 1 MB (out of 76 MB for just the trees). Not much, but >> hey... >> >> Regstrapping on x86_64-linux in progress. Okay for trunk?
Ok. Thanks, Richard. > Ahem. > > * lto.c (top_decls): New hash table. > (simple_tree_hash, simple_tree_eq, uniquify_top): New. > (LTO_FIXUP_TREE): Call uniquify_top. > (uniquify_nodes): Ditto. > (read_cgraph_and_symbols): Allocate and destroy top_decls. > > Index: lto/lto.c > =================================================================== > *** lto/lto.c (revision 174524) > --- lto/lto.c (working copy) > *************** lto_read_in_decl_state (struct data_in * > *** 231,236 **** > --- 231,316 ---- > that must be replaced with their prevailing variant. */ > static GTY((if_marked ("ggc_marked_p"), param_is (union tree_node))) htab_t > tree_with_vars; > + /* A hashtable of top-level trees that can potentially be merged with trees > + from other compilation units. */ > + static GTY((if_marked ("ggc_marked_p"), param_is (union tree_node))) htab_t > + top_decls; > + > + /* Given a tree in ITEM, return a hash value designed to easily recognize > + equal trees. */ > + static hashval_t > + simple_tree_hash (const void *item) > + { > + tree t = CONST_CAST_TREE ((const_tree) item); > + enum tree_code code = TREE_CODE (t); > + hashval_t hashcode = 0; > + hashcode = iterative_hash_object (code, hashcode); > + if (TREE_CODE (t) == STRING_CST) > + { > + if (TREE_TYPE (t)) > + hashcode = iterative_hash_object (TYPE_HASH (TREE_TYPE (t)), > hashcode); > + hashcode = iterative_hash_hashval_t ((hashval_t)TREE_STRING_LENGTH > (t), > + hashcode); > + hashcode = iterative_hash (TREE_STRING_POINTER (t), > + TREE_STRING_LENGTH (t), hashcode); > + return hashcode; > + } > + gcc_unreachable (); > + } > + > + /* Given two trees in VA and VB return true if both trees can be considered > + equal for easy merging across different compilation units. */ > + static int > + simple_tree_eq (const void *va, const void *vb) > + { > + tree a = CONST_CAST_TREE ((const_tree) va); > + tree b = CONST_CAST_TREE ((const_tree) vb); > + if (a == b) > + return 1; > + if (TREE_CODE (a) != TREE_CODE (b)) > + return 0; > + if (TREE_CODE (a) == STRING_CST) > + { > + return TREE_TYPE (a) == TREE_TYPE (b) > + && TREE_STRING_LENGTH (a) == TREE_STRING_LENGTH (b) > + && !memcmp (TREE_STRING_POINTER (a), TREE_STRING_POINTER (b), > + TREE_STRING_LENGTH (a)); > + } > + gcc_unreachable (); > + } > + > + /* Given a tree T return its canonical variant, considering merging > + of equal trees across different compilation units. */ > + static tree > + uniquify_top (tree t) > + { > + switch (TREE_CODE (t)) > + { > + case INTEGER_CST: > + { > + tree newtype = gimple_register_type (TREE_TYPE (t)); > + if (newtype != TREE_TYPE (t)) > + t = build_int_cst_wide (newtype, TREE_INT_CST_LOW (t), > + TREE_INT_CST_HIGH (t)); > + } > + break; > + case STRING_CST: > + { > + tree *t2; > + if (TREE_TYPE (t)) > + TREE_TYPE (t) = gimple_register_type (TREE_TYPE (t)); > + t2 = (tree *) htab_find_slot (top_decls, t, INSERT); > + if (*t2) > + t = *t2; > + else > + *t2 = t; > + } > + break; > + default: > + break; > + } > + return t; > + } > > /* Remember that T is a tree that (potentially) refers to a variable > or function decl that may be replaced with its prevailing variant. */ > *************** remember_with_vars (tree t) > *** 247,252 **** > --- 327,334 ---- > { \ > if (TYPE_P (tt)) \ > (tt) = gimple_register_type (tt); \ > + else\ > + (tt) = uniquify_top (tt); \ > if (VAR_OR_FUNCTION_DECL_P (tt) && TREE_PUBLIC (tt)) \ > remember_with_vars (t); \ > } \ > *************** lto_fixup_types (tree t) > *** 504,510 **** > /* Given a streamer cache structure DATA_IN (holding a sequence of trees > for one compilation unit) go over all trees starting at index FROM until > the > end of the sequence and replace fields of those trees, and the trees > ! themself with their canonical variants as per gimple_register_type. */ > > static void > uniquify_nodes (struct data_in *data_in, unsigned from) > --- 586,593 ---- > /* Given a streamer cache structure DATA_IN (holding a sequence of trees > for one compilation unit) go over all trees starting at index FROM until > the > end of the sequence and replace fields of those trees, and the trees > ! themself with their canonical variants as per gimple_register_type and > ! uniquify_top. */ > > static void > uniquify_nodes (struct data_in *data_in, unsigned from) > *************** uniquify_nodes (struct data_in *data_in, > *** 522,529 **** > for (i = len; i-- > from;) > { > tree t = VEC_index (tree, cache->nodes, i); > ! if (!t > ! || !TYPE_P (t)) > continue; > > gimple_register_type (t); > --- 605,611 ---- > for (i = len; i-- > from;) > { > tree t = VEC_index (tree, cache->nodes, i); > ! if (!t || !TYPE_P (t)) > continue; > > gimple_register_type (t); > *************** uniquify_nodes (struct data_in *data_in, > *** 540,552 **** > /* First fixup the fields of T. */ > lto_fixup_types (t); > > ! if (!TYPE_P (t)) > ! continue; > ! > ! /* Now try to find a canonical variant of T itself. */ > ! t = gimple_register_type (t); > > ! if (t == oldt) > { > /* The following re-creates proper variant lists while fixing up > the variant leaders. We do not stream TYPE_NEXT_VARIANT so the > --- 622,634 ---- > /* First fixup the fields of T. */ > lto_fixup_types (t); > > ! if (TYPE_P (t)) > ! /* Now try to find a canonical variant of T itself. */ > ! t = gimple_register_type (t); > ! else > ! t = uniquify_top (t); > > ! if (t == oldt && TYPE_P (t)) > { > /* The following re-creates proper variant lists while fixing up > the variant leaders. We do not stream TYPE_NEXT_VARIANT so the > *************** uniquify_nodes (struct data_in *data_in, > *** 609,619 **** > TYPE_REFERENCE_TO (TREE_TYPE (t)) = t; > } > } > - > else > { > if (RECORD_OR_UNION_TYPE_P (t)) > { > tree f1, f2; > if (TYPE_FIELDS (t) != TYPE_FIELDS (oldt)) > for (f1 = TYPE_FIELDS (t), f2 = TYPE_FIELDS (oldt); > --- 691,705 ---- > TYPE_REFERENCE_TO (TREE_TYPE (t)) = t; > } > } > else > { > if (RECORD_OR_UNION_TYPE_P (t)) > { > + /* We could record FIELD_DECLs either in top_decls, > + and uniquify them in uniquify_top, or we can simply replace > + all FIELD_DECLs in one go if we found an already registered > + type. The latter is faster and doesn't waste space in the > + hash table. */ > tree f1, f2; > if (TYPE_FIELDS (t) != TYPE_FIELDS (oldt)) > for (f1 = TYPE_FIELDS (t), f2 = TYPE_FIELDS (oldt); > *************** uniquify_nodes (struct data_in *data_in, > *** 656,663 **** > for (i = len; i-- > from;) > { > tree t = VEC_index (tree, cache->nodes, i); > ! if (!t > ! || !TYPE_P (t)) > continue; > > if (!TYPE_CANONICAL (t)) > --- 742,748 ---- > for (i = len; i-- > from;) > { > tree t = VEC_index (tree, cache->nodes, i); > ! if (!t || !TYPE_P (t)) > continue; > > if (!TYPE_CANONICAL (t)) > *************** read_cgraph_and_symbols (unsigned nfiles > *** 2301,2306 **** > --- 2386,2392 ---- > gcc_assert (num_objects == nfiles); > } > > + top_decls = htab_create_ggc (101, simple_tree_hash, simple_tree_eq, NULL); > tree_with_vars = htab_create_ggc (101, htab_hash_pointer, htab_eq_pointer, > NULL); > > *************** read_cgraph_and_symbols (unsigned nfiles > *** 2340,2345 **** > --- 2426,2433 ---- > lto_stats.num_input_files = count; > ggc_free(decl_data); > real_file_decl_data = NULL; > + htab_delete (top_decls); > + top_decls = NULL; > > if (resolution_file_name) > fclose (resolution); >