aboutsummaryrefslogtreecommitdiff
path: root/gcc/ipa-inline-transform.c
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2011-04-20 00:28:54 +0200
committerJan Hubicka <hubicka@gcc.gnu.org>2011-04-19 22:28:54 +0000
commitfee8b6dadd3847d6cdc5e48a467be48ce16fd71e (patch)
tree518ceb62ac7521423992dc5befe95e7c201bee2d /gcc/ipa-inline-transform.c
parent61eca8d71b276c84155b88d7fca656c49ff73b98 (diff)
downloadgcc-fee8b6dadd3847d6cdc5e48a467be48ce16fd71e.zip
gcc-fee8b6dadd3847d6cdc5e48a467be48ce16fd71e.tar.gz
gcc-fee8b6dadd3847d6cdc5e48a467be48ce16fd71e.tar.bz2
cgraph.h (save_inline_function_body): Remove.
* cgraph.h (save_inline_function_body): Remove. * ipa-inline-transform.c: New file, broke out of... * ipa-inline.c: ... this one; Update toplevel comment. (ncalls_inlined, nfunctions_inlined): Move to ipa-inline-transform.c; make global. (update_noncloned_frequencies): Move to ipa-inline-transform.c (cgraph_mark_inline_edge): Rename to inline_call; move to ipa-inline-transform.c. (cgraph_clone_inlined_nodes): Rename to clone_inlined_nodes; move to ipa-inline-transform.c (recursive_inlining, inline_small_functions, flatten_function, ipa_inline, inline_always_inline_functions, early_inline_small_functions): Update. (inline_transform): Move to ipa-inline-transform.c. * ipa-inline.h (inline_call, inline_transform, clone_inlined_nodes): Declare. * Makefile.in (ipa-inline-transform.o): New file. * cgraphunit.c (save_inline_function_body): Move to ipa-inline-transform.c From-SVN: r172739
Diffstat (limited to 'gcc/ipa-inline-transform.c')
-rw-r--r--gcc/ipa-inline-transform.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
new file mode 100644
index 0000000..0fb2448
--- /dev/null
+++ b/gcc/ipa-inline-transform.c
@@ -0,0 +1,328 @@
+/* Callgraph transformations to handle inlining
+ Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
+ Contributed by Jan Hubicka
+
+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/>. */
+
+/* The inline decisions are stored in callgraph in "inline plan" and
+ applied later.
+
+ To mark given call inline, use inline_call function.
+ The function marks the edge inlinable and, if necessary, produces
+ virtual clone in the callgraph representing the new copy of callee's
+ function body.
+
+ The inline plan is applied on given function body by inline_transform. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "cgraph.h"
+#include "timevar.h"
+#include "output.h"
+#include "intl.h"
+#include "coverage.h"
+#include "ggc.h"
+#include "tree-flow.h"
+#include "ipa-prop.h"
+#include "ipa-inline.h"
+#include "tree-inline.h"
+
+int ncalls_inlined;
+int nfunctions_inlined;
+
+/* Scale frequency of NODE edges by FREQ_SCALE and increase loop nest
+ by NEST. */
+
+static void
+update_noncloned_frequencies (struct cgraph_node *node,
+ int freq_scale, int nest)
+{
+ struct cgraph_edge *e;
+
+ /* We do not want to ignore high loop nest after freq drops to 0. */
+ if (!freq_scale)
+ freq_scale = 1;
+ for (e = node->callees; e; e = e->next_callee)
+ {
+ e->loop_nest += nest;
+ e->frequency = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
+ if (e->frequency > CGRAPH_FREQ_MAX)
+ e->frequency = CGRAPH_FREQ_MAX;
+ if (!e->inline_failed)
+ update_noncloned_frequencies (e->callee, freq_scale, nest);
+ }
+}
+
+
+/* E is expected to be an edge being inlined. Clone destination node of
+ the edge and redirect it to the new clone.
+ DUPLICATE is used for bookkeeping on whether we are actually creating new
+ clones or re-using node originally representing out-of-line function call.
+ */
+
+void
+clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
+ bool update_original, int *overall_size)
+{
+ HOST_WIDE_INT peak;
+ struct inline_summary *caller_info, *callee_info;
+
+ if (duplicate)
+ {
+ /* We may eliminate the need for out-of-line copy to be output.
+ In that case just go ahead and re-use it. This is not just an
+ memory optimization. Making offline copy of fuction disappear
+ from the program will improve future decisions on inlining. */
+ if (!e->callee->callers->next_caller
+ /* 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)
+ {
+ gcc_assert (!e->callee->global.inlined_to);
+ if (e->callee->analyzed && !DECL_EXTERNAL (e->callee->decl))
+ {
+ if (overall_size)
+ *overall_size -= inline_summary (e->callee)->size;
+ nfunctions_inlined++;
+ }
+ duplicate = false;
+ e->callee->local.externally_visible = false;
+ update_noncloned_frequencies (e->callee, e->frequency, e->loop_nest);
+ }
+ else
+ {
+ struct cgraph_node *n;
+ n = cgraph_clone_node (e->callee, e->callee->decl,
+ e->count, e->frequency, e->loop_nest,
+ update_original, NULL);
+ cgraph_redirect_edge_callee (e, n);
+ }
+ }
+
+ callee_info = inline_summary (e->callee);
+ caller_info = inline_summary (e->caller);
+
+ if (e->caller->global.inlined_to)
+ e->callee->global.inlined_to = e->caller->global.inlined_to;
+ else
+ e->callee->global.inlined_to = e->caller;
+ callee_info->stack_frame_offset
+ = caller_info->stack_frame_offset
+ + caller_info->estimated_self_stack_size;
+ peak = callee_info->stack_frame_offset
+ + callee_info->estimated_self_stack_size;
+ if (inline_summary (e->callee->global.inlined_to)->estimated_stack_size
+ < peak)
+ inline_summary (e->callee->global.inlined_to)->estimated_stack_size = peak;
+ cgraph_propagate_frequency (e->callee);
+
+ /* Recursively clone all bodies. */
+ for (e = e->callee->callees; e; e = e->next_callee)
+ if (!e->inline_failed)
+ clone_inlined_nodes (e, duplicate, update_original, overall_size);
+}
+
+
+/* Mark edge E as inlined and update callgraph accordingly. UPDATE_ORIGINAL
+ specify whether profile of original function should be updated. If any new
+ indirect edges are discovered in the process, add them to NEW_EDGES, unless
+ it is NULL. Return true iff any new callgraph edges were discovered as a
+ result of inlining. */
+
+bool
+inline_call (struct cgraph_edge *e, bool update_original,
+ VEC (cgraph_edge_p, heap) **new_edges,
+ int *overall_size)
+{
+ int old_size = 0, new_size = 0;
+ struct cgraph_node *to = NULL;
+ struct cgraph_edge *curr = e;
+ struct inline_summary *info;
+
+ /* 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);
+
+ e->inline_failed = CIF_OK;
+ DECL_POSSIBLY_INLINED (e->callee->decl) = true;
+
+ clone_inlined_nodes (e, true, update_original, overall_size);
+
+ /* Now update size of caller and all functions caller is inlined into. */
+ for (;e && !e->inline_failed; e = e->caller->callers)
+ {
+ to = e->caller;
+ info = inline_summary (to);
+ old_size = info->size;
+ new_size = estimate_size_after_inlining (to, curr);
+ info->size = new_size;
+ info->time = estimate_time_after_inlining (to, curr);
+ }
+ gcc_assert (curr->callee->global.inlined_to == to);
+ if (overall_size && new_size > old_size)
+ *overall_size += new_size - old_size;
+ ncalls_inlined++;
+
+ if (flag_indirect_inlining && optimize)
+ return ipa_propagate_indirect_call_infos (curr, new_edges);
+ else
+ return false;
+}
+
+
+/* Copy function body of NODE and redirect all inline clones to it.
+ This is done before inline plan is applied to NODE when there are
+ still some inline clones if it.
+
+ This is neccesary because inline decisions are not really transitive
+ and the other inline clones may have different bodies. */
+
+static struct cgraph_node *
+save_inline_function_body (struct cgraph_node *node)
+{
+ struct cgraph_node *first_clone, *n;
+
+ if (dump_file)
+ fprintf (dump_file, "\nSaving body of %s for later reuse\n",
+ cgraph_node_name (node));
+
+ gcc_assert (node == cgraph_get_node (node->decl));
+
+ /* first_clone will be turned into real function. */
+ first_clone = node->clones;
+ first_clone->decl = copy_node (node->decl);
+ cgraph_insert_node_to_hashtable (first_clone);
+ gcc_assert (first_clone == cgraph_get_node (first_clone->decl));
+
+ /* Now reshape the clone tree, so all other clones descends from
+ first_clone. */
+ if (first_clone->next_sibling_clone)
+ {
+ for (n = first_clone->next_sibling_clone; n->next_sibling_clone; n = n->next_sibling_clone)
+ n->clone_of = first_clone;
+ n->clone_of = first_clone;
+ n->next_sibling_clone = first_clone->clones;
+ if (first_clone->clones)
+ first_clone->clones->prev_sibling_clone = n;
+ first_clone->clones = first_clone->next_sibling_clone;
+ first_clone->next_sibling_clone->prev_sibling_clone = NULL;
+ first_clone->next_sibling_clone = NULL;
+ gcc_assert (!first_clone->prev_sibling_clone);
+ }
+ first_clone->clone_of = NULL;
+
+ /* Now node in question has no clones. */
+ node->clones = NULL;
+
+ if (first_clone->clones)
+ for (n = first_clone->clones; n != first_clone;)
+ {
+ gcc_assert (n->decl == node->decl);
+ n->decl = first_clone->decl;
+ if (n->clones)
+ n = n->clones;
+ else if (n->next_sibling_clone)
+ n = n->next_sibling_clone;
+ else
+ {
+ while (n != first_clone && !n->next_sibling_clone)
+ n = n->clone_of;
+ if (n != first_clone)
+ n = n->next_sibling_clone;
+ }
+ }
+
+ /* Copy the OLD_VERSION_NODE function tree to the new version. */
+ tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL,
+ NULL, NULL);
+
+ DECL_EXTERNAL (first_clone->decl) = 0;
+ DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
+ TREE_PUBLIC (first_clone->decl) = 0;
+ DECL_COMDAT (first_clone->decl) = 0;
+ VEC_free (ipa_opt_pass, heap,
+ first_clone->ipa_transforms_to_apply);
+ first_clone->ipa_transforms_to_apply = NULL;
+
+#ifdef ENABLE_CHECKING
+ verify_cgraph_node (first_clone);
+#endif
+ return first_clone;
+}
+
+
+/* Apply inline plan to function. */
+
+unsigned int
+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)
+ return 0;
+
+ /* 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))
+ 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;
+ }
+
+ 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 ();
+}