better wpa [1/n]: merge types during read-in

Richard Guenther richard.guenther@gmail.com
Wed Apr 20 13:18:00 GMT 2011


On Tue, Apr 19, 2011 at 10:01 PM, Michael Matz <matz@suse.de> wrote:
> Hi,
>
> I have a backlog of random improvements to the WPA phase of LTO
> compilations, all with the common goal of reducing peak memory usage.  I
> was basically dumping all trees that the WPA phase read in, and then tried
> to think about which trees can be merged with already existing ones very
> early or alternatively which shouldn't have been in the global section
> from the beginning.  I.e. whenever there were two trees left after reading
> in all units, that IMO should have been the same, I either tried to merge
> them, or find reasons why they in fact were not the same.
>
> This is the first part, that in essence merely moves the merging of types
> somewhat earlier.  Currently all non-local trees of all object files are
> read in, then symbols are merged, then types and prevailing decls are set.
> That's the maximum of peak memory usage you can get, because during
> read-in virtually no trees will be garbage collected.
>
> There's much one can merge already during read-in, types are the easiest
> thing, some obvious top-level trees are in the same league (integer and
> string constants, tree_lists used to collect arguments, field_decls of the
> just replaced types).  Later patches will extend this set.  This current
> one will deal just with the mentioned entities.
>
> One nice property of our reader is, that all trees are nicely collected in
> a sequential array (the reader cache).  This implies that we don't have to
> use walk_tree to get to all reachable trees, we merely have to iterate
> through that array and are sure we'll catch everything (except some
> builtin trees).  We also don't have to remember which trees we've already
> visited, we'll naturally visit each one just once.
>
> The current callbacks of walk_tree are used for two purposes: merging
> types and setting prevailing decls.  The latter can only be done after the
> prevailing decls are known, and that is only when we've seen all object
> files (even in the case of the linker plugin).  Hence, while uniquifying
> the read trees I'm also remembering those that (potentially) refer to
> variables or functions in a hashtable, which conveniently has support in
> the garbage collector to remove unreachable trees (so, we'll later only
> iterate over those trees that are known to be reachable).  I'm using that
> hash at the place where currently walk_tree is used to iterate over only
> those trees that potentially refer to a var/function_decl.
>
> The type merger is in essence a moved and slightly changed copy of the
> original walk_tree callback routines.  Via some asserts I've verified that
> I'm replacing all reachable vars or functions with their prevailing decl,
> before removing the old callbacks (the same cannot be done with the merged
> types, due to ordering issues type merging depends on other types already
> being merged, or, worse, some decls being merged; that's already currently
> the case, rerunning the type merger will produce more merged types).
>
> I'm starting to merge tree as soon as a top-level invocation of
> lto_input_tree is finished.  That is the earliest point at which all
> recursively reachable trees are known and read in.  It's better to do it
> at that place than to defer it until the whole unit is read in because so
> the next invocations of input_tree (possibly referring back to already
> existing trees in the cache) will make use of the already merged variants.
>
> Some statistics for an LTO bootstrap, in this case only for the cc1
> executable, only for the WPA phase, generated with a -O0 compiled lto1
> (the cc1 object are from stage3 of a normal lto bootstrap).  Without the
> patch:
>
> Merging declarations
>  {GC 320745k -> 204020k}Reading summaries
>  ipa lto gimple out    :   6.08 ( 7%) usr   0.14 ( 9%) sys   6.23 ( 7%) wall   0 kB ( 0%) ggc
>  ipa lto decl in       :  21.24 (23%) usr   0.38 (24%) sys  21.65 (23%) wall  311556 kB (65%) ggc
>  ipa lto decl out      :  17.89 (19%) usr   0.01 ( 1%) sys  17.91 (19%) wall   0 kB ( 0%) ggc
>  ipa lto decl merge    :  12.09 (13%) usr   0.13 ( 8%) sys  12.22 (13%) wall   7704 kB ( 2%) ggc
>  inline heuristics     :  21.05 (23%) usr   0.00 ( 0%) sys  21.07 (22%) wall   28972 kB ( 6%) ggc
>  TOTAL                 :  92.99             1.58            94.68             479817 kB
>
> I.e. right after reading in all units we have 320MB of ggc memory.
>
> With the patch:
>
> Merging declarations
>  {GC 193371k -> 176876k}Reading summaries
>  ipa lto gimple out    :   6.63 ( 8%) usr   0.19 ( 9%) sys  11.13 (12%) wall       0 kB ( 0%) ggc
>  ipa lto decl in       :  27.09 (32%) usr   0.30 (14%) sys  27.79 (30%) wall  318044 kB (66%) ggc
>  ipa lto decl out      :  13.96 (17%) usr   0.02 ( 1%) sys  13.99 (15%) wall       0 kB ( 0%) ggc
>  ipa lto decl merge    :   0.32 ( 0%) usr   0.00 ( 0%) sys   0.33 ( 0%) wall       5 kB ( 0%) ggc
>  inline heuristics     :  21.04 (25%) usr   0.03 ( 1%) sys  21.10 (23%) wall   28972 kB ( 6%) ggc
>  TOTAL                 :  84.10             2.17            91.78             478594 kB
>
> So, about 193MB of ggc memory after reading in all units (and even after
> merging using 28MB less).  As expected the decl-in phase is slower (27s vs
> 21s), but that is more than offsetted by the mostly trivial decl-merge
> phase (0s vs 12s).  Faster and better, good :)
>
> What do people think? (regstrapped with and without LTO bootstrap, the
> latter with all languages, the former without Ada, on x86_64-linux).
>
>
> Ciao,
> Michael.
> -------------------------------
>        * lto-streamer.c (lto_streamer_cache_insert_1): Accept to override
>        other trees that just builtins.
>        (lto_record_common_node): Don't leave NULL TYPE_CANONICAL.
>
> lto/
>        * lto.c (lto_read_in_decl_state): Don't merge types here.
>        (tree_with_vars, top_decls): New static hash tables.
>        (simple_tree_hash, simple_tree_eq, uniquify_top,
>        remember_with_vars): New static functions.
>        (LTO_FIXUP_TYPE): New macro.
>        (lto_ft_common, lto_ft_decl_minimal, lto_ft_decl_common,
>        lto_ft_decl_with_vis, lto_ft_decl_non_common, lto_ft_function,
>        lto_ft_field_decl, lto_ft_type, lto_ft_binfo, lto_ft_constructor,
>        lto_ft_expr, lto_fixup_types, uniquify_nodes): New static functions.
>        (lto_read_decls): Uniquify while reading in trees.
>        (lto_fixup_data_t, LTO_FIXUP_SUBTREE,
>        LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE, no_fixup_p, lto_fixup_common,
>        lto_fixup_decl_minimal, lto_fixup_decl_common, lto_fixup_decl_with_vis,
>        lto_fixup_decl_non_common, lto_fixup_function, lto_fixup_field_decl,
>        lto_fixup_type, lto_fixup_binfo, lto_fixup_constructor,
>        lto_fixup_tree): Remove.
>        (lto_fixup_state): Remove data argument.
>        (LTO_SET_PREVAIL, LTO_NO_PREVAIL): New macros.
>        (lto_fixup_prevailing_decls): New function.
>        (lto_fixup_state_aux): Argument aux is unused.
>        (lto_fixup_decls): Don't allocate pointer sets, don't use
>        lto_fixup_tree, use lto_fixup_prevailing_decls.
>        (read_cgraph_and_symbols): Allocate and remove top_decls and
>        tree_with_vars.
>
> Index: lto-streamer.c
> ===================================================================
> *** lto-streamer.c      (revision 172602)
> --- lto-streamer.c      (working copy)
> *************** lto_streamer_cache_insert_1 (struct lto_
> *** 383,401 ****
>        {
>          /* If the caller wants to insert T at a specific slot
>             location, and ENTRY->TO does not match *IX_P, add T to
> !            the requested location slot.  This situation arises when
> !            streaming builtin functions.
> !
> !            For instance, on the writer side we could have two
> !            FUNCTION_DECLS T1 and T2 that are represented by the same
> !            builtin function.  The reader will only instantiate the
> !            canonical builtin, but since T1 and T2 had been
> !            originally stored in different cache slots (S1 and S2),
> !            the reader must be able to find the canonical builtin
> !            function at slots S1 and S2.  */
> !         gcc_assert (lto_stream_as_builtin_p (t));
>          ix = *ix_p;
> -
>          lto_streamer_cache_add_to_node_array (cache, ix, t);
>        }
>
> --- 383,390 ----
>        {
>          /* If the caller wants to insert T at a specific slot
>             location, and ENTRY->TO does not match *IX_P, add T to
> !            the requested location slot.  */
>          ix = *ix_p;
>          lto_streamer_cache_add_to_node_array (cache, ix, t);
>        }
>
> *************** lto_record_common_node (tree *nodep, VEC
> *** 513,518 ****
> --- 502,509 ----
>        TYPE_CANONICAL (node) = NULL_TREE;
>        node = gimple_register_type (node);
>        TYPE_CANONICAL (node) = gimple_register_canonical_type (node);
> +       if (in_lto_p)
> +       TYPE_CANONICAL (*nodep) = TYPE_CANONICAL (node);
>        *nodep = node;
>      }
>
> Index: lto/lto.c
> ===================================================================
> *** lto/lto.c   (revision 172602)
> --- lto/lto.c   (working copy)
> *************** lto_read_in_decl_state (struct data_in *
> *** 215,228 ****
>        tree *decls = ggc_alloc_vec_tree (size);
>
>        for (j = 0; j < size; j++)
> !       {
> !         decls[j] = lto_streamer_cache_get (data_in->reader_cache, data[j]);
> !
> !         /* Register every type in the global type table.  If the
> !            type existed already, use the existing type.  */
> !         if (TYPE_P (decls[j]))
> !           decls[j] = gimple_register_type (decls[j]);
> !       }
>
>        state->streams[i].size = size;
>        state->streams[i].trees = decls;
> --- 215,221 ----
>        tree *decls = ggc_alloc_vec_tree (size);
>
>        for (j = 0; j < size; j++)
> !       decls[j] = lto_streamer_cache_get (data_in->reader_cache, data[j]);
>
>        state->streams[i].size = size;
>        state->streams[i].trees = decls;
> *************** lto_read_in_decl_state (struct data_in *
> *** 232,237 ****
> --- 225,783 ----
>    return data;
>  }
>
> + /* A hashtable of trees that potentially refer to variables or functions
> +    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.  */

It would have been nice to have the top-level tree merging as a separate
patch, as I am not convinced it is correct, but see below ...

> + 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) == TREE_LIST)
> +     {
> +       /* gcc_assert (!TREE_TYPE (t));
> +        Ick.  The C++ frontend uses TREE_TYPE of TREE_LIST
> +        entries in its binfo things (as BV_VCALL_INDEX).  We
> +        shouldn't even stream this.  */
> +       hashcode = iterative_hash_hashval_t (((size_t)TREE_TYPE (t)) >> 3,
> +                                          hashcode);
> +       hashcode = iterative_hash_hashval_t (((size_t)TREE_VALUE (t)) >> 3,
> +                                          hashcode);
> +       hashcode = iterative_hash_hashval_t (((size_t)TREE_PURPOSE (t)) >> 3,
> +                                          hashcode);
> +       hashcode = iterative_hash_hashval_t (((size_t)TREE_CHAIN (t)) >> 3,
> +                                          hashcode);
> +       return hashcode;
> +     }
> +   else 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) == TREE_LIST)
> +     {
> +       return TREE_VALUE (a) == TREE_VALUE (b)
> +            && TREE_PURPOSE (a) == TREE_PURPOSE (b)
> +            && TREE_CHAIN (a) == TREE_CHAIN (b)
> +            /* See simple_tree_hash.  */
> +            && TREE_TYPE (a) == TREE_TYPE (b);
> +     }
> +   else 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));
> +

excess vertical space.

> +       }
> +         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;
> +       case TREE_LIST:
> +       {
> +         tree *t2 = (tree *) htab_find_slot (top_decls, t, INSERT);
> +         if (*t2)
> +           t = *t2;
> +         else
> +           *t2 = t;

I don't think we can share TREE_LISTs.  Where do they hang off
when you do this?  Are not all but one of those dead?  All
TREE_LIST manipulation routines I know of do not even think about
the possibility of shared lists.

> +       }
> +         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.  */
> + static void
> + remember_with_vars (tree t)
> + {
> +   *(tree *) htab_find_slot (tree_with_vars, t, INSERT) = t;
> + }
> +
> + #define LTO_FIXUP_TYPE(tt) \

That's a bad name for what follows.  Maybe LTO_FIXUP_TREE?

> +   do \
> +     { \
> +       if (tt) \
> +       { \
> +         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); \
> +       } \
> +     } while (0)
> +
> + static void lto_fixup_types (tree);
>
> + /* Fix up fields of a tree_common T.  */
> +
> + static void
> + lto_ft_common (tree t)
> + {
> +   /* The following re-creates the TYPE_REFERENCE_TO and TYPE_POINTER_TO
> +      lists.  We do not stream TYPE_REFERENCE_TO, TYPE_POINTER_TO or
> +      TYPE_NEXT_PTR_TO and TYPE_NEXT_REF_TO.
> +      First remove us from any pointer list we are on.  */
> +   if (TREE_CODE (t) == POINTER_TYPE)
> +     {
> +       if (TYPE_POINTER_TO (TREE_TYPE (t)) == t)
> +       TYPE_POINTER_TO (TREE_TYPE (t)) = TYPE_NEXT_PTR_TO (t);
> +       else
> +       {
> +         tree tem = TYPE_POINTER_TO (TREE_TYPE (t));
> +         while (tem && TYPE_NEXT_PTR_TO (tem) != t)
> +           tem = TYPE_NEXT_PTR_TO (tem);
> +         if (tem)
> +           TYPE_NEXT_PTR_TO (tem) = TYPE_NEXT_PTR_TO (t);
> +       }
> +       TYPE_NEXT_PTR_TO (t) = NULL_TREE;
> +     }
> +   else if (TREE_CODE (t) == REFERENCE_TYPE)
> +     {
> +       if (TYPE_REFERENCE_TO (TREE_TYPE (t)) == t)
> +       TYPE_REFERENCE_TO (TREE_TYPE (t)) = TYPE_NEXT_REF_TO (t);
> +       else
> +       {
> +         tree tem = TYPE_REFERENCE_TO (TREE_TYPE (t));
> +         while (tem && TYPE_NEXT_REF_TO (tem) != t)
> +           tem = TYPE_NEXT_REF_TO (tem);
> +         if (tem)
> +           TYPE_NEXT_REF_TO (tem) = TYPE_NEXT_REF_TO (t);
> +       }
> +       TYPE_NEXT_REF_TO (t) = NULL_TREE;
> +     }
> +
> +   /* Fixup our type.  */
> +   LTO_FIXUP_TYPE (TREE_TYPE (t));
> +
> +   /* Second put us on the list of pointers of the new pointed-to type
> +      if we are a main variant.  This is done in lto_ft_type after
> +      fixing up our main variant.  */
> +   LTO_FIXUP_TYPE (TREE_CHAIN (t));
> + }
> +
> + /* Fix up fields of a decl_minimal T.  */
> +
> + static void
> + lto_ft_decl_minimal (tree t)
> + {
> +   lto_ft_common (t);
> +   LTO_FIXUP_TYPE (DECL_NAME (t));
> +   LTO_FIXUP_TYPE (DECL_CONTEXT (t));
> + }
> +
> + /* Fix up fields of a decl_common T.  */
> +
> + static void
> + lto_ft_decl_common (tree t)
> + {
> +   lto_ft_decl_minimal (t);
> +   LTO_FIXUP_TYPE (DECL_SIZE (t));
> +   LTO_FIXUP_TYPE (DECL_SIZE_UNIT (t));
> +   LTO_FIXUP_TYPE (DECL_INITIAL (t));
> +   LTO_FIXUP_TYPE (DECL_ATTRIBUTES (t));
> +   LTO_FIXUP_TYPE (DECL_ABSTRACT_ORIGIN (t));
> + }
> +
> + /* Fix up fields of a decl_with_vis T.  */
> +
> + static void
> + lto_ft_decl_with_vis (tree t)
> + {
> +   lto_ft_decl_common (t);
> +
> +   /* Accessor macro has side-effects, use field-name here. */
> +   LTO_FIXUP_TYPE (t->decl_with_vis.assembler_name);
> + }
> +
> + /* Fix up fields of a decl_non_common T.  */
> +
> + static void
> + lto_ft_decl_non_common (tree t)
> + {
> +   lto_ft_decl_with_vis (t);
> +   LTO_FIXUP_TYPE (DECL_ARGUMENT_FLD (t));
> +   LTO_FIXUP_TYPE (DECL_RESULT_FLD (t));
> +   LTO_FIXUP_TYPE (DECL_VINDEX (t));
> + }
> +
> + /* Fix up fields of a decl_non_common T.  */
> +
> + static void
> + lto_ft_function (tree t)
> + {
> +   lto_ft_decl_non_common (t);
> +   LTO_FIXUP_TYPE (DECL_FUNCTION_PERSONALITY (t));
> + }
> +
> + /* Fix up fields of a field_decl T.  */
> +
> + static void
> + lto_ft_field_decl (tree t)
> + {
> +   lto_ft_decl_common (t);
> +   LTO_FIXUP_TYPE (DECL_FIELD_OFFSET (t));
> +   LTO_FIXUP_TYPE (DECL_BIT_FIELD_TYPE (t));
> +   LTO_FIXUP_TYPE (DECL_QUALIFIER (t));

here (and earlier) we had some no_fixup_p asserts.  What happened to
them?  In particular, don't we need to fixup the DECL_FIELD_BIT_OFFSET
integer-cst?

> +   LTO_FIXUP_TYPE (DECL_FCONTEXT (t));
> + }
> +
> + /* Fix up fields of a type T.  */
> +
> + static void
> + lto_ft_type (tree t)
> + {
> +   tree tem, mv;
> +
> +   lto_ft_common (t);
> +   LTO_FIXUP_TYPE (TYPE_CACHED_VALUES (t));
> +   LTO_FIXUP_TYPE (TYPE_SIZE (t));
> +   LTO_FIXUP_TYPE (TYPE_SIZE_UNIT (t));
> +   LTO_FIXUP_TYPE (TYPE_ATTRIBUTES (t));
> +   LTO_FIXUP_TYPE (TYPE_NAME (t));
> +
> +   /* Accessors are for derived node types only. */
> +   if (!POINTER_TYPE_P (t))
> +     LTO_FIXUP_TYPE (t->type.minval);
> +   LTO_FIXUP_TYPE (t->type.maxval);
> +
> +   /* Accessor is for derived node types only. */
> +   LTO_FIXUP_TYPE (t->type.binfo);
> +
> +   LTO_FIXUP_TYPE (TYPE_CONTEXT (t));
> +
> +   /* Compute the canonical type of t and fix that up.  From this point
> +      there are no longer any types with TYPE_STRUCTURAL_EQUALITY_P
> +      and its type-based alias problems.  */
> +   if (!TYPE_CANONICAL (t))
> +     {
> +       TYPE_CANONICAL (t) = gimple_register_canonical_type (t);
> +       LTO_FIXUP_TYPE (TYPE_CANONICAL (t));
> +     }
> +
> +   /* The following re-creates proper variant lists while fixing up
> +      the variant leaders.  We do not stream TYPE_NEXT_VARIANT so the
> +      variant list state before fixup is broken.  */
> +
> +   /* Remove us from our main variant list if we are not the variant leader.  */
> +   if (TYPE_MAIN_VARIANT (t) != t)
> +     {
> +       tem = TYPE_MAIN_VARIANT (t);
> +       while (tem && TYPE_NEXT_VARIANT (tem) != t)
> +       tem = TYPE_NEXT_VARIANT (tem);
> +       if (tem)
> +       TYPE_NEXT_VARIANT (tem) = TYPE_NEXT_VARIANT (t);
> +       TYPE_NEXT_VARIANT (t) = NULL_TREE;
> +     }
> +
> +   /* Query our new main variant.  */
> +   mv = gimple_register_type (TYPE_MAIN_VARIANT (t));
> +
> +   /* If we were the variant leader and we get replaced ourselves drop
> +      all variants from our list.  */
> +   if (TYPE_MAIN_VARIANT (t) == t
> +       && mv != t)
> +     {
> +       tem = t;
> +       while (tem)
> +       {
> +         tree tem2 = TYPE_NEXT_VARIANT (tem);
> +         TYPE_NEXT_VARIANT (tem) = NULL_TREE;
> +         tem = tem2;
> +       }
> +     }
> +
> +   /* Finally adjust our main variant and fix it up.  */
> +   TYPE_MAIN_VARIANT (t) = mv;
> +   LTO_FIXUP_TYPE (TYPE_MAIN_VARIANT (t));
> +
> +   /* As the second step of reconstructing the pointer chains put us
> +      on the list of pointers of the new pointed-to type
> +      if we are a main variant.  See lto_ft_common for the first step.  */
> +   if (TREE_CODE (t) == POINTER_TYPE
> +       && TYPE_MAIN_VARIANT (t) == t)
> +     {
> +       TYPE_NEXT_PTR_TO (t) = TYPE_POINTER_TO (TREE_TYPE (t));
> +       TYPE_POINTER_TO (TREE_TYPE (t)) = t;
> +     }
> +   else if (TREE_CODE (t) == REFERENCE_TYPE
> +          && TYPE_MAIN_VARIANT (t) == t)
> +     {
> +       TYPE_NEXT_REF_TO (t) = TYPE_REFERENCE_TO (TREE_TYPE (t));
> +       TYPE_REFERENCE_TO (TREE_TYPE (t)) = t;
> +     }
> + }
> +
> + /* Fix up fields of a BINFO T.  */
> +
> + static void
> + lto_ft_binfo (tree t)
> + {
> +   unsigned HOST_WIDE_INT i, n;
> +   tree base, saved_base;
> +
> +   lto_ft_common (t);
> +   LTO_FIXUP_TYPE (BINFO_VTABLE (t));
> +   LTO_FIXUP_TYPE (BINFO_VIRTUALS (t));
> +   LTO_FIXUP_TYPE (BINFO_VPTR_FIELD (t));
> +   n = VEC_length (tree, BINFO_BASE_ACCESSES (t));
> +   for (i = 0; i < n; i++)
> +     {
> +       saved_base = base = BINFO_BASE_ACCESS (t, i);
> +       LTO_FIXUP_TYPE (base);
> +       if (base != saved_base)
> +       VEC_replace (tree, BINFO_BASE_ACCESSES (t), i, base);
> +     }
> +   LTO_FIXUP_TYPE (BINFO_INHERITANCE_CHAIN (t));
> +   LTO_FIXUP_TYPE (BINFO_SUBVTT_INDEX (t));
> +   LTO_FIXUP_TYPE (BINFO_VPTR_INDEX (t));
> +   n = BINFO_N_BASE_BINFOS (t);
> +   for (i = 0; i < n; i++)
> +     {
> +       saved_base = base = BINFO_BASE_BINFO (t, i);
> +       LTO_FIXUP_TYPE (base);
> +       if (base != saved_base)
> +       VEC_replace (tree, BINFO_BASE_BINFOS (t), i, base);
> +     }
> + }
> +
> + /* Fix up fields of a CONSTRUCTOR T.  */
> +
> + static void
> + lto_ft_constructor (tree t)
> + {
> +   unsigned HOST_WIDE_INT idx;
> +   constructor_elt *ce;
> +
> +   LTO_FIXUP_TYPE (TREE_TYPE (t));
> +
> +   for (idx = 0;
> +        VEC_iterate(constructor_elt, CONSTRUCTOR_ELTS (t), idx, ce);
> +        idx++)
> +     {
> +       LTO_FIXUP_TYPE (ce->index);
> +       LTO_FIXUP_TYPE (ce->value);
> +     }
> + }
> +
> + /* Fix up fields of an expression tree T.  */
> +
> + static void
> + lto_ft_expr (tree t)
> + {
> +   int i;
> +   lto_ft_common (t);
> +   for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i)
> +     LTO_FIXUP_TYPE (TREE_OPERAND (t, i));
> + }
> +
> + /* Given a tree T fixup fields of T by replacing types with their merged
> +    variant and other entities by an equal entity from an earlier compilation
> +    unit, or an entity being canonical in a different way.  This includes
> +    for instance integer or string constants.  */
> +
> + static void
> + lto_fixup_types (tree t)
> + {
> +   switch (TREE_CODE (t))
> +     {
> +     case IDENTIFIER_NODE:
> +       break;
> +
> +     case TREE_LIST:
> +       LTO_FIXUP_TYPE (TREE_VALUE (t));
> +       LTO_FIXUP_TYPE (TREE_PURPOSE (t));
> +       LTO_FIXUP_TYPE (TREE_CHAIN (t));
> +       break;
> +
> +     case FIELD_DECL:
> +       lto_ft_field_decl (t);
> +       break;
> +
> +     case LABEL_DECL:
> +     case CONST_DECL:
> +     case PARM_DECL:
> +     case RESULT_DECL:
> +     case IMPORTED_DECL:
> +       lto_ft_decl_common (t);
> +       break;
> +
> +     case VAR_DECL:
> +       lto_ft_decl_with_vis (t);
> +       break;
> +
> +     case TYPE_DECL:
> +       lto_ft_decl_non_common (t);
> +       break;
> +
> +     case FUNCTION_DECL:
> +       lto_ft_function (t);
> +       break;
> +
> +     case TREE_BINFO:
> +       lto_ft_binfo (t);
> +       break;
> +
> +     case PLACEHOLDER_EXPR:
> +       lto_ft_common (t);
> +       break;
> +
> +     case BLOCK:
> +     case TRANSLATION_UNIT_DECL:
> +     case OPTIMIZATION_NODE:
> +     case TARGET_OPTION_NODE:
> +       break;
> +
> +     default:
> +       if (TYPE_P (t))
> +       lto_ft_type (t);
> +       else if (TREE_CODE (t) == CONSTRUCTOR)
> +       lto_ft_constructor (t);
> +       else if (CONSTANT_CLASS_P (t))
> +       LTO_FIXUP_TYPE (TREE_TYPE (t));
> +       else if (EXPR_P (t))
> +       {
> +         lto_ft_expr (t);
> +       }
> +       else
> +       {
> +         remember_with_vars (t);
> +       }
> +     }
> + }
> +
> + /* 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)
> + {
> +   struct lto_streamer_cache_d *cache = data_in->reader_cache;
> +   unsigned len = VEC_length (tree, cache->nodes);
> +   unsigned i;
> +   /* Go backwards because childs streamed for the first time come
> +      as part of their parents, and hence are created after them.  */
> +   for (i = len; i-- > from;)
> +     {
> +       tree t = VEC_index (tree, cache->nodes, i);
> +       tree oldt = t;
> +       if (!t)
> +       continue;
> +
> +       /* First fixup the fields of T.  */
> +           lto_fixup_types (t);
> +
> +       /* Now try to find a canonical variant of T itself.  */
> +       if (TYPE_P (t))
> +       {

If t is a type, why fix up its field if it may not be the canonical variant?
Thus, why not

> +         t = gimple_register_type (t);

        lto_fixup_types (t);

?

> +         if (t == oldt
> +             && TYPE_MAIN_VARIANT (t) != t)
> +           {
> +             /* If this is its own type, link it into the variant
> +                chain.  */
> +             TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (TYPE_MAIN_VARIANT (t));
> +             TYPE_NEXT_VARIANT (TYPE_MAIN_VARIANT (t)) = t;

Hmm - isn't this taken care of in lto_fixup_types?

> +           }
> +       }
> +       else
> +       t = uniquify_top (t);
> +       if (t != oldt)
> +       {
> +         /* 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.  */
> +         if (RECORD_OR_UNION_TYPE_P (t))
> +           {
> +             tree f1, f2;
> +             unsigned ix;
> +             for (f1 = TYPE_FIELDS (t), f2 = TYPE_FIELDS (oldt);
> +                  f1 && f2; f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
> +               if (TREE_CODE (f1) == FIELD_DECL
> +                   && f1 != f2

This should always be true unless TYPE_FIELDS (t) == TYPE_FIELDS (oldt)

> +                   && lto_streamer_cache_lookup (cache, f2, &ix))

This lookup should always succeed, no?

> +                 {
> +                   gcc_assert (DECL_NAME (f1) == DECL_NAME (f2));
> +                   /* If we're going to replace an element which we'd
> +                      still visit in the next iterations, we wouldn't
> +                      handle it, so do it here.  */
> +                   if (ix < i)
> +                     lto_fixup_types (f2);

?  But it's dead, no?  Wouldn't this be a forward reference, too,
which shouldn't happen?

> +                   lto_streamer_cache_insert_at (cache, f1, ix);
> +                 }

Btw, nice.  Does this get rid of the need for the field-decl fixup code
in input_gimple_stmt?

> +           }
> +
> +         /* If we found a tree that is equal to oldt replace it in the
> +            cache, so that further users (in the various LTO sections)
> +            make use of it.  */
> +         lto_streamer_cache_insert_at (cache, t, i);
> +       }
> +     }
> + }
>
>  /* Read all the symbols from buffer DATA, using descriptors in DECL_DATA.
>     RESOLUTIONS is the set of symbols picked by the linker (read from the
> *************** lto_read_decls (struct lto_file_decl_dat
> *** 260,267 ****
>    /* Read the global declarations and types.  */
>    while (ib_main.p < ib_main.len)
>      {
> !       tree t = lto_input_tree (&ib_main, data_in);
>        gcc_assert (t && ib_main.p <= ib_main.len);
>      }
>
>    /* Read in lto_in_decl_state objects.  */
> --- 806,816 ----
>    /* Read the global declarations and types.  */
>    while (ib_main.p < ib_main.len)
>      {
> !       tree t;
> !       unsigned from = VEC_length (tree, data_in->reader_cache->nodes);
> !       t = lto_input_tree (&ib_main, data_in);
>        gcc_assert (t && ib_main.p <= ib_main.len);
> +       uniquify_nodes (data_in, from);
>      }
>
>    /* Read in lto_in_decl_state objects.  */
> *************** lto_wpa_write_files (void)
> *** 1514,1520 ****
>        fprintf (stderr, " %s (%s %i insns)", temp_filename, part->name, part->insns);
>        if (cgraph_dump_file)
>        {
> !         fprintf (cgraph_dump_file, "Writting partition %s to file %s, %i insns\n",
>                   part->name, temp_filename, part->insns);
>          fprintf (cgraph_dump_file, "cgraph nodes:");
>          dump_cgraph_node_set (cgraph_dump_file, set);
> --- 2063,2069 ----
>        fprintf (stderr, " %s (%s %i insns)", temp_filename, part->name, part->insns);
>        if (cgraph_dump_file)
>        {
> !         fprintf (cgraph_dump_file, "Writing partition %s to file %s, %i insns\n",
>                   part->name, temp_filename, part->insns);
>          fprintf (cgraph_dump_file, "cgraph nodes:");
>          dump_cgraph_node_set (cgraph_dump_file, set);
> *************** lto_wpa_write_files (void)
> *** 1548,1963 ****
>  }
>
>
> ! typedef struct {
> !   struct pointer_set_t *seen;
> ! } lto_fixup_data_t;
> !
> ! #define LTO_FIXUP_SUBTREE(t) \
> !   do \
> !     walk_tree (&(t), lto_fixup_tree, data, NULL); \
> !   while (0)
> !
> ! #define LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE(t) \
> !   do \
> !     { \
> !       if (t) \
> !       (t) = gimple_register_type (t); \
> !       walk_tree (&(t), lto_fixup_tree, data, NULL); \
> !     } \
> !   while (0)
> !
> ! static tree lto_fixup_tree (tree *, int *, void *);
> !
> ! /* Return true if T does not need to be fixed up recursively.  */
> !
> ! static inline bool
> ! no_fixup_p (tree t)
> ! {
> !   return (t == NULL
> !         || CONSTANT_CLASS_P (t)
> !         || TREE_CODE (t) == IDENTIFIER_NODE);
> ! }
>
> ! /* Fix up fields of a tree_common T.  DATA points to fix-up states.  */
>
>  static void
> ! lto_fixup_common (tree t, void *data)
>  {
> !   /* The following re-creates the TYPE_REFERENCE_TO and TYPE_POINTER_TO
> !      lists.  We do not stream TYPE_REFERENCE_TO, TYPE_POINTER_TO or
> !      TYPE_NEXT_PTR_TO and TYPE_NEXT_REF_TO.
> !      First remove us from any pointer list we are on.  */
> !   if (TREE_CODE (t) == POINTER_TYPE)
>      {
> !       if (TYPE_POINTER_TO (TREE_TYPE (t)) == t)
> !       TYPE_POINTER_TO (TREE_TYPE (t)) = TYPE_NEXT_PTR_TO (t);
> !       else
>        {
> !         tree tem = TYPE_POINTER_TO (TREE_TYPE (t));
> !         while (tem && TYPE_NEXT_PTR_TO (tem) != t)
> !           tem = TYPE_NEXT_PTR_TO (tem);
> !         if (tem)
> !           TYPE_NEXT_PTR_TO (tem) = TYPE_NEXT_PTR_TO (t);
>        }
> !       TYPE_NEXT_PTR_TO (t) = NULL_TREE;
> !     }
> !   else if (TREE_CODE (t) == REFERENCE_TYPE)
> !     {
> !       if (TYPE_REFERENCE_TO (TREE_TYPE (t)) == t)
> !       TYPE_REFERENCE_TO (TREE_TYPE (t)) = TYPE_NEXT_REF_TO (t);
> !       else
>        {
> !         tree tem = TYPE_REFERENCE_TO (TREE_TYPE (t));
> !         while (tem && TYPE_NEXT_REF_TO (tem) != t)
> !           tem = TYPE_NEXT_REF_TO (tem);
> !         if (tem)
> !           TYPE_NEXT_REF_TO (tem) = TYPE_NEXT_REF_TO (t);
>        }
> !       TYPE_NEXT_REF_TO (t) = NULL_TREE;
> !     }
> !
> !   /* Fixup our type.  */
> !   LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t));
> !
> !   /* Second put us on the list of pointers of the new pointed-to type
> !      if we are a main variant.  This is done in lto_fixup_type after
> !      fixing up our main variant.  */
> !
> !   /* This is not very efficient because we cannot do tail-recursion with
> !      a long chain of trees. */
> !   if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_COMMON))
> !     LTO_FIXUP_SUBTREE (TREE_CHAIN (t));
> ! }
> !
> ! /* Fix up fields of a decl_minimal T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_decl_minimal (tree t, void *data)
> ! {
> !   lto_fixup_common (t, data);
> !   LTO_FIXUP_SUBTREE (DECL_NAME (t));
> !   LTO_FIXUP_SUBTREE (DECL_CONTEXT (t));
> ! }
> !
> ! /* Fix up fields of a decl_common T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_decl_common (tree t, void *data)
> ! {
> !   lto_fixup_decl_minimal (t, data);
> !   LTO_FIXUP_SUBTREE (DECL_SIZE (t));
> !   LTO_FIXUP_SUBTREE (DECL_SIZE_UNIT (t));
> !   LTO_FIXUP_SUBTREE (DECL_INITIAL (t));
> !   LTO_FIXUP_SUBTREE (DECL_ATTRIBUTES (t));
> !   LTO_FIXUP_SUBTREE (DECL_ABSTRACT_ORIGIN (t));
> ! }
> !
> ! /* Fix up fields of a decl_with_vis T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_decl_with_vis (tree t, void *data)
> ! {
> !   lto_fixup_decl_common (t, data);
> !
> !   /* Accessor macro has side-effects, use field-name here. */
> !   LTO_FIXUP_SUBTREE (t->decl_with_vis.assembler_name);
> !
> !   gcc_assert (no_fixup_p (DECL_SECTION_NAME (t)));
> ! }
> !
> ! /* Fix up fields of a decl_non_common T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_decl_non_common (tree t, void *data)
> ! {
> !   lto_fixup_decl_with_vis (t, data);
> !   LTO_FIXUP_SUBTREE (DECL_ARGUMENT_FLD (t));
> !   LTO_FIXUP_SUBTREE (DECL_RESULT_FLD (t));
> !   LTO_FIXUP_SUBTREE (DECL_VINDEX (t));
> !
> !   /* SAVED_TREE should not cleared by now.  Also no accessor for base type. */
> !   gcc_assert (no_fixup_p (t->decl_non_common.saved_tree));
> ! }
> !
> ! /* Fix up fields of a decl_non_common T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_function (tree t, void *data)
> ! {
> !   lto_fixup_decl_non_common (t, data);
> !   LTO_FIXUP_SUBTREE (DECL_FUNCTION_PERSONALITY (t));
> ! }
> !
> ! /* Fix up fields of a field_decl T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_field_decl (tree t, void *data)
> ! {
> !   lto_fixup_decl_common (t, data);
> !   LTO_FIXUP_SUBTREE (DECL_FIELD_OFFSET (t));
> !   LTO_FIXUP_SUBTREE (DECL_BIT_FIELD_TYPE (t));
> !   LTO_FIXUP_SUBTREE (DECL_QUALIFIER (t));
> !   gcc_assert (no_fixup_p (DECL_FIELD_BIT_OFFSET (t)));
> !   LTO_FIXUP_SUBTREE (DECL_FCONTEXT (t));
> ! }
> !
> ! /* Fix up fields of a type T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_type (tree t, void *data)
> ! {
> !   tree tem, mv;
> !
> !   lto_fixup_common (t, data);
> !   LTO_FIXUP_SUBTREE (TYPE_CACHED_VALUES (t));
> !   LTO_FIXUP_SUBTREE (TYPE_SIZE (t));
> !   LTO_FIXUP_SUBTREE (TYPE_SIZE_UNIT (t));
> !   LTO_FIXUP_SUBTREE (TYPE_ATTRIBUTES (t));
> !   LTO_FIXUP_SUBTREE (TYPE_NAME (t));
> !
> !   /* Accessors are for derived node types only. */
> !   if (!POINTER_TYPE_P (t))
> !     LTO_FIXUP_SUBTREE (t->type.minval);
> !   LTO_FIXUP_SUBTREE (t->type.maxval);
> !
> !   /* Accessor is for derived node types only. */
> !   LTO_FIXUP_SUBTREE (t->type.binfo);
> !
> !   if (TYPE_CONTEXT (t))
> !     {
> !       if (TYPE_P (TYPE_CONTEXT (t)))
> !       LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TYPE_CONTEXT (t));
> !       else
> !       LTO_FIXUP_SUBTREE (TYPE_CONTEXT (t));
> !     }
> !
> !   /* Compute the canonical type of t and fix that up.  From this point
> !      there are no longer any types with TYPE_STRUCTURAL_EQUALITY_P
> !      and its type-based alias problems.  */
> !   if (!TYPE_CANONICAL (t))
> !     {
> !       TYPE_CANONICAL (t) = gimple_register_canonical_type (t);
> !       LTO_FIXUP_SUBTREE (TYPE_CANONICAL (t));
> !     }
> !
> !   /* The following re-creates proper variant lists while fixing up
> !      the variant leaders.  We do not stream TYPE_NEXT_VARIANT so the
> !      variant list state before fixup is broken.  */
> !
> !   /* Remove us from our main variant list if we are not the variant leader.  */
> !   if (TYPE_MAIN_VARIANT (t) != t)
> !     {
> !       tem = TYPE_MAIN_VARIANT (t);
> !       while (tem && TYPE_NEXT_VARIANT (tem) != t)
> !       tem = TYPE_NEXT_VARIANT (tem);
> !       if (tem)
> !       TYPE_NEXT_VARIANT (tem) = TYPE_NEXT_VARIANT (t);
> !       TYPE_NEXT_VARIANT (t) = NULL_TREE;
> !     }
> !
> !   /* Query our new main variant.  */
> !   mv = gimple_register_type (TYPE_MAIN_VARIANT (t));
> !
> !   /* If we were the variant leader and we get replaced ourselves drop
> !      all variants from our list.  */
> !   if (TYPE_MAIN_VARIANT (t) == t
> !       && mv != t)
> !     {
> !       tem = t;
> !       while (tem)
>        {
> !         tree tem2 = TYPE_NEXT_VARIANT (tem);
> !         TYPE_NEXT_VARIANT (tem) = NULL_TREE;
> !         tem = tem2;
>        }
>      }
> !
> !   /* If we are not our own variant leader link us into our new leaders
> !      variant list.  */
> !   if (mv != t)
> !     {
> !       TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (mv);
> !       TYPE_NEXT_VARIANT (mv) = t;
> !     }
> !
> !   /* Finally adjust our main variant and fix it up.  */
> !   TYPE_MAIN_VARIANT (t) = mv;
> !   LTO_FIXUP_SUBTREE (TYPE_MAIN_VARIANT (t));
> !
> !   /* As the second step of reconstructing the pointer chains put us
> !      on the list of pointers of the new pointed-to type
> !      if we are a main variant.  See lto_fixup_common for the first step.  */
> !   if (TREE_CODE (t) == POINTER_TYPE
> !       && TYPE_MAIN_VARIANT (t) == t)
> !     {
> !       TYPE_NEXT_PTR_TO (t) = TYPE_POINTER_TO (TREE_TYPE (t));
> !       TYPE_POINTER_TO (TREE_TYPE (t)) = t;
> !     }
> !   else if (TREE_CODE (t) == REFERENCE_TYPE
> !          && TYPE_MAIN_VARIANT (t) == t)
> !     {
> !       TYPE_NEXT_REF_TO (t) = TYPE_REFERENCE_TO (TREE_TYPE (t));
> !       TYPE_REFERENCE_TO (TREE_TYPE (t)) = t;
> !     }
> ! }
> !
> ! /* Fix up fields of a BINFO T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_binfo (tree t, void *data)
> ! {
> !   unsigned HOST_WIDE_INT i, n;
> !   tree base, saved_base;
> !
> !   lto_fixup_common (t, data);
> !   gcc_assert (no_fixup_p (BINFO_OFFSET (t)));
> !   LTO_FIXUP_SUBTREE (BINFO_VTABLE (t));
> !   LTO_FIXUP_SUBTREE (BINFO_VIRTUALS (t));
> !   LTO_FIXUP_SUBTREE (BINFO_VPTR_FIELD (t));
> !   n = VEC_length (tree, BINFO_BASE_ACCESSES (t));
> !   for (i = 0; i < n; i++)
> !     {
> !       saved_base = base = BINFO_BASE_ACCESS (t, i);
> !       LTO_FIXUP_SUBTREE (base);
> !       if (base != saved_base)
> !       VEC_replace (tree, BINFO_BASE_ACCESSES (t), i, base);
> !     }
> !   LTO_FIXUP_SUBTREE (BINFO_INHERITANCE_CHAIN (t));
> !   LTO_FIXUP_SUBTREE (BINFO_SUBVTT_INDEX (t));
> !   LTO_FIXUP_SUBTREE (BINFO_VPTR_INDEX (t));
> !   n = BINFO_N_BASE_BINFOS (t);
> !   for (i = 0; i < n; i++)
> !     {
> !       saved_base = base = BINFO_BASE_BINFO (t, i);
> !       LTO_FIXUP_SUBTREE (base);
> !       if (base != saved_base)
> !       VEC_replace (tree, BINFO_BASE_BINFOS (t), i, base);
> !     }
> ! }
> !
> ! /* Fix up fields of a CONSTRUCTOR T.  DATA points to fix-up states.  */
> !
> ! static void
> ! lto_fixup_constructor (tree t, void *data)
> ! {
> !   unsigned HOST_WIDE_INT idx;
> !   constructor_elt *ce;
> !
> !   LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t));
> !
> !   for (idx = 0;
> !        VEC_iterate(constructor_elt, CONSTRUCTOR_ELTS (t), idx, ce);
> !        idx++)
>      {
> !       LTO_FIXUP_SUBTREE (ce->index);
> !       LTO_FIXUP_SUBTREE (ce->value);
> !     }
> ! }
> !
> ! /* A walk_tree callback used by lto_fixup_state. TP is the pointer to the
> !    current tree. WALK_SUBTREES indicates if the subtrees will be walked.
> !    DATA is a pointer set to record visited nodes. */
> !
> ! static tree
> ! lto_fixup_tree (tree *tp, int *walk_subtrees, void *data)
> ! {
> !   tree t;
> !   lto_fixup_data_t *fixup_data = (lto_fixup_data_t *) data;
> !   tree prevailing;
>
> !   t = *tp;
> !   *walk_subtrees = 0;
> !   if (!t || pointer_set_contains (fixup_data->seen, t))
> !     return NULL;
>
> !   if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == FUNCTION_DECL)
> !     {
> !       prevailing = lto_symtab_prevailing_decl (t);
>
> !       if (t != prevailing)
> !       {
> !          /* Also replace t with prevailing defintion.  We don't want to
> !             insert the other defintion in the seen set as we want to
> !             replace all instances of it.  */
> !         *tp = prevailing;
> !         t = prevailing;
> !       }
>      }
> !   else if (TYPE_P (t))
>      {
> !       /* Replace t with the prevailing type.  We don't want to insert the
> !          other type in the seen set as we want to replace all instances of it.  */
> !       t = gimple_register_type (t);
> !       *tp = t;
>      }
> !
> !   if (pointer_set_insert (fixup_data->seen, t))
> !     return NULL;
> !
> !   /* walk_tree does not visit all reachable nodes that need to be fixed up.
> !      Hence we do special processing here for those kind of nodes. */
> !   switch (TREE_CODE (t))
>      {
> !     case FIELD_DECL:
> !       lto_fixup_field_decl (t, data);
> !       break;
> !
> !     case LABEL_DECL:
> !     case CONST_DECL:
> !     case PARM_DECL:
> !     case RESULT_DECL:
> !     case IMPORTED_DECL:
> !       lto_fixup_decl_common (t, data);
> !       break;
> !
> !     case VAR_DECL:
> !       lto_fixup_decl_with_vis (t, data);
> !       break;
> !
> !     case TYPE_DECL:
> !       lto_fixup_decl_non_common (t, data);
> !       break;
> !
> !     case FUNCTION_DECL:
> !       lto_fixup_function (t, data);
> !       break;
> !
> !     case TREE_BINFO:
> !       lto_fixup_binfo (t, data);
> !       break;
> !
> !     default:
> !       if (TYPE_P (t))
> !       lto_fixup_type (t, data);
> !       else if (TREE_CODE (t) == CONSTRUCTOR)
> !       lto_fixup_constructor (t, data);
> !       else if (CONSTANT_CLASS_P (t))
> !       LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t));
> !       else if (EXPR_P (t))
> !       {
> !         /* walk_tree only handles TREE_OPERANDs. Do the rest here.  */
> !         lto_fixup_common (t, data);
> !         LTO_FIXUP_SUBTREE (t->exp.block);
> !         *walk_subtrees = 1;
> !       }
> !       else
>        {
> !         /* Let walk_tree handle sub-trees.  */
> !         *walk_subtrees = 1;
>        }
>      }
> -
> -   return NULL;
>  }
>
>  /* Helper function of lto_fixup_decls. Walks the var and fn streams in STATE,
> !    replaces var and function decls with the corresponding prevailing def and
> !    records the old decl in the free-list in DATA. We also record visted nodes
> !    in the seen-set in DATA to avoid multiple visit for nodes that need not
> !    to be replaced.  */
>
>  static void
> ! lto_fixup_state (struct lto_in_decl_state *state, lto_fixup_data_t *data)
>  {
>    unsigned i, si;
>    struct lto_tree_ref_table *table;
> --- 2097,2202 ----
>  }
>
>
> ! /* If TT is a variable or function decl replace it with its
> !    prevailing variant.  */
> ! #define LTO_SET_PREVAIL(tt) \
> !   do {\
> !     if ((tt) && VAR_OR_FUNCTION_DECL_P (tt)) \
> !       tt = lto_symtab_prevailing_decl (tt); \
> !   } while (0)
>
> ! /* Ensure that TT isn't a replacable var of function decl.  */
> ! #define LTO_NO_PREVAIL(tt) \
> !   gcc_assert (!(tt) || !VAR_OR_FUNCTION_DECL_P (tt))
>
> + /* Given a tree T replace all fields referring to variables or functions
> +    with their prevailing variant.  */
>  static void
> ! lto_fixup_prevailing_decls (tree t)
>  {
> !   enum tree_code code = TREE_CODE (t);
> !   LTO_NO_PREVAIL (TREE_TYPE (t));
> !   LTO_NO_PREVAIL (TREE_CHAIN (t));
> !   if (DECL_P (t))
>      {
> !       LTO_NO_PREVAIL (DECL_NAME (t));
> !       LTO_SET_PREVAIL (DECL_CONTEXT (t));
> !       if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON))
>        {
> !         LTO_SET_PREVAIL (DECL_SIZE (t));
> !         LTO_SET_PREVAIL (DECL_SIZE_UNIT (t));
> !         LTO_SET_PREVAIL (DECL_INITIAL (t));
> !         LTO_NO_PREVAIL (DECL_ATTRIBUTES (t));
> !         LTO_SET_PREVAIL (DECL_ABSTRACT_ORIGIN (t));
>        }
> !       if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
>        {
> !         LTO_NO_PREVAIL (t->decl_with_vis.assembler_name);
> !         LTO_NO_PREVAIL (DECL_SECTION_NAME (t));
>        }
> !       if (CODE_CONTAINS_STRUCT (code, TS_DECL_NON_COMMON))
>        {
> !         LTO_NO_PREVAIL (DECL_ARGUMENT_FLD (t));
> !         LTO_NO_PREVAIL (DECL_RESULT_FLD (t));
> !         LTO_NO_PREVAIL (DECL_VINDEX (t));
> !       }
> !       if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
> !       LTO_SET_PREVAIL (DECL_FUNCTION_PERSONALITY (t));
> !       if (CODE_CONTAINS_STRUCT (code, TS_FIELD_DECL))
> !       {
> !         LTO_NO_PREVAIL (DECL_FIELD_OFFSET (t));
> !         LTO_NO_PREVAIL (DECL_BIT_FIELD_TYPE (t));
> !         LTO_NO_PREVAIL (DECL_QUALIFIER (t));
> !         LTO_NO_PREVAIL (DECL_FIELD_BIT_OFFSET (t));
> !         LTO_NO_PREVAIL (DECL_FCONTEXT (t));
>        }
>      }
> !   else if (TYPE_P (t))
>      {
> !       LTO_NO_PREVAIL (TYPE_CACHED_VALUES (t));
> !       LTO_SET_PREVAIL (TYPE_SIZE (t));
> !       LTO_SET_PREVAIL (TYPE_SIZE_UNIT (t));
> !       LTO_NO_PREVAIL (TYPE_ATTRIBUTES (t));
> !       LTO_NO_PREVAIL (TYPE_NAME (t));
>
> !       LTO_SET_PREVAIL (t->type.minval);
> !       LTO_SET_PREVAIL (t->type.maxval);
> !       LTO_SET_PREVAIL (t->type.binfo);
>
> !       LTO_SET_PREVAIL (TYPE_CONTEXT (t));
>
> !       LTO_NO_PREVAIL (TYPE_CANONICAL (t));
> !       LTO_NO_PREVAIL (TYPE_MAIN_VARIANT (t));
> !       LTO_NO_PREVAIL (TYPE_NEXT_VARIANT (t));
>      }
> !   else if (EXPR_P (t))
>      {
> !       int i;
> !       LTO_NO_PREVAIL (t->exp.block);
> !       for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i)
> !       LTO_SET_PREVAIL (TREE_OPERAND (t, i));
>      }
> !   else
>      {
> !       switch (code)
>        {
> !       case TREE_LIST:
> !         LTO_SET_PREVAIL (TREE_VALUE (t));
> !         LTO_SET_PREVAIL (TREE_PURPOSE (t));
> !         break;
> !       default:
> !         gcc_unreachable ();
>        }
>      }
>  }
> + #undef LTO_SET_PREVAIL
> + #undef LTO_NO_PREVAIL
>
>  /* Helper function of lto_fixup_decls. Walks the var and fn streams in STATE,
> !    replaces var and function decls with the corresponding prevailing def.  */
>
>  static void
> ! lto_fixup_state (struct lto_in_decl_state *state)
>  {
>    unsigned i, si;
>    struct lto_tree_ref_table *table;
> *************** lto_fixup_state (struct lto_in_decl_stat
> *** 1969,1986 ****
>      {
>        table = &state->streams[si];
>        for (i = 0; i < table->size; i++)
> !       walk_tree (table->trees + i, lto_fixup_tree, data, NULL);
>      }
>  }
>
> ! /* A callback of htab_traverse. Just extract a state from SLOT and the
> !    lto_fixup_data_t object from AUX and calls lto_fixup_state. */
>
>  static int
> ! lto_fixup_state_aux (void **slot, void *aux)
>  {
>    struct lto_in_decl_state *state = (struct lto_in_decl_state *) *slot;
> !   lto_fixup_state (state, (lto_fixup_data_t *) aux);
>    return 1;
>  }
>
> --- 2208,2229 ----
>      {
>        table = &state->streams[si];
>        for (i = 0; i < table->size; i++)
> !       {
> !         tree *tp = table->trees + i;
> !         if (VAR_OR_FUNCTION_DECL_P (*tp))
> !           *tp = lto_symtab_prevailing_decl (*tp);
> !       }
>      }
>  }
>
> ! /* A callback of htab_traverse. Just extracts a state from SLOT
> !    and calls lto_fixup_state. */
>
>  static int
> ! lto_fixup_state_aux (void **slot, void *aux ATTRIBUTE_UNUSED)
>  {
>    struct lto_in_decl_state *state = (struct lto_in_decl_state *) *slot;
> !   lto_fixup_state (state);
>    return 1;
>  }
>
> *************** static void
> *** 1991,2019 ****
>  lto_fixup_decls (struct lto_file_decl_data **files)
>  {
>    unsigned int i;
> -   tree decl;
> -   struct pointer_set_t *seen = pointer_set_create ();
> -   lto_fixup_data_t data;
>
> !   data.seen = seen;
>    for (i = 0; files[i]; i++)
>      {
>        struct lto_file_decl_data *file = files[i];
>        struct lto_in_decl_state *state = file->global_decl_state;
> !       lto_fixup_state (state, &data);
> !
> !       htab_traverse (file->function_decl_states, lto_fixup_state_aux, &data);
> !     }
>
> !   FOR_EACH_VEC_ELT (tree, lto_global_var_decls, i, decl)
> !     {
> !       tree saved_decl = decl;
> !       walk_tree (&decl, lto_fixup_tree, &data, NULL);
> !       if (decl != saved_decl)
> !       VEC_replace (tree, lto_global_var_decls, i, decl);
>      }
> -
> -   pointer_set_destroy (seen);
>  }
>
>  /* Read the options saved from each file in the command line.  Called
> --- 2234,2256 ----
>  lto_fixup_decls (struct lto_file_decl_data **files)
>  {
>    unsigned int i;
>
> !   for (i = 0; i < tree_with_vars->size; i++)
> !     {
> !       tree t = (tree)tree_with_vars->entries[i];
> !       if (t == (tree)HTAB_EMPTY_ENTRY || t == (tree)HTAB_DELETED_ENTRY)
> !       continue;

Ick.  Use FOR_EACH_HTAB_ELEMENT.

> !       lto_fixup_prevailing_decls (t);
> !     }
> !
>    for (i = 0; files[i]; i++)
>      {
>        struct lto_file_decl_data *file = files[i];
>        struct lto_in_decl_state *state = file->global_decl_state;
> !       lto_fixup_state (state);
>
> !       htab_traverse (file->function_decl_states, lto_fixup_state_aux, NULL);
>      }
>  }
>
>  /* Read the options saved from each file in the command line.  Called
> *************** static GTY((length ("real_file_count + 1
> *** 2109,2114 ****
> --- 2346,2353 ----
>     global call graph by aggregating all the sub-graphs found in each
>     file.  */
>
> + extern int ggc_alloced_and_marked_p (const void *p);
> +

?  I suppose a left-over.

>  static void
>  read_cgraph_and_symbols (unsigned nfiles, const char **fnames)
>  {
> *************** read_cgraph_and_symbols (unsigned nfiles
> *** 2144,2149 ****
> --- 2383,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);
> +
>    if (!quiet_flag)
>      fprintf (stderr, "Reading object files:");
>
> *************** read_cgraph_and_symbols (unsigned nfiles
> *** 2180,2185 ****
> --- 2423,2430 ----
>    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);
> *************** read_cgraph_and_symbols (unsigned nfiles
> *** 2211,2216 ****
> --- 2456,2463 ----
>
>    /* Fixup all decls and types and free the type hash tables.  */
>    lto_fixup_decls (all_file_decl_data);
> +   htab_delete (tree_with_vars);
> +   tree_with_vars = NULL;
>    free_gimple_type_tables ();
>    ggc_collect ();

The rest looks fine to me.

Richard.



More information about the Gcc-patches mailing list