+ This was causing an ICE in testsuite/g++.dg/lto/20080915.C. */
+ prev = NULL_TREE;
+ member = TYPE_FIELDS (type);
+ while (member)
+ {
+ if (TREE_CODE (member) == FIELD_DECL)
+ {
+ if (prev)
+ TREE_CHAIN (prev) = member;
+ else
+ TYPE_FIELDS (type) = member;
+ prev = member;
+ }
+
+ member = TREE_CHAIN (member);
+ }
+
+ if (prev)
+ TREE_CHAIN (prev) = NULL_TREE;
+ else
+ TYPE_FIELDS (type) = NULL_TREE;
+
+ TYPE_METHODS (type) = NULL_TREE;
+ if (TYPE_BINFO (type))
+ free_lang_data_in_binfo (TYPE_BINFO (type));
+ }
+ else
+ {
+ /* For non-aggregate types, clear out the language slot (which
+ overloads TYPE_BINFO). */
+ TYPE_LANG_SLOT_1 (type) = NULL_TREE;
+
+ if (INTEGRAL_TYPE_P (type)
+ || SCALAR_FLOAT_TYPE_P (type)
+ || FIXED_POINT_TYPE_P (type))
+ {
+ free_lang_data_in_one_sizepos (&TYPE_MIN_VALUE (type));
+ free_lang_data_in_one_sizepos (&TYPE_MAX_VALUE (type));
+ }
+ }
+
+ free_lang_data_in_one_sizepos (&TYPE_SIZE (type));
+ free_lang_data_in_one_sizepos (&TYPE_SIZE_UNIT (type));
+
+ if (debug_info_level < DINFO_LEVEL_TERSE
+ || (TYPE_CONTEXT (type)
+ && TREE_CODE (TYPE_CONTEXT (type)) != FUNCTION_DECL
+ && TREE_CODE (TYPE_CONTEXT (type)) != NAMESPACE_DECL))
+ TYPE_CONTEXT (type) = NULL_TREE;
+
+ if (debug_info_level < DINFO_LEVEL_TERSE)
+ TYPE_STUB_DECL (type) = NULL_TREE;
+}
+
+
+/* Return true if DECL may need an assembler name to be set. */
+
+static inline bool
+need_assembler_name_p (tree decl)
+{
+ /* Only FUNCTION_DECLs and VAR_DECLs are considered. */
+ if (TREE_CODE (decl) != FUNCTION_DECL
+ && TREE_CODE (decl) != VAR_DECL)
+ return false;
+
+ /* If DECL already has its assembler name set, it does not need a
+ new one. */
+ if (!HAS_DECL_ASSEMBLER_NAME_P (decl)
+ || DECL_ASSEMBLER_NAME_SET_P (decl))
+ return false;
+
+ /* Abstract decls do not need an assembler name. */
+ if (DECL_ABSTRACT (decl))
+ return false;
+
+ /* For VAR_DECLs, only static, public and external symbols need an
+ assembler name. */
+ if (TREE_CODE (decl) == VAR_DECL
+ && !TREE_STATIC (decl)
+ && !TREE_PUBLIC (decl)
+ && !DECL_EXTERNAL (decl))
+ return false;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ /* Do not set assembler name on builtins. Allow RTL expansion to
+ decide whether to expand inline or via a regular call. */
+ if (DECL_BUILT_IN (decl)
+ && DECL_BUILT_IN_CLASS (decl) != BUILT_IN_FRONTEND)
+ return false;
+
+ /* Functions represented in the callgraph need an assembler name. */
+ if (cgraph_get_node (decl) != NULL)
+ return true;
+
+ /* Unused and not public functions don't need an assembler name. */
+ if (!TREE_USED (decl) && !TREE_PUBLIC (decl))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Remove all the non-variable decls from BLOCK. LOCALS is the set of
+ variables in DECL_STRUCT_FUNCTION (FN)->local_decls. Every decl
+ in BLOCK that is not in LOCALS is removed. */
+
+static void
+free_lang_data_in_block (tree fn, tree block, struct pointer_set_t *locals)
+{
+ tree *tp, t;
+
+ tp = &BLOCK_VARS (block);
+ while (*tp)
+ {
+ if (!pointer_set_contains (locals, *tp))
+ *tp = TREE_CHAIN (*tp);
+ else
+ tp = &TREE_CHAIN (*tp);
+ }
+
+ for (t = BLOCK_SUBBLOCKS (block); t; t = BLOCK_CHAIN (t))
+ free_lang_data_in_block (fn, t, locals);
+}
+
+
+/* Reset all language specific information still present in symbol
+ DECL. */
+
+static void
+free_lang_data_in_decl (tree decl)
+{
+ gcc_assert (DECL_P (decl));
+
+ /* Give the FE a chance to remove its own data first. */
+ lang_hooks.free_lang_data (decl);
+
+ TREE_LANG_FLAG_0 (decl) = 0;
+ TREE_LANG_FLAG_1 (decl) = 0;
+ TREE_LANG_FLAG_2 (decl) = 0;
+ TREE_LANG_FLAG_3 (decl) = 0;
+ TREE_LANG_FLAG_4 (decl) = 0;
+ TREE_LANG_FLAG_5 (decl) = 0;
+ TREE_LANG_FLAG_6 (decl) = 0;
+
+ /* Identifiers need not have a type. */
+ if (DECL_NAME (decl))
+ TREE_TYPE (DECL_NAME (decl)) = NULL_TREE;
+
+ /* Ignore any intervening types, because we are going to clear their
+ TYPE_CONTEXT fields. */
+ if (TREE_CODE (decl) != FIELD_DECL
+ && TREE_CODE (decl) != FUNCTION_DECL)
+ DECL_CONTEXT (decl) = decl_function_context (decl);
+
+ if (DECL_CONTEXT (decl)
+ && TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL)
+ DECL_CONTEXT (decl) = NULL_TREE;
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ tree context = DECL_CONTEXT (decl);
+
+ if (context)
+ {
+ enum tree_code code = TREE_CODE (context);
+ if (code == FUNCTION_DECL && DECL_ABSTRACT (context))
+ {
+ /* Do not clear the decl context here, that will promote
+ all vars to global ones. */
+ DECL_INITIAL (decl) = NULL_TREE;
+ }
+
+ if (TREE_STATIC (decl))
+ DECL_CONTEXT (decl) = NULL_TREE;
+ }
+ }
+
+ free_lang_data_in_one_sizepos (&DECL_SIZE (decl));
+ free_lang_data_in_one_sizepos (&DECL_SIZE_UNIT (decl));
+ if (TREE_CODE (decl) == FIELD_DECL)
+ free_lang_data_in_one_sizepos (&DECL_FIELD_OFFSET (decl));
+
+ /* DECL_FCONTEXT is only used for debug info generation. */
+ if (TREE_CODE (decl) == FIELD_DECL
+ && debug_info_level < DINFO_LEVEL_TERSE)
+ DECL_FCONTEXT (decl) = NULL_TREE;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (gimple_has_body_p (decl))
+ {
+ tree t;
+ struct pointer_set_t *locals;
+
+ /* If DECL has a gimple body, then the context for its
+ arguments must be DECL. Otherwise, it doesn't really
+ matter, as we will not be emitting any code for DECL. In
+ general, there may be other instances of DECL created by
+ the front end and since PARM_DECLs are generally shared,
+ their DECL_CONTEXT changes as the replicas of DECL are
+ created. The only time where DECL_CONTEXT is important
+ is for the FUNCTION_DECLs that have a gimple body (since
+ the PARM_DECL will be used in the function's body). */
+ for (t = DECL_ARGUMENTS (decl); t; t = TREE_CHAIN (t))
+ DECL_CONTEXT (t) = decl;
+
+ /* Collect all the symbols declared in DECL. */
+ locals = pointer_set_create ();
+ t = DECL_STRUCT_FUNCTION (decl)->local_decls;
+ for (; t; t = TREE_CHAIN (t))
+ {
+ pointer_set_insert (locals, TREE_VALUE (t));
+
+ /* All the local symbols should have DECL as their
+ context. */
+ DECL_CONTEXT (TREE_VALUE (t)) = decl;
+ }
+
+ /* Get rid of any decl not in local_decls. */
+ free_lang_data_in_block (decl, DECL_INITIAL (decl), locals);
+
+ pointer_set_destroy (locals);
+ }
+
+ /* DECL_SAVED_TREE holds the GENERIC representation for DECL.
+ At this point, it is not needed anymore. */
+ DECL_SAVED_TREE (decl) = NULL_TREE;
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ tree expr = DECL_DEBUG_EXPR (decl);
+ if (expr
+ && TREE_CODE (expr) == VAR_DECL
+ && !TREE_STATIC (expr) && !DECL_EXTERNAL (expr))
+ SET_DECL_DEBUG_EXPR (decl, NULL_TREE);
+
+ if (DECL_EXTERNAL (decl)
+ && (!TREE_STATIC (decl) || !TREE_READONLY (decl)))
+ DECL_INITIAL (decl) = NULL_TREE;
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ DECL_INITIAL (decl) = NULL_TREE;
+
+ /* DECL_CONTEXT is overloaded as DECL_FIELD_CONTEXT for
+ FIELD_DECLs, which should be preserved. Otherwise,
+ we shouldn't be concerned with source-level lexical
+ nesting beyond this point. */
+ DECL_CONTEXT (decl) = NULL_TREE;
+ }
+}
+
+
+/* Data used when collecting DECLs and TYPEs for language data removal. */
+
+struct free_lang_data_d
+{
+ /* Worklist to avoid excessive recursion. */
+ VEC(tree,heap) *worklist;
+
+ /* Set of traversed objects. Used to avoid duplicate visits. */
+ struct pointer_set_t *pset;
+
+ /* Array of symbols to process with free_lang_data_in_decl. */
+ VEC(tree,heap) *decls;
+
+ /* Array of types to process with free_lang_data_in_type. */
+ VEC(tree,heap) *types;
+};
+
+
+/* Save all language fields needed to generate proper debug information
+ for DECL. This saves most fields cleared out by free_lang_data_in_decl. */
+
+static void
+save_debug_info_for_decl (tree t)
+{
+ /*struct saved_debug_info_d *sdi;*/
+
+ gcc_assert (debug_info_level > DINFO_LEVEL_TERSE && t && DECL_P (t));
+
+ /* FIXME. Partial implementation for saving debug info removed. */
+}
+
+
+/* Save all language fields needed to generate proper debug information
+ for TYPE. This saves most fields cleared out by free_lang_data_in_type. */
+
+static void
+save_debug_info_for_type (tree t)
+{
+ /*struct saved_debug_info_d *sdi;*/
+
+ gcc_assert (debug_info_level > DINFO_LEVEL_TERSE && t && TYPE_P (t));
+
+ /* FIXME. Partial implementation for saving debug info removed. */
+}
+
+
+/* Add type or decl T to one of the list of tree nodes that need their
+ language data removed. The lists are held inside FLD. */
+
+static void
+add_tree_to_fld_list (tree t, struct free_lang_data_d *fld)
+{
+ if (DECL_P (t))
+ {
+ VEC_safe_push (tree, heap, fld->decls, t);
+ if (debug_info_level > DINFO_LEVEL_TERSE)
+ save_debug_info_for_decl (t);
+ }
+ else if (TYPE_P (t))
+ {
+ VEC_safe_push (tree, heap, fld->types, t);
+ if (debug_info_level > DINFO_LEVEL_TERSE)
+ save_debug_info_for_type (t);
+ }
+ else
+ gcc_unreachable ();
+}
+
+/* Push tree node T into FLD->WORKLIST. */
+
+static inline void
+fld_worklist_push (tree t, struct free_lang_data_d *fld)
+{
+ if (t && !is_lang_specific (t) && !pointer_set_contains (fld->pset, t))
+ VEC_safe_push (tree, heap, fld->worklist, (t));
+}
+
+
+/* Operand callback helper for free_lang_data_in_node. *TP is the
+ subtree operand being considered. */
+
+static tree
+find_decls_types_r (tree *tp, int *ws, void *data)
+{
+ tree t = *tp;
+ struct free_lang_data_d *fld = (struct free_lang_data_d *) data;
+
+ if (TREE_CODE (t) == TREE_LIST)
+ return NULL_TREE;
+
+ /* Language specific nodes will be removed, so there is no need
+ to gather anything under them. */
+ if (is_lang_specific (t))
+ {
+ *ws = 0;
+ return NULL_TREE;
+ }
+
+ if (DECL_P (t))
+ {
+ /* Note that walk_tree does not traverse every possible field in
+ decls, so we have to do our own traversals here. */
+ add_tree_to_fld_list (t, fld);
+
+ fld_worklist_push (DECL_NAME (t), fld);
+ fld_worklist_push (DECL_CONTEXT (t), fld);
+ fld_worklist_push (DECL_SIZE (t), fld);
+ fld_worklist_push (DECL_SIZE_UNIT (t), fld);
+
+ /* We are going to remove everything under DECL_INITIAL for
+ TYPE_DECLs. No point walking them. */
+ if (TREE_CODE (t) != TYPE_DECL)
+ fld_worklist_push (DECL_INITIAL (t), fld);
+
+ fld_worklist_push (DECL_ATTRIBUTES (t), fld);
+ fld_worklist_push (DECL_ABSTRACT_ORIGIN (t), fld);
+
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ {
+ fld_worklist_push (DECL_ARGUMENTS (t), fld);
+ fld_worklist_push (DECL_RESULT (t), fld);
+ }
+ else if (TREE_CODE (t) == TYPE_DECL)
+ {
+ fld_worklist_push (DECL_ARGUMENT_FLD (t), fld);
+ fld_worklist_push (DECL_VINDEX (t), fld);
+ }
+ else if (TREE_CODE (t) == FIELD_DECL)
+ {
+ fld_worklist_push (DECL_FIELD_OFFSET (t), fld);
+ fld_worklist_push (DECL_BIT_FIELD_TYPE (t), fld);
+ fld_worklist_push (DECL_QUALIFIER (t), fld);
+ fld_worklist_push (DECL_FIELD_BIT_OFFSET (t), fld);
+ fld_worklist_push (DECL_FCONTEXT (t), fld);
+ }
+ else if (TREE_CODE (t) == VAR_DECL)
+ {
+ fld_worklist_push (DECL_SECTION_NAME (t), fld);
+ fld_worklist_push (DECL_COMDAT_GROUP (t), fld);
+ }
+
+ if ((TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL)
+ && DECL_HAS_VALUE_EXPR_P (t))
+ fld_worklist_push (DECL_VALUE_EXPR (t), fld);
+
+ if (TREE_CODE (t) != FIELD_DECL)
+ fld_worklist_push (TREE_CHAIN (t), fld);
+ *ws = 0;
+ }
+ else if (TYPE_P (t))
+ {
+ /* Note that walk_tree does not traverse every possible field in
+ types, so we have to do our own traversals here. */
+ add_tree_to_fld_list (t, fld);
+
+ if (!RECORD_OR_UNION_TYPE_P (t))
+ fld_worklist_push (TYPE_CACHED_VALUES (t), fld);
+ fld_worklist_push (TYPE_SIZE (t), fld);
+ fld_worklist_push (TYPE_SIZE_UNIT (t), fld);
+ fld_worklist_push (TYPE_ATTRIBUTES (t), fld);
+ fld_worklist_push (TYPE_POINTER_TO (t), fld);
+ fld_worklist_push (TYPE_REFERENCE_TO (t), fld);
+ fld_worklist_push (TYPE_NAME (t), fld);
+ fld_worklist_push (TYPE_MINVAL (t), fld);
+ if (!RECORD_OR_UNION_TYPE_P (t))
+ fld_worklist_push (TYPE_MAXVAL (t), fld);
+ fld_worklist_push (TYPE_MAIN_VARIANT (t), fld);
+ fld_worklist_push (TYPE_NEXT_VARIANT (t), fld);
+ fld_worklist_push (TYPE_CONTEXT (t), fld);
+ fld_worklist_push (TYPE_CANONICAL (t), fld);
+
+ if (RECORD_OR_UNION_TYPE_P (t) && TYPE_BINFO (t))
+ {
+ unsigned i;
+ tree tem;
+ for (i = 0; VEC_iterate (tree, BINFO_BASE_BINFOS (TYPE_BINFO (t)),
+ i, tem); ++i)
+ fld_worklist_push (TREE_TYPE (tem), fld);
+ tem = BINFO_VIRTUALS (TYPE_BINFO (t));
+ if (tem
+ /* The Java FE overloads BINFO_VIRTUALS for its own purpose. */
+ && TREE_CODE (tem) == TREE_LIST)
+ do
+ {
+ fld_worklist_push (TREE_VALUE (tem), fld);
+ tem = TREE_CHAIN (tem);
+ }
+ while (tem);
+ }
+ if (RECORD_OR_UNION_TYPE_P (t))
+ {
+ tree tem;
+ /* Push all TYPE_FIELDS - there can be interleaving interesting
+ and non-interesting things. */
+ tem = TYPE_FIELDS (t);
+ while (tem)
+ {
+ if (TREE_CODE (tem) == FIELD_DECL)
+ fld_worklist_push (tem, fld);
+ tem = TREE_CHAIN (tem);
+ }
+ }
+
+ fld_worklist_push (TREE_CHAIN (t), fld);
+ *ws = 0;
+ }
+
+ fld_worklist_push (TREE_TYPE (t), fld);
+
+ return NULL_TREE;
+}
+
+
+/* Find decls and types in T. */
+
+static void
+find_decls_types (tree t, struct free_lang_data_d *fld)
+{
+ while (1)
+ {
+ if (!pointer_set_contains (fld->pset, t))
+ walk_tree (&t, find_decls_types_r, fld, fld->pset);
+ if (VEC_empty (tree, fld->worklist))
+ break;
+ t = VEC_pop (tree, fld->worklist);
+ }
+}
+
+/* Translate all the types in LIST with the corresponding runtime
+ types. */
+
+static tree
+get_eh_types_for_runtime (tree list)
+{
+ tree head, prev;
+
+ if (list == NULL_TREE)
+ return NULL_TREE;
+
+ head = build_tree_list (0, lookup_type_for_runtime (TREE_VALUE (list)));
+ prev = head;
+ list = TREE_CHAIN (list);
+ while (list)
+ {
+ tree n = build_tree_list (0, lookup_type_for_runtime (TREE_VALUE (list)));
+ TREE_CHAIN (prev) = n;
+ prev = TREE_CHAIN (prev);
+ list = TREE_CHAIN (list);
+ }
+
+ return head;
+}
+
+
+/* Find decls and types referenced in EH region R and store them in
+ FLD->DECLS and FLD->TYPES. */
+
+static void
+find_decls_types_in_eh_region (eh_region r, struct free_lang_data_d *fld)
+{
+ switch (r->type)
+ {
+ case ERT_CLEANUP:
+ break;
+
+ case ERT_TRY:
+ {
+ eh_catch c;
+
+ /* The types referenced in each catch must first be changed to the
+ EH types used at runtime. This removes references to FE types
+ in the region. */
+ for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
+ {
+ c->type_list = get_eh_types_for_runtime (c->type_list);
+ walk_tree (&c->type_list, find_decls_types_r, fld, fld->pset);
+ }
+ }
+ break;
+
+ case ERT_ALLOWED_EXCEPTIONS:
+ r->u.allowed.type_list
+ = get_eh_types_for_runtime (r->u.allowed.type_list);
+ walk_tree (&r->u.allowed.type_list, find_decls_types_r, fld, fld->pset);
+ break;
+
+ case ERT_MUST_NOT_THROW:
+ walk_tree (&r->u.must_not_throw.failure_decl,
+ find_decls_types_r, fld, fld->pset);
+ break;
+ }
+}
+
+
+/* Find decls and types referenced in cgraph node N and store them in
+ FLD->DECLS and FLD->TYPES. Unlike pass_referenced_vars, this will
+ look for *every* kind of DECL and TYPE node reachable from N,
+ including those embedded inside types and decls (i.e,, TYPE_DECLs,
+ NAMESPACE_DECLs, etc). */
+
+static void
+find_decls_types_in_node (struct cgraph_node *n, struct free_lang_data_d *fld)
+{
+ basic_block bb;
+ struct function *fn;
+ tree t;
+
+ find_decls_types (n->decl, fld);
+
+ if (!gimple_has_body_p (n->decl))
+ return;
+
+ gcc_assert (current_function_decl == NULL_TREE && cfun == NULL);
+
+ fn = DECL_STRUCT_FUNCTION (n->decl);
+
+ /* Traverse locals. */
+ for (t = fn->local_decls; t; t = TREE_CHAIN (t))
+ find_decls_types (TREE_VALUE (t), fld);
+
+ /* Traverse EH regions in FN. */
+ {
+ eh_region r;
+ FOR_ALL_EH_REGION_FN (r, fn)
+ find_decls_types_in_eh_region (r, fld);
+ }
+
+ /* Traverse every statement in FN. */
+ FOR_EACH_BB_FN (bb, fn)
+ {
+ gimple_stmt_iterator si;
+ unsigned i;
+
+ for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
+ {
+ gimple phi = gsi_stmt (si);
+
+ for (i = 0; i < gimple_phi_num_args (phi); i++)
+ {
+ tree *arg_p = gimple_phi_arg_def_ptr (phi, i);
+ find_decls_types (*arg_p, fld);
+ }
+ }
+
+ for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+ {
+ gimple stmt = gsi_stmt (si);
+
+ for (i = 0; i < gimple_num_ops (stmt); i++)
+ {
+ tree arg = gimple_op (stmt, i);
+ find_decls_types (arg, fld);
+ }
+ }
+ }
+}
+
+
+/* Find decls and types referenced in varpool node N and store them in
+ FLD->DECLS and FLD->TYPES. Unlike pass_referenced_vars, this will
+ look for *every* kind of DECL and TYPE node reachable from N,
+ including those embedded inside types and decls (i.e,, TYPE_DECLs,
+ NAMESPACE_DECLs, etc). */
+
+static void
+find_decls_types_in_var (struct varpool_node *v, struct free_lang_data_d *fld)
+{
+ find_decls_types (v->decl, fld);
+}
+
+/* If T needs an assembler name, have one created for it. */
+
+void
+assign_assembler_name_if_neeeded (tree t)
+{
+ if (need_assembler_name_p (t))
+ {
+ /* When setting DECL_ASSEMBLER_NAME, the C++ mangler may emit
+ diagnostics that use input_location to show locus
+ information. The problem here is that, at this point,
+ input_location is generally anchored to the end of the file
+ (since the parser is long gone), so we don't have a good
+ position to pin it to.
+
+ To alleviate this problem, this uses the location of T's
+ declaration. Examples of this are
+ testsuite/g++.dg/template/cond2.C and
+ testsuite/g++.dg/template/pr35240.C. */
+ location_t saved_location = input_location;
+ input_location = DECL_SOURCE_LOCATION (t);
+
+ decl_assembler_name (t);
+
+ input_location = saved_location;
+ }
+}
+
+
+/* Free language specific information for every operand and expression
+ in every node of the call graph. This process operates in three stages:
+
+ 1- Every callgraph node and varpool node is traversed looking for
+ decls and types embedded in them. This is a more exhaustive
+ search than that done by find_referenced_vars, because it will
+ also collect individual fields, decls embedded in types, etc.
+
+ 2- All the decls found are sent to free_lang_data_in_decl.
+
+ 3- All the types found are sent to free_lang_data_in_type.
+
+ The ordering between decls and types is important because
+ free_lang_data_in_decl sets assembler names, which includes
+ mangling. So types cannot be freed up until assembler names have
+ been set up. */
+
+static void
+free_lang_data_in_cgraph (void)
+{
+ struct cgraph_node *n;
+ struct varpool_node *v;
+ struct free_lang_data_d fld;
+ tree t;
+ unsigned i;
+ alias_pair *p;
+
+ /* Initialize sets and arrays to store referenced decls and types. */
+ fld.pset = pointer_set_create ();
+ fld.worklist = NULL;
+ fld.decls = VEC_alloc (tree, heap, 100);
+ fld.types = VEC_alloc (tree, heap, 100);
+
+ /* Find decls and types in the body of every function in the callgraph. */
+ for (n = cgraph_nodes; n; n = n->next)
+ find_decls_types_in_node (n, &fld);
+
+ for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
+ find_decls_types (p->decl, &fld);
+
+ /* Find decls and types in every varpool symbol. */
+ for (v = varpool_nodes_queue; v; v = v->next_needed)
+ find_decls_types_in_var (v, &fld);
+
+ /* Set the assembler name on every decl found. We need to do this
+ now because free_lang_data_in_decl will invalidate data needed
+ for mangling. This breaks mangling on interdependent decls. */
+ for (i = 0; VEC_iterate (tree, fld.decls, i, t); i++)
+ assign_assembler_name_if_neeeded (t);
+
+ /* Traverse every decl found freeing its language data. */
+ for (i = 0; VEC_iterate (tree, fld.decls, i, t); i++)
+ free_lang_data_in_decl (t);
+
+ /* Traverse every type found freeing its language data. */
+ for (i = 0; VEC_iterate (tree, fld.types, i, t); i++)
+ free_lang_data_in_type (t);
+
+ pointer_set_destroy (fld.pset);
+ VEC_free (tree, heap, fld.worklist);
+ VEC_free (tree, heap, fld.decls);
+ VEC_free (tree, heap, fld.types);
+}
+
+
+/* Free resources that are used by FE but are not needed once they are done. */
+
+static unsigned
+free_lang_data (void)
+{
+ unsigned i;
+
+ /* If we are the LTO frontend we have freed lang-specific data already. */
+ if (in_lto_p
+ || !flag_generate_lto)
+ return 0;
+
+ /* Allocate and assign alias sets to the standard integer types
+ while the slots are still in the way the frontends generated them. */
+ for (i = 0; i < itk_none; ++i)
+ if (integer_types[i])
+ TYPE_ALIAS_SET (integer_types[i]) = get_alias_set (integer_types[i]);
+
+ /* Traverse the IL resetting language specific information for
+ operands, expressions, etc. */
+ free_lang_data_in_cgraph ();
+
+ /* Create gimple variants for common types. */
+ ptrdiff_type_node = integer_type_node;
+ fileptr_type_node = ptr_type_node;
+ if (TREE_CODE (boolean_type_node) != BOOLEAN_TYPE
+ || (TYPE_MODE (boolean_type_node)
+ != mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0))
+ || TYPE_PRECISION (boolean_type_node) != 1
+ || !TYPE_UNSIGNED (boolean_type_node))
+ {
+ boolean_type_node = make_unsigned_type (BOOL_TYPE_SIZE);
+ TREE_SET_CODE (boolean_type_node, BOOLEAN_TYPE);
+ TYPE_MAX_VALUE (boolean_type_node) = build_int_cst (boolean_type_node, 1);
+ TYPE_PRECISION (boolean_type_node) = 1;
+ boolean_false_node = TYPE_MIN_VALUE (boolean_type_node);
+ boolean_true_node = TYPE_MAX_VALUE (boolean_type_node);
+ }
+
+ /* Unify char_type_node with its properly signed variant. */
+ if (TYPE_UNSIGNED (char_type_node))
+ unsigned_char_type_node = char_type_node;
+ else
+ signed_char_type_node = char_type_node;
+
+ /* Reset some langhooks. Do not reset types_compatible_p, it may
+ still be used indirectly via the get_alias_set langhook. */
+ lang_hooks.callgraph.analyze_expr = NULL;
+ lang_hooks.dwarf_name = lhd_dwarf_name;
+ lang_hooks.decl_printable_name = gimple_decl_printable_name;
+ lang_hooks.set_decl_assembler_name = lhd_set_decl_assembler_name;
+ lang_hooks.fold_obj_type_ref = gimple_fold_obj_type_ref;
+
+ /* Reset diagnostic machinery. */
+ diagnostic_starter (global_dc) = default_diagnostic_starter;
+ diagnostic_finalizer (global_dc) = default_diagnostic_finalizer;
+ diagnostic_format_decoder (global_dc) = default_tree_printer;
+
+ return 0;
+}
+
+
+struct simple_ipa_opt_pass pass_ipa_free_lang_data =
+{
+ {
+ SIMPLE_IPA_PASS,
+ "*free_lang_data", /* name */
+ NULL, /* gate */
+ free_lang_data, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IPA_FREE_LANG_DATA, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_ggc_collect /* todo_flags_finish */
+ }
+};
+
+/* Return nonzero if IDENT is a valid name for attribute ATTR,
+ or zero if not.
+
+ We try both `text' and `__text__', ATTR may be either one. */
+/* ??? It might be a reasonable simplification to require ATTR to be only
+ `text'. One might then also require attribute lists to be stored in
+ their canonicalized form. */
+
+static int
+is_attribute_with_length_p (const char *attr, int attr_len, const_tree ident)
+{
+ int ident_len;
+ const char *p;
+
+ if (TREE_CODE (ident) != IDENTIFIER_NODE)
+ return 0;
+
+ p = IDENTIFIER_POINTER (ident);
+ ident_len = IDENTIFIER_LENGTH (ident);
+
+ if (ident_len == attr_len
+ && strcmp (attr, p) == 0)
+ return 1;
+
+ /* If ATTR is `__text__', IDENT must be `text'; and vice versa. */
+ if (attr[0] == '_')
+ {
+ gcc_assert (attr[1] == '_');
+ gcc_assert (attr[attr_len - 2] == '_');
+ gcc_assert (attr[attr_len - 1] == '_');
+ if (ident_len == attr_len - 4
+ && strncmp (attr + 2, p, attr_len - 4) == 0)
+ return 1;
+ }
+ else
+ {
+ if (ident_len == attr_len + 4
+ && p[0] == '_' && p[1] == '_'
+ && p[ident_len - 2] == '_' && p[ident_len - 1] == '_'
+ && strncmp (attr, p + 2, attr_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return nonzero if IDENT is a valid name for attribute ATTR,
+ or zero if not.
+
+ We try both `text' and `__text__', ATTR may be either one. */
+
+int
+is_attribute_p (const char *attr, const_tree ident)
+{
+ return is_attribute_with_length_p (attr, strlen (attr), ident);
+}
+
+/* Given an attribute name and a list of attributes, return a pointer to the
+ attribute's list element if the attribute is part of the list, or NULL_TREE
+ if not found. If the attribute appears more than once, this only
+ returns the first occurrence; the TREE_CHAIN of the return value should
+ be passed back in if further occurrences are wanted. */
+
+tree
+lookup_attribute (const char *attr_name, tree list)
+{
+ tree l;
+ size_t attr_len = strlen (attr_name);
+
+ for (l = list; l; l = TREE_CHAIN (l))
+ {
+ gcc_assert (TREE_CODE (TREE_PURPOSE (l)) == IDENTIFIER_NODE);
+ if (is_attribute_with_length_p (attr_name, attr_len, TREE_PURPOSE (l)))
+ return l;
+ }
+ return NULL_TREE;
+}
+
+/* Remove any instances of attribute ATTR_NAME in LIST and return the
+ modified list. */
+
+tree
+remove_attribute (const char *attr_name, tree list)
+{
+ tree *p;
+ size_t attr_len = strlen (attr_name);
+
+ for (p = &list; *p; )
+ {
+ tree l = *p;
+ gcc_assert (TREE_CODE (TREE_PURPOSE (l)) == IDENTIFIER_NODE);
+ if (is_attribute_with_length_p (attr_name, attr_len, TREE_PURPOSE (l)))
+ *p = TREE_CHAIN (l);
+ else
+ p = &TREE_CHAIN (l);
+ }
+
+ return list;
+}
+
+/* Return an attribute list that is the union of a1 and a2. */
+
+tree
+merge_attributes (tree a1, tree a2)
+{
+ tree attributes;
+
+ /* Either one unset? Take the set one. */
+
+ if ((attributes = a1) == 0)
+ attributes = a2;
+
+ /* One that completely contains the other? Take it. */
+
+ else if (a2 != 0 && ! attribute_list_contained (a1, a2))