X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fipa-inline-transform.c;h=75b8e9daf11f9d2dcf5908424e581c7372e9235b;hb=1a96c96a90b1a4581bb04b9d6d6fb99e754c11bc;hp=7465325a35b59583b01a1e243b3ad41609f62066;hpb=0835ad03325b16dbc28ea1e7b53b3b2491dc2d6d;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c index 7465325a35b..75b8e9daf11 100644 --- a/gcc/ipa-inline-transform.c +++ b/gcc/ipa-inline-transform.c @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-prop.h" #include "ipa-inline.h" #include "tree-inline.h" +#include "tree-pass.h" int ncalls_inlined; int nfunctions_inlined; @@ -76,6 +77,55 @@ update_noncloned_frequencies (struct cgraph_node *node, } } +/* We removed or are going to remove the last call to NODE. + Return true if we can and want proactively remove the NODE now. + This is important to do, since we want inliner to know when offline + copy of function was removed. */ + +static bool +can_remove_node_now_p_1 (struct cgraph_node *node) +{ + /* FIXME: When address is taken of DECL_EXTERNAL function we still + can remove its offline copy, but we would need to keep unanalyzed node in + the callgraph so references can point to it. */ + return (!node->address_taken + && !ipa_ref_has_aliases_p (&node->ref_list) + && cgraph_can_remove_if_no_direct_calls_p (node) + /* Inlining might enable more devirtualizing, so we want to remove + those only after all devirtualizable virtual calls are processed. + Lacking may edges in callgraph we just preserve them post + inlining. */ + && (!DECL_VIRTUAL_P (node->decl) + || (!DECL_COMDAT (node->decl) + && !DECL_EXTERNAL (node->decl))) + /* During early inlining some unanalyzed cgraph nodes might be in the + callgraph and they might reffer the function in question. */ + && !cgraph_new_nodes); +} + +/* We are going to eliminate last direct call to NODE (or alias of it) via edge E. + Verify that the NODE can be removed from unit and if it is contained in comdat + group that the whole comdat group is removable. */ + +static bool +can_remove_node_now_p (struct cgraph_node *node, struct cgraph_edge *e) +{ + struct cgraph_node *next; + if (!can_remove_node_now_p_1 (node)) + return false; + + /* When we see same comdat group, we need to be sure that all + items can be removed. */ + if (!node->same_comdat_group) + return true; + for (next = node->same_comdat_group; + next != node; next = next->same_comdat_group) + if ((next->callers && next->callers != e) + || !can_remove_node_now_p_1 (next)) + return false; + return true; +} + /* E is expected to be an edge being inlined. Clone destination node of the edge and redirect it to the new clone. @@ -97,26 +147,15 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, /* Recursive inlining never wants the master clone to be overwritten. */ && update_original - /* FIXME: When address is taken of DECL_EXTERNAL function we still - can remove its offline copy, but we would need to keep unanalyzed - node in the callgraph so references can point to it. */ - && !e->callee->address_taken - && cgraph_can_remove_if_no_direct_calls_p (e->callee) - /* Inlining might enable more devirtualizing, so we want to remove - those only after all devirtualizable virtual calls are processed. - Lacking may edges in callgraph we just preserve them post - inlining. */ - && (!DECL_VIRTUAL_P (e->callee->decl) - || (!DECL_COMDAT (e->callee->decl) - && !DECL_EXTERNAL (e->callee->decl))) - /* Don't reuse if more than one function shares a comdat group. - If the other function(s) are needed, we need to emit even - this function out of line. */ - && !e->callee->same_comdat_group - /* During early inlining some unanalyzed cgraph nodes might be in the - callgraph and they might reffer the function in question. */ - && !cgraph_new_nodes) + && can_remove_node_now_p (e->callee, e)) { + /* TODO: When callee is in a comdat group, we could remove all of it, + including all inline clones inlined into it. That would however + need small function inlining to register edge removal hook to + maintain the priority queue. + + For now we keep the ohter functions in the group in program until + cgraph_remove_unreachable_functions gets rid of them. */ gcc_assert (!e->callee->global.inlined_to); if (e->callee->analyzed && !DECL_EXTERNAL (e->callee->decl)) { @@ -133,7 +172,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, struct cgraph_node *n; n = cgraph_clone_node (e->callee, e->callee->decl, e->count, e->frequency, - update_original, NULL); + update_original, NULL, true); cgraph_redirect_edge_callee (e, n); } } @@ -164,19 +203,40 @@ inline_call (struct cgraph_edge *e, bool update_original, int old_size = 0, new_size = 0; struct cgraph_node *to = NULL; struct cgraph_edge *curr = e; + struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL); /* Don't inline inlined edges. */ gcc_assert (e->inline_failed); /* Don't even think of inlining inline clone. */ - gcc_assert (!e->callee->global.inlined_to); + gcc_assert (!callee->global.inlined_to); e->inline_failed = CIF_OK; - DECL_POSSIBLY_INLINED (e->callee->decl) = true; + DECL_POSSIBLY_INLINED (callee->decl) = true; to = e->caller; if (to->global.inlined_to) to = to->global.inlined_to; + /* If aliases are involved, redirect edge to the actual destination and + possibly remove the aliases. */ + if (e->callee != callee) + { + struct cgraph_node *alias = e->callee, *next_alias; + cgraph_redirect_edge_callee (e, callee); + while (alias && alias != callee) + { + if (!alias->callers + && can_remove_node_now_p (alias, e)) + { + next_alias = cgraph_alias_aliased_node (alias); + cgraph_remove_node (alias); + alias = next_alias; + } + else + break; + } + } + clone_inlined_nodes (e, true, update_original, overall_size); gcc_assert (curr->callee->global.inlined_to == to); @@ -184,11 +244,13 @@ inline_call (struct cgraph_edge *e, bool update_original, old_size = inline_summary (to)->size; inline_merge_summary (e); new_size = inline_summary (to)->size; - if (overall_size && new_size > old_size) + if (overall_size) *overall_size += new_size - old_size; ncalls_inlined++; - if (flag_indirect_inlining && optimize) + /* This must happen after inline_merge_summary that rely on jump + functions of callee to not be updated. */ + if (optimize) return ipa_propagate_indirect_call_infos (curr, new_edges); else return false; @@ -262,7 +324,7 @@ save_inline_function_body (struct cgraph_node *node) /* Copy the OLD_VERSION_NODE function tree to the new version. */ tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL, - NULL, NULL); + false, NULL, NULL); /* The function will be short lived and removed after we inline all the clones, but make it internal so we won't confuse ourself. */ @@ -288,8 +350,7 @@ inline_transform (struct cgraph_node *node) { unsigned int todo = 0; struct cgraph_edge *e; - bool inline_p = false; - + /* FIXME: Currently the pass manager is adding inline transform more than once to some clones. This needs revisiting after WPA cleanups. */ if (cfun->after_inlining) @@ -297,23 +358,24 @@ inline_transform (struct cgraph_node *node) /* We might need the body of this function so that we can expand it inline somewhere else. */ - if (cgraph_preserve_function_body_p (node->decl)) + if (cgraph_preserve_function_body_p (node)) save_inline_function_body (node); for (e = node->callees; e; e = e->next_callee) - { - cgraph_redirect_edge_call_stmt_to_callee (e); - if (!e->inline_failed || warn_inline) - inline_p = true; - } + cgraph_redirect_edge_call_stmt_to_callee (e); + + timevar_push (TV_INTEGRATION); + if (node->callees) + todo = optimize_inline_calls (current_function_decl); + timevar_pop (TV_INTEGRATION); - if (inline_p) - { - timevar_push (TV_INTEGRATION); - todo = optimize_inline_calls (current_function_decl); - timevar_pop (TV_INTEGRATION); - } cfun->always_inline_functions_inlined = true; cfun->after_inlining = true; - return todo | execute_fixup_cfg (); + todo |= execute_fixup_cfg (); + + if (!(todo & TODO_update_ssa_any)) + /* Redirecting edges might lead to a need for vops to be recomputed. */ + todo |= TODO_update_ssa_only_virtuals; + + return todo; }