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. */ + 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)); + + } + 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; + } + 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) \ + 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)); + 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)) + { + t = gimple_register_type (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; + } + } + 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 + && lto_streamer_cache_lookup (cache, f2, &ix)) + { + 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); + lto_streamer_cache_insert_at (cache, f1, ix); + } + } + + /* 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; ! 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); + 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 ();