diff options
Diffstat (limited to 'gcc-4.9/gcc/l-ipo.c')
-rw-r--r-- | gcc-4.9/gcc/l-ipo.c | 2394 |
1 files changed, 2394 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/l-ipo.c b/gcc-4.9/gcc/l-ipo.c new file mode 100644 index 000000000..843ad557d --- /dev/null +++ b/gcc-4.9/gcc/l-ipo.c @@ -0,0 +1,2394 @@ +/* Copyright (C) 2009. Free Software Foundation, Inc. + Contributed by Xinliang David Li (davidxl@google.com) and + Raksit Ashok (raksit@google.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "is-a.h" +#include "predict.h" +#include "function.h" +#include "basic-block.h" +#include "stor-layout.h" +#include "pointer-set.h" +#include "stringpool.h" +#include "c-family/c-common.h" +#include "toplev.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "diagnostic.h" +#include "debug.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "cgraph.h" +#include "l-ipo.h" +#include "coverage.h" +#include "gcov-io.h" +#include "timevar.h" +#include "vec.h" +#include "params.h" + +unsigned ggc_total_memory; /* in KB */ + +struct GTY(()) saved_module_scope +{ + vec<tree, va_gc> *module_decls; + unsigned module_id; +}; + +static GTY (()) struct saved_module_scope *current_module_scope; +static GTY ((param_is (saved_module_scope))) htab_t saved_module_scope_map; +static int primary_module_last_funcdef_no = 0; +/* Function id space for each module are qualified by the module id. After all the files + are parsed, we need to reset the funcdef_no to the max value from all module so that + the function clones do not assigned with ids colliding with some other orignal function + in the same module. */ +static int max_funcdef_no = 0; +static location_t primary_module_last_loc; +/* Primary module pending templates. */ +/* Referenced asm ids in primary module. */ +static GTY (()) vec<tree, va_gc> *referenced_asm_ids = NULL; +bool parser_parsing_start = false; +/* Nonzero if we're done parsing and into end-of-file activities. */ +int at_eof; + +static int aggr_has_equiv_id (tree t1, tree t2); + +/* Module scope hash function. */ + +static hashval_t +htab_module_scope_hash (const void *ent) +{ + const struct saved_module_scope *const entry + = (const struct saved_module_scope *) ent; + return (hashval_t) entry->module_id; +} + +/* Module scope equality function. */ + +static int +htab_module_scope_eq (const void *ent1, const void *ent2) +{ + const struct saved_module_scope *const entry1 + = (const struct saved_module_scope *) ent1; + const struct saved_module_scope *const entry2 + = (const struct saved_module_scope *) ent2; + + return entry1->module_id == entry2->module_id; +} + +/* Returns the module scope given a module id MOD_ID. */ + +static struct saved_module_scope * +get_module_scope (unsigned mod_id) +{ + struct saved_module_scope **slot, key, *module_scope; + + gcc_assert (mod_id); + + if (saved_module_scope_map == NULL) + saved_module_scope_map = htab_create_ggc (10, htab_module_scope_hash, + htab_module_scope_eq, NULL); + key.module_id = mod_id; + slot = (struct saved_module_scope **) + htab_find_slot (saved_module_scope_map, &key, INSERT); + module_scope = *slot; + if (!module_scope) + { + module_scope = ggc_alloc_cleared_saved_module_scope (); + module_scope->module_id = mod_id; + *slot = module_scope; + } + return module_scope; +} + +/* Allocate memory for struct lang_decl for tree T. */ + +static struct lang_decl * +alloc_lang_decl (tree t) +{ + size_t size; + size = lang_hooks.l_ipo.get_lang_decl_size (t); + return ggc_alloc_cleared_lang_decl (size); +} + +/* Return a cloned copy of tree SRC. */ + +tree +lipo_save_decl (tree src) +{ + tree saved = copy_node (src); + enum tree_code tc = TREE_CODE (src); + if (TREE_CODE_CLASS (tc) == tcc_declaration) + { + struct lang_decl *ls = NULL; + struct function *func = NULL; + DECL_CONTEXT (saved) = DECL_CONTEXT (src); + if (DECL_LANG_SPECIFIC (src)) + { + ls = alloc_lang_decl (src); + memcpy (ls, DECL_LANG_SPECIFIC (src), + lang_hooks.l_ipo.get_lang_decl_size (src)); + } + DECL_LANG_SPECIFIC (saved) = ls; + if (tc == FUNCTION_DECL && DECL_STRUCT_FUNCTION (src)) + { + func = ggc_alloc_cleared_function (); + *func = *(DECL_STRUCT_FUNCTION (src)); + DECL_STRUCT_FUNCTION (saved) = func; + } + } + else + { + gcc_assert (TREE_CODE_CLASS (tc) == tcc_type && + TYPE_MAIN_VARIANT (src) == src); + TYPE_CONTEXT (saved) = TYPE_CONTEXT (src); + lang_hooks.l_ipo.dup_lang_type (src, saved); + } + + return saved; +} + +/* Copy tree SAVED to tree DEST. */ + +void +lipo_restore_decl (tree dest, tree saved) +{ + enum tree_code tc; + unsigned old_uid; + struct lang_decl *oldls; + + tc = TREE_CODE (saved); + if (TREE_CODE_CLASS (tc) == tcc_declaration) + { + struct function *oldfunc = NULL; + old_uid = DECL_UID (dest); + oldls = DECL_LANG_SPECIFIC (dest); + oldfunc + = (tc == FUNCTION_DECL ? DECL_STRUCT_FUNCTION (dest) : NULL); + + memcpy ((char *) dest + sizeof (struct tree_common), + (char *) saved + sizeof (struct tree_common), + sizeof (struct tree_decl_common) - sizeof (struct tree_common)); + + if (tc == FUNCTION_DECL) + memcpy ((char *) dest + sizeof (struct tree_decl_common), + (char *) saved + sizeof (struct tree_decl_common), + sizeof (struct tree_function_decl) + - sizeof (struct tree_decl_common)); + + DECL_UID (dest) = old_uid; + if (DECL_LANG_SPECIFIC (saved)) + { + if (!oldls) + oldls = alloc_lang_decl (dest); + memcpy (oldls, DECL_LANG_SPECIFIC (saved), + lang_hooks.l_ipo.get_lang_decl_size (saved)); + DECL_LANG_SPECIFIC (dest) = oldls; + } + else + DECL_LANG_SPECIFIC (dest) = NULL; + + if (tc == FUNCTION_DECL) + { + if (DECL_STRUCT_FUNCTION (saved)) + { + if (!oldfunc) + oldfunc = ggc_alloc_cleared_function (); + *oldfunc = *(DECL_STRUCT_FUNCTION (saved)); + DECL_STRUCT_FUNCTION (dest) = oldfunc; + } + else + DECL_STRUCT_FUNCTION (dest) = NULL; + } + } + else + { + gcc_assert (TREE_CODE_CLASS (tc) == tcc_type); + lang_hooks.l_ipo.copy_lang_type (saved, dest); + } +} + + +/* Return the name for tree TD which is either a decl or type. */ + +tree +get_type_or_decl_name (tree td) +{ + tree id; + + if (DECL_P (td)) + id = DECL_NAME (td); + else + { + id = TYPE_NAME (td); + if (DECL_P (id)) + id = DECL_NAME (id); + } + return id; +} + +/* For a DECL (a type or a decl) in SCOPE, check to see if it is in + global or namespace scope. If yes, add it to the current module scope. */ + +void +add_decl_to_current_module_scope (tree decl, void *scope) +{ + struct saved_module_scope *module_scope; + tree id; + + if (!flag_dyn_ipa) + return; + + if (!parser_parsing_start) + { + /* The source file may contains only global variable declations + -- there is no module grouping data associated with it, so + neither primary_module_id nor current_module_id is set. */ + lang_hooks.l_ipo.add_built_in_decl (decl); + return; + } + + if (!L_IPO_COMP_MODE) + return; + + if (!lang_hooks.l_ipo.has_global_name (decl, scope)) + return; + + /* Unlike C++ where names are attached to type decls, for C, the type name + is identifier node. Thus we need to track type names as well. */ + id = get_type_or_decl_name (decl); + if (!id) + return; + + module_scope = current_module_scope; + gcc_assert (module_scope && module_scope->module_id == current_module_id); + vec_safe_push (module_scope->module_decls, decl); +} + +/* Clear name bindings for all decls created in MODULE_SCOPE. */ + +static void +clear_module_scope_bindings (struct saved_module_scope *module_scope) +{ + size_t i; + tree decl; + + for (i = 0; + vec_safe_iterate (module_scope->module_decls, i, &decl); + ++i) + { + lang_hooks.l_ipo.clear_global_name_bindings ( + get_type_or_decl_name (decl)); + /* Now force creating assembly name. */ + if (VAR_OR_FUNCTION_DECL_P (decl)) + { + tree assembler_name; + + if (HAS_DECL_ASSEMBLER_NAME_P (decl) + && DECL_ASSEMBLER_NAME_SET_P (decl)) + { + assembler_name = DECL_ASSEMBLER_NAME (decl); + lang_hooks.l_ipo.clear_global_name_bindings (assembler_name); + } + } + } +} + +/* The referenced attribute of a decl is not associated with the + decl itself but with the assembler name. Remember the referenced + bits before clearing them. */ + +static void +save_assembler_name_reference_bit (void) +{ + varpool_get_referenced_asm_ids (&referenced_asm_ids); +} + +/* Clear the reference bits for assembler names before closing the + module scope. */ + +static void +clear_assembler_name_reference_bit (void) +{ + varpool_clear_asm_id_reference_bit (); +} + +/* Restore the reference bits for assembler names. */ + +static void +restore_assembler_name_reference_bit (void) +{ + size_t i; + tree nm; + for (i = 0; + vec_safe_iterate (referenced_asm_ids, i, &nm); + ++i) + TREE_SYMBOL_REFERENCED (nm) = 1; +} + +/* Set up the module scope before the parsing of the + associated source file. */ + +void +push_module_scope (void) +{ + struct saved_module_scope *prev_module_scope; + + if (!flag_dyn_ipa || !L_IPO_COMP_MODE) + { + parser_parsing_start = true; + return; + } + + prev_module_scope = current_module_scope; + if (L_IPO_IS_PRIMARY_MODULE) + { + gcc_assert (!prev_module_scope); + lang_hooks.l_ipo.save_built_in_decl_pre_parsing (); + parser_parsing_start = true; + } + + gcc_assert (current_module_id); + + /* Set up the module scope. */ + current_module_scope = get_module_scope (current_module_id); + return; +} + +/* Restore the shared decls to their post parsing states. */ + +static void +restore_post_parsing_states (void) +{ + current_module_id = primary_module_id; + current_module_scope = get_module_scope (primary_module_id); + set_funcdef_no (max_funcdef_no); + input_location = primary_module_last_loc; + + restore_assembler_name_reference_bit (); + lang_hooks.l_ipo.restore_built_in_decl_post_module_parsing (); +} + +/* Pop the current module scope (by clearing name bindings etc.) + and prepare for parsing of the next module. In particular, + built-in decls need to be restored to the state before file + parsing starts. */ + +void +pop_module_scope (void) +{ + bool is_last = false; + int last_funcdef_no; + + if (!flag_dyn_ipa || !L_IPO_COMP_MODE) + return; + + gcc_assert (current_module_id && current_module_scope); + + if (L_IPO_IS_PRIMARY_MODULE) + primary_module_last_loc = input_location; + + at_eof = 1; + cgraph_process_same_body_aliases (); + lang_hooks.l_ipo.process_pending_decls (input_location); + lang_hooks.l_ipo.clear_deferred_fns (); + at_eof = 0; + + is_last = is_last_module (current_module_id); + + last_funcdef_no = get_last_funcdef_no (); + if (last_funcdef_no > max_funcdef_no) + max_funcdef_no = last_funcdef_no; + + lang_hooks.l_ipo.save_built_in_decl_post_module_parsing (); + /* Save primary module state if needed (when module group + size > 1) */ + if (L_IPO_IS_PRIMARY_MODULE && num_in_fnames > 1) + { + save_assembler_name_reference_bit (); + primary_module_last_funcdef_no = last_funcdef_no; + } + + if (!is_last) + { + /* More aux modules are anticipated, clear + the parsing state. */ + gcc_assert (num_in_fnames > 1); + clear_assembler_name_reference_bit (); + clear_module_scope_bindings (current_module_scope); + /* Restore symtab bindings for builtins */ + lang_hooks.l_ipo.restore_built_in_decl_pre_parsing (); + /* The map can not be cleared because the names of operator + decls are used to store the information about the conversion + target type. This forces the coversion operator ids to be + incremented across different modules, and assember id must + be used for checksum computation. */ + /* cp_clear_conv_type_map (); */ + } + else if (num_in_fnames > 1) + { + clear_module_scope_bindings (current_module_scope); + restore_post_parsing_states (); + } + else + gcc_assert (L_IPO_IS_PRIMARY_MODULE && num_in_fnames == 1); +} + + +/* Type merging support for LIPO */ + +struct type_ec +{ + tree rep_type; + vec<tree> *eq_types; +}; + +static vec<tree> *pending_types = NULL; +static struct pointer_set_t *type_set = NULL; +static htab_t type_hash_tab = NULL; + +/* Hash function for the type table. */ + +static hashval_t +type_hash_hash (const void *ent) +{ + tree type, name; + const struct type_ec *const entry + = (const struct type_ec *) ent; + + type = entry->rep_type; + name = TYPE_NAME (type); + if (DECL_P (name)) + name = DECL_NAME (name); + + return htab_hash_string (IDENTIFIER_POINTER (name)); +} + +/* Equality function for type hash table. */ + +static int +type_hash_eq (const void *ent1, const void *ent2) +{ + tree type1, type2; + const struct type_ec *const entry1 + = (const struct type_ec *) ent1; + const struct type_ec *const entry2 + = (const struct type_ec *) ent2; + + type1 = entry1->rep_type; + type2 = entry2->rep_type; + + return aggr_has_equiv_id (type1, type2); +} + +/* Function to delete type hash entries. */ + +static void +type_hash_del (void *ent) +{ + struct type_ec *const entry + = (struct type_ec *) ent; + + vec_free (entry->eq_types); + free (entry); +} + +struct GTY(()) type_ent +{ + tree type; + unsigned eq_id; +}; + +static GTY ((param_is (type_ent))) htab_t l_ipo_type_tab = 0; +static unsigned l_ipo_eq_id = 0; + +/* Address hash function for struct type_ent. */ + +static hashval_t +type_addr_hash (const void *ent) +{ + const struct type_ent *const entry + = (const struct type_ent *) ent; + return (hashval_t) (uintptr_t) entry->type; +} + +/* Address equality function for type_ent. */ + +static int +type_addr_eq (const void *ent1, const void *ent2) +{ + const struct type_ent *const entry1 + = (const struct type_ent *) ent1; + const struct type_ent *const entry2 + = (const struct type_ent *) ent2; + return entry1->type == entry2->type; +} + +/* Returns 1 if NS1 and NS2 refer to the same namespace. */ + +static int +is_ns_equiv (tree ns1, tree ns2) +{ + tree n1, n2; + if (ns1 == NULL && ns2 == NULL) + return 1; + + if ((!ns1 && ns2) || (ns1 && !ns2)) + return 0; + + gcc_assert (DECL_P (ns1) && DECL_P (ns2)); + + if (!is_ns_equiv (DECL_CONTEXT (ns1), + DECL_CONTEXT (ns2))) + return 0; + + n1 = DECL_NAME (ns1); + n2 = DECL_NAME (ns2); + if (n1 == 0 && n2 == 0) + /* Conservative (which can happen when two NSes are from + different modules but with same UID) quivalence is allowed. */ + return DECL_UID (ns1) == DECL_UID (ns2); + if (!n1 || !n2) + return 0; + + if (!strcmp (IDENTIFIER_POINTER (n1), + IDENTIFIER_POINTER (n2))) + return 1; + + return 0; +} + +/* Returns 1 if aggregate type T1 and T2 have equivalent qualified + ids. */ + +static int +aggr_has_equiv_id (tree t1, tree t2) +{ + int ctx_match; + tree ctx1, ctx2, tn1, tn2; + gcc_assert (TYPE_P (t1) && TYPE_P (t2)); + + ctx1 = TYPE_CONTEXT (t1); + ctx2 = TYPE_CONTEXT (t2); + + if ((ctx1 && !ctx2) || (!ctx1 && ctx2)) + return 0; + + if (ctx1 && TREE_CODE (ctx1) != TREE_CODE (ctx2)) + return 0; + + if (ctx1 && (TREE_CODE (ctx1) == FUNCTION_DECL + || TREE_CODE (ctx1) == BLOCK)) + return 0; + + if (!ctx1) + { + ctx_match = 1; + gcc_assert (!ctx2); + } + else if (TREE_CODE (ctx1) == NAMESPACE_DECL) + ctx_match = is_ns_equiv (ctx1, ctx2); + else if (TYPE_P (ctx1)) + ctx_match = aggr_has_equiv_id (ctx1, ctx2); + else + { + gcc_assert (TREE_CODE (ctx1) == TRANSLATION_UNIT_DECL); + ctx_match = 1; + } + + if (!ctx_match) + return 0; + + /* Now compare the name of the types. */ + tn1 = TYPE_NAME (t1); + tn2 = TYPE_NAME (t2); + if ((tn1 && !tn2) || !(tn1 && tn2)) + return 0; + else if (!tn1 && !tn2) + /* Be conservative on unamed types. */ + return 1; + + if (DECL_P (tn1)) + tn1 = DECL_NAME (tn1); + if (DECL_P (tn2)) + tn2 = DECL_NAME (tn2); + if (strcmp (IDENTIFIER_POINTER (tn1), + IDENTIFIER_POINTER (tn2))) + return 0; + + return lang_hooks.l_ipo.cmp_lang_type (t1, t2); +} + +/* Return the canonical type of the type's main variant. */ +static inline tree +get_norm_type (const_tree type) +{ + tree cano_type = TYPE_MAIN_VARIANT (type); + if (TYPE_CANONICAL (cano_type)) + cano_type = TYPE_CANONICAL (cano_type); + + return cano_type; +} + +/* Return 1 if type T1 and T2 are equivalent. Struct/union/class + types are compared using qualified name ids. Alias sets of + equivalent types will be merged. Client code may choose to do + structural equivalence check for sanity. Note the difference + between the types_compatible_p (and its langhooks subroutines) + and this interface. The former is mainly used to remove useless + type conversion and value numbering computation. It returns 1 + only when it is sure and should not be used in contexts where + erroneously returning 0 causes problems. This interface + lipo_cmp_type behaves differently - it returns 1 when it is not + sure -- as the primary purpose of the interface is for alias + set computation. */ + +int +lipo_cmp_type (tree t1, tree t2) +{ + if (TREE_CODE (t1) != TREE_CODE (t2)) + return 0; + if (TYPE_READONLY (t1) != TYPE_READONLY (t2)) + return 0; + if (TYPE_VOLATILE (t1) != TYPE_VOLATILE (t2)) + return 0; + + t1 = get_norm_type (t1); + t2 = get_norm_type (t2); + + switch (TREE_CODE (t1)) + { + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + return aggr_has_equiv_id (t1, t2); + + case POINTER_TYPE: + case REFERENCE_TYPE: + case COMPLEX_TYPE: + case TYPE_PACK_EXPANSION: + return lipo_cmp_type (TREE_TYPE (t1), TREE_TYPE (t2)); + case ARRAY_TYPE: + return (TYPE_DOMAIN (t1) == NULL || TYPE_DOMAIN (t2) == NULL + || (lipo_cmp_type (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2)) + && lipo_cmp_type (TREE_TYPE (t1), TREE_TYPE (t2)))); + case METHOD_TYPE: + return lipo_cmp_type (TYPE_METHOD_BASETYPE (t1), + TYPE_METHOD_BASETYPE (t2)); + case FUNCTION_TYPE: + { + tree arg1, arg2; + for (arg1 = TYPE_ARG_TYPES (t1), arg2 = TYPE_ARG_TYPES (t2); + arg1 && arg2; + arg1 = TREE_CHAIN (arg1), arg2 = TREE_CHAIN (arg2)) + if (!lipo_cmp_type (TREE_VALUE (arg1), + TREE_VALUE (arg2))) + return 0; + if (arg1 || arg2) + return 0; + return 1; + } + case OFFSET_TYPE: + return lipo_cmp_type (TYPE_OFFSET_BASETYPE (t1), + TYPE_OFFSET_BASETYPE (t2)); + case ENUMERAL_TYPE: + return lipo_cmp_type (TREE_TYPE (t1), TREE_TYPE (t2)); + case REAL_TYPE: + case FIXED_POINT_TYPE: + case INTEGER_TYPE: + return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2) + && TYPE_MODE (t1) == TYPE_MODE (t2) + && TYPE_MIN_VALUE (t1) == TYPE_MIN_VALUE (t2) + && TYPE_MAX_VALUE (t1) == TYPE_MAX_VALUE (t2)); + case VECTOR_TYPE: + return (TYPE_VECTOR_SUBPARTS (t1) == TYPE_VECTOR_SUBPARTS (t2) + && lipo_cmp_type (TREE_TYPE (t1), TREE_TYPE (t2))); + case VOID_TYPE: + case BOOLEAN_TYPE: + case NULLPTR_TYPE: + return 1; + case TEMPLATE_TYPE_PARM: + return 1; + default: + gcc_unreachable (); + } +} + +#ifndef ANON_AGGRNAME_PREFIX +#define ANON_AGGRNAME_PREFIX "__anon_" +#endif +#ifndef ANON_AGGRNAME_P +#define ANON_AGGRNAME_P(ID_NODE) \ + (!strncmp (IDENTIFIER_POINTER (ID_NODE), ANON_AGGRNAME_PREFIX, \ + sizeof (ANON_AGGRNAME_PREFIX) - 1)) +#endif + +/* Callback function used in tree walk to find referenced struct types. */ + +static tree +find_struct_types (tree *tp, + int *walk_subtrees ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + if (!(*tp)) + return NULL_TREE; + + if (TYPE_P (*tp)) + { + if (lang_hooks.l_ipo.is_compiler_generated_type (*tp)) + return NULL_TREE; + + switch (TREE_CODE (*tp)) + { + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + tree cano_type, name; + tree context; + tree field; + + cano_type = get_norm_type (*tp); + name = TYPE_NAME (cano_type); + if (!name) + { + /* the main variant of typedef of unnamed struct + has no name, use the orignal type for equivalence. */ + cano_type = *tp; + name = TYPE_NAME (cano_type); + } + if (!name) + return NULL_TREE; + if (DECL_P (name) + && (DECL_IGNORED_P (name) + || ANON_AGGRNAME_P (DECL_NAME (name)))) + return NULL_TREE; + + if (!pointer_set_insert (type_set, cano_type)) + pending_types->safe_push (cano_type); + else + return NULL_TREE; /* Or use walk tree without dups. */ + + context = TYPE_CONTEXT (cano_type); + if (context && TYPE_P (context)) + walk_tree (&context, find_struct_types, NULL, NULL); + + /* Instantiate a nested work as the tree walker does not + get to the fields. */ + if (TYPE_BINFO (cano_type)) + { + int i; + tree binfo, base_binfo; + + for (binfo = TYPE_BINFO (cano_type), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + walk_tree (&BINFO_TYPE (base_binfo), find_struct_types, + NULL, NULL); + } + for (field = TYPE_FIELDS (cano_type); + field != 0; + field = TREE_CHAIN (field)) + walk_tree (&TREE_TYPE (field), find_struct_types, + NULL, NULL); + return NULL_TREE; + } + default: + return NULL_TREE; + } + } + else if (DECL_P (*tp)) + /* walk tree does not walk down decls, so do a nested walk here. */ + walk_tree (&(TREE_TYPE (*tp)), find_struct_types, NULL, NULL); + + return NULL_TREE; +} + +/* Collect referenced struct types. */ + +static void +cgraph_collect_type_referenced (void) +{ + basic_block bb; + gimple_stmt_iterator gi; + + FOR_EACH_BB_FN (bb, cfun) + { + for (gi = gsi_start_bb (bb); !gsi_end_p (gi); gsi_next (&gi)) + { + unsigned i; + gimple stmt = gsi_stmt (gi); + for (i = 0; i < gimple_num_ops (stmt); i++) + walk_tree (gimple_op_ptr (stmt, i), find_struct_types, NULL, NULL); + } + } +} + +/* Check type equivalence. Returns 1 if T1 and T2 are equivalent + for tbaa; return 0 if not. -1 is returned if it is unknown. */ + +int +equivalent_struct_types_for_tbaa (const_tree t1, const_tree t2) +{ + struct type_ent key, *tent1, *tent2, **slot; + + if (!l_ipo_type_tab) + return -1; + + t1 = get_norm_type (t1); + t2 = get_norm_type (t2); + + key.type = (tree) (uintptr_t) t1; + slot = (struct type_ent **) + htab_find_slot (l_ipo_type_tab, &key, NO_INSERT); + if (!slot || !*slot) + return -1; + tent1 = *slot; + + key.type = (tree) (uintptr_t) t2; + slot = (struct type_ent **) + htab_find_slot (l_ipo_type_tab, &key, NO_INSERT); + if (!slot || !*slot) + return -1; + tent2 = *slot; + + return tent1->eq_id == tent2->eq_id; +} + +/* Build type hash table. */ + +static void +cgraph_build_type_equivalent_classes (void) +{ + unsigned n, i; + n = pending_types->length (); + for (i = 0; i < n; i++) + { + struct type_ec **slot; + struct type_ec te; + te.rep_type = (*pending_types)[i]; + te.eq_types = NULL; + slot = (struct type_ec **) htab_find_slot (type_hash_tab, + &te, INSERT); + if (!*slot) + { + *slot = XCNEW (struct type_ec); + (*slot)->rep_type = te.rep_type; + vec_alloc ((*slot)->eq_types, 10); + } + (*slot)->eq_types->safe_push (te.rep_type); + } +} + +/* Re-propagate component types's alias set to that of TYPE. PROCESSED + is the pointer set of processed types. */ + +static void +re_record_component_aliases (tree type, + struct pointer_set_t *processed) +{ + alias_set_type superset = get_alias_set (type); + tree field; + + if (superset == 0) + return; + + if (pointer_set_insert (processed, type)) + return; + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + /* Recursively record aliases for the base classes, if there are any. */ + if (TYPE_BINFO (type)) + { + int i; + tree binfo, base_binfo; + + for (binfo = TYPE_BINFO (type), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + re_record_component_aliases (BINFO_TYPE (base_binfo), + processed); + record_alias_subset (superset, + get_alias_set (BINFO_TYPE (base_binfo))); + } + } + for (field = TYPE_FIELDS (type); field != 0; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL && !DECL_NONADDRESSABLE_P (field)) + { + re_record_component_aliases (TREE_TYPE (field), processed); + record_alias_subset (superset, get_alias_set (TREE_TYPE (field))); + } + break; + + case COMPLEX_TYPE: + re_record_component_aliases (TREE_TYPE (type), processed); + record_alias_subset (superset, get_alias_set (TREE_TYPE (type))); + break; + + /* VECTOR_TYPE and ARRAY_TYPE share the alias set with their + element type. */ + + default: + break; + } +} + +/* The callback function to merge alias sets of equivalent types. */ + +static int +type_eq_process (void **slot, void *data ATTRIBUTE_UNUSED) +{ + unsigned i; + alias_set_type alias_set, ptr_alias_set = -1; + tree rep_type, type; + vec<tree> *eq_types; + struct type_ec ** te = (struct type_ec **)slot; + bool zero_set = false, ptr_zero_set = false; + struct type_ent **slot2, key, *tent; + + + rep_type = (*te)->rep_type; + eq_types = (*te)->eq_types; + alias_set = get_alias_set (rep_type); + + for (i = 0; eq_types->iterate (i, &type); ++i) + { + alias_set_type als, ptr_als = -1; + tree type_ptr = TYPE_POINTER_TO (type);; + + als = get_alias_set (type); + if (als == 0) + zero_set = true; + + if (alias_set && als && alias_set != als) + record_alias_subset (alias_set, als); + + if (type_ptr) + { + ptr_als = get_alias_set (type_ptr); + if (ptr_als == 0) + ptr_zero_set = true; + + if (ptr_alias_set == -1) + ptr_alias_set = ptr_als; + else + { + if (!ptr_zero_set && ptr_alias_set != ptr_als) + record_alias_subset (ptr_alias_set, ptr_als); + } + } + } + + /* Now propagate back. */ + for (i = 0; eq_types->iterate (i, &type); ++i) + { + alias_set_type als, ptr_als; + tree ptr_type = TYPE_POINTER_TO (type); + + als = get_alias_set (type); + + if (zero_set) + TYPE_ALIAS_SET (type) = 0; + else if (alias_set != als) + record_alias_subset (als, alias_set); + + if (ptr_type) + { + ptr_als = get_alias_set (ptr_type); + if (ptr_zero_set) + TYPE_ALIAS_SET (ptr_type) = 0; + else if (ptr_alias_set != ptr_als) + record_alias_subset (ptr_als, ptr_alias_set); + } + } + + + /* Now populate the type table. */ + l_ipo_eq_id++; + for (i = 0; eq_types->iterate (i, &type); ++i) + { + key.type = type; + slot2 = (struct type_ent **) + htab_find_slot (l_ipo_type_tab, &key, INSERT); + tent = *slot2; + gcc_assert (!tent); + tent = ggc_alloc_cleared_type_ent (); + tent->type = key.type; + tent->eq_id = l_ipo_eq_id; + *slot2 = tent; + } + + return 1; +} + +/* Regenerate alias set for aggregate types. */ + +static void +record_components_for_parent_types (void) +{ + unsigned n, i; + struct pointer_set_t *processed_types; + + processed_types = pointer_set_create (); + n = pending_types->length (); + for (i = 0; i < n; i++) + { + tree type = (*pending_types)[i]; + re_record_component_aliases (type, processed_types); + } + + pointer_set_destroy (processed_types); +} + +/* Unify type alias sets for equivalent types. */ + +void +cgraph_unify_type_alias_sets (void) +{ + struct cgraph_node *node; + struct varpool_node *pv; + + /* Only need to do type unification when we are in LIPO mode + and have a non-trivial module group (size is >1). However, + override the size check under non-zero PARAM_LIPO_RANDOM_GROUP_SIZE, + which indicates that we are stress-testing LIPO. In that case + try to flush out problems with type unification by always + performing it. */ + if (!L_IPO_COMP_MODE + || (num_in_fnames == 1 + && PARAM_VALUE (PARAM_LIPO_RANDOM_GROUP_SIZE) == 0)) + return; + + vec_alloc (pending_types, 100); + type_set = pointer_set_create (); + type_hash_tab = htab_create (10, type_hash_hash, + type_hash_eq, type_hash_del); + l_ipo_type_tab = htab_create_ggc (10, type_addr_hash, + type_addr_eq, NULL); + + FOR_EACH_DEFINED_FUNCTION (node) + { + if (!gimple_has_body_p (node->decl)) + continue; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + current_function_decl = node->decl; + if (gimple_has_body_p (current_function_decl)) + cgraph_collect_type_referenced (); + current_function_decl = NULL; + set_cfun (NULL); + pop_cfun (); + } + + FOR_EACH_VARIABLE (pv) + walk_tree (&pv->decl, find_struct_types, NULL, NULL); + + /* Compute type equivalent classes. */ + cgraph_build_type_equivalent_classes (); + /* Now unify alias sets of equivelent types. */ + htab_traverse (type_hash_tab, type_eq_process, NULL); + /* Finally re-populating parent's alias set. */ + record_components_for_parent_types (); + + pointer_set_destroy (type_set); + vec_free (pending_types); + htab_delete (type_hash_tab); +} + +/* Return true if NODE->decl from an auxiliary module has external + definition (and therefore is not needed for expansion). */ + +bool +cgraph_is_aux_decl_external (struct cgraph_node *node) +{ + tree decl = node->decl; + + if (!L_IPO_COMP_MODE) + return false; + + if (!cgraph_is_auxiliary (decl)) + return false; + + /* Versioned clones from auxiliary moduels are not external. */ + if (node->is_versioned_clone) + return false; + + /* Comdat or weak functions in aux modules are not external -- + there is no guarantee that the definitition will be emitted + in the primary compilation of this auxiliary module. */ + if (DECL_COMDAT (decl) || DECL_WEAK (decl)) + return false; + + /* virtual functions won't be deleted in the primary module. */ + if (DECL_VIRTUAL_P (decl)) + return true; + + if (!TREE_PUBLIC (decl)) + return false; + + /* The others from aux modules are external. */ + return true; +} + +/* Linked function symbol (cgraph node) table. */ +static GTY((param_is (cgraph_sym))) htab_t cgraph_symtab; + +/* This is true when global linking is needed and performed (for C++). + For C, symbol linking is performed on the fly during parsing, and + the cgraph_symtab is used only for keeping additional information + for any already merged symbol if needed. */ + +static bool global_link_performed = 0; + +/* For an external (non-defined) function DECL, return the primary + module id (even though when the declaration is declared in an aux + module). For a defined function DECL, return the module id in which + it is defined. */ + +unsigned +cgraph_get_module_id (tree decl) +{ + struct function *func = DECL_STRUCT_FUNCTION (decl); + /* Not defined. */ + if (!func) + return primary_module_id; + return FUNC_DECL_MODULE_ID (func); +} + +/* Return true if function decl is defined in an auxiliary module. */ + +bool +cgraph_is_auxiliary (tree decl) +{ + return (cgraph_get_module_id (decl) != primary_module_id); +} + +/* Return the hash value for cgraph_sym pointed to by P. The + hash value is computed using function's assembler name. */ + +static hashval_t +hash_sym_by_assembler_name (const void *p) +{ + const struct cgraph_sym *n = (const struct cgraph_sym *) p; + return (hashval_t) decl_assembler_name_hash (n->assembler_name); +} + +/* Return nonzero if P1 and P2 are equal. */ + +static int +eq_assembler_name (const void *p1, const void *p2) +{ + const struct cgraph_sym *n1 = (const struct cgraph_sym *) p1; + const_tree name = (const_tree) p2; + return (decl_assembler_name_equal (n1->rep_decl, name)); +} + +/* Return the cgraph_sym for function declaration DECL. */ + +static struct cgraph_sym ** +cgraph_sym (tree decl) +{ + struct cgraph_sym **slot; + tree name; + + if (!cgraph_symtab) + { + gcc_assert (!global_link_performed); + return NULL; + } + + name = DECL_ASSEMBLER_NAME (decl); + slot = (struct cgraph_sym **) + htab_find_slot_with_hash (cgraph_symtab, name, + decl_assembler_name_hash (name), + NO_INSERT); + return slot; +} + +/* Return the representative declaration for assembler name + ASM_NAME. */ + +tree +cgraph_find_decl (tree asm_name) +{ + struct cgraph_sym **slot; + if (!L_IPO_COMP_MODE) + return NULL; + if (!cgraph_symtab || !global_link_performed) + return NULL; + + slot = (struct cgraph_sym **) + htab_find_slot_with_hash (cgraph_symtab, asm_name, + decl_assembler_name_hash (asm_name), + NO_INSERT); + if (!slot || !*slot) + return NULL; + + return (*slot)->rep_node->decl; +} + +/* Return true if function declaration DECL is originally file scope + static, which is promoted to global scope. */ + +bool +cgraph_is_promoted_static_func (tree decl) +{ + struct cgraph_sym ** sym; + gcc_assert (L_IPO_COMP_MODE); + + /* cgraph_symtab will be created when any symbol got + promoted. */ + if (!cgraph_symtab) + return false; + + sym = cgraph_sym (decl); + if (!sym) + return false; + return (*sym)->is_promoted_static; +} + +/* Hash function for module information table. ENT + is a pointer to a cgraph_module_info. */ + +static hashval_t +htab_sym_hash (const void *ent) +{ + const struct cgraph_mod_info * const mi + = (const struct cgraph_mod_info * const ) ent; + return (hashval_t) mi->module_id; +} + +/* Hash equality function for module information table. */ + +static int +htab_sym_eq (const void *ent1, const void *ent2) +{ + const struct cgraph_mod_info * const mi1 + = (const struct cgraph_mod_info * const ) ent1; + const struct cgraph_mod_info * const mi2 + = (const struct cgraph_mod_info * const ) ent2; + return (mi1->module_id == mi2->module_id); +} + +/* cgraph_sym SYM may be defined in more than one source modules. + Add declaration DECL's definiting module to SYM. */ + +static void +add_define_module (struct cgraph_sym *sym, tree decl) +{ + unsigned module_id; + struct cgraph_mod_info **slot; + struct cgraph_mod_info mi; + + struct function *f = DECL_STRUCT_FUNCTION (decl); + if (!f) + return; + module_id = FUNC_DECL_MODULE_ID (f); + + if (!sym->def_module_hash) + sym->def_module_hash + = htab_create_ggc (10, htab_sym_hash, htab_sym_eq, NULL); + + mi.module_id = module_id; + slot = (struct cgraph_mod_info **)htab_find_slot (sym->def_module_hash, + &mi, INSERT); + if (!*slot) + { + *slot = ggc_alloc_cleared_cgraph_mod_info (); + (*slot)->module_id = module_id; + } + else + gcc_assert ((*slot)->module_id == module_id); +} + +static int +add_def_module (void **slot, void *data) +{ + struct cgraph_mod_info **m = (struct cgraph_mod_info **)slot; + htab_t mod_set = (htab_t) data; + struct cgraph_mod_info **new_slot; + + new_slot = (struct cgraph_mod_info **)htab_find_slot (mod_set, *m, INSERT); + if (!*new_slot) + { + *new_slot = ggc_alloc_cleared_cgraph_mod_info (); + (*new_slot)->module_id = (*m)->module_id; + } + else + gcc_assert ((*new_slot)->module_id == (*m)->module_id); + return 1; +} + +/* Clone defined module hash table from ORIG to CLONE. */ + +void +copy_defined_module_set (tree clone, tree orig) +{ + struct cgraph_sym **orig_sym, **clone_sym; + + orig_sym = cgraph_sym (orig); + clone_sym = cgraph_sym (clone); + if (!orig_sym || !(*orig_sym)->def_module_hash) + return; + if (!(*clone_sym)->def_module_hash) + (*clone_sym)->def_module_hash + = htab_create_ggc (10, htab_sym_hash, htab_sym_eq, NULL); + htab_traverse ((*orig_sym)->def_module_hash, add_def_module, (*clone_sym)->def_module_hash); +} + +/* Return true if the symbol associated with DECL is defined in module + MODULE_ID. This interface is used by the inliner to make sure profile-gen + and profile-use pass (L-IPO mode) make consistent inline decision. */ + +bool +cgraph_is_inline_body_available_in_module (tree decl, unsigned module_id) +{ + struct cgraph_sym **sym; + void **slot; + struct cgraph_mod_info mi; + + gcc_assert (L_IPO_COMP_MODE); + + if (DECL_BUILT_IN (decl)) + return true; + + /* TODO: revisit this. */ + if (DECL_IN_SYSTEM_HEADER (decl) && DECL_DECLARED_INLINE_P (decl)) + return true; + + gcc_assert (TREE_STATIC (decl) || DECL_DECLARED_INLINE_P (decl)); + + if (cgraph_get_module_id (decl) == module_id) + return true; + + sym = cgraph_sym (decl); + if (!sym || !(*sym)->def_module_hash) + return false; + + mi.module_id = module_id; + slot = htab_find_slot ((*sym)->def_module_hash, &mi, NO_INSERT); + if (slot) + { + gcc_assert (((struct cgraph_mod_info*)*slot)->module_id == module_id); + return true; + } + return false; +} + +/* Return the linked cgraph node using DECL's assembler name. DO_ASSERT + is a flag indicating that a non null link target must be returned. */ + +struct cgraph_node * +cgraph_lipo_get_resolved_node_1 (tree decl, bool do_assert) +{ + struct cgraph_sym **slot; + + /* Handle alias decl. */ + slot = cgraph_sym (decl); + + if (!slot || !*slot) + { + if (!do_assert) + return NULL; + else + { + /* Nodes that are indirectly called are not 'reachable' in + the callgraph. If they are not needed (comdat, inline + extern etc), they may be removed from the link table + before direct calls to them are exposed (via indirect + call promtion by const folding etc). When this happens, + the node will need to be relinked. A probably better fix + is to modify the callgraph so that they are not eliminated + in the first place -- this will allow inlining to happen. */ + + struct cgraph_node *n = cgraph_get_create_node (decl); + if (!n->analyzed) + { + gcc_assert (DECL_EXTERNAL (decl) + || cgraph_is_aux_decl_external (n) + || DECL_VIRTUAL_P (decl)); + gcc_assert (/* This is the case for explicit extern instantiation, + when cgraph node is not created before link. */ + DECL_EXTERNAL (decl)); + cgraph_link_node (n); + return n; + } + else + gcc_unreachable (); + } + } + else + { + struct cgraph_sym *sym = *slot; + return sym->rep_node; + } +} + +/* Return the cgraph_node of DECL if decl has definition; otherwise return + the cgraph node of the representative decl, which is the declaration DECL + is resolved to after linking/symbol resolution. */ + +struct cgraph_node * +cgraph_lipo_get_resolved_node (tree decl) +{ + struct cgraph_node *node = NULL; + + gcc_assert (L_IPO_COMP_MODE && global_link_performed); + gcc_assert (cgraph_symtab); + + /* Never merged. */ + if (!TREE_PUBLIC (decl) || DECL_ARTIFICIAL (decl) + /* builtin function decls are shared across modules, but 'linking' + is still performed for them to keep track of the set of defining + modules. Skip the real resolution here to avoid merging '__builtin_xxx' + with 'xxx'. */ + || DECL_BUILT_IN (decl)) + return cgraph_get_create_node (decl); + + node = cgraph_lipo_get_resolved_node_1 (decl, true); + return node; +} + +/* When NODE->decl is dead function eliminated, + remove the entry in the link table. */ + +void +cgraph_remove_link_node (struct cgraph_node *node) +{ + tree name, decl; + + if (!L_IPO_COMP_MODE || !cgraph_symtab) + return; + + decl = node->decl; + + /* Skip nodes that are not in the link table. */ + if (!TREE_PUBLIC (decl) || DECL_ARTIFICIAL (decl)) + return; + + /* Skip if node is an inline clone or if the node has + defintion that is not really resolved to the merged node. */ + if (cgraph_lipo_get_resolved_node_1 (decl, false) != node) + return; + + name = DECL_ASSEMBLER_NAME (decl); + htab_remove_elt_with_hash (cgraph_symtab, name, + decl_assembler_name_hash (name)); +} + +/* Return true if the function body for DECL has profile information. */ + +static bool +has_profile_info (tree decl) +{ + gcov_type *ctrs = NULL; + unsigned n; + struct function* f = DECL_STRUCT_FUNCTION (decl); + + ctrs = get_coverage_counts_no_warn (f, GCOV_COUNTER_ARCS, &n); + if (ctrs) + { + unsigned i; + for (i = 0; i < n; i++) + if (ctrs[i]) + return true; + } + + return false; +} + +/* Resolve delaration NODE->decl for function symbol *SLOT. */ + +static void +resolve_cgraph_node (struct cgraph_sym **slot, struct cgraph_node *node) +{ + tree decl1, decl2; + int decl1_defined = 0; + int decl2_defined = 0; + + decl1 = (*slot)->rep_decl; + decl2 = node->decl; + + decl1_defined = gimple_has_body_p (decl1); + decl2_defined = gimple_has_body_p (decl2); + + if (decl1_defined && !decl2_defined) + return; + + if (!decl1_defined && decl2_defined) + { + (*slot)->rep_node = node; + (*slot)->rep_decl = decl2; + add_define_module (*slot, decl2); + return; + } + + if (decl2_defined) + { + bool has_prof1 = false; + bool has_prof2 = false; + gcc_assert (decl1_defined); + add_define_module (*slot, decl2); + + /* Pick the node that cannot be removed, to avoid a situation + where we remove the resolved node and later try to access + it for the remaining non-removable copy. E.g. one may be + extern and the other weak, only the extern copy can be removed. */ + if (cgraph_can_remove_if_no_direct_calls_and_refs_p ((*slot)->rep_node) + && !cgraph_can_remove_if_no_direct_calls_and_refs_p (node)) + { + (*slot)->rep_node = node; + (*slot)->rep_decl = decl2; + return; + } + + has_prof1 = has_profile_info (decl1); + if (has_prof1) + return; + has_prof2 = has_profile_info (decl2); + if (has_prof2) + { + (*slot)->rep_node = node; + (*slot)->rep_decl = decl2; + } + return; + } + + /* Handle aliases properly. Make sure the alias symbol resolution + is consistent with alias target */ + if (node->alias && !node->thunk.thunk_p) + { + struct cgraph_node *decl2_tgt = cgraph_function_or_thunk_node (node, NULL); + if (cgraph_lipo_get_resolved_node_1 (decl2_tgt->decl, false) == decl2_tgt) + { + (*slot)->rep_node = node; + (*slot)->rep_decl = decl2; + } + } + return; +} + + +/* Resolve NODE->decl in the function symbol table. */ + +struct cgraph_sym * +cgraph_link_node (struct cgraph_node *node) +{ + void **slot; + tree name; + + if (!L_IPO_COMP_MODE) + return NULL; + + if (!cgraph_symtab) + return NULL; + + /* Skip the cases when the defintion can be locally resolved, and + when we do not need to keep track of defining modules. */ + if (!TREE_PUBLIC (node->decl) || DECL_ARTIFICIAL (node->decl)) + return NULL; + + name = DECL_ASSEMBLER_NAME (node->decl); + slot = htab_find_slot_with_hash (cgraph_symtab, name, + decl_assembler_name_hash (name), + INSERT); + if (*slot) + resolve_cgraph_node ((struct cgraph_sym **) slot, node); + else + { + struct cgraph_sym *sym = ggc_alloc_cleared_cgraph_sym (); + sym->rep_node = node; + sym->rep_decl = node->decl; + sym->assembler_name = name; + add_define_module (sym, node->decl); + *slot = sym; + } + return (struct cgraph_sym *) *slot; +} + +/* Perform cross module linking of function declarations. */ + +void +cgraph_do_link (void) +{ + struct cgraph_node *node; + + if (!L_IPO_COMP_MODE) + return; + + global_link_performed = 1; + gcc_assert (cgraph_pre_profiling_inlining_done); + + if (!cgraph_symtab) + cgraph_symtab + = htab_create_ggc (10, hash_sym_by_assembler_name, + eq_assembler_name, NULL); + + FOR_EACH_FUNCTION (node) + { + gcc_assert (!node->global.inlined_to); + /* Delay aliases */ + if (node->alias && !node->thunk.thunk_p) + continue; + cgraph_link_node (node); + } + + /* Now handle aliases */ + FOR_EACH_FUNCTION (node) + { + if (node->alias && !node->thunk.thunk_p) + cgraph_link_node (node); + } +} + +struct promo_ent +{ + char* assemb_name; + tree decl; + int seq; +}; + +/* Hash function for promo_ent table. */ + +static hashval_t +promo_ent_hash (const void *ent) +{ + const struct promo_ent *const entry + = (const struct promo_ent *) ent; + + return htab_hash_string (entry->assemb_name); +} + +/* Hash_eq function for promo_ent table. */ + +static int +promo_ent_eq (const void *ent1, const void *ent2) +{ + const struct promo_ent *const entry1 + = (const struct promo_ent *) ent1; + const struct promo_ent *const entry2 + = (const struct promo_ent *) ent2; + if (!strcmp (entry1->assemb_name, entry2->assemb_name)) + return 1; + return 0; +} + +/* Delete function for promo_ent hash table. */ + +static void +promo_ent_del (void *ent) +{ + struct promo_ent *const entry + = (struct promo_ent *) ent; + + free (entry->assemb_name); + free (entry); +} + +static htab_t promo_ent_hash_tab = NULL; + +/* Make the var decl for weak symbol as extern. */ + +static inline void +externalize_weak_decl (tree decl) +{ + gcc_assert (TREE_CODE (decl) == VAR_DECL && DECL_WEAK (decl)); + + DECL_EXTERNAL (decl) = 1; + TREE_STATIC (decl) = 0; + DECL_INITIAL (decl) = NULL; + + /* Keep the context so that devirt_variable_node_removal_hook + can do cleanup properly for vtables. + DECL_CONTEXT (decl) = NULL; */ +} + +/* Return a unique sequence number for NAME. This is needed to avoid + name conflict -- function scope statics may have identical names. + + When DECL is NULL, + this function returns a zero sequence number if it is called with + a particular NAME for the first time, and non-zero otherwise. + This fact is used to keep track of unseen weak variables. + + When DECL is not NULL, this function is supposed to be called by + varpool_remove_duplicate_weak_decls. */ + +static int +get_name_seq_num (const char *name, tree decl) +{ + struct promo_ent **slot; + struct promo_ent ent; + int ret = 0; + + gcc_assert (!decl || TREE_CODE (decl) == VAR_DECL); + ent.assemb_name = xstrdup (name); + ent.seq = 0; + + slot = (struct promo_ent **) + htab_find_slot (promo_ent_hash_tab, &ent, INSERT); + + if (!*slot) + { + *slot = XCNEW (struct promo_ent); + (*slot)->assemb_name = ent.assemb_name; + (*slot)->decl = decl; + } + else + { + /* During output, the previously selected weak decl may not be + referenced by any function that is expanded thus they do not have + DECL_RTL_SET_P to be true and therefore can be eliminated by + varpool_remove_unreferenced_decls later. To avoid that, logic is + added to replace previously selected decl when needed. */ + if (decl && DECL_RTL_SET_P (decl) + && !DECL_RTL_SET_P ((*slot)->decl)) + { + externalize_weak_decl ((*slot)->decl); + (*slot)->decl = decl; + ret = 0; + } + else + ret = ++(*slot)->seq; + free (ent.assemb_name); + } + return ret; +} + +/* Returns a unique assembler name for DECL. */ + +static tree +create_unique_name (tree decl, unsigned module_id) +{ + tree id, assemb_id; + char *assembler_name; + const char *name; + struct function *context = NULL; + int seq = 0; + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + if (!DECL_CONTEXT (decl) + || TREE_CODE (DECL_CONTEXT (decl)) == TRANSLATION_UNIT_DECL) + { + id = DECL_NAME (decl); + /* if (IDENTIFIER_OPNAME_P (id)) */ + if (TREE_LANG_FLAG_2 (id)) + id = DECL_ASSEMBLER_NAME (decl); + } + else + id = DECL_ASSEMBLER_NAME (decl); + } + else + { + if (!DECL_CONTEXT (decl)) + id = DECL_NAME (decl); + else if (TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL) + id = DECL_ASSEMBLER_NAME (decl); + else if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL) + { + id = DECL_NAME (decl); + context = DECL_STRUCT_FUNCTION (DECL_CONTEXT (decl)); + } + else + /* file scope context */ + id = DECL_NAME (decl); + } + + name = IDENTIFIER_POINTER (id); + if (context) + { + char *n; + unsigned fno = FUNC_DECL_FUNC_ID (context); + n = (char *)alloca (strlen (name) + 15); + sprintf (n, "%s.%u", name, fno); + name = n; + } + + assembler_name = (char*) alloca (strlen (name) + 30); + sprintf (assembler_name, "%s.cmo.%u", name, module_id); + seq = get_name_seq_num (assembler_name, NULL); + if (seq) + sprintf (assembler_name, "%s.%d", assembler_name, seq); + + assemb_id = get_identifier (assembler_name); + + return assemb_id; +} + +/* Promote DECL to be global. MODULE_ID is the id of the module where + DECL is defined. IS_EXTERN is a flag indicating if externalization + is needed. */ + +static void +promote_static_var_func (unsigned module_id, tree decl, bool is_extern) +{ + tree assemb_id; + tree alias; + + /* No need to promote symbol alias. */ + alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl)); + if (alias) + return; + + /* Function decls in C++ may contain characters not taken by assembler. + Similarly, function scope static variable has UID as the assembler name + suffix which is not consistent across modules. */ + assemb_id = create_unique_name (decl, module_id); + + if (DECL_ASSEMBLER_NAME_SET_P (decl)) + { + if (TREE_CODE (decl) == FUNCTION_DECL) + unlink_from_assembler_name_hash (cgraph_get_create_node (decl), + false); + else + unlink_from_assembler_name_hash (varpool_get_node (decl), false); + } + + SET_DECL_ASSEMBLER_NAME (decl, assemb_id); + TREE_PUBLIC (decl) = 1; + DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN; + DECL_VISIBILITY_SPECIFIED (decl) = 1; + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + struct cgraph_node *node = cgraph_get_create_node (decl); + + node->resolution = LDPR_UNKNOWN; + insert_to_assembler_name_hash (node, false); + } + else + { + struct varpool_node *node = varpool_get_node (decl); + node->resolution = LDPR_UNKNOWN; + /* Statics from exported primary module are very likely + referenced by other modules, so they should be made + externally visible (to be avoided to be localized again). + Another way to do this is to set force_output bit or + change the logic in varpool_externally_visible in ipa.c. */ + if (!is_extern) + { + node->resolution = LDPR_PREVAILING_DEF; + node->externally_visible = true; + } + varpool_link_node (node); + insert_to_assembler_name_hash (node, false); + } + + if (is_extern) + { + if (TREE_CODE (decl) == VAR_DECL) + { + TREE_STATIC (decl) = 0; + DECL_EXTERNAL (decl) = 1; + /* Keep the initializer to allow const prop. */ + /* DECL_INITIAL (decl) = 0; */ + /* Keep the context so that devirt_variable_node_removal_hook + can do cleanup properly for vtables. + DECL_CONTEXT (decl) = 0; */ + } + /* else + Function body will be deleted later before expansion. */ + } + else + TREE_STATIC (decl) = 1; +} + +/* Externalize global variables from aux modules and promote + static variables. + WEAK variables are treated especially in + varpool_remove_duplicate_weak_decls. */ + +static void +process_module_scope_static_var (struct varpool_node *vnode) +{ + tree decl = vnode->decl; + + if (varpool_is_auxiliary (vnode)) + { + gcc_assert (vnode->module_id != primary_module_id); + if (TREE_PUBLIC (decl)) + { + /* Externalize non-weak variables. */ + if (!DECL_WEAK (decl)) + { + DECL_EXTERNAL (decl) = 1; + TREE_STATIC (decl) = 0; + /* Keep the initializer to allow const prop. */ + /* DECL_INITIAL (decl) = NULL; */ + if (DECL_CONTEXT (decl)) + { + DECL_ASSEMBLER_NAME (decl); + } + /* Keep the context so that devirt_variable_node_removal_hook + can do cleanup properly for vtables. + DECL_CONTEXT (decl) = NULL; */ + } + } + else + { + /* Promote static vars to global. */ + if (vnode->module_id) + promote_static_var_func (vnode->module_id, decl, + varpool_is_auxiliary (vnode)); + } + } + else + { + if (PRIMARY_MODULE_EXPORTED && !TREE_PUBLIC (decl)) + promote_static_var_func (vnode->module_id, decl, + varpool_is_auxiliary (vnode)); + } +} + +/* Promote all aliases of CNODE. */ + +static void +promote_function_aliases (struct cgraph_node *cnode, unsigned mod_id, + bool is_extern) +{ + int i; + struct ipa_ref *ref; + + for (i = 0; ipa_ref_list_referring_iterate (&cnode->ref_list, i, ref); + i++) + { + if (ref->use == IPA_REF_ALIAS) + { + struct cgraph_node *alias = ipa_ref_referring_node (ref); + tree alias_decl = alias->decl; + /* Should assert */ + if (cgraph_get_module_id (alias_decl) == mod_id) + promote_static_var_func (mod_id, alias_decl, is_extern); + } + } +} + +/* Promote static function CNODE->decl to be global. */ + +static void +process_module_scope_static_func (struct cgraph_node *cnode) +{ + tree decl = cnode->decl; + bool addr_taken; + unsigned mod_id; + struct ipa_ref *ref; + int i; + + if (TREE_PUBLIC (decl) + || !TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || DECL_ARTIFICIAL (decl)) + return; + + if (flag_ripa_no_promote_always_inline + && lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl)) != NULL) + return; + + /* Can be local -- the promotion pass need to be done after + callgraph build when address taken bit is set. */ + addr_taken = cnode->address_taken; + if (!addr_taken) + { + for (i = 0; ipa_ref_list_referring_iterate (&cnode->ref_list, i, ref); + i++) + if (ref->use == IPA_REF_ALIAS) + { + struct cgraph_node *alias = ipa_ref_referring_node (ref); + if (alias->address_taken) + addr_taken = true; + } + } + if (!addr_taken) + { + tree assemb_id = create_unique_name (decl, cgraph_get_module_id (decl)); + + if (DECL_ASSEMBLER_NAME_SET_P (decl)) + unlink_from_assembler_name_hash (cnode, false); + SET_DECL_ASSEMBLER_NAME (decl, assemb_id); + insert_to_assembler_name_hash (cnode, false); + return; + } + + mod_id = cgraph_get_module_id (decl); + if (cgraph_is_auxiliary (decl)) + { + gcc_assert (mod_id != primary_module_id); + /* Promote static function to global. */ + if (mod_id) + { + promote_static_var_func (mod_id, decl, 1); + promote_function_aliases (cnode, mod_id, 1); + } + } + else + { + if (PRIMARY_MODULE_EXPORTED + /* skip static_init routines. */ + && !DECL_ARTIFICIAL (decl)) + { + promote_static_var_func (mod_id, decl, 0); + + promote_function_aliases (cnode, mod_id, 0); + } + } +} + +/* Process var_decls, func_decls with static storage. */ + +void +cgraph_process_module_scope_statics (void) +{ + struct cgraph_node *pf; + struct varpool_node *pv; + + if (!L_IPO_COMP_MODE) + return; + + promo_ent_hash_tab = htab_create (10, promo_ent_hash, + promo_ent_eq, promo_ent_del); + + /* Process variable first. */ + FOR_EACH_DEFINED_VARIABLE (pv) + process_module_scope_static_var (pv); + + FOR_EACH_FUNCTION (pf) + process_module_scope_static_func (pf); + + htab_delete (promo_ent_hash_tab); +} + +/* There could be duplicate non-extern WEAK decls in the varpool queue, + coming from different modules. All but one of these need to be externalized + and removed from the varpool queue. + Duplicate WEAK decls can be added to varpool queue as late as + cgraph_expand_function, when a WEAK decl is marked referenced as assembler + is being output. Therefore, a call to this function should be made after + cgraph_expand_function. */ + +void +varpool_remove_duplicate_weak_decls (void) +{ + struct varpool_node *node = NULL; + + if (!L_IPO_COMP_MODE) + return; + + promo_ent_hash_tab = htab_create (10, promo_ent_hash, + promo_ent_eq, promo_ent_del); + + FOR_EACH_VARIABLE (node) + { + tree decl = node->decl; + + if (TREE_PUBLIC (decl) && DECL_WEAK (decl) && !DECL_EXTERNAL (decl) + && get_name_seq_num (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), decl)) + externalize_weak_decl (decl); + } + + htab_delete (promo_ent_hash_tab); +} + +static GTY((param_is (symtab_node))) htab_t varpool_symtab; + +/* Hash function for varpool node. */ + +static hashval_t +hash_node_by_assembler_name (const void *p) +{ + const struct varpool_node *n = (const struct varpool_node *) p; + return (hashval_t) decl_assembler_name_hash ( + DECL_ASSEMBLER_NAME (n->decl)); +} + +/* Returns nonzero if P1 and P2 are equal. */ + +static int +eq_node_assembler_name (const void *p1, const void *p2) +{ + const struct varpool_node *n1 = (const struct varpool_node *) p1; + const_tree name = (const_tree)p2; + return (decl_assembler_name_equal (n1->decl, name)); +} + +/* Return true if NODE's decl is declared in an auxiliary module. */ + +bool +varpool_is_auxiliary (struct varpool_node *node) +{ + return (node->module_id + && node->module_id != primary_module_id); +} + +/* Return the varpool_node to which DECL is resolved to during linking. + This method can not be used after static to global promotion happens. */ + +static struct varpool_node * +real_varpool_node_1 (tree decl, bool assert) +{ + void **slot; + tree name; + + if (!L_IPO_COMP_MODE || !varpool_symtab) + return varpool_get_node (decl); + + if (!TREE_PUBLIC (decl) || DECL_ARTIFICIAL (decl)) + return varpool_get_node (decl); + + name = DECL_ASSEMBLER_NAME (decl); + slot = htab_find_slot_with_hash (varpool_symtab, name, + decl_assembler_name_hash (name), + NO_INSERT); + if (!slot) + { + gcc_assert (!assert); + return NULL; + } + + gcc_assert (slot && *slot); + return (struct varpool_node *)*slot; +} + +struct varpool_node * +real_varpool_node (tree decl) +{ + return real_varpool_node_1 (decl, true); +} + +/* Remove NODE from the link table. */ + +void +varpool_remove_link_node (struct varpool_node *node) +{ + tree name; + tree decl; + + if (!L_IPO_COMP_MODE || !varpool_symtab) + return; + + decl = node->decl; + + if (!TREE_PUBLIC (decl) || DECL_ARTIFICIAL (decl)) + return; + + if (real_varpool_node_1 (decl, false) != node) + return; + + name = DECL_ASSEMBLER_NAME (decl); + htab_remove_elt_with_hash (varpool_symtab, name, + decl_assembler_name_hash (name)); +} + +/* Merge the addressable attribute from DECL2 to DECL1. */ + +static inline void +merge_addressable_attr (tree decl1, tree decl2) +{ + if (TREE_ADDRESSABLE (decl2)) + TREE_ADDRESSABLE (decl1) = 1; +} + +/* Resolve NODE->decl to symbol table entry *SLOT. */ + +static void +resolve_varpool_node (struct varpool_node **slot, struct varpool_node *node) +{ + tree decl1, decl2; + + decl1 = (*slot)->decl; + decl2 = node->decl; + + /* Take the decl with the complete type. */ + if (COMPLETE_TYPE_P (TREE_TYPE (decl1)) + && !COMPLETE_TYPE_P (TREE_TYPE (decl2))) + { + merge_addressable_attr (decl1, decl2); + return; + } + if (!COMPLETE_TYPE_P (TREE_TYPE (decl1)) + && COMPLETE_TYPE_P (TREE_TYPE (decl2))) + { + *slot = node; + merge_addressable_attr (decl2, decl1); + return; + } + + if (DECL_INITIAL (decl1) && !DECL_INITIAL (decl2)) + { + merge_addressable_attr (decl1, decl2); + return; + } + + if (!DECL_INITIAL (decl1) && DECL_INITIAL (decl2)) + { + *slot = node; + merge_addressable_attr (decl2, decl1); + return; + } + + /* Either all complete or neither's type is complete. Just + pick the primary module's decl. */ + if (!varpool_is_auxiliary (*slot)) + { + merge_addressable_attr (decl1, decl2); + return; + } + + if (!varpool_is_auxiliary (node)) + { + *slot = node; + merge_addressable_attr (decl2, decl1); + return; + } + + merge_addressable_attr (decl1, decl2); + return; +} + +/* Link NODE into var_decl symbol table. */ + +void +varpool_link_node (struct varpool_node *node) +{ + tree name; + void **slot; + + if (!L_IPO_COMP_MODE || !varpool_symtab) + return; + + if (!TREE_PUBLIC (node->decl) || DECL_ARTIFICIAL (node->decl)) + return; + + name = DECL_ASSEMBLER_NAME (node->decl); + slot = htab_find_slot_with_hash (varpool_symtab, name, + decl_assembler_name_hash (name), + INSERT); + if (*slot) + resolve_varpool_node ((struct varpool_node **) slot, node); + else + *slot = node; +} + +/* Fixup references of VNODE. */ + +static void +fixup_reference_list (struct varpool_node *node) +{ + int i; + struct ipa_ref *ref; + struct ipa_ref_list *list = &node->ref_list; + vec<cgraph_node_ptr> new_refered; + vec<int> new_refered_type; + struct cgraph_node *c; + enum ipa_ref_use use_type = IPA_REF_LOAD; + + new_refered.create (10); + new_refered_type.create (10); + for (i = 0; ipa_ref_list_reference_iterate (list, i, ref); i++) + { + if (!is_a <cgraph_node> (ref->referred)) + continue; + + struct cgraph_node *cnode = ipa_ref_node (ref); + struct cgraph_node *r_cnode + = cgraph_lipo_get_resolved_node (cnode->decl); + if (r_cnode != cnode) + { + new_refered.safe_push (r_cnode); + use_type = ref->use; + new_refered_type.safe_push ((int) use_type); + } + } + for (i = 0; new_refered.iterate (i, &c); ++i) + { + ipa_record_reference (node, c, + (enum ipa_ref_use) new_refered_type[i], NULL); + } +} + +/* Perform cross module linking for var_decls. */ + +void +varpool_do_link (void) +{ + struct varpool_node *node; + + if (!L_IPO_COMP_MODE) + return; + + varpool_symtab + = htab_create_ggc (10, hash_node_by_assembler_name, + eq_node_assembler_name, NULL); + FOR_EACH_VARIABLE (node) + varpool_link_node (node); + + /* Merge the externally visible attribute. */ + FOR_EACH_VARIABLE (node) + { + if (node->externally_visible) + (real_varpool_node (node->decl))->externally_visible = true; + fixup_reference_list (node); + } +} + +/* Get the list of assembler name ids with reference bit set. */ + +void +varpool_get_referenced_asm_ids (vec<tree,va_gc> **ids) +{ + struct varpool_node *node; + FOR_EACH_VARIABLE (node) + { + tree asm_id = NULL; + tree decl = node->decl; + if (DECL_ASSEMBLER_NAME_SET_P (decl)) + { + asm_id = DECL_ASSEMBLER_NAME (decl); + vec_safe_push (*ids, asm_id); + } + } +} + +/* Clear the referenced bit in all assembler ids. */ + +void +varpool_clear_asm_id_reference_bit (void) +{ + struct varpool_node *node; + FOR_EACH_VARIABLE (node) + { + tree asm_id = NULL; + tree decl = node->decl; + if (DECL_ASSEMBLER_NAME_SET_P (decl)) + { + asm_id = DECL_ASSEMBLER_NAME (decl); + TREE_SYMBOL_REFERENCED (asm_id) = 0; + } + } +} + + +#include "gt-l-ipo.h" |