/* Inlining decision heuristics.
- Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
cgraph_decide_inlining implements heuristics taking whole callgraph
into account, while cgraph_decide_inlining_incrementally considers
- only one function at a time and is used in non-unit-at-a-time mode.
+ only one function at a time and is used by early inliner.
The inliner itself is split into several passes:
to do inlining expanding code size it might result in unbounded growth of
whole unit.
- This is the main inlining pass in non-unit-at-a-time.
-
- With unit-at-a-time the pass is run during conversion into SSA form.
- Only functions already converted into SSA form are inlined, so the
- conversion must happen in topological order on the callgraph (that is
- maintained by pass manager). The functions after inlining are early
- optimized so the early inliner sees unoptimized function itself, but
- all considered callees are already optimized allowing it to unfold
- abstraction penalty on C++ effectively and cheaply.
+ The pass is run during conversion into SSA form. Only functions already
+ converted into SSA form are inlined, so the conversion must happen in
+ topological order on the callgraph (that is maintained by pass manager).
+ The functions after inlining are early optimized so the early inliner sees
+ unoptimized function itself, but all considered callees are already
+ optimized allowing it to unfold abstraction penalty on C++ effectively and
+ cheaply.
pass_ipa_early_inlining
#include "ggc.h"
#include "tree-flow.h"
#include "rtl.h"
+#include "ipa-prop.h"
/* Mode incremental inliner operate on:
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
static int overall_insns;
static gcov_type max_count;
+/* Holders of ipa cgraph hooks: */
+static struct cgraph_node_hook_list *function_insertion_hook_holder;
+
static inline struct inline_summary *
inline_summary (struct cgraph_node *node)
{
clones or re-using node originally representing out-of-line function call.
*/
void
-cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original)
+cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
+ bool update_original)
{
HOST_WIDE_INT peak;
+
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. */
if (!e->callee->callers->next_caller
&& !e->callee->needed
- && !cgraph_new_nodes
- && flag_unit_at_a_time)
+ && !cgraph_new_nodes)
{
gcc_assert (!e->callee->global.inlined_to);
- if (DECL_SAVED_TREE (e->callee->decl))
+ if (e->callee->analyzed)
overall_insns -= e->callee->global.insns, nfunctions_inlined++;
duplicate = false;
}
cgraph_clone_inlined_nodes (e, duplicate, update_original);
}
-/* Mark edge E as inlined and update callgraph accordingly.
- UPDATE_ORIGINAL specify whether profile of original function should be
- updated. */
+/* 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. */
-void
-cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original)
+static bool
+cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
+ VEC (cgraph_edge_p, heap) **new_edges)
{
int old_insns = 0, new_insns = 0;
struct cgraph_node *to = NULL, *what;
+ struct cgraph_edge *curr = e;
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)
+ if (!e->callee->global.inlined)
DECL_POSSIBLY_INLINED (e->callee->decl) = true;
e->callee->global.inlined = true;
if (new_insns > old_insns)
overall_insns += new_insns - old_insns;
ncalls_inlined++;
+
+ if (flag_indirect_inlining)
+ return ipa_propagate_indirect_call_infos (curr, new_edges);
+ else
+ return false;
}
/* Mark all calls of EDGE->CALLEE inlined into EDGE->CALLER.
struct cgraph_node *what = edge->callee;
struct cgraph_edge *e, *next;
- gcc_assert (!CALL_STMT_CANNOT_INLINE_P (edge->call_stmt));
+ gcc_assert (!gimple_call_cannot_inline_p (edge->call_stmt));
/* Look for all calls, mark them inline and clone recursively
all inlined functions. */
for (e = what->callers; e; e = next)
next = e->next_caller;
if (e->caller == to && e->inline_failed)
{
- cgraph_mark_inline_edge (e, true);
+ cgraph_mark_inline_edge (e, true, NULL);
if (e == edge)
edge = next;
}
{
int growth = 0;
struct cgraph_edge *e;
+ bool self_recursive = false;
+
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 (e->caller == node)
+ self_recursive = true;
+ if (e->inline_failed)
+ growth += (cgraph_estimate_size_after_inlining (1, e->caller, node)
+ - e->caller->global.insns);
+ }
- /* ??? Wrong for self recursive functions or cases where we decide to not
- inline for different reasons, but it is not big deal as in that case
- we will keep the body around, but we will also avoid some inlining. */
- if (!node->needed && !DECL_EXTERNAL (node->decl))
+ /* ??? Wrong for non-trivially self recursive functions or cases where
+ we decide to not inline for different reasons, but it is not big deal
+ as in that case we will keep the body around, but we will also avoid
+ some inlining. */
+ if (!node->needed && !DECL_EXTERNAL (node->decl) && !self_recursive)
growth -= node->global.insns;
node->global.estimated_growth = growth;
return recursive;
}
-/* Return true if the call can be hot. */
-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;
- if (lookup_attribute ("cold", DECL_ATTRIBUTES (edge->callee->decl))
- || lookup_attribute ("cold", DECL_ATTRIBUTES (edge->caller->decl)))
- return false;
- if (lookup_attribute ("hot", DECL_ATTRIBUTES (edge->caller->decl)))
- return true;
- if (flag_guess_branch_prob
- && edge->frequency < (CGRAPH_FREQ_MAX
- / PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION)))
- return false;
- return true;
-}
-
/* A cost model driving the inlining heuristics in a way so the edges with
smallest badness are inlined first. After each inlining is performed
the costs of all caller edges of nodes affected are recomputed so the
within function, the function itself is infrequent.
Other objective to optimize for is number of different calls inlined.
- We add the estimated growth after inlining all functions to biass the
+ We add the estimated growth after inlining all functions to bias the
priorities slightly in this direction (so fewer times called functions
of the same size gets priority). */
else if (flag_guess_branch_prob)
}
/* 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. If any new indirect edges
+ are discovered in the process, add them to *NEW_EDGES, unless NEW_EDGES
+ is NULL. */
static bool
-cgraph_decide_recursive_inlining (struct cgraph_node *node)
+cgraph_decide_recursive_inlining (struct cgraph_node *node,
+ VEC (cgraph_edge_p, heap) **new_edges)
{
int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO);
int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO);
int depth = 0;
int n = 0;
- if (optimize_size
+ if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION (node->decl))
|| (!flag_inline_functions && !DECL_DECLARED_INLINE_P (node->decl)))
return false;
fprintf (dump_file, "\n");
}
cgraph_redirect_edge_callee (curr, master_clone);
- cgraph_mark_inline_edge (curr, false);
+ cgraph_mark_inline_edge (curr, false, new_edges);
lookup_recursive_calls (node, curr->callee, heap);
n++;
}
* (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100);
}
+/* Compute badness of all edges in NEW_EDGES and add them to the HEAP. */
+static void
+add_new_edges_to_heap (fibheap_t heap, VEC (cgraph_edge_p, heap) *new_edges)
+{
+ while (VEC_length (cgraph_edge_p, new_edges) > 0)
+ {
+ struct cgraph_edge *edge = VEC_pop (cgraph_edge_p, new_edges);
+
+ gcc_assert (!edge->aux);
+ edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge), edge);
+ }
+}
+
+
/* We use greedy algorithm for inlining of small functions:
All inline candidates are put into prioritized heap based on estimated
growth of the overall number of instructions and then update the estimates.
fibheap_t heap = fibheap_new ();
bitmap updated_nodes = BITMAP_ALLOC (NULL);
int min_insns, max_insns;
+ VEC (cgraph_edge_p, heap) *new_indirect_edges = NULL;
+
+ if (flag_indirect_inlining)
+ new_indirect_edges = VEC_alloc (cgraph_edge_p, heap, 8);
if (dump_file)
fprintf (dump_file, "\nDeciding on smaller functions:\n");
is not good idea so prohibit the recursive inlining.
??? When the frequencies are taken into account we might not need this
- restriction. */
- if (!max_count)
+ restriction.
+
+ We need to be cureful here, in some testcases, e.g. directivec.c in
+ libcpp, we can estimate self recursive function to have negative growth
+ for inlining completely.
+ */
+ if (!edge->count)
{
where = edge->caller;
while (where->global.inlined_to)
if (!flag_inline_functions
&& !DECL_DECLARED_INLINE_P (edge->callee->decl))
not_good = N_("function not declared inline and code size would grow");
- if (optimize_size)
+ if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl)))
not_good = N_("optimizing for size and code size would grow");
if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0)
{
}
continue;
}
+ if (!tree_can_inline_p (edge->caller->decl, edge->callee->decl))
+ {
+ gimple_call_set_cannot_inline (edge->call_stmt, true);
+ edge->inline_failed = N_("target specific option mismatch");
+ 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))
+ if (!cgraph_decide_recursive_inlining (where,
+ flag_indirect_inlining
+ ? &new_indirect_edges : NULL))
continue;
+ if (flag_indirect_inlining)
+ add_new_edges_to_heap (heap, new_indirect_edges);
update_callee_keys (heap, where, updated_nodes);
}
else
{
struct cgraph_node *callee;
- if (CALL_STMT_CANNOT_INLINE_P (edge->call_stmt)
+ if (gimple_call_cannot_inline_p (edge->call_stmt)
|| !cgraph_check_inline_limits (edge->caller, edge->callee,
&edge->inline_failed, true))
{
continue;
}
callee = edge->callee;
- cgraph_mark_inline_edge (edge, true);
+ cgraph_mark_inline_edge (edge, true, &new_indirect_edges);
+ if (flag_indirect_inlining)
+ add_new_edges_to_heap (heap, new_indirect_edges);
+
update_callee_keys (heap, callee, updated_nodes);
}
where = edge->caller;
&edge->inline_failed))
edge->inline_failed = N_("--param inline-unit-growth limit reached");
}
+
+ if (new_indirect_edges)
+ VEC_free (cgraph_edge_p, heap, new_indirect_edges);
fibheap_delete (heap);
BITMAP_FREE (updated_nodes);
}
int old_insns = 0;
int i;
int initial_insns = 0;
+ bool redo_always_inline = true;
+
+ cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
max_count = 0;
for (node = cgraph_nodes; node; node = node->next)
/* In the first pass mark all always_inline edges. Do this with a priority
so none of our later choices will make this impossible. */
- for (i = nnodes - 1; i >= 0; i--)
+ while (redo_always_inline)
{
- struct cgraph_edge *e, *next;
+ redo_always_inline = false;
+ for (i = nnodes - 1; i >= 0; i--)
+ {
+ struct cgraph_edge *e, *next;
- node = order[i];
+ node = order[i];
- /* Handle nodes to be flattened, but don't update overall unit size. */
- if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
- {
- if (dump_file)
- fprintf (dump_file,
- "Flattening %s\n", cgraph_node_name (node));
- cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
- }
+ /* Handle nodes to be flattened, but don't update overall unit
+ size. */
+ if (lookup_attribute ("flatten",
+ DECL_ATTRIBUTES (node->decl)) != NULL)
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Flattening %s\n", cgraph_node_name (node));
+ cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
+ }
- 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 || CALL_STMT_CANNOT_INLINE_P (e->call_stmt))
- continue;
- if (cgraph_recursive_inlining_p (e->caller, e->callee,
- &e->inline_failed))
+ if (!node->local.disregard_inline_limits)
continue;
- cgraph_mark_inline_edge (e, true);
+ 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
+ || gimple_call_cannot_inline_p (e->call_stmt))
+ continue;
+ if (cgraph_recursive_inlining_p (e->caller, e->callee,
+ &e->inline_failed))
+ continue;
+ if (!tree_can_inline_p (e->caller->decl, e->callee->decl))
+ {
+ gimple_call_set_cannot_inline (e->call_stmt, true);
+ continue;
+ }
+ if (cgraph_mark_inline_edge (e, true, NULL))
+ redo_always_inline = 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);
+ }
+ /* Inlining self recursive function might introduce new calls to
+ themselves 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 into %s which now has %i insns.\n",
- cgraph_node_name (e->caller),
- e->caller->global.insns);
+ " Inlined for a net change of %+i insns.\n",
+ overall_insns - old_insns);
}
- /* Inlining self recursive function might introduce new calls to
- themselves 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",
- overall_insns - old_insns);
}
- if (!flag_really_no_inline)
- cgraph_decide_inlining_of_small_functions ();
+ cgraph_decide_inlining_of_small_functions ();
- if (!flag_really_no_inline
- && flag_inline_functions_called_once)
+ if (flag_inline_functions_called_once)
{
if (dump_file)
fprintf (dump_file, "\nDeciding on functions called once:\n");
/* And finally decide what functions are called once. */
-
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
- && !CALL_STMT_CANNOT_INLINE_P (node->callers->call_stmt)
- && !DECL_EXTERNAL (node->decl) && !DECL_COMDAT (node->decl))
+ if (node->callers
+ && !node->callers->next_caller
+ && !node->needed
+ && node->local.inlinable
+ && node->callers->inline_failed
+ && !gimple_call_cannot_inline_p (node->callers->call_stmt)
+ && !DECL_EXTERNAL (node->decl)
+ && !DECL_COMDAT (node->decl))
{
if (dump_file)
{
}
}
+ /* Free ipa-prop structures if they are no longer needed. */
+ if (flag_indirect_inlining)
+ free_all_ipa_structures_after_iinln ();
+
if (dump_file)
fprintf (dump_file,
"\nInlined %i calls, eliminated %i functions, "
if (e->inline_failed)
cgraph_mark_inline (e);
- /* In order to fully inline always_inline functions at -O0, we need to
+ /* In order to fully inline always_inline functions, 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)
+ if (mode == INLINE_ALL || always_inline)
cgraph_decide_inlining_incrementally (e->callee, mode, depth + 1);
callee->aux = (void *)(size_t) callee_mode;
return true;
if (!e->callee->local.disregard_inline_limits
&& (mode != INLINE_ALL || !e->callee->local.inlinable))
continue;
- if (CALL_STMT_CANNOT_INLINE_P (e->call_stmt))
+ if (gimple_call_cannot_inline_p (e->call_stmt))
continue;
/* When the edge is already inlined, we just need to recurse into
it in order to fully flatten the leaves. */
}
continue;
}
+ if (!tree_can_inline_p (node->decl, e->callee->decl))
+ {
+ gimple_call_set_cannot_inline (e->call_stmt, true);
+ if (dump_file)
+ {
+ indent_to (dump_file, depth);
+ fprintf (dump_file,
+ "Not inlining: Target specific option mismatch.\n");
+ }
+ continue;
+ }
if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
!= gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
{
}
continue;
}
- if (!DECL_SAVED_TREE (e->callee->decl) && !e->callee->inline_decl)
+ if (!e->callee->analyzed && !e->callee->inline_decl)
{
if (dump_file)
{
}
/* Now do the automatic inlining. */
- if (!flag_really_no_inline && mode != INLINE_ALL
- && mode != INLINE_ALWAYS_INLINE)
+ if (mode != INLINE_ALL && mode != INLINE_ALWAYS_INLINE)
for (e = node->callees; e; e = e->next_callee)
{
if (!e->callee->local.inlinable
}
if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
false)
- || CALL_STMT_CANNOT_INLINE_P (e->call_stmt))
+ || gimple_call_cannot_inline_p (e->call_stmt))
{
if (dump_file)
{
}
continue;
}
- if (!DECL_SAVED_TREE (e->callee->decl) && !e->callee->inline_decl)
+ if (!e->callee->analyzed && !e->callee->inline_decl)
{
if (dump_file)
{
}
continue;
}
+ if (!tree_can_inline_p (node->decl, e->callee->decl))
+ {
+ gimple_call_set_cannot_inline (e->call_stmt, true);
+ if (dump_file)
+ {
+ indent_to (dump_file, depth);
+ fprintf (dump_file,
+ "Not inlining: Target specific option mismatch.\n");
+ }
+ continue;
+ }
if (cgraph_default_inline_p (e->callee, &failed_reason))
inlined |= try_inline (e, mode, depth);
- else if (!flag_unit_at_a_time)
- e->inline_failed = failed_reason;
}
node->aux = (void *)(size_t) old_mode;
return inlined;
}
-/* When inlining shall be performed. */
-static bool
-cgraph_gate_inlining (void)
-{
- return flag_inline_trees;
-}
-
/* Because inlining might remove no-longer reachable nodes, we need to
keep the array visible to garbage collector to avoid reading collected
out nodes. */
if (sorrycount || errorcount)
return 0;
- if (cgraph_decide_inlining_incrementally (node,
- flag_unit_at_a_time || optimize_size
- ? INLINE_SIZE : INLINE_SPEED, 0))
+ if (cgraph_decide_inlining_incrementally (node, INLINE_SIZE, 0))
{
timevar_push (TV_INTEGRATION);
todo = optimize_inline_calls (current_function_decl);
timevar_pop (TV_INTEGRATION);
}
+ cfun->always_inline_functions_inlined = true;
return todo;
}
static bool
cgraph_gate_early_inlining (void)
{
- return flag_inline_trees && flag_early_inlining;
+ return flag_early_inlining;
}
struct gimple_opt_pass pass_early_inline =
static bool
cgraph_gate_ipa_early_inlining (void)
{
- return (flag_inline_trees && flag_early_inlining
+ return (flag_early_inlining
&& (flag_branch_probabilities || flag_test_coverage
|| profile_arc_flag));
}
unsigned int
compute_inline_parameters (struct cgraph_node *node)
{
+ HOST_WIDE_INT self_stack_size;
+
gcc_assert (!node->global.inlined_to);
- inline_summary (node)->estimated_self_stack_size
- = estimated_stack_frame_size ();
- node->global.estimated_stack_size
- = inline_summary (node)->estimated_self_stack_size;
+
+ /* Estimate the stack size for the function. But not at -O0
+ because estimated_stack_frame_size is a quadratic problem. */
+ self_stack_size = optimize ? estimated_stack_frame_size () : 0;
+ inline_summary (node)->estimated_self_stack_size = self_stack_size;
+ node->global.estimated_stack_size = self_stack_size;
node->global.stack_frame_offset = 0;
+
+ /* Can this function be inlined at all? */
node->local.inlinable = tree_inlinable_function_p (current_function_decl);
- inline_summary (node)->self_insns = estimate_num_insns (current_function_decl,
- &eni_inlining_weights);
+
+ /* Estimate the number of instructions for this function.
+ ??? At -O0 we don't use this information except for the dumps, and
+ even then only for always_inline functions. But disabling this
+ causes ICEs in the inline heuristics... */
+ inline_summary (node)->self_insns
+ = estimate_num_insns_fn (current_function_decl, &eni_inlining_weights);
if (node->local.inlinable && !node->local.disregard_inline_limits)
node->local.disregard_inline_limits
= DECL_DISREGARD_INLINE_LIMITS (current_function_decl);
- if (flag_really_no_inline && !node->local.disregard_inline_limits)
- node->local.inlinable = 0;
+
/* Inlining characteristics are maintained by the cgraph_mark_inline. */
node->global.insns = inline_summary (node)->self_insns;
return 0;
return 0;
}
-/* When inlining shall be performed. */
-static bool
-gate_inline_passes (void)
-{
- return flag_inline_trees;
-}
-
struct gimple_opt_pass pass_inline_parameters =
{
{
GIMPLE_PASS,
NULL, /* name */
- gate_inline_passes, /* gate */
+ NULL, /* gate */
compute_inline_parameters_for_current,/* execute */
NULL, /* sub */
NULL, /* next */
}
};
+/* This function performs intraprocedural analyzis in NODE that is required to
+ inline indirect calls. */
+static void
+inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
+{
+ struct cgraph_edge *cs;
+
+ if (!flag_ipa_cp)
+ {
+ ipa_initialize_node_params (node);
+ ipa_detect_param_modifications (node);
+ }
+ ipa_analyze_params_uses (node);
+
+ if (!flag_ipa_cp)
+ for (cs = node->callees; cs; cs = cs->next_callee)
+ {
+ ipa_count_arguments (cs);
+ ipa_compute_jump_functions (cs);
+ }
+
+ if (dump_file)
+ {
+ ipa_print_node_params (dump_file, node);
+ ipa_print_node_jump_functions (dump_file, node);
+ }
+}
+
+/* Note function body size. */
+static void
+analyze_function (struct cgraph_node *node)
+{
+ push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+ current_function_decl = node->decl;
+
+ compute_inline_parameters (node);
+ if (flag_indirect_inlining)
+ inline_indirect_intraprocedural_analysis (node);
+
+ current_function_decl = NULL;
+ pop_cfun ();
+}
+
+/* Called when new function is inserted to callgraph late. */
+static void
+add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
+{
+ analyze_function (node);
+}
+
/* Note function body size. */
static void
inline_generate_summary (void)
{
- struct cgraph_node **order =
- XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
- int nnodes = cgraph_postorder (order);
- int i;
+ struct cgraph_node *node;
+
+ function_insertion_hook_holder =
+ cgraph_add_function_insertion_hook (&add_new_function, NULL);
- for (i = nnodes - 1; i >= 0; i--)
+ if (flag_indirect_inlining)
{
- struct cgraph_node *node = order[i];
-
- /* Allow possibly removed nodes to be garbage collected. */
- order[i] = NULL;
- if (node->analyzed && (node->needed || node->reachable))
- {
- push_cfun (DECL_STRUCT_FUNCTION (node->decl));
- current_function_decl = node->decl;
- compute_inline_parameters (node);
- pop_cfun ();
- }
+ ipa_register_cgraph_hooks ();
+ ipa_check_create_node_params ();
+ ipa_check_create_edge_args ();
}
+
+ for (node = cgraph_nodes; node; node = node->next)
+ if (node->analyzed)
+ analyze_function (node);
- current_function_decl = NULL;
- free (order);
return;
}
/* We might need the body of this function so that we can expand
it inline somewhere else. */
- if (cgraph_preserve_function_body_p (current_function_decl))
+ if (cgraph_preserve_function_body_p (node->decl))
save_inline_function_body (node);
for (e = node->callees; e; e = e->next_callee)
if (!e->inline_failed || warn_inline)
break;
+
if (e)
{
timevar_push (TV_INTEGRATION);
{
IPA_PASS,
"inline", /* name */
- cgraph_gate_inlining, /* gate */
+ NULL, /* gate */
cgraph_decide_inlining, /* execute */
NULL, /* sub */
NULL, /* next */
};
-/* When inlining shall be performed. */
-static bool
-cgraph_gate_O0_always_inline (void)
-{
- return !flag_unit_at_a_time || !flag_inline_trees;
-}
-
-static unsigned int
-cgraph_O0_always_inline (void)
-{
- struct cgraph_node *node = cgraph_node (current_function_decl);
- unsigned int todo = 0;
- bool inlined;
-
- if (sorrycount || errorcount)
- return 0;
- inlined = 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. */
- if (cgraph_preserve_function_body_p (current_function_decl))
- save_inline_function_body (node);
- if (inlined || warn_inline)
- {
- timevar_push (TV_INTEGRATION);
- todo = optimize_inline_calls (current_function_decl);
- timevar_pop (TV_INTEGRATION);
- }
- /* In non-unit-at-a-time we must mark all referenced functions as needed. */
- if (!flag_unit_at_a_time)
- {
- struct cgraph_edge *e;
- for (e = node->callees; e; e = e->next_callee)
- if (e->callee->analyzed)
- cgraph_mark_needed_node (e->callee);
- }
- return todo | execute_fixup_cfg ();
-}
-
-struct gimple_opt_pass pass_O0_always_inline =
-{
- {
- GIMPLE_PASS,
- "always_inline", /* name */
- cgraph_gate_O0_always_inline, /* gate */
- cgraph_O0_always_inline, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_INLINE_HEURISTICS, /* tv_id */
- 0, /* properties_required */
- PROP_cfg, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_dump_func | TODO_verify_flow
- | TODO_verify_stmts /* todo_flags_finish */
- }
-};
-
#include "gt-ipa-inline.h"