#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "tree-inline.h"
#include "langhooks.h"
#include "flags.h"
#include "cgraph.h"
#include "diagnostic.h"
#include "timevar.h"
#include "params.h"
#include "fibheap.h"
#include "intl.h"
#include "tree-pass.h"
#include "hashtab.h"
#include "coverage.h"
#include "ggc.h"
static int ncalls_inlined;
static int nfunctions_inlined;
static int initial_insns;
static int overall_insns;
static int max_insns;
static gcov_type max_count;
static int
cgraph_estimate_size_after_inlining (int times, struct cgraph_node *to,
struct cgraph_node *what)
{
int size;
tree fndecl = what->decl, arg;
int call_insns = PARAM_VALUE (PARAM_INLINE_CALL_COST);
for (arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
call_insns += estimate_move_cost (TREE_TYPE (arg));
size = (what->global.insns - call_insns) * times + to->global.insns;
gcc_assert (size >= 0);
return size;
}
void
cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original)
{
if (duplicate)
{
if (!e->callee->callers->next_caller
&& !e->callee->needed
&& flag_unit_at_a_time)
{
gcc_assert (!e->callee->global.inlined_to);
if (DECL_SAVED_TREE (e->callee->decl))
overall_insns -= e->callee->global.insns, nfunctions_inlined++;
duplicate = false;
}
else
{
struct cgraph_node *n;
n = cgraph_clone_node (e->callee, e->count, e->loop_nest,
update_original);
cgraph_redirect_edge_callee (e, n);
}
}
if (e->caller->global.inlined_to)
e->callee->global.inlined_to = e->caller->global.inlined_to;
else
e->callee->global.inlined_to = e->caller;
for (e = e->callee->callees; e; e = e->next_callee)
if (!e->inline_failed)
cgraph_clone_inlined_nodes (e, duplicate, update_original);
}
void
cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original)
{
int old_insns = 0, new_insns = 0;
struct cgraph_node *to = NULL, *what;
if (e->callee->inline_decl)
cgraph_redirect_edge_callee (e, cgraph_node (e->callee->inline_decl));
gcc_assert (e->inline_failed);
e->inline_failed = NULL;
if (!e->callee->global.inlined && flag_unit_at_a_time)
DECL_POSSIBLY_INLINED (e->callee->decl) = true;
e->callee->global.inlined = true;
cgraph_clone_inlined_nodes (e, true, update_original);
what = e->callee;
for (;e && !e->inline_failed; e = e->caller->callers)
{
old_insns = e->caller->global.insns;
new_insns = cgraph_estimate_size_after_inlining (1, e->caller,
what);
gcc_assert (new_insns >= 0);
to = e->caller;
to->global.insns = new_insns;
}
gcc_assert (what->global.inlined_to == to);
if (new_insns > old_insns)
overall_insns += new_insns - old_insns;
ncalls_inlined++;
}
static struct cgraph_edge *
cgraph_mark_inline (struct cgraph_edge *edge)
{
struct cgraph_node *to = edge->caller;
struct cgraph_node *what = edge->callee;
struct cgraph_edge *e, *next;
int times = 0;
for (e = what->callers; e; e = next)
{
next = e->next_caller;
if (e->caller == to && e->inline_failed)
{
cgraph_mark_inline_edge (e, true);
if (e == edge)
edge = next;
times++;
}
}
gcc_assert (times);
return edge;
}
static int
cgraph_estimate_growth (struct cgraph_node *node)
{
int growth = 0;
struct cgraph_edge *e;
if (node->global.estimated_growth != INT_MIN)
return node->global.estimated_growth;
for (e = node->callers; e; e = e->next_caller)
if (e->inline_failed)
growth += (cgraph_estimate_size_after_inlining (1, e->caller, node)
- e->caller->global.insns);
if (!node->needed && !DECL_EXTERNAL (node->decl))
growth -= node->global.insns;
node->global.estimated_growth = growth;
return growth;
}
static bool
cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
const char **reason, bool one_only)
{
int times = 0;
struct cgraph_edge *e;
int newsize;
int limit;
if (one_only)
times = 1;
else
for (e = to->callees; e; e = e->next_callee)
if (e->callee == what)
times++;
if (to->global.inlined_to)
to = to->global.inlined_to;
if (to->local.self_insns > what->local.self_insns)
limit = to->local.self_insns;
else
limit = what->local.self_insns;
limit += limit * PARAM_VALUE (PARAM_LARGE_FUNCTION_GROWTH) / 100;
newsize = cgraph_estimate_size_after_inlining (times, to, what);
if (newsize >= to->global.insns
&& newsize > PARAM_VALUE (PARAM_LARGE_FUNCTION_INSNS)
&& newsize > limit)
{
if (reason)
*reason = N_("--param large-function-growth limit reached");
return false;
}
return true;
}
bool
cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
{
tree decl = n->decl;
if (n->inline_decl)
decl = n->inline_decl;
if (!DECL_INLINE (decl))
{
if (reason)
*reason = N_("function not inlinable");
return false;
}
if (!DECL_STRUCT_FUNCTION (decl)->cfg)
{
if (reason)
*reason = N_("function body not available");
return false;
}
if (DECL_DECLARED_INLINE_P (decl))
{
if (n->global.insns >= MAX_INLINE_INSNS_SINGLE)
{
if (reason)
*reason = N_("--param max-inline-insns-single limit reached");
return false;
}
}
else
{
if (n->global.insns >= MAX_INLINE_INSNS_AUTO)
{
if (reason)
*reason = N_("--param max-inline-insns-auto limit reached");
return false;
}
}
return true;
}
static bool
cgraph_recursive_inlining_p (struct cgraph_node *to,
struct cgraph_node *what,
const char **reason)
{
bool recursive;
if (to->global.inlined_to)
recursive = what->decl == to->global.inlined_to->decl;
else
recursive = what->decl == to->decl;
if (recursive && reason)
*reason = (what->local.disregard_inline_limits
? N_("recursive inlining") : "");
return recursive;
}
static bool
cgraph_maybe_hot_edge_p (struct cgraph_edge *edge)
{
if (profile_info && flag_branch_probabilities
&& (edge->count
<= profile_info->sum_max / PARAM_VALUE (HOT_BB_COUNT_FRACTION)))
return false;
return true;
}
static int
cgraph_edge_badness (struct cgraph_edge *edge)
{
if (max_count)
{
int growth =
cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee);
growth -= edge->caller->global.insns;
if (growth <= 0)
return INT_MIN - growth;
return ((int)((double)edge->count * INT_MIN / max_count)) / growth;
}
else
{
int nest = MIN (edge->loop_nest, 8);
int badness = cgraph_estimate_growth (edge->callee) * 256;
if (badness > 0)
badness >>= nest;
else
badness <<= nest;
if (cgraph_recursive_inlining_p (edge->caller, edge->callee, NULL))
return badness + 1;
else
return badness;
}
}
static void
update_caller_keys (fibheap_t heap, struct cgraph_node *node,
bitmap updated_nodes)
{
struct cgraph_edge *edge;
const char *failed_reason;
if (!node->local.inlinable || node->local.disregard_inline_limits
|| node->global.inlined_to)
return;
if (bitmap_bit_p (updated_nodes, node->uid))
return;
bitmap_set_bit (updated_nodes, node->uid);
node->global.estimated_growth = INT_MIN;
if (!node->local.inlinable)
return;
if (!cgraph_default_inline_p (node, &failed_reason))
{
for (edge = node->callers; edge; edge = edge->next_caller)
if (edge->aux)
{
fibheap_delete_node (heap, edge->aux);
edge->aux = NULL;
if (edge->inline_failed)
edge->inline_failed = failed_reason;
}
return;
}
for (edge = node->callers; edge; edge = edge->next_caller)
if (edge->inline_failed)
{
int badness = cgraph_edge_badness (edge);
if (edge->aux)
{
fibnode_t n = edge->aux;
gcc_assert (n->data == edge);
if (n->key == badness)
continue;
if (fibheap_replace_key (heap, n, badness))
continue;
fibheap_delete_node (heap, edge->aux);
}
edge->aux = fibheap_insert (heap, badness, edge);
}
}
static void
update_callee_keys (fibheap_t heap, struct cgraph_node *node,
bitmap updated_nodes)
{
struct cgraph_edge *e;
node->global.estimated_growth = INT_MIN;
for (e = node->callees; e; e = e->next_callee)
if (e->inline_failed)
update_caller_keys (heap, e->callee, updated_nodes);
else if (!e->inline_failed)
update_callee_keys (heap, e->callee, updated_nodes);
}
static void
lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
fibheap_t heap)
{
static int priority;
struct cgraph_edge *e;
for (e = where->callees; e; e = e->next_callee)
if (e->callee == node)
{
fibheap_insert (heap,
!max_count ? priority++
: -(e->count / ((max_count + (1<<24) - 1) / (1<<24))),
e);
}
for (e = where->callees; e; e = e->next_callee)
if (!e->inline_failed)
lookup_recursive_calls (node, e->callee, heap);
}
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;
}
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)
{
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));
}
}
static bool
cgraph_decide_recursive_inlining (struct cgraph_node *node)
{
int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO);
int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO);
int probability = PARAM_VALUE (PARAM_MIN_INLINE_RECURSIVE_PROBABILITY);
fibheap_t heap;
struct cgraph_edge *e;
struct cgraph_node *master_clone, *next;
int depth = 0;
int n = 0;
if (DECL_DECLARED_INLINE_P (node->decl))
{
limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE);
max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH);
}
if (!max_depth
|| cgraph_estimate_size_after_inlining (1, node, node) >= limit)
return false;
heap = fibheap_new ();
lookup_recursive_calls (node, node, heap);
if (fibheap_empty (heap))
{
fibheap_delete (heap);
return false;
}
if (dump_file)
fprintf (dump_file,
" Performing recursive inlining on %s\n",
cgraph_node_name (node));
master_clone = cgraph_clone_node (node, node->count, 1, false);
master_clone->needed = true;
for (e = master_clone->callees; e; e = e->next_callee)
if (!e->inline_failed)
cgraph_clone_inlined_nodes (e, true, false);
while (!fibheap_empty (heap)
&& (cgraph_estimate_size_after_inlining (1, node, master_clone)
<= limit))
{
struct cgraph_edge *curr = fibheap_extract_min (heap);
struct cgraph_node *cnode;
depth = 1;
for (cnode = curr->caller;
cnode->global.inlined_to; cnode = cnode->callers->caller)
if (node->decl == curr->callee->decl)
depth++;
if (depth > max_depth)
{
if (dump_file)
fprintf (dump_file,
" maxmal depth reached\n");
continue;
}
if (max_count)
{
if (!cgraph_maybe_hot_edge_p (curr))
{
if (dump_file)
fprintf (dump_file, " Not inlining cold call\n");
continue;
}
if (curr->count * 100 / node->count < probability)
{
if (dump_file)
fprintf (dump_file,
" Probability of edge is too small\n");
continue;
}
}
if (dump_file)
{
fprintf (dump_file,
" Inlining call of depth %i", depth);
if (node->count)
{
fprintf (dump_file, " called approx. %.2f times per call",
(double)curr->count / node->count);
}
fprintf (dump_file, "\n");
}
cgraph_redirect_edge_callee (curr, master_clone);
cgraph_mark_inline_edge (curr, false);
lookup_recursive_calls (node, curr->callee, heap);
n++;
}
if (!fibheap_empty (heap) && dump_file)
fprintf (dump_file, " Recursive inlining growth limit met.\n");
fibheap_delete (heap);
if (dump_file)
fprintf (dump_file,
"\n Inlined %i times, body grown from %i to %i insns\n", n,
master_clone->global.insns, node->global.insns);
for (node = cgraph_nodes; node != master_clone;
node = next)
{
next = node->next;
if (node->global.inlined_to == master_clone)
cgraph_remove_node (node);
}
cgraph_remove_node (master_clone);
return n > 0;
}
static void
cgraph_set_inline_failed (struct cgraph_node *node, const char *reason)
{
struct cgraph_edge *e;
if (dump_file)
fprintf (dump_file, "Inlining failed: %s\n", reason);
for (e = node->callers; e; e = e->next_caller)
if (e->inline_failed)
e->inline_failed = reason;
}
static void
cgraph_decide_inlining_of_small_functions (void)
{
struct cgraph_node *node;
struct cgraph_edge *edge;
const char *failed_reason;
fibheap_t heap = fibheap_new ();
bitmap updated_nodes = BITMAP_ALLOC (NULL);
if (dump_file)
fprintf (dump_file, "\nDeciding on smaller functions:\n");
for (node = cgraph_nodes; node; node = node->next)
{
if (!node->local.inlinable || !node->callers
|| node->local.disregard_inline_limits)
continue;
if (dump_file)
fprintf (dump_file, "Considering inline candidate %s.\n", cgraph_node_name (node));
node->global.estimated_growth = INT_MIN;
if (!cgraph_default_inline_p (node, &failed_reason))
{
cgraph_set_inline_failed (node, failed_reason);
continue;
}
for (edge = node->callers; edge; edge = edge->next_caller)
if (edge->inline_failed)
{
gcc_assert (!edge->aux);
edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge), edge);
}
}
while (overall_insns <= max_insns && (edge = fibheap_extract_min (heap)))
{
int old_insns = overall_insns;
struct cgraph_node *where;
int growth =
cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee);
growth -= edge->caller->global.insns;
if (dump_file)
{
fprintf (dump_file,
"\nConsidering %s with %i insns\n",
cgraph_node_name (edge->callee),
edge->callee->global.insns);
fprintf (dump_file,
" to be inlined into %s\n"
" Estimated growth after inlined into all callees is %+i insns.\n"
" Estimated badness is %i.\n",
cgraph_node_name (edge->caller),
cgraph_estimate_growth (edge->callee),
cgraph_edge_badness (edge));
if (edge->count)
fprintf (dump_file," Called "HOST_WIDEST_INT_PRINT_DEC"x\n", edge->count);
}
gcc_assert (edge->aux);
edge->aux = NULL;
if (!edge->inline_failed)
continue;
if (!max_count)
{
where = edge->caller;
while (where->global.inlined_to)
{
if (where->decl == edge->callee->decl)
break;
where = where->callers->caller;
}
if (where->global.inlined_to)
{
edge->inline_failed
= (edge->callee->local.disregard_inline_limits ? N_("recursive inlining") : "");
if (dump_file)
fprintf (dump_file, " inline_failed:Recursive inlining performed only for function itself.\n");
continue;
}
}
if (!cgraph_maybe_hot_edge_p (edge) && growth > 0)
{
if (!cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
{
edge->inline_failed =
N_("call is unlikely");
if (dump_file)
fprintf (dump_file, " inline_failed:%s.\n", edge->inline_failed);
}
continue;
}
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)
fprintf (dump_file, " inline_failed:%s.\n", edge->inline_failed);
}
continue;
}
if (cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
{
where = edge->caller;
if (where->global.inlined_to)
where = where->global.inlined_to;
if (!cgraph_decide_recursive_inlining (where))
continue;
update_callee_keys (heap, where, updated_nodes);
}
else
{
struct cgraph_node *callee;
if (!cgraph_check_inline_limits (edge->caller, edge->callee,
&edge->inline_failed, true))
{
if (dump_file)
fprintf (dump_file, " Not inlining into %s:%s.\n",
cgraph_node_name (edge->caller), edge->inline_failed);
continue;
}
callee = edge->callee;
cgraph_mark_inline_edge (edge, true);
update_callee_keys (heap, callee, updated_nodes);
}
where = edge->caller;
if (where->global.inlined_to)
where = where->global.inlined_to;
update_caller_keys (heap, where, updated_nodes);
bitmap_clear (updated_nodes);
if (dump_file)
{
fprintf (dump_file,
" Inlined into %s which now has %i insns,"
"net change of %+i insns.\n",
cgraph_node_name (edge->caller),
edge->caller->global.insns,
overall_insns - old_insns);
}
}
while ((edge = fibheap_extract_min (heap)) != NULL)
{
gcc_assert (edge->aux);
edge->aux = NULL;
if (!edge->callee->local.disregard_inline_limits && edge->inline_failed
&& !cgraph_recursive_inlining_p (edge->caller, edge->callee,
&edge->inline_failed))
edge->inline_failed = N_("--param inline-unit-growth limit reached");
}
fibheap_delete (heap);
BITMAP_FREE (updated_nodes);
}
static unsigned int
cgraph_decide_inlining (void)
{
struct cgraph_node *node;
int nnodes;
struct cgraph_node **order =
XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
int old_insns = 0;
int i;
timevar_push (TV_INLINE_HEURISTICS);
max_count = 0;
for (node = cgraph_nodes; node; node = node->next)
if (node->analyzed && (node->needed || node->reachable))
{
struct cgraph_edge *e;
if (!flag_early_inlining)
node->local.self_insns = node->global.insns
= estimate_num_insns (node->decl);
initial_insns += node->local.self_insns;
gcc_assert (node->local.self_insns == node->global.insns);
for (e = node->callees; e; e = e->next_callee)
if (max_count < e->count)
max_count = e->count;
}
overall_insns = initial_insns;
gcc_assert (!max_count || (profile_info && flag_branch_probabilities));
max_insns = overall_insns;
if (max_insns < PARAM_VALUE (PARAM_LARGE_UNIT_INSNS))
max_insns = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS);
max_insns = ((HOST_WIDEST_INT) max_insns
* (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100);
nnodes = cgraph_postorder (order);
if (dump_file)
fprintf (dump_file,
"\nDeciding on inlining. Starting with %i insns.\n",
initial_insns);
for (node = cgraph_nodes; node; node = node->next)
node->aux = 0;
if (dump_file)
fprintf (dump_file, "\nInlining always_inline functions:\n");
for (i = nnodes - 1; i >= 0; i--)
{
struct cgraph_edge *e, *next;
node = order[i];
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);
overall_insns = old_overall_insns;
continue;
}
if (!node->local.disregard_inline_limits)
continue;
if (dump_file)
fprintf (dump_file,
"\nConsidering %s %i insns (always inline)\n",
cgraph_node_name (node), node->global.insns);
old_insns = overall_insns;
for (e = node->callers; e; e = next)
{
next = e->next_caller;
if (!e->inline_failed)
continue;
if (cgraph_recursive_inlining_p (e->caller, e->callee,
&e->inline_failed))
continue;
cgraph_mark_inline_edge (e, true);
if (dump_file)
fprintf (dump_file,
" Inlined into %s which now has %i insns.\n",
cgraph_node_name (e->caller),
e->caller->global.insns);
}
if (dump_file)
fprintf (dump_file,
" Inlined for a net change of %+i insns.\n",
overall_insns - old_insns);
}
if (!flag_really_no_inline)
cgraph_decide_inlining_of_small_functions ();
if (!flag_really_no_inline
&& flag_inline_functions_called_once)
{
if (dump_file)
fprintf (dump_file, "\nDeciding on functions called once:\n");
for (i = nnodes - 1; i >= 0; i--)
{
node = order[i];
if (node->callers && !node->callers->next_caller && !node->needed
&& node->local.inlinable && node->callers->inline_failed
&& !DECL_EXTERNAL (node->decl) && !DECL_COMDAT (node->decl))
{
bool ok = true;
struct cgraph_node *node1;
for (node1 = node->callers->caller;
node1->callers && !node1->callers->inline_failed
&& ok; node1 = node1->callers->caller)
if (node1->callers->next_caller || node1->needed)
ok = false;
if (ok)
{
if (dump_file)
{
fprintf (dump_file,
"\nConsidering %s %i insns.\n",
cgraph_node_name (node), node->global.insns);
fprintf (dump_file,
" Called once from %s %i insns.\n",
cgraph_node_name (node->callers->caller),
node->callers->caller->global.insns);
}
old_insns = overall_insns;
if (cgraph_check_inline_limits (node->callers->caller, node,
NULL, false))
{
cgraph_mark_inline (node->callers);
if (dump_file)
fprintf (dump_file,
" Inlined into %s which now has %i insns"
" for a net change of %+i insns.\n",
cgraph_node_name (node->callers->caller),
node->callers->caller->global.insns,
overall_insns - old_insns);
}
else
{
if (dump_file)
fprintf (dump_file,
" Inline limit reached, not inlined.\n");
}
}
}
}
}
if (dump_file)
fprintf (dump_file,
"\nInlined %i calls, eliminated %i functions, "
"%i insns turned to %i insns.\n\n",
ncalls_inlined, nfunctions_inlined, initial_insns,
overall_insns);
free (order);
timevar_pop (TV_INLINE_HEURISTICS);
return 0;
}
bool
cgraph_decide_inlining_incrementally (struct cgraph_node *node, bool early)
{
struct cgraph_edge *e;
bool inlined = false;
const char *failed_reason;
for (e = node->callees; e; e = e->next_callee)
if (e->callee->local.disregard_inline_limits
&& e->inline_failed
&& !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
{
if (dump_file && early)
{
fprintf (dump_file, " Early 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_really_no_inline)
for (e = node->callees; e; e = e->next_callee)
if (e->callee->local.inlinable
&& e->inline_failed
&& !e->callee->local.disregard_inline_limits
&& !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
&& (!early
|| (cgraph_estimate_size_after_inlining (1, e->caller, e->callee)
<= e->caller->global.insns))
&& cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
false)
&& (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
{
if (cgraph_default_inline_p (e->callee, &failed_reason))
{
if (dump_file && early)
{
fprintf (dump_file, " Early inlining %s",
cgraph_node_name (e->callee));
fprintf (dump_file, " into %s\n", cgraph_node_name (node));
}
cgraph_mark_inline (e);
inlined = true;
}
else if (!early)
e->inline_failed = failed_reason;
}
if (early && inlined)
{
push_cfun (DECL_STRUCT_FUNCTION (node->decl));
tree_register_cfg_hooks ();
current_function_decl = node->decl;
optimize_inline_calls (current_function_decl);
node->local.self_insns = node->global.insns;
current_function_decl = NULL;
pop_cfun ();
}
return inlined;
}
static bool
cgraph_gate_inlining (void)
{
return flag_inline_trees;
}
struct tree_opt_pass pass_ipa_inline =
{
"inline",
cgraph_gate_inlining,
cgraph_decide_inlining,
NULL,
NULL,
0,
TV_INTEGRATION,
0,
PROP_cfg,
0,
0,
TODO_dump_cgraph | TODO_dump_func,
0
};
static int nnodes;
static GTY ((length ("nnodes"))) struct cgraph_node **order;
static unsigned int
cgraph_early_inlining (void)
{
struct cgraph_node *node;
int i;
if (sorrycount || errorcount)
return 0;
#ifdef ENABLE_CHECKING
for (node = cgraph_nodes; node; node = node->next)
gcc_assert (!node->aux);
#endif
order = ggc_alloc (sizeof (*order) * cgraph_n_nodes);
nnodes = cgraph_postorder (order);
for (i = nnodes - 1; i >= 0; i--)
{
node = order[i];
if (node->analyzed && (node->needed || node->reachable))
node->local.self_insns = node->global.insns
= estimate_num_insns (node->decl);
}
for (i = nnodes - 1; i >= 0; i--)
{
node = order[i];
if (node->analyzed && node->local.inlinable
&& (node->needed || node->reachable)
&& node->callers)
{
if (cgraph_decide_inlining_incrementally (node, true))
ggc_collect ();
}
}
cgraph_remove_unreachable_nodes (true, dump_file);
#ifdef ENABLE_CHECKING
for (node = cgraph_nodes; node; node = node->next)
gcc_assert (!node->global.inlined_to);
#endif
ggc_free (order);
order = NULL;
nnodes = 0;
return 0;
}
static bool
cgraph_gate_early_inlining (void)
{
return flag_inline_trees && flag_early_inlining;
}
struct tree_opt_pass pass_early_ipa_inline =
{
"einline",
cgraph_gate_early_inlining,
cgraph_early_inlining,
NULL,
NULL,
0,
TV_INTEGRATION,
0,
PROP_cfg,
0,
0,
TODO_dump_cgraph | TODO_dump_func,
0
};
#include "gt-ipa-inline.h"