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
This commit is contained in:
parent
a63a095941
commit
7fa49e7be7
@ -1,3 +1,16 @@
|
|||||||
|
2007-01-21 Jan Hubicka <jh@suse.cz>
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
2007-01-21 Dirk Mueller <dmueller@suse.de>
|
2007-01-21 Dirk Mueller <dmueller@suse.de>
|
||||||
|
|
||||||
PR bootstrap/30511
|
PR bootstrap/30511
|
||||||
|
279
gcc/ipa-inline.c
279
gcc/ipa-inline.c
@ -140,6 +140,32 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
|||||||
#include "ggc.h"
|
#include "ggc.h"
|
||||||
#include "tree-flow.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. */
|
/* Statistics we collect about inlining algorithm. */
|
||||||
static int ncalls_inlined;
|
static int ncalls_inlined;
|
||||||
static int nfunctions_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);
|
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,
|
/* Decide on recursive inlining: in the case function has recursive calls,
|
||||||
inline until body size reaches given argument. */
|
inline until body size reaches given argument. */
|
||||||
|
|
||||||
@ -1030,18 +997,11 @@ cgraph_decide_inlining (void)
|
|||||||
if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
|
if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
|
||||||
{
|
{
|
||||||
int old_overall_insns = overall_insns;
|
int old_overall_insns = overall_insns;
|
||||||
htab_t cycles;
|
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
fprintf (dump_file,
|
fprintf (dump_file,
|
||||||
"Flattening %s\n", cgraph_node_name (node));
|
"Flattening %s\n", cgraph_node_name (node));
|
||||||
cycles = htab_create (7, htab_hash_pointer, htab_eq_pointer, NULL);
|
cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
|
||||||
cgraph_find_cycles (node, cycles);
|
|
||||||
cgraph_flatten_node (node, cycles);
|
|
||||||
htab_delete (cycles);
|
|
||||||
overall_insns = old_overall_insns;
|
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)
|
if (!node->local.disregard_inline_limits)
|
||||||
@ -1066,6 +1026,12 @@ cgraph_decide_inlining (void)
|
|||||||
cgraph_node_name (e->caller),
|
cgraph_node_name (e->caller),
|
||||||
e->caller->global.insns);
|
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)
|
if (dump_file)
|
||||||
fprintf (dump_file,
|
fprintf (dump_file,
|
||||||
" Inlined for a net change of %+i insns.\n",
|
" Inlined for a net change of %+i insns.\n",
|
||||||
@ -1136,66 +1102,128 @@ cgraph_decide_inlining (void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum inlining_mode {
|
/* Try to inline edge E from incremental inliner. MODE specifies mode
|
||||||
INLINE_SIZE,
|
of inliner.
|
||||||
INLINE_SPEED,
|
|
||||||
INLINE_ALL
|
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
|
/* 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
|
static bool
|
||||||
cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mode mode)
|
cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mode mode,
|
||||||
|
int depth)
|
||||||
{
|
{
|
||||||
struct cgraph_edge *e;
|
struct cgraph_edge *e;
|
||||||
bool inlined = false;
|
bool inlined = false;
|
||||||
const char *failed_reason;
|
const char *failed_reason;
|
||||||
unsigned int todo = 0;
|
enum inlining_mode old_mode;
|
||||||
|
|
||||||
#ifdef ENABLE_CHECKING
|
#ifdef ENABLE_CHECKING
|
||||||
verify_cgraph_node (node);
|
verify_cgraph_node (node);
|
||||||
#endif
|
#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)
|
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;
|
mode = INLINE_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node->aux = (void *)(size_t) mode;
|
||||||
|
|
||||||
/* First of all look for always inline functions. */
|
/* First of all look for always inline functions. */
|
||||||
for (e = node->callees; e; e = e->next_callee)
|
for (e = node->callees; e; e = e->next_callee)
|
||||||
if ((e->callee->local.disregard_inline_limits
|
{
|
||||||
|| (mode == INLINE_ALL && e->callee->local.inlinable))
|
if (dump_file && e->callee->local.inlinable
|
||||||
&& e->inline_failed
|
&& (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
|
||||||
&& (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
|
!= gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl))))
|
||||||
== gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
|
{
|
||||||
&& !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
|
fprintf (dump_file, " Ignoring %s: SSA form not computed yet.\n",
|
||||||
/* ??? It is possible that renaming variable removed the function body
|
cgraph_node_name (e->callee));
|
||||||
in duplicate_decls. See gcc.c-torture/compile/20011119-2.c */
|
}
|
||||||
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
|
if ((e->callee->local.disregard_inline_limits
|
||||||
{
|
|| (mode == INLINE_ALL && e->callee->local.inlinable))
|
||||||
if (dump_file)
|
&& e->inline_failed
|
||||||
{
|
&& (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
|
||||||
fprintf (dump_file, " Inlining always_inline %s",
|
== gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
|
||||||
cgraph_node_name (e->callee));
|
&& !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
|
||||||
fprintf (dump_file, " into %s\n", cgraph_node_name (node));
|
/* ??? It is possible that renaming variable removed the function body
|
||||||
}
|
in duplicate_decls. See gcc.c-torture/compile/20011119-2.c */
|
||||||
cgraph_mark_inline (e);
|
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
|
||||||
/* In order to fully inline always_inline functions at -O0, we need to
|
{
|
||||||
recurse here, since the inlined functions might not be processed by
|
inlined |= try_inline (e, mode, depth);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now do the automatic inlining. */
|
/* 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)
|
for (e = node->callees; e; e = e->next_callee)
|
||||||
if (e->callee->local.inlinable
|
if (e->callee->local.inlinable
|
||||||
&& e->inline_failed
|
&& 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))
|
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
|
||||||
{
|
{
|
||||||
if (cgraph_default_inline_p (e->callee, &failed_reason))
|
if (cgraph_default_inline_p (e->callee, &failed_reason))
|
||||||
{
|
inlined |= try_inline (e, mode, depth);
|
||||||
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);
|
|
||||||
}
|
|
||||||
else if (!flag_unit_at_a_time)
|
else if (!flag_unit_at_a_time)
|
||||||
e->inline_failed = failed_reason;
|
e->inline_failed = failed_reason;
|
||||||
}
|
}
|
||||||
if (flag_unit_at_a_time && inlined && !node->global.inlined_to)
|
node->aux = (void *)(size_t) old_mode;
|
||||||
{
|
return inlined;
|
||||||
timevar_push (TV_INTEGRATION);
|
|
||||||
todo = optimize_inline_calls (current_function_decl);
|
|
||||||
timevar_pop (TV_INTEGRATION);
|
|
||||||
}
|
|
||||||
return todo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When inlining shall be performed. */
|
/* When inlining shall be performed. */
|
||||||
@ -1273,12 +1285,19 @@ static unsigned int
|
|||||||
cgraph_early_inlining (void)
|
cgraph_early_inlining (void)
|
||||||
{
|
{
|
||||||
struct cgraph_node *node = cgraph_node (current_function_decl);
|
struct cgraph_node *node = cgraph_node (current_function_decl);
|
||||||
|
unsigned int todo = 0;
|
||||||
|
|
||||||
if (sorrycount || errorcount)
|
if (sorrycount || errorcount)
|
||||||
return 0;
|
return 0;
|
||||||
return cgraph_decide_inlining_incrementally (node,
|
if (cgraph_decide_inlining_incrementally (node,
|
||||||
flag_unit_at_a_time
|
flag_unit_at_a_time
|
||||||
? INLINE_SIZE : INLINE_SPEED);
|
? 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. */
|
/* When inlining shall be performed. */
|
||||||
@ -1390,7 +1409,7 @@ apply_inline (void)
|
|||||||
/* Even when not optimizing, ensure that always_inline functions get inlined.
|
/* Even when not optimizing, ensure that always_inline functions get inlined.
|
||||||
*/
|
*/
|
||||||
if (!optimize)
|
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
|
/* We might need the body of this function so that we can expand
|
||||||
it inline somewhere else. */
|
it inline somewhere else. */
|
||||||
|
Loading…
Reference in New Issue
Block a user