aboutsummaryrefslogtreecommitdiff
path: root/gcc/ipa-inline.c
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2007-01-21 19:35:27 +0100
committerJan Hubicka <hubicka@gcc.gnu.org>2007-01-21 18:35:27 +0000
commit7fa49e7be7cf815dd55f9ef2ead04ee002d687a8 (patch)
treea24a52039878918494071aabc8020dbb0f11b90d /gcc/ipa-inline.c
parenta63a09594132dfe461a87dfd71cf607b987fc770 (diff)
downloadgcc-7fa49e7be7cf815dd55f9ef2ead04ee002d687a8.zip
gcc-7fa49e7be7cf815dd55f9ef2ead04ee002d687a8.tar.gz
gcc-7fa49e7be7cf815dd55f9ef2ead04ee002d687a8.tar.bz2
ipa-inline.c (inlining_mode): Comment, move up.
* ipa-inline.c (inlining_mode): Comment, move up. (cgraph_decide_inlining_incrementally): Do not perform inlining itself; fix handling of flattening of self recursive functions. (cgraph_find_cycles): Remove. (cgraph_flatten_node): Remove. (cgraph_decide_inlining): Use incremental inliner to handle flattening. (try_inline): New function. (cgraph_early_inlining): Update call of cgraph_decide_inlining_incrementally. Apply inlining here. (apply_inline): Update call of cgraph_decide_inlining_incrementally. From-SVN: r121034
Diffstat (limited to 'gcc/ipa-inline.c')
-rw-r--r--gcc/ipa-inline.c279
1 files changed, 149 insertions, 130 deletions
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index d1983d9..646b72e 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -140,6 +140,32 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "ggc.h"
#include "tree-flow.h"
+/* Mode incremental inliner operate on:
+
+ In ALWAYS_INLINE only functions marked
+ always_inline are inlined. This mode is used after detecting cycle during
+ flattening.
+
+ In SIZE mode, only functions that reduce function body size after inlining
+ are inlined, this is used during early inlining.
+
+ In SPEED mode, all small functions are inlined. This might result in
+ unbounded growth of compilation unit and is used only in non-unit-at-a-time
+ mode.
+
+ in ALL mode, everything is inlined. This is used during flattening. */
+enum inlining_mode {
+ INLINE_NONE = 0,
+ INLINE_ALWAYS_INLINE,
+ INLINE_SIZE,
+ INLINE_SPEED,
+ INLINE_ALL
+};
+static bool
+cgraph_decide_inlining_incrementally (struct cgraph_node *, enum inlining_mode,
+ int);
+
+
/* Statistics we collect about inlining algorithm. */
static int ncalls_inlined;
static int nfunctions_inlined;
@@ -587,65 +613,6 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
lookup_recursive_calls (node, e->callee, heap);
}
-/* Find callgraph nodes closing a circle in the graph. The
- resulting hashtab can be used to avoid walking the circles.
- Uses the cgraph nodes ->aux field which needs to be zero
- before and will be zero after operation. */
-
-static void
-cgraph_find_cycles (struct cgraph_node *node, htab_t cycles)
-{
- struct cgraph_edge *e;
-
- if (node->aux)
- {
- void **slot;
- slot = htab_find_slot (cycles, node, INSERT);
- if (!*slot)
- {
- if (dump_file)
- fprintf (dump_file, "Cycle contains %s\n", cgraph_node_name (node));
- *slot = node;
- }
- return;
- }
-
- node->aux = node;
- for (e = node->callees; e; e = e->next_callee)
- cgraph_find_cycles (e->callee, cycles);
- node->aux = 0;
-}
-
-/* Flatten the cgraph node. We have to be careful in recursing
- as to not run endlessly in circles of the callgraph.
- We do so by using a hashtab of cycle entering nodes as generated
- by cgraph_find_cycles. */
-
-static void
-cgraph_flatten_node (struct cgraph_node *node, htab_t cycles)
-{
- struct cgraph_edge *e;
-
- for (e = node->callees; e; e = e->next_callee)
- {
- /* Inline call, if possible, and recurse. Be sure we are not
- entering callgraph circles here. */
- if (e->inline_failed
- && e->callee->local.inlinable
- && !cgraph_recursive_inlining_p (node, e->callee,
- &e->inline_failed)
- && !htab_find (cycles, e->callee))
- {
- if (dump_file)
- fprintf (dump_file, " inlining %s", cgraph_node_name (e->callee));
- cgraph_mark_inline_edge (e, true);
- cgraph_flatten_node (e->callee, cycles);
- }
- else if (dump_file)
- fprintf (dump_file, " !inlining %s", cgraph_node_name (e->callee));
- }
-}
-
/* Decide on recursive inlining: in the case function has recursive calls,
inline until body size reaches given argument. */
@@ -1030,18 +997,11 @@ cgraph_decide_inlining (void)
if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
{
int old_overall_insns = overall_insns;
- htab_t cycles;
if (dump_file)
fprintf (dump_file,
"Flattening %s\n", cgraph_node_name (node));
- cycles = htab_create (7, htab_hash_pointer, htab_eq_pointer, NULL);
- cgraph_find_cycles (node, cycles);
- cgraph_flatten_node (node, cycles);
- htab_delete (cycles);
+ cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
overall_insns = old_overall_insns;
- /* We don't need to consider always_inline functions inside the flattened
- function anymore. */
- continue;
}
if (!node->local.disregard_inline_limits)
@@ -1066,6 +1026,12 @@ cgraph_decide_inlining (void)
cgraph_node_name (e->caller),
e->caller->global.insns);
}
+ /* Inlining self recursive function might introduce new calls to
+ thsemselves we didn't see in the loop above. Fill in the proper
+ reason why inline failed. */
+ for (e = node->callers; e; e = e->next_caller)
+ if (e->inline_failed)
+ e->inline_failed = N_("recursive inlining");
if (dump_file)
fprintf (dump_file,
" Inlined for a net change of %+i insns.\n",
@@ -1136,66 +1102,128 @@ cgraph_decide_inlining (void)
return 0;
}
-enum inlining_mode {
- INLINE_SIZE,
- INLINE_SPEED,
- INLINE_ALL
-};
+/* Try to inline edge E from incremental inliner. MODE specifies mode
+ of inliner.
+
+ We are detecting cycles by storing mode of inliner into cgraph_node last
+ time we visited it in the recursion. In general when mode is set, we have
+ recursive inlining, but as an special case, we want to try harder inline
+ ALWAYS_INLINE functions: consider callgraph a->b->c->b, with a being
+ flatten, b being always inline. Flattening 'a' will collapse
+ a->b->c before hitting cycle. To accomondate always inline, we however
+ need to inline a->b->c->b.
+
+ So after hitting cycle first time, we switch into ALWAYS_INLINE mode and
+ stop inlining only after hitting ALWAYS_INLINE in ALWAY_INLINE mode. */
+static bool
+try_inline (struct cgraph_edge *e, enum inlining_mode mode, int depth)
+{
+ struct cgraph_node *callee = e->callee;
+ enum inlining_mode callee_mode = (size_t) callee->aux;
+ bool always_inline = e->callee->local.disregard_inline_limits;
+
+ /* We've hit cycle? */
+ if (callee_mode)
+ {
+ /* It is first time we see it and we are not in ALWAY_INLINE only
+ mode yet. and the function in question is always_inline. */
+ if (always_inline && mode != INLINE_ALWAYS_INLINE)
+ mode = INLINE_ALWAYS_INLINE;
+ /* Otheriwse it is time to give up. */
+ else
+ {
+ if (dump_file)
+ {
+ indent_to (dump_file, depth);
+ fprintf (dump_file,
+ "Not inlining %s into %s to avoid cycle.\n",
+ cgraph_node_name (callee),
+ cgraph_node_name (e->caller));
+ }
+ e->inline_failed = (e->callee->local.disregard_inline_limits
+ ? N_("recursive inlining") : "");
+ return false;
+ }
+ }
+
+ callee->aux = (void *)(size_t) mode;
+ if (dump_file)
+ {
+ indent_to (dump_file, depth);
+ fprintf (dump_file, " Inlining %s into %s.\n",
+ cgraph_node_name (e->callee),
+ cgraph_node_name (e->caller));
+ }
+ cgraph_mark_inline (e);
+
+ /* In order to fully inline always_inline functions at -O0, we need to
+ recurse here, since the inlined functions might not be processed by
+ incremental inlining at all yet.
+
+ Also flattening needs to be done recursively. */
+
+ if (!flag_unit_at_a_time || mode == INLINE_ALL || always_inline)
+ cgraph_decide_inlining_incrementally (e->callee, mode, depth + 1);
+ callee->aux = (void *)(size_t) callee_mode;
+ return true;
+}
/* Decide on the inlining. We do so in the topological order to avoid
- expenses on updating data structures. */
+ expenses on updating data structures.
+ DEPTH is depth of recursion, used only for debug output. */
-static unsigned int
-cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mode mode)
+static bool
+cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mode mode,
+ int depth)
{
struct cgraph_edge *e;
bool inlined = false;
const char *failed_reason;
- unsigned int todo = 0;
+ enum inlining_mode old_mode;
#ifdef ENABLE_CHECKING
verify_cgraph_node (node);
#endif
- if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
+
+ old_mode = (size_t)node->aux;
+
+ if (mode != INLINE_ALWAYS_INLINE
+ && lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
{
if (dump_file)
- fprintf (dump_file, " Flattening %s\n", cgraph_node_name (node));
+ fprintf (dump_file, " Flattening %s\n", cgraph_node_name (node));
mode = INLINE_ALL;
}
+ node->aux = (void *)(size_t) mode;
+
/* First of all look for always inline functions. */
for (e = node->callees; e; e = e->next_callee)
- if ((e->callee->local.disregard_inline_limits
- || (mode == INLINE_ALL && e->callee->local.inlinable))
- && e->inline_failed
- && (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
- == gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
- && !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
- /* ??? It is possible that renaming variable removed the function body
- in duplicate_decls. See gcc.c-torture/compile/20011119-2.c */
- && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
- {
- if (dump_file)
- {
- fprintf (dump_file, " Inlining always_inline %s",
- cgraph_node_name (e->callee));
- fprintf (dump_file, " into %s\n", cgraph_node_name (node));
- }
- cgraph_mark_inline (e);
- /* In order to fully inline always_inline functions at -O0, we need to
- recurse here, since the inlined functions might not be processed by
- incremental inlining at all yet.
-
- Also flattening needs to be done recursively. */
-
- if (!flag_unit_at_a_time || mode == INLINE_ALL)
- cgraph_decide_inlining_incrementally (e->callee, mode);
-
- inlined = true;
- }
+ {
+ if (dump_file && e->callee->local.inlinable
+ && (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
+ != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl))))
+ {
+ fprintf (dump_file, " Ignoring %s: SSA form not computed yet.\n",
+ cgraph_node_name (e->callee));
+ }
+ if ((e->callee->local.disregard_inline_limits
+ || (mode == INLINE_ALL && e->callee->local.inlinable))
+ && e->inline_failed
+ && (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
+ == gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
+ && !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
+ /* ??? It is possible that renaming variable removed the function body
+ in duplicate_decls. See gcc.c-torture/compile/20011119-2.c */
+ && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
+ {
+ inlined |= try_inline (e, mode, depth);
+ }
+ }
/* Now do the automatic inlining. */
- if (!flag_really_no_inline && mode != INLINE_ALL)
+ if (!flag_really_no_inline && mode != INLINE_ALL
+ && mode != INLINE_ALWAYS_INLINE)
for (e = node->callees; e; e = e->next_callee)
if (e->callee->local.inlinable
&& e->inline_failed
@@ -1211,28 +1239,12 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mo
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
{
if (cgraph_default_inline_p (e->callee, &failed_reason))
- {
- if (dump_file)
- {
- fprintf (dump_file, " Inlining %s",
- cgraph_node_name (e->callee));
- fprintf (dump_file, " into %s\n", cgraph_node_name (node));
- }
- cgraph_mark_inline (e);
- inlined = true;
- if (!flag_unit_at_a_time)
- cgraph_decide_inlining_incrementally (e->callee, mode);
- }
+ inlined |= try_inline (e, mode, depth);
else if (!flag_unit_at_a_time)
e->inline_failed = failed_reason;
}
- if (flag_unit_at_a_time && inlined && !node->global.inlined_to)
- {
- timevar_push (TV_INTEGRATION);
- todo = optimize_inline_calls (current_function_decl);
- timevar_pop (TV_INTEGRATION);
- }
- return todo;
+ node->aux = (void *)(size_t) old_mode;
+ return inlined;
}
/* When inlining shall be performed. */
@@ -1273,12 +1285,19 @@ static unsigned int
cgraph_early_inlining (void)
{
struct cgraph_node *node = cgraph_node (current_function_decl);
+ unsigned int todo = 0;
if (sorrycount || errorcount)
return 0;
- return cgraph_decide_inlining_incrementally (node,
- flag_unit_at_a_time
- ? INLINE_SIZE : INLINE_SPEED);
+ if (cgraph_decide_inlining_incrementally (node,
+ flag_unit_at_a_time
+ ? INLINE_SIZE : INLINE_SPEED, 0))
+ {
+ timevar_push (TV_INTEGRATION);
+ todo = optimize_inline_calls (current_function_decl);
+ timevar_pop (TV_INTEGRATION);
+ }
+ return todo;
}
/* When inlining shall be performed. */
@@ -1390,7 +1409,7 @@ apply_inline (void)
/* Even when not optimizing, ensure that always_inline functions get inlined.
*/
if (!optimize)
- cgraph_decide_inlining_incrementally (node, false);
+ cgraph_decide_inlining_incrementally (node, INLINE_SPEED, 0);
/* We might need the body of this function so that we can expand
it inline somewhere else. */