X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcgraph.c;h=9cc36903a8c5c6b982d08dd7798f960c620e3c45;hb=97f190cd6b8b78295b9d73b2e0e6279e48e7773d;hp=da020fe1ab29dc6f32c6aa132726df896d0ad1c3;hpb=0835ad03325b16dbc28ea1e7b53b3b2491dc2d6d;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cgraph.c b/gcc/cgraph.c index da020fe1ab2..9cc36903a8c 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -110,7 +110,8 @@ const char * const ld_plugin_symbol_resolution_names[]= "preempted_ir", "resolved_ir", "resolved_exec", - "resolved_dyn" + "resolved_dyn", + "prevailing_def_ironly_exp" }; static void cgraph_node_remove_callers (struct cgraph_node *node); @@ -208,6 +209,9 @@ static GTY(()) struct cgraph_node *free_nodes; Do not GTY((delete)) this list so UIDs gets reliably recycled. */ static GTY(()) struct cgraph_edge *free_edges; +/* Did procss_same_body_aliases run? */ +bool same_body_aliases_done; + /* Macros to access the next item in the list of free cgraph nodes and edges. */ #define NEXT_FREE_NODE(NODE) (NODE)->next @@ -408,7 +412,7 @@ cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *entry) } /* Call all node duplication hooks. */ -static void +void cgraph_call_node_duplication_hooks (struct cgraph_node *node1, struct cgraph_node *node2) { @@ -542,33 +546,23 @@ cgraph_get_create_node (tree 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 (struct cgraph_node *decl_node, tree alias, tree decl) +struct cgraph_node * +cgraph_create_function_alias (tree alias, tree decl) { - struct cgraph_node key, *alias_node, **slot; + struct cgraph_node *alias_node; gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); gcc_assert (TREE_CODE (alias) == FUNCTION_DECL); - - key.decl = alias; - - slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT); - - /* If the cgraph_node has been already created, fail. */ - if (*slot) - return NULL; - - alias_node = cgraph_allocate_node (); - alias_node->decl = alias; - alias_node->same_body_alias = 1; - alias_node->same_body = decl_node; - alias_node->previous = NULL; - if (decl_node->same_body) - decl_node->same_body->previous = alias_node; - alias_node->next = decl_node->same_body; + alias_node = cgraph_get_create_node (alias); + gcc_assert (!alias_node->local.finalized); alias_node->thunk.alias = decl; - decl_node->same_body = alias_node; - *slot = alias_node; + alias_node->local.finalized = true; + alias_node->alias = 1; + + if ((TREE_PUBLIC (alias) && !DECL_COMDAT (alias) && !DECL_EXTERNAL (alias)) + || (DECL_VIRTUAL_P (alias) + && (DECL_COMDAT (alias) || DECL_EXTERNAL (alias)))) + cgraph_mark_reachable_node (alias_node); return alias_node; } @@ -578,16 +572,24 @@ cgraph_same_body_alias_1 (struct cgraph_node *decl_node, tree alias, tree decl) and cgraph_get_node (ALIAS) transparently returns cgraph_get_node (DECL). */ struct cgraph_node * -cgraph_same_body_alias (struct cgraph_node *decl_node, tree alias, tree decl) +cgraph_same_body_alias (struct cgraph_node *decl_node ATTRIBUTE_UNUSED, tree alias, tree decl) { + struct cgraph_node *n; #ifndef ASM_OUTPUT_DEF /* If aliases aren't supported by the assembler, fail. */ return NULL; #endif + /* Langhooks can create same body aliases of symbols not defined. + Those are useless. Drop them on the floor. */ + if (cgraph_global_info_ready) + return NULL; - /*gcc_assert (!assembler_name_hash);*/ - - return cgraph_same_body_alias_1 (decl_node, alias, decl); + n = cgraph_create_function_alias (alias, decl); + n->same_body_alias = true; + if (same_body_aliases_done) + ipa_record_reference (n, NULL, cgraph_get_node (decl), NULL, IPA_REF_ALIAS, + NULL); + return n; } /* Add thunk alias into callgraph. The alias declaration is ALIAS and it @@ -595,55 +597,45 @@ cgraph_same_body_alias (struct cgraph_node *decl_node, tree alias, tree decl) 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, +cgraph_add_thunk (struct cgraph_node *decl_node ATTRIBUTE_UNUSED, + tree alias, tree decl, bool this_adjusting, HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value, tree virtual_offset, tree real_alias) { - struct cgraph_node *node = cgraph_get_node (alias); + struct cgraph_node *node; + node = cgraph_get_node (alias); if (node) { gcc_assert (node->local.finalized); - gcc_assert (!node->same_body); + gcc_assert (!node->alias); + gcc_assert (!node->thunk.thunk_p); cgraph_remove_node (node); } - node = cgraph_same_body_alias_1 (decl_node, alias, decl); - gcc_assert (node); + node = cgraph_create_node (alias); gcc_checking_assert (!virtual_offset - || tree_int_cst_equal (virtual_offset, - size_int (virtual_value))); + || double_int_equal_p + (tree_to_double_int (virtual_offset), + shwi_to_double_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_or_alias (const_tree decl) -{ - struct cgraph_node key, *node = NULL, **slot; - - gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + node->local.finalized = true; - if (!cgraph_hash) - return NULL; + if (cgraph_decide_is_function_needed (node, decl)) + cgraph_mark_needed_node (node); - key.decl = CONST_CAST2 (tree, const_tree, decl); + if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) + || (DECL_VIRTUAL_P (decl) + && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl)))) + cgraph_mark_reachable_node (node); - slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, - NO_INSERT); - - if (slot && *slot) - node = *slot; return node; } @@ -666,11 +658,7 @@ cgraph_get_node (const_tree decl) NO_INSERT); if (slot && *slot) - { - node = *slot; - if (node->same_body_alias) - node = node->same_body; - } + node = *slot; return node; } @@ -733,21 +721,6 @@ cgraph_node_for_asm (tree asmname) so lets hope for the best. */ if (!*slot) *slot = node; - if (node->same_body) - { - struct cgraph_node *alias; - - for (alias = node->same_body; alias; alias = alias->next) - { - hashval_t hash; - name = DECL_ASSEMBLER_NAME (alias->decl); - hash = decl_assembler_name_hash (name); - slot = htab_find_slot_with_hash (assembler_name_hash, name, - hash, INSERT); - if (!*slot) - *slot = alias; - } - } } } @@ -758,8 +731,6 @@ cgraph_node_for_asm (tree asmname) if (slot) { node = (struct cgraph_node *) *slot; - if (node->same_body_alias) - node = node->same_body; return node; } return NULL; @@ -865,7 +836,7 @@ cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt) struct cgraph_node *new_callee = cgraph_get_node (decl); gcc_checking_assert (new_callee); - cgraph_make_edge_direct (e, new_callee, 0); + cgraph_make_edge_direct (e, new_callee); } push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl)); @@ -1017,8 +988,12 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee, edge->can_throw_external = call_stmt ? stmt_can_throw_external (call_stmt) : false; pop_cfun (); - edge->call_stmt_cannot_inline_p = - (call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false); + if (call_stmt + && callee && callee->decl + && !gimple_check_call_matching_types (call_stmt, callee->decl)) + edge->call_stmt_cannot_inline_p = true; + else + edge->call_stmt_cannot_inline_p = false; if (call_stmt && caller->call_site_hash) cgraph_add_edge_to_call_site_hash (edge); @@ -1191,11 +1166,9 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n) pointer (first parameter) to compensate for skipping a thunk adjustment. */ void -cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee, - HOST_WIDE_INT delta) +cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee) { edge->indirect_unknown_callee = 0; - edge->indirect_info->thunk_delta = delta; /* Get the edge out of the indirect edge list. */ if (edge->prev_callee) @@ -1215,6 +1188,10 @@ cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee, /* Insert to callers list of the new callee. */ cgraph_set_edge_callee (edge, callee); + if (edge->call_stmt) + edge->call_stmt_cannot_inline_p + = !gimple_check_call_matching_types (edge->call_stmt, callee->decl); + /* We need to re-determine the inlining status of the edge. */ initialize_inline_failed (edge); } @@ -1222,13 +1199,17 @@ cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee, /* Update or remove the corresponding cgraph edge if a GIMPLE_CALL OLD_STMT changed into NEW_STMT. OLD_CALL is gimple_call_fndecl - of OLD_STMT if it was previously call statement. */ + of OLD_STMT if it was previously call statement. + If NEW_STMT is NULL, the call has been dropped without any + replacement. */ static void cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node, - gimple old_stmt, tree old_call, gimple new_stmt) + gimple old_stmt, tree old_call, + gimple new_stmt) { - tree new_call = (is_gimple_call (new_stmt)) ? gimple_call_fndecl (new_stmt) : 0; + tree new_call = (new_stmt && is_gimple_call (new_stmt)) + ? gimple_call_fndecl (new_stmt) : 0; /* We are seeing indirect calls, then there is nothing to update. */ if (!new_call && !old_call) @@ -1266,7 +1247,7 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node, frequency = e->frequency; cgraph_remove_edge (e); } - else + else if (new_call) { /* We are seeing new direct call; compute profile info based on BB. */ basic_block bb = gimple_bb (new_stmt); @@ -1416,44 +1397,6 @@ cgraph_release_function_body (struct cgraph_node *node) DECL_INITIAL (node->decl) = error_mark_node; } -/* Remove same body alias node. */ - -void -cgraph_remove_same_body_alias (struct cgraph_node *node) -{ - void **slot; - int uid = node->uid; - - gcc_assert (node->same_body_alias); - if (node->previous) - node->previous->next = node->next; - else - node->same_body->same_body = node->next; - if (node->next) - node->next->previous = node->previous; - node->next = NULL; - node->previous = NULL; - slot = htab_find_slot (cgraph_hash, node, NO_INSERT); - if (*slot == node) - htab_clear_slot (cgraph_hash, slot); - if (assembler_name_hash) - { - tree name = DECL_ASSEMBLER_NAME (node->decl); - slot = htab_find_slot_with_hash (assembler_name_hash, name, - decl_assembler_name_hash (name), - NO_INSERT); - if (slot && *slot == node) - htab_clear_slot (assembler_name_hash, slot); - } - - /* Clear out the node to NULL all pointers and add the node to the free - list. */ - memset (node, 0, sizeof(*node)); - node->uid = uid; - NEXT_FREE_NODE (node) = free_nodes; - free_nodes = node; -} - /* Remove the node from cgraph. */ void @@ -1615,9 +1558,6 @@ cgraph_remove_node (struct cgraph_node *node) } } - while (node->same_body) - cgraph_remove_same_body_alias (node->same_body); - if (node->same_comdat_group) { struct cgraph_node *prev; @@ -1674,6 +1614,31 @@ cgraph_remove_node (struct cgraph_node *node) free_nodes = node; } +/* Add NEW_ to the same comdat group that OLD is in. */ + +void +cgraph_add_to_same_comdat_group (struct cgraph_node *new_, + struct cgraph_node *old) +{ + gcc_assert (DECL_ONE_ONLY (old->decl)); + gcc_assert (!new_->same_comdat_group); + gcc_assert (new_ != old); + + DECL_COMDAT_GROUP (new_->decl) = DECL_COMDAT_GROUP (old->decl); + new_->same_comdat_group = old; + if (!old->same_comdat_group) + old->same_comdat_group = new_; + else + { + struct cgraph_node *n; + for (n = old->same_comdat_group; + n->same_comdat_group != old; + n = n->same_comdat_group) + ; + n->same_comdat_group = new_; + } +} + /* Remove the node from cgraph. */ void @@ -1731,6 +1696,14 @@ cgraph_mark_address_taken_node (struct cgraph_node *node) { gcc_assert (!node->global.inlined_to); cgraph_mark_reachable_node (node); + /* FIXME: address_taken flag is used both as a shortcut for testing whether + IPA_REF_ADDR reference exists (and thus it should be set on node + representing alias we take address of) and as a test whether address + of the object was taken (and thus it should be set on node alias is + referring to). We should remove the first use and the remove the + following set. */ + node->address_taken = 1; + node = cgraph_function_or_thunk_node (node, NULL); node->address_taken = 1; } @@ -1873,8 +1846,35 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) fprintf (f, " only_called_at_startup"); if (node->only_called_at_exit) fprintf (f, " only_called_at_exit"); + else if (node->alias) + fprintf (f, " alias"); + if (node->tm_clone) + fprintf (f, " tm_clone"); + + fprintf (f, "\n"); + + if (node->thunk.thunk_p) + { + fprintf (f, " thunk of %s (asm: %s) fixed offset %i virtual value %i has " + "virtual offset %i)\n", + lang_hooks.decl_printable_name (node->thunk.alias, 2), + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->thunk.alias)), + (int)node->thunk.fixed_offset, + (int)node->thunk.virtual_value, + (int)node->thunk.virtual_offset_p); + } + if (node->alias && node->thunk.alias) + { + fprintf (f, " alias of %s", + lang_hooks.decl_printable_name (node->thunk.alias, 2)); + if (DECL_ASSEMBLER_NAME_SET_P (node->thunk.alias)) + fprintf (f, " (asm: %s)", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->thunk.alias))); + fprintf (f, "\n"); + } + + fprintf (f, " called by: "); - fprintf (f, "\n called by: "); for (edge = node->callers; edge; edge = edge->next_caller) { fprintf (f, "%s/%i ", cgraph_node_name (edge->caller), @@ -1922,29 +1922,6 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) if (indirect_calls_count) fprintf (f, " has %i outgoing edges for indirect calls.\n", indirect_calls_count); - - if (node->same_body) - { - struct cgraph_node *n; - fprintf (f, " aliases & thunks:"); - for (n = node->same_body; n; n = n->next) - { - fprintf (f, " %s/%i", cgraph_node_name (n), n->uid); - if (n->thunk.thunk_p) - { - 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, - (int)n->thunk.virtual_value, - (int)n->thunk.virtual_offset_p); - fprintf (f, ")"); - } - if (DECL_ASSEMBLER_NAME_SET_P (n->decl)) - fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl))); - } - fprintf (f, "\n"); - } } @@ -1995,7 +1972,7 @@ change_decl_assembler_name (tree decl, tree name) if (assembler_name_hash && TREE_CODE (decl) == FUNCTION_DECL - && (node = cgraph_get_node_or_alias (decl)) != NULL) + && (node = cgraph_get_node (decl)) != NULL) { tree old_name = DECL_ASSEMBLER_NAME (decl); slot = htab_find_slot_with_hash (assembler_name_hash, old_name, @@ -2013,7 +1990,7 @@ change_decl_assembler_name (tree decl, tree name) } if (assembler_name_hash && TREE_CODE (decl) == FUNCTION_DECL - && (node = cgraph_get_node_or_alias (decl)) != NULL) + && (node = cgraph_get_node (decl)) != NULL) { slot = htab_find_slot_with_hash (assembler_name_hash, name, decl_assembler_name_hash (name), @@ -2113,6 +2090,7 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n, return new_edge; } + /* Create node representing clone of N executed COUNT times. Decrease the execution counts from original node too. The new clone will have decl set to DECL that may or may not be the same @@ -2120,11 +2098,15 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n, When UPDATE_ORIGINAL is true, the counts are subtracted from the original function's profile to reflect the fact that part of execution is handled - by node. */ + by node. + When CALL_DUPLICATOIN_HOOK is true, the ipa passes are acknowledged about + the new clone. Otherwise the caller is responsible for doing so later. */ + struct cgraph_node * cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq, bool update_original, - VEC(cgraph_edge_p,heap) *redirect_callers) + VEC(cgraph_edge_p,heap) *redirect_callers, + bool call_duplication_hook) { struct cgraph_node *new_node = cgraph_create_node_1 (); struct cgraph_edge *e; @@ -2187,7 +2169,6 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq, n->clones = new_node; new_node->clone_of = n; - cgraph_call_node_duplication_hooks (n, new_node); if (n->decl != decl) { struct cgraph_node **slot; @@ -2206,6 +2187,9 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq, *aslot = new_node; } } + + if (call_duplication_hook) + cgraph_call_node_duplication_hooks (n, new_node); return new_node; } @@ -2262,7 +2246,7 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node, if (!args_to_skip) new_decl = copy_node (old_decl); else - new_decl = build_function_decl_skip_args (old_decl, args_to_skip); + new_decl = build_function_decl_skip_args (old_decl, args_to_skip, false); DECL_STRUCT_FUNCTION (new_decl) = NULL; /* Generate a new name for the new version. */ @@ -2272,7 +2256,7 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node, new_node = cgraph_clone_node (old_node, new_decl, old_node->count, CGRAPH_FREQ_BASE, false, - redirect_callers); + redirect_callers, false); /* Update the properties. Make clone visible only within this translation unit. Make sure that is not weak also. @@ -2285,6 +2269,8 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node, TREE_PUBLIC (new_node->decl) = 0; DECL_COMDAT (new_node->decl) = 0; DECL_WEAK (new_node->decl) = 0; + DECL_STATIC_CONSTRUCTOR (new_node->decl) = 0; + DECL_STATIC_DESTRUCTOR (new_node->decl) = 0; new_node->clone.tree_map = tree_map; new_node->clone.args_to_skip = args_to_skip; FOR_EACH_VEC_ELT (ipa_replace_map_p, tree_map, i, map) @@ -2341,6 +2327,8 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node, new_node->lowered = true; new_node->reachable = true; + cgraph_call_node_duplication_hooks (old_node, new_node); + return new_node; } @@ -2478,6 +2466,16 @@ cgraph_add_new_function (tree fndecl, bool lowered) DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality (); } +/* Worker for cgraph_node_can_be_local_p. */ +static bool +cgraph_node_cannot_be_local_p_1 (struct cgraph_node *node, + void *data ATTRIBUTE_UNUSED) +{ + return !(!node->needed + && ((DECL_COMDAT (node->decl) && !node->same_comdat_group) + || !node->local.externally_visible)); +} + /* Return true if NODE can be made local for API change. Extern inline functions and C++ COMDAT functions can be made local at the expense of possible code size growth if function is used in multiple @@ -2485,9 +2483,10 @@ cgraph_add_new_function (tree fndecl, bool lowered) bool cgraph_node_can_be_local_p (struct cgraph_node *node) { - return (!node->needed && !node->address_taken - && ((DECL_COMDAT (node->decl) && !node->same_comdat_group) - || !node->local.externally_visible)); + return (!node->address_taken + && !cgraph_for_node_and_aliases (node, + cgraph_node_cannot_be_local_p_1, + NULL, true)); } /* Make DECL local. FIXME: We shouldn't need to mess with rtl this early, @@ -2501,7 +2500,7 @@ cgraph_make_decl_local (tree decl) DECL_COMMON (decl) = 0; else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); - if (DECL_COMDAT (decl)) + if (DECL_ONE_ONLY (decl) || 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 @@ -2514,7 +2513,7 @@ cgraph_make_decl_local (tree decl) old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); if (TREE_CODE (decl) == FUNCTION_DECL) { - struct cgraph_node *node = cgraph_get_node_or_alias (decl); + struct cgraph_node *node = cgraph_get_node (decl); change_decl_assembler_name (decl, clone_function_name (decl, "local")); if (node->local.lto_file_data) @@ -2561,119 +2560,215 @@ cgraph_make_decl_local (tree decl) SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); } -/* Bring NODE local. */ -void -cgraph_make_node_local (struct cgraph_node *node) +/* Call calback on NODE, thunks and aliases asociated to NODE. + When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are + skipped. */ + +bool +cgraph_for_node_thunks_and_aliases (struct cgraph_node *node, + bool (*callback) (struct cgraph_node *, void *), + void *data, + bool include_overwritable) +{ + struct cgraph_edge *e; + int i; + struct ipa_ref *ref; + + if (callback (node, data)) + return true; + for (e = node->callers; e; e = e->next_caller) + if (e->caller->thunk.thunk_p + && (include_overwritable + || cgraph_function_body_availability (e->caller) > AVAIL_OVERWRITABLE)) + if (cgraph_for_node_thunks_and_aliases (e->caller, callback, data, + include_overwritable)) + return true; + for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++) + if (ref->use == IPA_REF_ALIAS) + { + struct cgraph_node *alias = ipa_ref_refering_node (ref); + if (include_overwritable + || cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_thunks_and_aliases (alias, callback, data, + include_overwritable)) + return true; + } + return false; +} + +/* Call calback on NODE and aliases asociated to NODE. + When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are + skipped. */ + +bool +cgraph_for_node_and_aliases (struct cgraph_node *node, + bool (*callback) (struct cgraph_node *, void *), + void *data, + bool include_overwritable) +{ + int i; + struct ipa_ref *ref; + + if (callback (node, data)) + return true; + for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++) + if (ref->use == IPA_REF_ALIAS) + { + struct cgraph_node *alias = ipa_ref_refering_node (ref); + if (include_overwritable + || cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_and_aliases (alias, callback, data, + include_overwritable)) + return true; + } + return false; +} + +/* Worker to bring NODE local. */ + +static bool +cgraph_make_node_local_1 (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) { - gcc_assert (cgraph_node_can_be_local_p (node)); + gcc_checking_assert (cgraph_node_can_be_local_p (node)); if (DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl)) { - struct cgraph_node *alias; cgraph_make_decl_local (node->decl); - for (alias = node->same_body; alias; alias = alias->next) - cgraph_make_decl_local (alias->decl); - node->local.externally_visible = false; node->local.local = true; node->resolution = LDPR_PREVAILING_DEF_IRONLY; gcc_assert (cgraph_function_body_availability (node) == AVAIL_LOCAL); } + return false; +} + +/* Bring NODE local. */ + +void +cgraph_make_node_local (struct cgraph_node *node) +{ + cgraph_for_node_thunks_and_aliases (node, cgraph_make_node_local_1, + NULL, true); +} + +/* Worker to set nothrow flag. */ + +static bool +cgraph_set_nothrow_flag_1 (struct cgraph_node *node, void *data) +{ + struct cgraph_edge *e; + + TREE_NOTHROW (node->decl) = data != NULL; + + if (data != NULL) + for (e = node->callers; e; e = e->next_caller) + e->can_throw_external = false; + return false; } -/* Set TREE_NOTHROW on NODE's decl and on same_body aliases of NODE +/* Set TREE_NOTHROW on NODE's decl and on aliases of NODE if any to NOTHROW. */ void cgraph_set_nothrow_flag (struct cgraph_node *node, bool nothrow) { - struct cgraph_node *alias; - TREE_NOTHROW (node->decl) = nothrow; - for (alias = node->same_body; alias; alias = alias->next) - TREE_NOTHROW (alias->decl) = nothrow; + cgraph_for_node_thunks_and_aliases (node, cgraph_set_nothrow_flag_1, + (void *)(size_t)nothrow, false); } -/* Set TREE_READONLY on NODE's decl and on same_body aliases of NODE - if any to READONLY. */ +/* Worker to set const flag. */ -void -cgraph_set_const_flag (struct cgraph_node *node, bool readonly, bool looping) +static bool +cgraph_set_const_flag_1 (struct cgraph_node *node, void *data) { - struct cgraph_node *alias; /* Static constructors and destructors without a side effect can be optimized out. */ - if (!looping && readonly) + if (data && !((size_t)data & 2)) { 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; - DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping; - } + TREE_READONLY (node->decl) = data != NULL; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0; + return false; } -/* Set DECL_PURE_P on NODE's decl and on same_body aliases of NODE - if any to PURE. */ +/* Set TREE_READONLY on NODE's decl and on aliases of NODE + if any to READONLY. */ void -cgraph_set_pure_flag (struct cgraph_node *node, bool pure, bool looping) +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 + cgraph_for_node_thunks_and_aliases (node, cgraph_set_const_flag_1, + (void *)(size_t)(readonly + (int)looping * 2), + false); +} + +/* Worker to set pure flag. */ + +static bool +cgraph_set_pure_flag_1 (struct cgraph_node *node, void *data) +{ + /* Static pureructors and destructors without a side effect can be optimized out. */ - if (!looping && pure) + if (data && !((size_t)data & 2)) { 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; - DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping; - } + DECL_PURE_P (node->decl) = data != NULL; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0; + return false; } -/* See if the frequency of NODE can be updated based on frequencies of its - callers. */ -bool -cgraph_propagate_frequency (struct cgraph_node *node) +/* Set DECL_PURE_P on NODE's decl and on aliases of NODE + if any to PURE. */ + +void +cgraph_set_pure_flag (struct cgraph_node *node, bool pure, bool looping) { - 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; + cgraph_for_node_thunks_and_aliases (node, cgraph_set_pure_flag_1, + (void *)(size_t)(pure + (int)looping * 2), + false); +} - if (!node->local.local) - return false; - gcc_assert (node->analyzed); - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Processing frequency %s\n", cgraph_node_name (node)); +/* Data used by cgraph_propagate_frequency. */ + +struct cgraph_propagate_frequency_data +{ + bool maybe_unlikely_executed; + bool maybe_executed_once; + bool only_called_at_startup; + bool only_called_at_exit; +}; + +/* Worker for cgraph_propagate_frequency_1. */ + +static bool +cgraph_propagate_frequency_1 (struct cgraph_node *node, void *data) +{ + struct cgraph_propagate_frequency_data *d; + struct cgraph_edge *edge; + d = (struct cgraph_propagate_frequency_data *)data; for (edge = node->callers; - edge && (maybe_unlikely_executed || maybe_executed_once - || only_called_at_startup || only_called_at_exit); + edge && (d->maybe_unlikely_executed || d->maybe_executed_once + || d->only_called_at_startup || d->only_called_at_exit); edge = edge->next_caller) { if (edge->caller != node) { - only_called_at_startup &= edge->caller->only_called_at_startup; + d->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; + d->only_called_at_startup = 0; + d->only_called_at_exit &= edge->caller->only_called_at_exit; } if (!edge->frequency) continue; @@ -2685,10 +2780,10 @@ cgraph_propagate_frequency (struct cgraph_node *node) if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Called by %s that is executed once\n", cgraph_node_name (edge->caller)); - maybe_unlikely_executed = false; + d->maybe_unlikely_executed = false; if (inline_edge_summary (edge)->loop_depth) { - maybe_executed_once = false; + d->maybe_executed_once = false; if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Called in loop\n"); } @@ -2698,12 +2793,31 @@ cgraph_propagate_frequency (struct cgraph_node *node) if (dump_file && (dump_flags & TDF_DETAILS)) 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; + d->maybe_unlikely_executed = false; + d->maybe_executed_once = false; break; } } - if ((only_called_at_startup && !only_called_at_exit) + return edge != NULL; +} + +/* See if the frequency of NODE can be updated based on frequencies of its + callers. */ +bool +cgraph_propagate_frequency (struct cgraph_node *node) +{ + struct cgraph_propagate_frequency_data d = {true, true, true, true}; + bool changed = false; + + if (!node->local.local) + return false; + gcc_assert (node->analyzed); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Processing frequency %s\n", cgraph_node_name (node)); + + cgraph_for_node_and_aliases (node, cgraph_propagate_frequency_1, &d, true); + + if ((d.only_called_at_startup && !d.only_called_at_exit) && !node->only_called_at_startup) { node->only_called_at_startup = true; @@ -2712,7 +2826,7 @@ cgraph_propagate_frequency (struct cgraph_node *node) cgraph_node_name (node)); changed = true; } - if ((only_called_at_exit && !only_called_at_startup) + if ((d.only_called_at_exit && !d.only_called_at_startup) && !node->only_called_at_exit) { node->only_called_at_exit = true; @@ -2725,7 +2839,7 @@ cgraph_propagate_frequency (struct cgraph_node *node) if (node->frequency == NODE_FREQUENCY_HOT || node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED) return changed; - if (maybe_unlikely_executed) + if (d.maybe_unlikely_executed) { node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED; if (dump_file) @@ -2733,7 +2847,7 @@ cgraph_propagate_frequency (struct cgraph_node *node) cgraph_node_name (node)); changed = true; } - else if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE) + else if (d.maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE) { node->frequency = NODE_FREQUENCY_EXECUTED_ONCE; if (dump_file) @@ -2805,6 +2919,36 @@ cgraph_can_remove_if_no_direct_calls_and_refs_p (struct cgraph_node *node) return true; } +/* Worker for cgraph_can_remove_if_no_direct_calls_p. */ + +static bool +nonremovable_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) +{ + return !cgraph_can_remove_if_no_direct_calls_and_refs_p (node); +} + +/* Return true when function NODE and its aliases can be removed from callgraph + if all direct calls are eliminated. */ + +bool +cgraph_can_remove_if_no_direct_calls_p (struct cgraph_node *node) +{ + /* Extern inlines can always go, we will use the external definition. */ + if (DECL_EXTERNAL (node->decl)) + return true; + if (node->address_taken) + return false; + return !cgraph_for_node_and_aliases (node, nonremovable_p, NULL, true); +} + +/* Worker for cgraph_can_remove_if_no_direct_calls_p. */ + +static bool +used_from_object_file_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) +{ + return cgraph_used_from_object_file_p (node); +} + /* Return true when function NODE can be expected to be removed from program when direct calls in this compilation unit are removed. @@ -2823,7 +2967,7 @@ 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)) + if (cgraph_for_node_and_aliases (node, used_from_object_file_p, NULL, true)) return false; if (!in_lto_p && !flag_whole_program) return cgraph_only_called_directly_p (node); @@ -2847,24 +2991,70 @@ resolution_used_from_other_file_p (enum ld_plugin_symbol_resolution resolution) || 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; } +/* Worker for cgraph_only_called_directly_p. */ + +static bool +cgraph_not_only_called_directly_p_1 (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) +{ + return !cgraph_only_called_directly_or_aliased_p (node); +} + +/* Return true when function NODE and all its aliases are only called + directly. + i.e. it is not externally visible, address was not taken and + it is not used in any other non-standard way. */ + +bool +cgraph_only_called_directly_p (struct cgraph_node *node) +{ + gcc_assert (cgraph_function_or_thunk_node (node, NULL) == node); + return !cgraph_for_node_and_aliases (node, cgraph_not_only_called_directly_p_1, + NULL, true); +} + + +/* Collect all callers of NODE. Worker for collect_callers_of_node. */ + +static bool +collect_callers_of_node_1 (struct cgraph_node *node, void *data) +{ + VEC (cgraph_edge_p, heap) ** redirect_callers = (VEC (cgraph_edge_p, heap) **)data; + struct cgraph_edge *cs; + enum availability avail; + cgraph_function_or_thunk_node (node, &avail); + + if (avail > AVAIL_OVERWRITABLE) + for (cs = node->callers; cs != NULL; cs = cs->next_caller) + if (!cs->indirect_inlining_edge) + VEC_safe_push (cgraph_edge_p, heap, *redirect_callers, cs); + return false; +} + +/* Collect all callers of NODE and its aliases that are known to lead to NODE + (i.e. are not overwritable). */ + +VEC (cgraph_edge_p, heap) * +collect_callers_of_node (struct cgraph_node *node) +{ + VEC (cgraph_edge_p, heap) * redirect_callers = NULL; + cgraph_for_node_and_aliases (node, collect_callers_of_node_1, + &redirect_callers, false); + return redirect_callers; +} + #include "gt-cgraph.h"