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