Mercurial > hg > CbC > CbC_gcc
diff gcc/cgraph.c @ 67:f6334be47118
update gcc from gcc-4.6-20100522 to gcc-4.6-20110318
author | nobuyasu <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 22 Mar 2011 17:18:12 +0900 |
parents | b7f97abdc517 |
children | 04ced10e8804 |
line wrap: on
line diff
--- a/gcc/cgraph.c Tue May 25 18:58:51 2010 +0900 +++ b/gcc/cgraph.c Tue Mar 22 17:18:12 2011 +0900 @@ -94,9 +94,23 @@ #include "tree-flow.h" #include "value-prof.h" #include "except.h" -#include "diagnostic.h" +#include "diagnostic-core.h" #include "rtl.h" #include "ipa-utils.h" +#include "lto-streamer.h" + +const char * const ld_plugin_symbol_resolution_names[]= +{ + "", + "undef", + "prevailing_def", + "prevailing_def_ironly", + "preempted_reg", + "preempted_ir", + "resolved_ir", + "resolved_exec", + "resolved_dyn" +}; static void cgraph_node_remove_callers (struct cgraph_node *node); static inline void cgraph_edge_remove_caller (struct cgraph_edge *e); @@ -150,28 +164,28 @@ them, to support -fno-toplevel-reorder. */ int cgraph_order; -/* List of hooks trigerred on cgraph_edge events. */ +/* List of hooks triggered on cgraph_edge events. */ struct cgraph_edge_hook_list { cgraph_edge_hook hook; void *data; struct cgraph_edge_hook_list *next; }; -/* List of hooks trigerred on cgraph_node events. */ +/* List of hooks triggered on cgraph_node events. */ struct cgraph_node_hook_list { cgraph_node_hook hook; void *data; struct cgraph_node_hook_list *next; }; -/* List of hooks trigerred on events involving two cgraph_edges. */ +/* List of hooks triggered on events involving two cgraph_edges. */ struct cgraph_2edge_hook_list { cgraph_2edge_hook hook; void *data; struct cgraph_2edge_hook_list *next; }; -/* List of hooks trigerred on events involving two cgraph_nodes. */ +/* List of hooks triggered on events involving two cgraph_nodes. */ struct cgraph_2node_hook_list { cgraph_2node_hook hook; void *data; @@ -442,7 +456,7 @@ } else { - node = GGC_CNEW (struct cgraph_node); + node = ggc_alloc_cleared_cgraph_node (); node->uid = cgraph_max_uid++; } @@ -464,6 +478,7 @@ node->previous = NULL; node->global.estimated_growth = INT_MIN; node->frequency = NODE_FREQUENCY_NORMAL; + node->count_materialization_scale = REG_BR_PROB_BASE; ipa_empty_ref_list (&node->ref_list); cgraph_nodes = node; cgraph_n_nodes++; @@ -521,16 +536,16 @@ return node; } -/* Mark ALIAS as an alias to DECL. */ +/* Mark ALIAS as an alias to DECL. DECL_NODE is cgraph node representing + the function body is associated with (not neccesarily cgraph_node (DECL). */ static struct cgraph_node * -cgraph_same_body_alias_1 (tree alias, tree decl) +cgraph_same_body_alias_1 (struct cgraph_node *decl_node, tree alias, tree decl) { - struct cgraph_node key, *alias_node, *decl_node, **slot; + struct cgraph_node key, *alias_node, **slot; gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); gcc_assert (TREE_CODE (alias) == FUNCTION_DECL); - decl_node = cgraph_node (decl); key.decl = alias; @@ -554,25 +569,31 @@ return alias_node; } -/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. +/* Attempt to mark ALIAS as an alias to DECL. Return alias node if successful + and NULL otherwise. Same body aliases are output whenever the body of DECL is output, and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */ -bool -cgraph_same_body_alias (tree alias, tree decl) +struct cgraph_node * +cgraph_same_body_alias (struct cgraph_node *decl_node, tree alias, tree decl) { #ifndef ASM_OUTPUT_DEF /* If aliases aren't supported by the assembler, fail. */ - return false; + return NULL; #endif /*gcc_assert (!assembler_name_hash);*/ - return cgraph_same_body_alias_1 (alias, decl) != NULL; + return cgraph_same_body_alias_1 (decl_node, alias, decl); } -void -cgraph_add_thunk (tree alias, tree decl, bool this_adjusting, +/* Add thunk alias into callgraph. The alias declaration is ALIAS and it + aliases DECL with an adjustments made into the first parameter. + See comments in thunk_adjust for detail on the parameters. */ + +struct cgraph_node * +cgraph_add_thunk (struct cgraph_node *decl_node, tree alias, tree decl, + bool this_adjusting, HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value, tree virtual_offset, tree real_alias) @@ -586,25 +607,25 @@ cgraph_remove_node (node); } - node = cgraph_same_body_alias_1 (alias, decl); + node = cgraph_same_body_alias_1 (decl_node, alias, decl); gcc_assert (node); -#ifdef ENABLE_CHECKING - gcc_assert (!virtual_offset - || tree_int_cst_equal (virtual_offset, size_int (virtual_value))); -#endif + gcc_checking_assert (!virtual_offset + || tree_int_cst_equal (virtual_offset, + size_int (virtual_value))); node->thunk.fixed_offset = fixed_offset; node->thunk.this_adjusting = this_adjusting; node->thunk.virtual_value = virtual_value; node->thunk.virtual_offset_p = virtual_offset != NULL; node->thunk.alias = real_alias; node->thunk.thunk_p = true; + return node; } /* Returns the cgraph node assigned to DECL or NULL if no cgraph node is assigned. */ struct cgraph_node * -cgraph_get_node (tree decl) +cgraph_get_node_or_alias (const_tree decl) { struct cgraph_node key, *node = NULL, **slot; @@ -613,7 +634,30 @@ if (!cgraph_hash) return NULL; - key.decl = decl; + key.decl = CONST_CAST2 (tree, const_tree, decl); + + slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, + NO_INSERT); + + if (slot && *slot) + node = *slot; + return node; +} + +/* Returns the cgraph node assigned to DECL or NULL if no cgraph node + is assigned. */ + +struct cgraph_node * +cgraph_get_node (const_tree decl) +{ + struct cgraph_node key, *node = NULL, **slot; + + gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + + if (!cgraph_hash) + return NULL; + + key.decl = CONST_CAST2 (tree, const_tree, decl); slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, NO_INSERT); @@ -817,7 +861,7 @@ indirect call into a direct one. */ struct cgraph_node *new_callee = cgraph_node (decl); - cgraph_make_edge_direct (e, new_callee); + cgraph_make_edge_direct (e, new_callee, 0); } push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl)); @@ -894,7 +938,7 @@ /* It is possible that clones already contain the edge while master didn't. Either we promoted indirect call into direct call in the clone or we are processing clones of unreachable - master where edges has been rmeoved. */ + master where edges has been removed. */ if (edge) cgraph_set_call_stmt (edge, stmt); else if (!cgraph_edge (node, stmt)) @@ -954,11 +998,9 @@ have not been loaded yet. */ if (call_stmt) { -#ifdef ENABLE_CHECKING - /* This is rather pricely check possibly trigerring construction of - call stmt hashtable. */ - gcc_assert (!cgraph_edge (caller, call_stmt)); -#endif + /* This is a rather expensive check possibly triggering + construction of call stmt hashtable. */ + gcc_checking_assert (!cgraph_edge (caller, call_stmt)); gcc_assert (is_gimple_call (call_stmt)); } @@ -970,7 +1012,7 @@ } else { - edge = GGC_NEW (struct cgraph_edge); + edge = ggc_alloc_cgraph_edge (); edge->uid = cgraph_edge_max_uid++; } @@ -1029,6 +1071,17 @@ return edge; } +/* Allocate cgraph_indirect_call_info and set its fields to default values. */ + +struct cgraph_indirect_call_info * +cgraph_allocate_init_indirect_info (void) +{ + struct cgraph_indirect_call_info *ii; + + ii = ggc_alloc_cleared_cgraph_indirect_call_info (); + ii->param_index = -1; + return ii; +} /* Create an indirect edge with a yet-undetermined callee where the call statement destination is a formal parameter of the caller with index @@ -1045,8 +1098,7 @@ edge->indirect_unknown_callee = 1; initialize_inline_failed (edge); - edge->indirect_info = GGC_CNEW (struct cgraph_indirect_call_info); - edge->indirect_info->param_index = -1; + edge->indirect_info = cgraph_allocate_init_indirect_info (); edge->indirect_info->ecf_flags = ecf_flags; edge->next_callee = caller->indirect_calls; @@ -1154,12 +1206,15 @@ } /* Make an indirect EDGE with an unknown callee an ordinary edge leading to - CALLEE. */ + CALLEE. DELTA is an integer constant that is to be added to the this + pointer (first parameter) to compensate for skipping a thunk adjustment. */ void -cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee) +cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee, + HOST_WIDE_INT delta) { edge->indirect_unknown_callee = 0; + edge->indirect_info->thunk_delta = delta; /* Get the edge out of the indirect edge list. */ if (edge->prev_callee) @@ -1198,7 +1253,7 @@ if (!new_call && !old_call) return; /* See if we turned indirect call into direct call or folded call to one builtin - into different bultin. */ + into different builtin. */ if (old_call != new_call) { struct cgraph_edge *e = cgraph_edge (node, old_stmt); @@ -1211,9 +1266,18 @@ { /* See if the edge is already there and has the correct callee. It might be so because of indirect inlining has already updated - it. */ - if (new_call && e->callee && e->callee->decl == new_call) - return; + it. We also might've cloned and redirected the edge. */ + if (new_call && e->callee) + { + struct cgraph_node *callee = e->callee; + while (callee) + { + if (callee->decl == new_call + || callee->former_clone_of == new_call) + return; + callee = callee->clone_of; + } + } /* Otherwise remove edge and create new one; we can't simply redirect since function has changed, so inline plan and other information @@ -1659,7 +1723,8 @@ /* Verify that function does not appear to be needed out of blue during the optimization process. This can happen for extern inlines when bodies was removed after inlining. */ - gcc_assert ((node->analyzed || DECL_EXTERNAL (node->decl))); + gcc_assert ((node->analyzed || node->in_other_partition + || DECL_EXTERNAL (node->decl))); } else notice_global_symbol (node->decl); @@ -1686,6 +1751,7 @@ void cgraph_mark_address_taken_node (struct cgraph_node *node) { + gcc_assert (!node->global.inlined_to); cgraph_mark_reachable_node (node); node->address_taken = 1; } @@ -1776,6 +1842,10 @@ fprintf (f, " (inline copy in %s/%i)", cgraph_node_name (node->global.inlined_to), node->global.inlined_to->uid); + if (node->same_comdat_group) + fprintf (f, " (same comdat group as %s/%i)", + cgraph_node_name (node->same_comdat_group), + node->same_comdat_group->uid); if (node->clone_of) fprintf (f, " (clone of %s/%i)", cgraph_node_name (node->clone_of), @@ -1824,6 +1894,9 @@ fprintf (f, " local"); if (node->local.externally_visible) fprintf (f, " externally_visible"); + if (node->resolution != LDPR_UNKNOWN) + fprintf (f, " %s", + ld_plugin_symbol_resolution_names[(int)node->resolution]); if (node->local.finalized) fprintf (f, " finalized"); if (node->local.disregard_inline_limits) @@ -1836,6 +1909,10 @@ fprintf (f, " redefined_extern_inline"); if (TREE_ASM_WRITTEN (node->decl)) fprintf (f, " asm_written"); + if (node->only_called_at_startup) + fprintf (f, " only_called_at_startup"); + if (node->only_called_at_exit) + fprintf (f, " only_called_at_exit"); fprintf (f, "\n called by: "); for (edge = node->callers; edge; edge = edge->next_caller) @@ -1897,7 +1974,7 @@ fprintf (f, " %s/%i", cgraph_node_name (n), n->uid); if (n->thunk.thunk_p) { - fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has " + fprintf (f, " (thunk of %s fixed offset %i virtual value %i has " "virtual offset %i", lang_hooks.decl_printable_name (n->thunk.alias, 2), (int)n->thunk.fixed_offset, @@ -1915,7 +1992,7 @@ /* Dump call graph node NODE to stderr. */ -void +DEBUG_FUNCTION void debug_cgraph_node (struct cgraph_node *node) { dump_cgraph_node (stderr, node); @@ -1937,7 +2014,7 @@ /* Dump the call graph to stderr. */ -void +DEBUG_FUNCTION void debug_cgraph (void) { dump_cgraph (stderr); @@ -1949,20 +2026,43 @@ void change_decl_assembler_name (tree decl, tree name) { - gcc_assert (!assembler_name_hash); + struct cgraph_node *node; + void **slot; if (!DECL_ASSEMBLER_NAME_SET_P (decl)) + SET_DECL_ASSEMBLER_NAME (decl, name); + else { + if (name == DECL_ASSEMBLER_NAME (decl)) + return; + + if (assembler_name_hash + && TREE_CODE (decl) == FUNCTION_DECL + && (node = cgraph_get_node_or_alias (decl)) != NULL) + { + tree old_name = DECL_ASSEMBLER_NAME (decl); + slot = htab_find_slot_with_hash (assembler_name_hash, old_name, + decl_assembler_name_hash (old_name), + NO_INSERT); + /* Inline clones are not hashed. */ + if (slot && *slot == node) + htab_clear_slot (assembler_name_hash, slot); + } + if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)) + && DECL_RTL_SET_P (decl)) + warning (0, "%D renamed after being referenced in assembly", decl); + SET_DECL_ASSEMBLER_NAME (decl, name); - return; } - if (name == DECL_ASSEMBLER_NAME (decl)) - return; - - if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)) - && DECL_RTL_SET_P (decl)) - warning (0, "%D renamed after being referenced in assembly", decl); - - SET_DECL_ASSEMBLER_NAME (decl, name); + if (assembler_name_hash + && TREE_CODE (decl) == FUNCTION_DECL + && (node = cgraph_get_node_or_alias (decl)) != NULL) + { + slot = htab_find_slot_with_hash (assembler_name_hash, name, + decl_assembler_name_hash (name), + INSERT); + gcc_assert (!*slot); + *slot = node; + } } /* Add a top-level asm statement to the list. */ @@ -1972,7 +2072,7 @@ { struct cgraph_asm_node *node; - node = GGC_CNEW (struct cgraph_asm_node); + node = ggc_alloc_cleared_cgraph_asm_node (); node->asm_str = asm_str; node->order = cgraph_order++; node->next = NULL; @@ -2030,12 +2130,23 @@ } } else - new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq, - e->loop_nest + loop_nest); + { + new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq, + e->loop_nest + loop_nest); + if (e->indirect_info) + { + new_edge->indirect_info + = ggc_alloc_cleared_cgraph_indirect_call_info (); + *new_edge->indirect_info = *e->indirect_info; + } + } new_edge->inline_failed = e->inline_failed; new_edge->indirect_inlining_edge = e->indirect_inlining_edge; new_edge->lto_stmt_uid = stmt_uid; + /* Clone flags that depend on call_stmt availability manually. */ + new_edge->can_throw_external = e->can_throw_external; + new_edge->call_stmt_cannot_inline_p = e->call_stmt_cannot_inline_p; if (update_original) { e->count -= new_edge->count; @@ -2098,7 +2209,7 @@ n->count = 0; } - for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++) + FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e) { /* Redirect calls to the old version node to point to its new version. */ @@ -2143,24 +2254,26 @@ return new_node; } -/* Create a new name for omp child function. Returns an identifier. */ +/* Create a new name for clone of DECL, add SUFFIX. Returns an identifier. */ static GTY(()) unsigned int clone_fn_id_num; -static tree -clone_function_name (tree decl) +tree +clone_function_name (tree decl, const char *suffix) { tree name = DECL_ASSEMBLER_NAME (decl); size_t len = IDENTIFIER_LENGTH (name); char *tmp_name, *prefix; - prefix = XALLOCAVEC (char, len + strlen ("_clone") + 1); + prefix = XALLOCAVEC (char, len + strlen (suffix) + 2); memcpy (prefix, IDENTIFIER_POINTER (name), len); - strcpy (prefix + len, "_clone"); + strcpy (prefix + len + 1, suffix); #ifndef NO_DOT_IN_LABEL prefix[len] = '.'; #elif !defined NO_DOLLAR_IN_LABEL prefix[len] = '$'; +#else + prefix[len] = '_'; #endif ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++); return get_identifier (tmp_name); @@ -2176,7 +2289,8 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node, VEC(cgraph_edge_p,heap) *redirect_callers, VEC(ipa_replace_map_p,gc) *tree_map, - bitmap args_to_skip) + bitmap args_to_skip, + const char * suffix) { tree old_decl = old_node->decl; struct cgraph_node *new_node = NULL; @@ -2184,10 +2298,10 @@ size_t i; struct ipa_replace_map *map; -#ifdef ENABLE_CHECKING if (!flag_wpa) - gcc_assert (tree_versionable_function_p (old_decl)); -#endif + gcc_checking_assert (tree_versionable_function_p (old_decl)); + + gcc_assert (old_node->local.can_change_signature || !args_to_skip); /* Make a new FUNCTION_DECL tree node */ if (!args_to_skip) @@ -2197,7 +2311,7 @@ DECL_STRUCT_FUNCTION (new_decl) = NULL; /* Generate a new name for the new version. */ - DECL_NAME (new_decl) = clone_function_name (old_decl); + DECL_NAME (new_decl) = clone_function_name (old_decl, suffix); SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); SET_DECL_RTL (new_decl, NULL); @@ -2210,13 +2324,15 @@ ??? We cannot use COMDAT linkage because there is no ABI support for this. */ DECL_EXTERNAL (new_node->decl) = 0; + if (DECL_ONE_ONLY (old_decl)) + DECL_SECTION_NAME (new_node->decl) = NULL; DECL_COMDAT_GROUP (new_node->decl) = 0; TREE_PUBLIC (new_node->decl) = 0; DECL_COMDAT (new_node->decl) = 0; DECL_WEAK (new_node->decl) = 0; new_node->clone.tree_map = tree_map; new_node->clone.args_to_skip = args_to_skip; - for (i = 0; VEC_iterate (ipa_replace_map_p, tree_map, i, map); i++) + FOR_EACH_VEC_ELT (ipa_replace_map_p, tree_map, i, map) { tree var = map->new_tree; @@ -2246,7 +2362,7 @@ struct cgraph_node *orig_node; for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of) ; - for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = TREE_CHAIN (arg), oldi++) + for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = DECL_CHAIN (arg), oldi++) { if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi)) { @@ -2296,8 +2412,8 @@ avail = AVAIL_LOCAL; else if (!node->local.externally_visible) avail = AVAIL_AVAILABLE; - /* Inline functions are safe to be analyzed even if their sybol can - be overwritten at runtime. It is not meaningful to enfore any sane + /* Inline functions are safe to be analyzed even if their symbol can + be overwritten at runtime. It is not meaningful to enforce any sane behaviour on replacing inline function by different body. */ else if (DECL_DECLARED_INLINE_P (node->decl)) avail = AVAIL_AVAILABLE; @@ -2312,7 +2428,7 @@ AVAIL_AVAILABLE here? That would be good reason to preserve this bit. */ - else if (DECL_REPLACEABLE_P (node->decl) && !DECL_EXTERNAL (node->decl)) + else if (decl_replaceable_p (node->decl) && !DECL_EXTERNAL (node->decl)) avail = AVAIL_OVERWRITABLE; else avail = AVAIL_AVAILABLE; @@ -2422,15 +2538,50 @@ if (TREE_CODE (decl) == VAR_DECL) DECL_COMMON (decl) = 0; - else if (TREE_CODE (decl) == FUNCTION_DECL) + else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + + if (DECL_COMDAT (decl)) { + /* It is possible that we are linking against library defining same COMDAT + function. To avoid conflict we need to rename our local name of the + function just in the case WHOPR partitioning decide to make it hidden + to avoid cross partition references. */ + if (flag_wpa) + { + const char *old_name; + + old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + if (TREE_CODE (decl) == FUNCTION_DECL) + { + struct cgraph_node *node = cgraph_get_node_or_alias (decl); + change_decl_assembler_name (decl, + clone_function_name (decl, "local")); + if (node->local.lto_file_data) + lto_record_renamed_decl (node->local.lto_file_data, + old_name, + IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (decl))); + } + else if (TREE_CODE (decl) == VAR_DECL) + { + struct varpool_node *vnode = varpool_get_node (decl); + /* change_decl_assembler_name will warn here on vtables because + C++ frontend still sets TREE_SYMBOL_REFERENCED on them. */ + SET_DECL_ASSEMBLER_NAME (decl, + clone_function_name (decl, "local")); + if (vnode->lto_file_data) + lto_record_renamed_decl (vnode->lto_file_data, + old_name, + IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (decl))); + } + } + DECL_SECTION_NAME (decl) = 0; DECL_COMDAT (decl) = 0; - DECL_COMDAT_GROUP (decl) = 0; - DECL_WEAK (decl) = 0; - DECL_EXTERNAL (decl) = 0; } - else - gcc_unreachable (); + DECL_COMDAT_GROUP (decl) = 0; + DECL_WEAK (decl) = 0; + DECL_EXTERNAL (decl) = 0; TREE_PUBLIC (decl) = 0; if (!DECL_RTL_SET_P (decl)) return; @@ -2464,6 +2615,7 @@ node->local.externally_visible = false; node->local.local = true; + node->resolution = LDPR_PREVAILING_DEF_IRONLY; gcc_assert (cgraph_function_body_availability (node) == AVAIL_LOCAL); } } @@ -2484,37 +2636,50 @@ if any to READONLY. */ void -cgraph_set_readonly_flag (struct cgraph_node *node, bool readonly) +cgraph_set_const_flag (struct cgraph_node *node, bool readonly, bool looping) { struct cgraph_node *alias; + /* Static constructors and destructors without a side effect can be + optimized out. */ + if (!looping && readonly) + { + if (DECL_STATIC_CONSTRUCTOR (node->decl)) + DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + if (DECL_STATIC_DESTRUCTOR (node->decl)) + DECL_STATIC_DESTRUCTOR (node->decl) = 0; + } TREE_READONLY (node->decl) = readonly; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping; for (alias = node->same_body; alias; alias = alias->next) - TREE_READONLY (alias->decl) = readonly; + { + TREE_READONLY (alias->decl) = readonly; + DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping; + } } /* Set DECL_PURE_P on NODE's decl and on same_body aliases of NODE if any to PURE. */ void -cgraph_set_pure_flag (struct cgraph_node *node, bool pure) +cgraph_set_pure_flag (struct cgraph_node *node, bool pure, bool looping) { struct cgraph_node *alias; + /* Static constructors and destructors without a side effect can be + optimized out. */ + if (!looping && pure) + { + if (DECL_STATIC_CONSTRUCTOR (node->decl)) + DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + if (DECL_STATIC_DESTRUCTOR (node->decl)) + DECL_STATIC_DESTRUCTOR (node->decl) = 0; + } DECL_PURE_P (node->decl) = pure; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping; for (alias = node->same_body; alias; alias = alias->next) - DECL_PURE_P (alias->decl) = pure; -} - -/* Set DECL_LOOPING_CONST_OR_PURE_P on NODE's decl and on - same_body aliases of NODE if any to LOOPING_CONST_OR_PURE. */ - -void -cgraph_set_looping_const_or_pure_flag (struct cgraph_node *node, - bool looping_const_or_pure) -{ - struct cgraph_node *alias; - DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping_const_or_pure; - for (alias = node->same_body; alias; alias = alias->next) - DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping_const_or_pure; + { + DECL_PURE_P (alias->decl) = pure; + DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping; + } } /* See if the frequency of NODE can be updated based on frequencies of its @@ -2523,20 +2688,32 @@ cgraph_propagate_frequency (struct cgraph_node *node) { bool maybe_unlikely_executed = true, maybe_executed_once = true; + bool only_called_at_startup = true; + bool only_called_at_exit = true; + bool changed = false; struct cgraph_edge *edge; + if (!node->local.local) return false; gcc_assert (node->analyzed); - if (node->frequency == NODE_FREQUENCY_HOT) - return false; - if (node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED) - return false; if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Processing frequency %s\n", cgraph_node_name (node)); + for (edge = node->callers; - edge && (maybe_unlikely_executed || maybe_executed_once); + edge && (maybe_unlikely_executed || maybe_executed_once + || only_called_at_startup || only_called_at_exit); edge = edge->next_caller) { + if (edge->caller != node) + { + only_called_at_startup &= edge->caller->only_called_at_startup; + /* It makes sense to put main() together with the static constructors. + It will be executed for sure, but rest of functions called from + main are definitely not at startup only. */ + if (MAIN_NAME_P (DECL_NAME (edge->caller->decl))) + only_called_at_startup = 0; + only_called_at_exit &= edge->caller->only_called_at_exit; + } if (!edge->frequency) continue; switch (edge->caller->frequency) @@ -2545,7 +2722,8 @@ break; case NODE_FREQUENCY_EXECUTED_ONCE: if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, " Called by %s that is executed once\n", cgraph_node_name (node)); + fprintf (dump_file, " Called by %s that is executed once\n", + cgraph_node_name (edge->caller)); maybe_unlikely_executed = false; if (edge->loop_nest) { @@ -2557,27 +2735,175 @@ case NODE_FREQUENCY_HOT: case NODE_FREQUENCY_NORMAL: if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, " Called by %s that is normal or hot\n", cgraph_node_name (node)); + fprintf (dump_file, " Called by %s that is normal or hot\n", + cgraph_node_name (edge->caller)); maybe_unlikely_executed = false; maybe_executed_once = false; break; } } - if (maybe_unlikely_executed) - { - node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED; + if ((only_called_at_startup && !only_called_at_exit) + && !node->only_called_at_startup) + { + node->only_called_at_startup = true; + if (dump_file) + fprintf (dump_file, "Node %s promoted to only called at startup.\n", + cgraph_node_name (node)); + changed = true; + } + if ((only_called_at_exit && !only_called_at_startup) + && !node->only_called_at_exit) + { + node->only_called_at_exit = true; if (dump_file) - fprintf (dump_file, "Node %s promoted to unlikely executed.\n", cgraph_node_name (node)); - return true; - } - if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE) - { - node->frequency = NODE_FREQUENCY_EXECUTED_ONCE; - if (dump_file) - fprintf (dump_file, "Node %s promoted to executed once.\n", cgraph_node_name (node)); - return true; - } - return false; + fprintf (dump_file, "Node %s promoted to only called at exit.\n", + cgraph_node_name (node)); + changed = true; + } + /* These come either from profile or user hints; never update them. */ + if (node->frequency == NODE_FREQUENCY_HOT + || node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED) + return changed; + if (maybe_unlikely_executed) + { + node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED; + if (dump_file) + fprintf (dump_file, "Node %s promoted to unlikely executed.\n", + cgraph_node_name (node)); + changed = true; + } + else if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE) + { + node->frequency = NODE_FREQUENCY_EXECUTED_ONCE; + if (dump_file) + fprintf (dump_file, "Node %s promoted to executed once.\n", + cgraph_node_name (node)); + changed = true; + } + return changed; +} + +/* Return true when NODE can not return or throw and thus + it is safe to ignore its side effects for IPA analysis. */ + +bool +cgraph_node_cannot_return (struct cgraph_node *node) +{ + int flags = flags_from_decl_or_type (node->decl); + if (!flag_exceptions) + return (flags & ECF_NORETURN) != 0; + else + return ((flags & (ECF_NORETURN | ECF_NOTHROW)) + == (ECF_NORETURN | ECF_NOTHROW)); +} + +/* Return true when call of E can not lead to return from caller + and thus it is safe to ignore its side effects for IPA analysis + when computing side effects of the caller. + FIXME: We could actually mark all edges that have no reaching + patch to EXIT_BLOCK_PTR or throw to get better results. */ +bool +cgraph_edge_cannot_lead_to_return (struct cgraph_edge *e) +{ + if (cgraph_node_cannot_return (e->caller)) + return true; + if (e->indirect_unknown_callee) + { + int flags = e->indirect_info->ecf_flags; + if (!flag_exceptions) + return (flags & ECF_NORETURN) != 0; + else + return ((flags & (ECF_NORETURN | ECF_NOTHROW)) + == (ECF_NORETURN | ECF_NOTHROW)); + } + else + return cgraph_node_cannot_return (e->callee); +} + +/* Return true when function NODE can be removed from callgraph + if all direct calls are eliminated. */ + +bool +cgraph_can_remove_if_no_direct_calls_and_refs_p (struct cgraph_node *node) +{ + gcc_assert (!node->global.inlined_to); + /* Extern inlines can always go, we will use the external definition. */ + if (DECL_EXTERNAL (node->decl)) + return true; + /* When function is needed, we can not remove it. */ + if (node->needed || node->reachable_from_other_partition) + return false; + if (DECL_STATIC_CONSTRUCTOR (node->decl) + || DECL_STATIC_DESTRUCTOR (node->decl)) + return false; + /* Only COMDAT functions can be removed if externally visible. */ + if (node->local.externally_visible + && (!DECL_COMDAT (node->decl) + || cgraph_used_from_object_file_p (node))) + return false; + return true; +} + +/* Return true when function NODE can be expected to be removed + from program when direct calls in this compilation unit are removed. + + As a special case COMDAT functions are + cgraph_can_remove_if_no_direct_calls_p while the are not + cgraph_only_called_directly_p (it is possible they are called from other + unit) + + This function behaves as cgraph_only_called_directly_p because eliminating + all uses of COMDAT function does not make it necessarily disappear from + the program unless we are compiling whole program or we do LTO. In this + case we know we win since dynamic linking will not really discard the + linkonce section. */ + +bool +cgraph_will_be_removed_from_program_if_no_direct_calls (struct cgraph_node *node) +{ + gcc_assert (!node->global.inlined_to); + if (cgraph_used_from_object_file_p (node)) + return false; + if (!in_lto_p && !flag_whole_program) + return cgraph_only_called_directly_p (node); + else + { + if (DECL_EXTERNAL (node->decl)) + return true; + return cgraph_can_remove_if_no_direct_calls_p (node); + } +} + +/* Return true when RESOLUTION indicate that linker will use + the symbol from non-LTO object files. */ + +bool +resolution_used_from_other_file_p (enum ld_plugin_symbol_resolution resolution) +{ + return (resolution == LDPR_PREVAILING_DEF + || resolution == LDPR_PREEMPTED_REG + || resolution == LDPR_RESOLVED_EXEC + || resolution == LDPR_RESOLVED_DYN); +} + +/* Return true when NODE is known to be used from other (non-LTO) object file. + Known only when doing LTO via linker plugin. */ + +bool +cgraph_used_from_object_file_p (struct cgraph_node *node) +{ + struct cgraph_node *alias; + + gcc_assert (!node->global.inlined_to); + if (!TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl)) + return false; + if (resolution_used_from_other_file_p (node->resolution)) + return true; + for (alias = node->same_body; alias; alias = alias->next) + if (TREE_PUBLIC (alias->decl) + && resolution_used_from_other_file_p (alias->resolution)) + return true; + return false; } #include "gt-cgraph.h"