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