re PR middle-end/47663 (Very simple wrapper not inlined)

2011-04-06  Richard Guenther  <rguenther@suse.de>
 
	PR tree-optimization/47663
	* cgraph.h (struct cgraph_edge): Add call_stmt_size and
	call_stmt_time fields.
	(cgraph_edge_inlinable_p): Declare.
	(cgraph_edge_recursive_p): New inline function.
	* cgraph.c (cgraph_create_edge_1): Initialize call_stmt_size.
	(cgraph_clone_edge): Copy it.
	* ipa-inline.c (cgraph_estimate_edge_time): New function.
	Account for call stmt time.
	(cgraph_estimate_time_after_inlining): Take edge argument.
	(cgraph_estimate_edge_growth): Account call stmt size.
	(cgraph_estimate_size_after_inlining): Take edge argument.
	(cgraph_mark_inline_edge): Adjust.
	(cgraph_check_inline_limits): Likewise.
	(cgraph_recursive_inlining_p): Remove.
	(cgraph_edge_badness): Use cgraph_edge_recursive_p.
	(cgraph_decide_recursive_inlining): Take edge argument and
	adjust.
	(cgraph_decide_inlining_of_small_functions): Do not avoid
	diags for recursive inlining here.
	(cgraph_flatten): Adjust.
	(cgraph_decide_inlining_incrementally): Likewise.
	(estimate_function_body_sizes): Remove call cost handling.
	(compute_inline_parameters): Initialize caller edge call costs.
	(cgraph_estimate_edge_growth): New function.
	(cgraph_estimate_growth): Use it.
	(cgraph_edge_badness): Likewise.
	(cgraph_check_inline_limits): Take an edge argument.
	(cgraph_decide_inlining_of_small_functions): Adjust.
	(cgraph_decide_inlining): Likewise.
	* tree-inline.c (estimate_num_insns): Only account for call
	return value if it is used.
	(expand_call_inline): Avoid diagnostics on recursive inline
	functions here.
	* lto-cgraph.c (lto_output_edge): Output edge call costs.
	(input_edge): Input edge call costs.

	* gcc.dg/tree-ssa/inline-8.c: New testcase.

From-SVN: r172023
This commit is contained in:
Richard Guenther 2011-04-06 08:51:23 +00:00 committed by Richard Biener
parent 2feb95f7d8
commit d7d1d041ae
8 changed files with 232 additions and 137 deletions

View File

@ -1,3 +1,42 @@
2011-04-06 Richard Guenther <rguenther@suse.de>
PR tree-optimization/47663
* cgraph.h (struct cgraph_edge): Add call_stmt_size and
call_stmt_time fields.
(cgraph_edge_inlinable_p): Declare.
(cgraph_edge_recursive_p): New inline function.
* cgraph.c (cgraph_create_edge_1): Initialize call_stmt_size.
(cgraph_clone_edge): Copy it.
* ipa-inline.c (cgraph_estimate_edge_time): New function.
Account for call stmt time.
(cgraph_estimate_time_after_inlining): Take edge argument.
(cgraph_estimate_edge_growth): Account call stmt size.
(cgraph_estimate_size_after_inlining): Take edge argument.
(cgraph_mark_inline_edge): Adjust.
(cgraph_check_inline_limits): Likewise.
(cgraph_recursive_inlining_p): Remove.
(cgraph_edge_badness): Use cgraph_edge_recursive_p.
(cgraph_decide_recursive_inlining): Take edge argument and
adjust.
(cgraph_decide_inlining_of_small_functions): Do not avoid
diags for recursive inlining here.
(cgraph_flatten): Adjust.
(cgraph_decide_inlining_incrementally): Likewise.
(estimate_function_body_sizes): Remove call cost handling.
(compute_inline_parameters): Initialize caller edge call costs.
(cgraph_estimate_edge_growth): New function.
(cgraph_estimate_growth): Use it.
(cgraph_edge_badness): Likewise.
(cgraph_check_inline_limits): Take an edge argument.
(cgraph_decide_inlining_of_small_functions): Adjust.
(cgraph_decide_inlining): Likewise.
* tree-inline.c (estimate_num_insns): Only account for call
return value if it is used.
(expand_call_inline): Avoid diagnostics on recursive inline
functions here.
* lto-cgraph.c (lto_output_edge): Output edge call costs.
(input_edge): Input edge call costs.
2011-04-06 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> 2011-04-06 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* config/alpha/osf5.h (MAX_OFILE_ALIGNMENT): Define. * config/alpha/osf5.h (MAX_OFILE_ALIGNMENT): Define.

View File

@ -1032,6 +1032,8 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
edge->loop_nest = nest; edge->loop_nest = nest;
edge->call_stmt = call_stmt; edge->call_stmt = call_stmt;
edge->call_stmt_size = 0;
edge->call_stmt_time = 0;
push_cfun (DECL_STRUCT_FUNCTION (caller->decl)); push_cfun (DECL_STRUCT_FUNCTION (caller->decl));
edge->can_throw_external edge->can_throw_external
= call_stmt ? stmt_can_throw_external (call_stmt) : false; = call_stmt ? stmt_can_throw_external (call_stmt) : false;
@ -2141,6 +2143,8 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
} }
} }
new_edge->call_stmt_size = e->call_stmt_size;
new_edge->call_stmt_time = e->call_stmt_time;
new_edge->inline_failed = e->inline_failed; new_edge->inline_failed = e->inline_failed;
new_edge->indirect_inlining_edge = e->indirect_inlining_edge; new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
new_edge->lto_stmt_uid = stmt_uid; new_edge->lto_stmt_uid = stmt_uid;

View File

@ -431,6 +431,9 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgrap
int frequency; int frequency;
/* Unique id of the edge. */ /* Unique id of the edge. */
int uid; int uid;
/* Estimated size and time of the call statement. */
int call_stmt_size;
int call_stmt_time;
/* Depth of loop nest, 1 means no loop nest. */ /* Depth of loop nest, 1 means no loop nest. */
unsigned short int loop_nest; unsigned short int loop_nest;
/* Whether this edge was made direct by indirect inlining. */ /* Whether this edge was made direct by indirect inlining. */
@ -771,6 +774,7 @@ varpool_next_static_initializer (struct varpool_node *node)
/* In ipa-inline.c */ /* In ipa-inline.c */
void cgraph_clone_inlined_nodes (struct cgraph_edge *, bool, bool); void cgraph_clone_inlined_nodes (struct cgraph_edge *, bool, bool);
void compute_inline_parameters (struct cgraph_node *); void compute_inline_parameters (struct cgraph_node *);
cgraph_inline_failed_t cgraph_edge_inlinable_p (struct cgraph_edge *);
/* Create a new static variable of type TYPE. */ /* Create a new static variable of type TYPE. */
@ -958,6 +962,17 @@ varpool_all_refs_explicit_p (struct varpool_node *vnode)
/* Constant pool accessor function. */ /* Constant pool accessor function. */
htab_t constant_pool_htab (void); htab_t constant_pool_htab (void);
/* Return true when the edge E represents a direct recursion. */
static inline bool
cgraph_edge_recursive_p (struct cgraph_edge *e)
{
if (e->caller->global.inlined_to)
return e->caller->global.inlined_to->decl == e->callee->decl;
else
return e->caller->decl == e->callee->decl;
}
/* FIXME: inappropriate dependency of cgraph on IPA. */ /* FIXME: inappropriate dependency of cgraph on IPA. */
#include "ipa-ref-inline.h" #include "ipa-ref-inline.h"

View File

@ -164,16 +164,31 @@ inline_summary (struct cgraph_node *node)
return &node->local.inline_summary; return &node->local.inline_summary;
} }
/* Estimate self time of the function after inlining WHAT into TO. */ /* Estimate the time cost for the caller when inlining EDGE. */
static inline int
cgraph_estimate_edge_time (struct cgraph_edge *edge)
{
int call_stmt_time;
/* ??? We throw away cgraph edges all the time so the information
we store in edges doesn't persist for early inlining. Ugh. */
if (!edge->call_stmt)
call_stmt_time = edge->call_stmt_time;
else
call_stmt_time = estimate_num_insns (edge->call_stmt, &eni_time_weights);
return (((gcov_type)edge->callee->global.time
- inline_summary (edge->callee)->time_inlining_benefit
- call_stmt_time) * edge->frequency
+ CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
}
/* Estimate self time of the function NODE after inlining EDGE. */
static int static int
cgraph_estimate_time_after_inlining (int frequency, struct cgraph_node *to, cgraph_estimate_time_after_inlining (struct cgraph_node *node,
struct cgraph_node *what) struct cgraph_edge *edge)
{ {
gcov_type time = (((gcov_type)what->global.time gcov_type time = node->global.time + cgraph_estimate_edge_time (edge);
- inline_summary (what)->time_inlining_benefit)
* frequency + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE
+ to->global.time;
if (time < 0) if (time < 0)
time = 0; time = 0;
if (time > MAX_TIME) if (time > MAX_TIME)
@ -181,14 +196,31 @@ cgraph_estimate_time_after_inlining (int frequency, struct cgraph_node *to,
return time; return time;
} }
/* Estimate self size of the function after inlining WHAT into TO. */ /* Estimate the growth of the caller when inlining EDGE. */
static inline int static inline int
cgraph_estimate_size_after_inlining (struct cgraph_node *to, cgraph_estimate_edge_growth (struct cgraph_edge *edge)
struct cgraph_node *what)
{ {
int size = ((what->global.size - inline_summary (what)->size_inlining_benefit) int call_stmt_size;
+ to->global.size); /* ??? We throw away cgraph edges all the time so the information
we store in edges doesn't persist for early inlining. Ugh. */
if (!edge->call_stmt)
call_stmt_size = edge->call_stmt_size;
else
call_stmt_size = estimate_num_insns (edge->call_stmt, &eni_size_weights);
return (edge->callee->global.size
- inline_summary (edge->callee)->size_inlining_benefit
- call_stmt_size);
}
/* Estimate the size of NODE after inlining EDGE which should be an
edge to either NODE or a call inlined into NODE. */
static inline int
cgraph_estimate_size_after_inlining (struct cgraph_node *node,
struct cgraph_edge *edge)
{
int size = node->global.size + cgraph_estimate_edge_growth (edge);
gcc_assert (size >= 0); gcc_assert (size >= 0);
return size; return size;
} }
@ -301,9 +333,8 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
VEC (cgraph_edge_p, heap) **new_edges) VEC (cgraph_edge_p, heap) **new_edges)
{ {
int old_size = 0, new_size = 0; int old_size = 0, new_size = 0;
struct cgraph_node *to = NULL, *what; struct cgraph_node *to = NULL;
struct cgraph_edge *curr = e; struct cgraph_edge *curr = e;
int freq;
/* Don't inline inlined edges. */ /* Don't inline inlined edges. */
gcc_assert (e->inline_failed); gcc_assert (e->inline_failed);
@ -315,19 +346,16 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
cgraph_clone_inlined_nodes (e, true, update_original); cgraph_clone_inlined_nodes (e, true, update_original);
what = e->callee;
freq = e->frequency;
/* Now update size of caller and all functions caller is inlined into. */ /* Now update size of caller and all functions caller is inlined into. */
for (;e && !e->inline_failed; e = e->caller->callers) for (;e && !e->inline_failed; e = e->caller->callers)
{ {
to = e->caller; to = e->caller;
old_size = e->caller->global.size; old_size = e->caller->global.size;
new_size = cgraph_estimate_size_after_inlining (to, what); new_size = cgraph_estimate_size_after_inlining (to, e);
to->global.size = new_size; to->global.size = new_size;
to->global.time = cgraph_estimate_time_after_inlining (freq, to, what); to->global.time = cgraph_estimate_time_after_inlining (to, e);
} }
gcc_assert (what->global.inlined_to == to); gcc_assert (curr->callee->global.inlined_to == to);
if (new_size > old_size) if (new_size > old_size)
overall_size += new_size - old_size; overall_size += new_size - old_size;
ncalls_inlined++; ncalls_inlined++;
@ -357,8 +385,7 @@ cgraph_estimate_growth (struct cgraph_node *node)
if (e->caller == node) if (e->caller == node)
self_recursive = true; self_recursive = true;
if (e->inline_failed) if (e->inline_failed)
growth += (cgraph_estimate_size_after_inlining (e->caller, node) growth += cgraph_estimate_edge_growth (e);
- e->caller->global.size);
} }
/* ??? Wrong for non-trivially self recursive functions or cases where /* ??? Wrong for non-trivially self recursive functions or cases where
@ -379,17 +406,17 @@ cgraph_estimate_growth (struct cgraph_node *node)
return growth; return growth;
} }
/* Return false when inlining WHAT into TO is not good idea /* Return false when inlining edge E is not good idea
as it would cause too large growth of function bodies. as it would cause too large growth of the callers function body
When ONE_ONLY is true, assume that only one call site is going or stack frame size. *REASON if non-NULL is updated if the
to be inlined, otherwise figure out how many call sites in inlining is not a good idea. */
TO calls WHAT and verify that all can be inlined.
*/
static bool static bool
cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what, cgraph_check_inline_limits (struct cgraph_edge *e,
cgraph_inline_failed_t *reason) cgraph_inline_failed_t *reason)
{ {
struct cgraph_node *to = e->caller;
struct cgraph_node *what = e->callee;
int newsize; int newsize;
int limit; int limit;
HOST_WIDE_INT stack_size_limit, inlined_stack; HOST_WIDE_INT stack_size_limit, inlined_stack;
@ -408,7 +435,7 @@ cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
/* Check the size after inlining against the function limits. But allow /* Check the size after inlining against the function limits. But allow
the function to shrink if it went over the limits by forced inlining. */ the function to shrink if it went over the limits by forced inlining. */
newsize = cgraph_estimate_size_after_inlining (to, what); newsize = cgraph_estimate_size_after_inlining (to, e);
if (newsize >= to->global.size if (newsize >= to->global.size
&& newsize > PARAM_VALUE (PARAM_LARGE_FUNCTION_INSNS) && newsize > PARAM_VALUE (PARAM_LARGE_FUNCTION_INSNS)
&& newsize > limit) && newsize > limit)
@ -487,28 +514,6 @@ cgraph_default_inline_p (struct cgraph_node *n, cgraph_inline_failed_t *reason)
return true; return true;
} }
/* Return true when inlining WHAT would create recursive inlining.
We call recursive inlining all cases where same function appears more than
once in the single recursion nest path in the inline graph. */
static inline bool
cgraph_recursive_inlining_p (struct cgraph_node *to,
struct cgraph_node *what,
cgraph_inline_failed_t *reason)
{
bool recursive;
if (to->global.inlined_to)
recursive = what->decl == to->global.inlined_to->decl;
else
recursive = what->decl == to->decl;
/* Marking recursive function inline has sane semantic and thus we should
not warn on it. */
if (recursive && reason)
*reason = (what->local.disregard_inline_limits
? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
return recursive;
}
/* A cost model driving the inlining heuristics in a way so the edges with /* A cost model driving the inlining heuristics in a way so the edges with
smallest badness are inlined first. After each inlining is performed smallest badness are inlined first. After each inlining is performed
the costs of all caller edges of nodes affected are recomputed so the the costs of all caller edges of nodes affected are recomputed so the
@ -524,9 +529,7 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
if (edge->callee->local.disregard_inline_limits) if (edge->callee->local.disregard_inline_limits)
return INT_MIN; return INT_MIN;
growth = growth = cgraph_estimate_edge_growth (edge);
(cgraph_estimate_size_after_inlining (edge->caller, edge->callee)
- edge->caller->global.size);
if (dump) if (dump)
{ {
@ -536,9 +539,11 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
fprintf (dump_file, " growth %i, time %i-%i, size %i-%i\n", fprintf (dump_file, " growth %i, time %i-%i, size %i-%i\n",
growth, growth,
edge->callee->global.time, edge->callee->global.time,
inline_summary (edge->callee)->time_inlining_benefit, inline_summary (edge->callee)->time_inlining_benefit
+ edge->call_stmt_time,
edge->callee->global.size, edge->callee->global.size,
inline_summary (edge->callee)->size_inlining_benefit); inline_summary (edge->callee)->size_inlining_benefit
+ edge->call_stmt_size);
} }
/* Always prefer inlining saving code size. */ /* Always prefer inlining saving code size. */
@ -557,7 +562,8 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
badness = badness =
((int) ((int)
((double) edge->count * INT_MIN / max_count / (max_benefit + 1)) * ((double) edge->count * INT_MIN / max_count / (max_benefit + 1)) *
(inline_summary (edge->callee)->time_inlining_benefit + 1)) / growth; (inline_summary (edge->callee)->time_inlining_benefit
+ edge->call_stmt_time + 1)) / growth;
if (dump) if (dump)
{ {
fprintf (dump_file, fprintf (dump_file,
@ -566,7 +572,8 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
(int) badness, (double) badness / INT_MIN, (int) badness, (double) badness / INT_MIN,
(double) edge->count / max_count, (double) edge->count / max_count,
(double) (inline_summary (edge->callee)-> (double) (inline_summary (edge->callee)->
time_inlining_benefit + 1) / (max_benefit + 1)); time_inlining_benefit
+ edge->call_stmt_time + 1) / (max_benefit + 1));
} }
} }
@ -586,8 +593,9 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
int growth_for_all; int growth_for_all;
badness = growth * 10000; badness = growth * 10000;
benefitperc = benefitperc =
100 * inline_summary (edge->callee)->time_inlining_benefit 100 * (inline_summary (edge->callee)->time_inlining_benefit
/ (edge->callee->global.time + 1) +1; + edge->call_stmt_time)
/ (edge->callee->global.time + 1) + 1;
benefitperc = MIN (benefitperc, 100); benefitperc = MIN (benefitperc, 100);
div *= benefitperc; div *= benefitperc;
@ -636,7 +644,7 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
gcc_assert (badness >= INT_MIN); gcc_assert (badness >= INT_MIN);
gcc_assert (badness <= INT_MAX - 1); gcc_assert (badness <= INT_MAX - 1);
/* Make recursive inlining happen always after other inlining is done. */ /* Make recursive inlining happen always after other inlining is done. */
if (cgraph_recursive_inlining_p (edge->caller, edge->callee, NULL)) if (cgraph_edge_recursive_p (edge))
return badness + 1; return badness + 1;
else else
return badness; return badness;
@ -822,18 +830,23 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
is NULL. */ is NULL. */
static bool static bool
cgraph_decide_recursive_inlining (struct cgraph_node *node, cgraph_decide_recursive_inlining (struct cgraph_edge *edge,
VEC (cgraph_edge_p, heap) **new_edges) VEC (cgraph_edge_p, heap) **new_edges)
{ {
int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO); int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO);
int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO); int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO);
int probability = PARAM_VALUE (PARAM_MIN_INLINE_RECURSIVE_PROBABILITY); int probability = PARAM_VALUE (PARAM_MIN_INLINE_RECURSIVE_PROBABILITY);
fibheap_t heap; fibheap_t heap;
struct cgraph_node *node;
struct cgraph_edge *e; struct cgraph_edge *e;
struct cgraph_node *master_clone, *next; struct cgraph_node *master_clone, *next;
int depth = 0; int depth = 0;
int n = 0; int n = 0;
node = edge->caller;
if (node->global.inlined_to)
node = node->global.inlined_to;
/* It does not make sense to recursively inline always-inline functions /* It does not make sense to recursively inline always-inline functions
as we are going to sorry() on the remaining calls anyway. */ as we are going to sorry() on the remaining calls anyway. */
if (node->local.disregard_inline_limits if (node->local.disregard_inline_limits
@ -852,7 +865,7 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node,
/* Make sure that function is small enough to be considered for inlining. */ /* Make sure that function is small enough to be considered for inlining. */
if (!max_depth if (!max_depth
|| cgraph_estimate_size_after_inlining (node, node) >= limit) || cgraph_estimate_size_after_inlining (node, edge) >= limit)
return false; return false;
heap = fibheap_new (); heap = fibheap_new ();
lookup_recursive_calls (node, node, heap); lookup_recursive_calls (node, node, heap);
@ -876,14 +889,15 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node,
cgraph_clone_inlined_nodes (e, true, false); cgraph_clone_inlined_nodes (e, true, false);
/* Do the inlining and update list of recursive call during process. */ /* Do the inlining and update list of recursive call during process. */
while (!fibheap_empty (heap) while (!fibheap_empty (heap))
&& (cgraph_estimate_size_after_inlining (node, master_clone)
<= limit))
{ {
struct cgraph_edge *curr struct cgraph_edge *curr
= (struct cgraph_edge *) fibheap_extract_min (heap); = (struct cgraph_edge *) fibheap_extract_min (heap);
struct cgraph_node *cnode; struct cgraph_node *cnode;
if (cgraph_estimate_size_after_inlining (node, curr) > limit)
break;
depth = 1; depth = 1;
for (cnode = curr->caller; for (cnode = curr->caller;
cnode->global.inlined_to; cnode = cnode->callers->caller) cnode->global.inlined_to; cnode = cnode->callers->caller)
@ -1083,10 +1097,7 @@ cgraph_decide_inlining_of_small_functions (void)
} }
callee = edge->callee; callee = edge->callee;
growth = cgraph_estimate_edge_growth (edge);
growth = (cgraph_estimate_size_after_inlining (edge->caller, edge->callee)
- edge->caller->global.size);
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, fprintf (dump_file,
@ -1154,26 +1165,18 @@ cgraph_decide_inlining_of_small_functions (void)
else if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl))) else if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl)))
not_good = CIF_OPTIMIZING_FOR_SIZE; not_good = CIF_OPTIMIZING_FOR_SIZE;
if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0) if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0)
{
if (!cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
{ {
edge->inline_failed = not_good; edge->inline_failed = not_good;
if (dump_file) if (dump_file)
fprintf (dump_file, " inline_failed:%s.\n", fprintf (dump_file, " inline_failed:%s.\n",
cgraph_inline_failed_string (edge->inline_failed)); cgraph_inline_failed_string (edge->inline_failed));
}
continue; continue;
} }
if (!cgraph_default_inline_p (edge->callee, &edge->inline_failed)) if (!cgraph_default_inline_p (edge->callee, &edge->inline_failed))
{
if (!cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, " inline_failed:%s.\n", fprintf (dump_file, " inline_failed:%s.\n",
cgraph_inline_failed_string (edge->inline_failed)); cgraph_inline_failed_string (edge->inline_failed));
}
continue; continue;
} }
if (!tree_can_inline_p (edge) if (!tree_can_inline_p (edge)
@ -1184,16 +1187,18 @@ cgraph_decide_inlining_of_small_functions (void)
cgraph_inline_failed_string (edge->inline_failed)); cgraph_inline_failed_string (edge->inline_failed));
continue; continue;
} }
if (cgraph_recursive_inlining_p (edge->caller, edge->callee, if (cgraph_edge_recursive_p (edge))
&edge->inline_failed))
{ {
where = edge->caller; where = edge->caller;
if (where->global.inlined_to) if (where->global.inlined_to)
where = where->global.inlined_to; where = where->global.inlined_to;
if (!cgraph_decide_recursive_inlining (where, if (!cgraph_decide_recursive_inlining (edge,
flag_indirect_inlining flag_indirect_inlining
? &new_indirect_edges : NULL)) ? &new_indirect_edges : NULL))
{
edge->inline_failed = CIF_RECURSIVE_INLINING;
continue; continue;
}
if (flag_indirect_inlining) if (flag_indirect_inlining)
add_new_edges_to_heap (heap, new_indirect_edges); add_new_edges_to_heap (heap, new_indirect_edges);
update_all_callee_keys (heap, where, updated_nodes); update_all_callee_keys (heap, where, updated_nodes);
@ -1201,8 +1206,7 @@ cgraph_decide_inlining_of_small_functions (void)
else else
{ {
struct cgraph_node *callee; struct cgraph_node *callee;
if (!cgraph_check_inline_limits (edge->caller, edge->callee, if (!cgraph_check_inline_limits (edge, &edge->inline_failed))
&edge->inline_failed))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, " Not inlining into %s:%s.\n", fprintf (dump_file, " Not inlining into %s:%s.\n",
@ -1295,9 +1299,7 @@ cgraph_decide_inlining_of_small_functions (void)
if (dump_flags & TDF_DETAILS) if (dump_flags & TDF_DETAILS)
cgraph_edge_badness (edge, true); cgraph_edge_badness (edge, true);
} }
if (!edge->callee->local.disregard_inline_limits && edge->inline_failed if (!edge->callee->local.disregard_inline_limits && edge->inline_failed)
&& !cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT; edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT;
} }
@ -1359,7 +1361,7 @@ cgraph_flatten (struct cgraph_node *node)
continue; continue;
} }
if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)) if (cgraph_edge_recursive_p (e))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Not inlining: recursive call.\n"); fprintf (dump_file, "Not inlining: recursive call.\n");
@ -1431,10 +1433,14 @@ cgraph_decide_inlining (void)
if (!DECL_EXTERNAL (node->decl)) if (!DECL_EXTERNAL (node->decl))
initial_size += node->global.size; initial_size += node->global.size;
for (e = node->callees; e; e = e->next_callee) for (e = node->callees; e; e = e->next_callee)
{
int benefit = (inline_summary (node)->time_inlining_benefit
+ e->call_stmt_time);
if (max_count < e->count) if (max_count < e->count)
max_count = e->count; max_count = e->count;
if (max_benefit < inline_summary (node)->time_inlining_benefit) if (max_benefit < benefit)
max_benefit = inline_summary (node)->time_inlining_benefit; max_benefit = benefit;
}
} }
gcc_assert (in_lto_p gcc_assert (in_lto_p
|| !max_count || !max_count
@ -1516,8 +1522,7 @@ cgraph_decide_inlining (void)
node->callers->caller->global.size); node->callers->caller->global.size);
} }
if (cgraph_check_inline_limits (node->callers->caller, node, if (cgraph_check_inline_limits (node->callers, &reason))
&reason))
{ {
struct cgraph_node *caller = node->callers->caller; struct cgraph_node *caller = node->callers->caller;
cgraph_mark_inline_edge (node->callers, true, NULL); cgraph_mark_inline_edge (node->callers, true, NULL);
@ -1602,7 +1607,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
fprintf (dump_file, fprintf (dump_file,
"Considering to always inline inline candidate %s.\n", "Considering to always inline inline candidate %s.\n",
cgraph_node_name (e->callee)); cgraph_node_name (e->callee));
if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)) if (cgraph_edge_recursive_p (e))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Not inlining: recursive call.\n"); fprintf (dump_file, "Not inlining: recursive call.\n");
@ -1656,7 +1661,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
if (dump_file) if (dump_file)
fprintf (dump_file, "Considering inline candidate %s.\n", fprintf (dump_file, "Considering inline candidate %s.\n",
cgraph_node_name (e->callee)); cgraph_node_name (e->callee));
if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)) if (cgraph_edge_recursive_p (e))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Not inlining: recursive call.\n"); fprintf (dump_file, "Not inlining: recursive call.\n");
@ -1681,16 +1686,13 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
if (((mode == INLINE_SIZE || mode == INLINE_SIZE_NORECURSIVE) if (((mode == INLINE_SIZE || mode == INLINE_SIZE_NORECURSIVE)
|| (!flag_inline_functions || (!flag_inline_functions
&& !DECL_DECLARED_INLINE_P (e->callee->decl))) && !DECL_DECLARED_INLINE_P (e->callee->decl)))
&& (cgraph_estimate_size_after_inlining (e->caller, e->callee) && cgraph_estimate_edge_growth (e) > allowed_growth
> e->caller->global.size + allowed_growth)
&& cgraph_estimate_growth (e->callee) > allowed_growth) && cgraph_estimate_growth (e->callee) > allowed_growth)
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, fprintf (dump_file,
"Not inlining: code size would grow by %i.\n", "Not inlining: code size would grow by %i.\n",
cgraph_estimate_size_after_inlining (e->caller, cgraph_estimate_edge_growth (e));
e->callee)
- e->caller->global.size);
continue; continue;
} }
if (e->call_stmt_cannot_inline_p if (e->call_stmt_cannot_inline_p
@ -1708,7 +1710,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
"Not inlining: Function body no longer available.\n"); "Not inlining: Function body no longer available.\n");
continue; continue;
} }
if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed)) if (!cgraph_check_inline_limits (e, &e->inline_failed))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Not inlining: %s.\n", fprintf (dump_file, "Not inlining: %s.\n",
@ -1901,9 +1903,7 @@ estimate_function_body_sizes (struct cgraph_node *node)
basic_block bb; basic_block bb;
gimple_stmt_iterator bsi; gimple_stmt_iterator bsi;
struct function *my_function = DECL_STRUCT_FUNCTION (node->decl); struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
tree arg;
int freq; int freq;
tree funtype = TREE_TYPE (node->decl);
if (dump_file) if (dump_file)
fprintf (dump_file, "Analyzing function body size: %s\n", fprintf (dump_file, "Analyzing function body size: %s\n",
@ -1944,35 +1944,16 @@ estimate_function_body_sizes (struct cgraph_node *node)
time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE) time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE)
/ (CGRAPH_FREQ_BASE * 2)); / (CGRAPH_FREQ_BASE * 2));
size_inlining_benefit = (size_inlining_benefit + 1) / 2; size_inlining_benefit = (size_inlining_benefit + 1) / 2;
if (dump_file)
fprintf (dump_file, "Overall function body time: %i-%i size: %i-%i\n",
(int)time, (int)time_inlining_benefit,
size, size_inlining_benefit);
time_inlining_benefit += eni_time_weights.call_cost;
size_inlining_benefit += eni_size_weights.call_cost;
if (!VOID_TYPE_P (TREE_TYPE (funtype)))
{
int cost = estimate_move_cost (TREE_TYPE (funtype));
time_inlining_benefit += cost;
size_inlining_benefit += cost;
}
for (arg = DECL_ARGUMENTS (node->decl); arg; arg = DECL_CHAIN (arg))
if (!VOID_TYPE_P (TREE_TYPE (arg)))
{
int cost = estimate_move_cost (TREE_TYPE (arg));
time_inlining_benefit += cost;
size_inlining_benefit += cost;
}
if (time_inlining_benefit > MAX_TIME) if (time_inlining_benefit > MAX_TIME)
time_inlining_benefit = MAX_TIME; time_inlining_benefit = MAX_TIME;
if (time > MAX_TIME) if (time > MAX_TIME)
time = MAX_TIME; time = MAX_TIME;
inline_summary (node)->self_time = time;
inline_summary (node)->self_size = size;
if (dump_file) if (dump_file)
fprintf (dump_file, "With function call overhead time: %i-%i size: %i-%i\n", fprintf (dump_file, "Overall function body time: %i-%i size: %i-%i\n",
(int)time, (int)time_inlining_benefit, (int)time, (int)time_inlining_benefit,
size, size_inlining_benefit); size, size_inlining_benefit);
inline_summary (node)->self_time = time;
inline_summary (node)->self_size = size;
inline_summary (node)->time_inlining_benefit = time_inlining_benefit; inline_summary (node)->time_inlining_benefit = time_inlining_benefit;
inline_summary (node)->size_inlining_benefit = size_inlining_benefit; inline_summary (node)->size_inlining_benefit = size_inlining_benefit;
} }
@ -1982,6 +1963,7 @@ void
compute_inline_parameters (struct cgraph_node *node) compute_inline_parameters (struct cgraph_node *node)
{ {
HOST_WIDE_INT self_stack_size; HOST_WIDE_INT self_stack_size;
struct cgraph_edge *e;
gcc_assert (!node->global.inlined_to); gcc_assert (!node->global.inlined_to);
@ -2001,8 +1983,6 @@ compute_inline_parameters (struct cgraph_node *node)
node->local.can_change_signature = true; node->local.can_change_signature = true;
else else
{ {
struct cgraph_edge *e;
/* Functions calling builtin_apply can not change signature. */ /* Functions calling builtin_apply can not change signature. */
for (e = node->callees; e; e = e->next_callee) for (e = node->callees; e; e = e->next_callee)
if (DECL_BUILT_IN (e->callee->decl) if (DECL_BUILT_IN (e->callee->decl)
@ -2012,6 +1992,17 @@ compute_inline_parameters (struct cgraph_node *node)
node->local.can_change_signature = !e; node->local.can_change_signature = !e;
} }
estimate_function_body_sizes (node); estimate_function_body_sizes (node);
/* Compute size of call statements. We have to do this for callers here,
those sizes need to be present for edges _to_ us as early as
we are finished with early opts. */
for (e = node->callers; e; e = e->next_caller)
if (e->call_stmt)
{
e->call_stmt_size
= estimate_num_insns (e->call_stmt, &eni_size_weights);
e->call_stmt_time
= estimate_num_insns (e->call_stmt, &eni_time_weights);
}
/* Inlining characteristics are maintained by the cgraph_mark_inline. */ /* Inlining characteristics are maintained by the cgraph_mark_inline. */
node->global.time = inline_summary (node)->self_time; node->global.time = inline_summary (node)->self_time;
node->global.size = inline_summary (node)->self_size; node->global.size = inline_summary (node)->self_size;

View File

@ -285,6 +285,8 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
bp_pack_value (&bp, uid, HOST_BITS_PER_INT); bp_pack_value (&bp, uid, HOST_BITS_PER_INT);
bp_pack_value (&bp, edge->inline_failed, HOST_BITS_PER_INT); bp_pack_value (&bp, edge->inline_failed, HOST_BITS_PER_INT);
bp_pack_value (&bp, edge->frequency, HOST_BITS_PER_INT); bp_pack_value (&bp, edge->frequency, HOST_BITS_PER_INT);
bp_pack_value (&bp, edge->call_stmt_size, HOST_BITS_PER_INT);
bp_pack_value (&bp, edge->call_stmt_time, HOST_BITS_PER_INT);
bp_pack_value (&bp, edge->loop_nest, 30); bp_pack_value (&bp, edge->loop_nest, 30);
bp_pack_value (&bp, edge->indirect_inlining_edge, 1); bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1); bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@ -1215,6 +1217,7 @@ input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes,
cgraph_inline_failed_t inline_failed; cgraph_inline_failed_t inline_failed;
struct bitpack_d bp; struct bitpack_d bp;
int ecf_flags = 0; int ecf_flags = 0;
int call_stmt_time, call_stmt_size;
caller = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib)); caller = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib));
if (caller == NULL || caller->decl == NULL_TREE) if (caller == NULL || caller->decl == NULL_TREE)
@ -1236,6 +1239,8 @@ input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes,
inline_failed = (cgraph_inline_failed_t) bp_unpack_value (&bp, inline_failed = (cgraph_inline_failed_t) bp_unpack_value (&bp,
HOST_BITS_PER_INT); HOST_BITS_PER_INT);
freq = (int) bp_unpack_value (&bp, HOST_BITS_PER_INT); freq = (int) bp_unpack_value (&bp, HOST_BITS_PER_INT);
call_stmt_size = (int) bp_unpack_value (&bp, HOST_BITS_PER_INT);
call_stmt_time = (int) bp_unpack_value (&bp, HOST_BITS_PER_INT);
nest = (unsigned) bp_unpack_value (&bp, 30); nest = (unsigned) bp_unpack_value (&bp, 30);
if (indirect) if (indirect)
@ -1248,6 +1253,8 @@ input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes,
edge->inline_failed = inline_failed; edge->inline_failed = inline_failed;
edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1); edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
edge->can_throw_external = bp_unpack_value (&bp, 1); edge->can_throw_external = bp_unpack_value (&bp, 1);
edge->call_stmt_size = call_stmt_size;
edge->call_stmt_time = call_stmt_time;
if (indirect) if (indirect)
{ {
if (bp_unpack_value (&bp, 1)) if (bp_unpack_value (&bp, 1))

View File

@ -1,3 +1,8 @@
2011-04-06 Richard Guenther <rguenther@suse.de>
PR tree-optimization/47663
* gcc.dg/tree-ssa/inline-8.c: New testcase.
2011-04-05 Eric Botcazou <ebotcazou@adacore.com> 2011-04-05 Eric Botcazou <ebotcazou@adacore.com>
* gcc.dg/torture/pr47917.c: Add -D_XOPEN_SOURCE=500 to dg-options for * gcc.dg/torture/pr47917.c: Add -D_XOPEN_SOURCE=500 to dg-options for

View File

@ -0,0 +1,31 @@
/* { dg-do compile } */
/* { dg-options "-O -finline-small-functions --param early-inlining-insns=0 -fdump-tree-einline" } */
int foo0();
void bar0() { foo0(); }
void foobar0() { bar0(); }
void foo1();
void bar1() { foo1(); }
void foobar1() { bar1(); }
#if 0
int foo2();
int bar2() { return foo2(); }
/* The size estimate fails to see that inlining the call statement in bar2
will make its lhs dead. */
void foobar2() { bar2(); }
#endif
int foo3();
int bar3() { return foo3(); }
int foobar3() { return bar3(); }
int bar4() { return 0; }
void foobar4() { bar4(); }
int bar5() { return 0; }
int foobar5() { return bar5(); }
/* { dg-final { scan-tree-dump-times "Inlining" 5 "einline" } } */
/* { dg-final { cleanup-tree-dump "einline" } } */

View File

@ -3514,7 +3514,8 @@ estimate_num_insns (gimple stmt, eni_weights *weights)
if (decl) if (decl)
funtype = TREE_TYPE (decl); funtype = TREE_TYPE (decl);
if (!VOID_TYPE_P (TREE_TYPE (funtype))) if (!VOID_TYPE_P (TREE_TYPE (funtype))
&& gimple_call_lhs (stmt))
cost += estimate_move_cost (TREE_TYPE (funtype)); cost += estimate_move_cost (TREE_TYPE (funtype));
if (funtype) if (funtype)
@ -3812,6 +3813,8 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
&& !DECL_IN_SYSTEM_HEADER (fn) && !DECL_IN_SYSTEM_HEADER (fn)
&& reason != CIF_UNSPECIFIED && reason != CIF_UNSPECIFIED
&& !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)) && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn))
/* Do not warn about not inlined recursive calls. */
&& !cgraph_edge_recursive_p (cg_edge)
/* Avoid warnings during early inline pass. */ /* Avoid warnings during early inline pass. */
&& cgraph_global_info_ready) && cgraph_global_info_ready)
{ {