#include "output.h"
#include "coverage.h"
#include "plugin.h"
+#include "ipa-inline.h"
+#include "ipa-utils.h"
static void cgraph_expand_all_functions (void);
static void cgraph_mark_functions_to_output (void);
cgraph_finalize_function (fndecl, false);
cgraph_mark_reachable_node (node);
output = true;
+ cgraph_call_function_insertion_hooks (node);
break;
case CGRAPH_STATE_IPA:
|| !optimize)
execute_pass_list (pass_early_local_passes.pass.sub);
else
- compute_inline_parameters (node);
+ compute_inline_parameters (node, true);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
current_function_decl = NULL;
+ cgraph_call_function_insertion_hooks (node);
break;
case CGRAPH_STATE_EXPANSION:
/* Functions created during expansion shall be compiled
directly. */
node->process = 0;
+ cgraph_call_function_insertion_hooks (node);
cgraph_expand_function (node);
break;
gcc_unreachable ();
break;
}
- cgraph_call_function_insertion_hooks (node);
varpool_analyze_pending_decls ();
}
return output;
if (node->local.finalized)
cgraph_reset_node (node);
- node->pid = cgraph_max_pid ++;
notice_global_symbol (decl);
node->local.finalized = true;
node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
to those so we need to analyze them.
FIXME: We should introduce may edges for this purpose and update
their handling in unreachable function removal and inliner too. */
- || (DECL_VIRTUAL_P (decl) && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
+ || (DECL_VIRTUAL_P (decl)
+ && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
cgraph_mark_reachable_node (node);
/* If we've not yet emitted decl, tell the debug info about it. */
}
if (gimple_has_body_p (e->caller->decl)
&& !e->caller->global.inlined_to
+ /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
+ Remove this once edges are actualy removed from the function at that time. */
+ && (e->frequency
+ || (inline_edge_summary_vec
+ && !inline_edge_summary (e)->predicate))
&& (e->frequency
!= compute_call_stmt_bb_frequency (e->caller->decl,
gimple_bb (e->call_stmt))))
while (n != node);
}
- if (node->analyzed && gimple_has_body_p (node->decl)
- && !TREE_ASM_WRITTEN (node->decl)
- && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
- && !flag_wpa)
+ if (node->analyzed && node->thunk.thunk_p)
+ {
+ if (!node->callees)
+ {
+ error ("No edge out of thunk node");
+ error_found = true;
+ }
+ else if (node->callees->next_callee)
+ {
+ error ("More than one edge out of thunk node");
+ error_found = true;
+ }
+ if (gimple_has_body_p (node->decl))
+ {
+ error ("Thunk is not supposed to have body");
+ error_found = true;
+ }
+ }
+ else if (node->analyzed && gimple_has_body_p (node->decl)
+ && !TREE_ASM_WRITTEN (node->decl)
+ && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
+ && !flag_wpa)
{
if (this_cfun->cfg)
{
}
if (!e->indirect_unknown_callee)
{
- struct cgraph_node *n;
-
if (e->callee->same_body_alias)
{
error ("edge points to same body alias:");
debug_tree (decl);
error_found = true;
}
- else if (decl
- && (n = cgraph_get_node_or_alias (decl))
- && (n->same_body_alias
- && n->thunk.thunk_p))
- {
- error ("a call to thunk improperly represented "
- "in the call graph:");
- cgraph_debug_gimple_stmt (this_cfun, stmt);
- error_found = true;
- }
}
else if (decl)
{
tree save = current_function_decl;
tree decl = node->decl;
- current_function_decl = decl;
- push_cfun (DECL_STRUCT_FUNCTION (decl));
+ if (node->thunk.thunk_p)
+ {
+ cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
+ NULL, 0, CGRAPH_FREQ_BASE);
+ }
+ else
+ {
+ current_function_decl = decl;
+ push_cfun (DECL_STRUCT_FUNCTION (decl));
- assign_assembler_name_if_neeeded (node->decl);
+ assign_assembler_name_if_neeeded (node->decl);
- /* Make sure to gimplify bodies only once. During analyzing a
- function we lower it, which will require gimplified nested
- functions, so we can end up here with an already gimplified
- body. */
- if (!gimple_body (decl))
- gimplify_function_tree (decl);
- dump_function (TDI_generic, decl);
+ /* Make sure to gimplify bodies only once. During analyzing a
+ function we lower it, which will require gimplified nested
+ functions, so we can end up here with an already gimplified
+ body. */
+ if (!gimple_body (decl))
+ gimplify_function_tree (decl);
+ dump_function (TDI_generic, decl);
- cgraph_lower_function (node);
+ cgraph_lower_function (node);
+ pop_cfun ();
+ }
node->analyzed = true;
- pop_cfun ();
current_function_decl = save;
}
/* ??? It is possible to create extern inline function and later using
weak alias attribute to kill its body. See
gcc.c-torture/compile/20011119-1.c */
- if (!DECL_STRUCT_FUNCTION (decl))
+ if (!DECL_STRUCT_FUNCTION (decl)
+ && !node->thunk.thunk_p)
{
cgraph_reset_node (node);
continue;
for (edge = node->callees; edge; edge = edge->next_callee)
if (!edge->callee->reachable)
cgraph_mark_reachable_node (edge->callee);
+ for (edge = node->callers; edge; edge = edge->next_caller)
+ if (!edge->caller->reachable && edge->caller->thunk.thunk_p)
+ cgraph_mark_reachable_node (edge->caller);
if (node->same_comdat_group)
{
tree decl = node->decl;
next = node->next;
- if (node->local.finalized && !gimple_has_body_p (decl))
+ if (node->local.finalized && !gimple_has_body_p (decl)
+ && !node->thunk.thunk_p)
cgraph_reset_node (node);
- if (!node->reachable && gimple_has_body_p (decl))
+ if (!node->reachable
+ && (gimple_has_body_p (decl) || node->thunk.thunk_p))
{
if (cgraph_dump_file)
fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
}
else
node->next_needed = NULL;
- gcc_assert (!node->local.finalized || gimple_has_body_p (decl));
+ gcc_assert (!node->local.finalized || node->thunk.thunk_p
+ || gimple_has_body_p (decl));
gcc_assert (node->analyzed == node->local.finalized);
}
if (cgraph_dump_file)
always inlined, as well as those that are reachable from
outside the current compilation unit. */
if (node->analyzed
+ && !node->thunk.thunk_p
&& !node->global.inlined_to
&& (!cgraph_only_called_directly_p (node)
|| (e && node->reachable))
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
- next->process = 1;
+ if (!next->thunk.thunk_p)
+ next->process = 1;
}
}
else if (node->same_comdat_group)
free_after_compilation (cfun);
set_cfun (NULL);
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+ node->thunk.thunk_p = false;
+ node->analyzed = false;
}
else
{
delete_unreachable_blocks ();
update_ssa (TODO_update_ssa);
- cgraph_remove_same_body_alias (node);
/* Since we want to emit the thunk, we explicitly mark its name as
referenced. */
+ node->thunk.thunk_p = false;
+ cgraph_node_remove_callees (node);
cgraph_add_new_function (thunk_fndecl, true);
bitmap_obstack_release (NULL);
}
current_function_decl = NULL;
}
+
+/* Assemble thunks asociated to NODE. */
+
+static void
+assemble_thunks (struct cgraph_node *node)
+{
+ struct cgraph_edge *e;
+ for (e = node->callers; e;)
+ if (e->caller->thunk.thunk_p)
+ {
+ struct cgraph_node *thunk = e->caller;
+
+ e = e->next_caller;
+ assemble_thunks (thunk);
+ assemble_thunk (thunk);
+ }
+ else
+ e = e->next_caller;
+}
+
/* Expand function specified by NODE. */
static void
if (!alias->thunk.thunk_p)
assemble_alias (alias->decl,
DECL_ASSEMBLER_NAME (alias->thunk.alias));
- else
- assemble_thunk (alias);
}
node->alias = saved_alias;
cgraph_process_new_functions ();
}
+ assemble_thunks (node);
gcc_assert (node->lowered);
/* Generate RTL for the body of DECL. */
/* Make sure that BE didn't give up on compiling. */
gcc_assert (TREE_ASM_WRITTEN (decl));
current_function_decl = NULL;
- gcc_assert (!cgraph_preserve_function_body_p (decl));
+ gcc_assert (!cgraph_preserve_function_body_p (node));
cgraph_release_function_body (node);
/* Eliminate all call edges. This is important so the GIMPLE_CALL no longer
points to the dead function body. */
int order_pos, new_order_pos = 0;
int i;
- order_pos = cgraph_postorder (order);
+ order_pos = ipa_reverse_postorder (order);
gcc_assert (order_pos == cgraph_n_nodes);
/* Garbage collector may remove inline clones we eliminate during
for (pf = cgraph_nodes; pf; pf = pf->next)
{
- if (pf->process)
+ if (pf->process && !pf->thunk.thunk_p)
{
i = pf->order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
/* Return true when function body of DECL still needs to be kept around
for later re-use. */
bool
-cgraph_preserve_function_body_p (tree decl)
+cgraph_preserve_function_body_p (struct cgraph_node *node)
{
- struct cgraph_node *node;
-
gcc_assert (cgraph_global_info_ready);
+ gcc_assert (!node->same_body_alias);
+
/* Look if there is any clone around. */
- node = cgraph_get_node (decl);
if (node->clones)
return true;
return false;
cgraph_clone_edge (e, new_version, e->call_stmt,
e->lto_stmt_uid, REG_BR_PROB_BASE,
CGRAPH_FREQ_BASE,
- e->loop_nest, true);
+ true);
for (e = old_version->indirect_calls; e; e=e->next_callee)
if (!bbs_to_copy
|| bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
cgraph_clone_edge (e, new_version, e->call_stmt,
e->lto_stmt_uid, REG_BR_PROB_BASE,
CGRAPH_FREQ_BASE,
- e->loop_nest, true);
+ true);
FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
{
/* Redirect calls to the old version node to point to its new
return new_version_node;
}
-/* Produce separate function body for inline clones so the offline copy can be
- modified without affecting them. */
-struct cgraph_node *
-save_inline_function_body (struct cgraph_node *node)
-{
- struct cgraph_node *first_clone, *n;
-
- gcc_assert (node == cgraph_get_node (node->decl));
-
- cgraph_lower_function (node);
-
- first_clone = node->clones;
-
- first_clone->decl = copy_node (node->decl);
- cgraph_insert_node_to_hashtable (first_clone);
- gcc_assert (first_clone == cgraph_get_node (first_clone->decl));
- if (first_clone->next_sibling_clone)
- {
- for (n = first_clone->next_sibling_clone; n->next_sibling_clone; n = n->next_sibling_clone)
- n->clone_of = first_clone;
- n->clone_of = first_clone;
- n->next_sibling_clone = first_clone->clones;
- if (first_clone->clones)
- first_clone->clones->prev_sibling_clone = n;
- first_clone->clones = first_clone->next_sibling_clone;
- first_clone->next_sibling_clone->prev_sibling_clone = NULL;
- first_clone->next_sibling_clone = NULL;
- gcc_assert (!first_clone->prev_sibling_clone);
- }
- first_clone->clone_of = NULL;
- node->clones = NULL;
-
- if (first_clone->clones)
- for (n = first_clone->clones; n != first_clone;)
- {
- gcc_assert (n->decl == node->decl);
- n->decl = first_clone->decl;
- if (n->clones)
- n = n->clones;
- else if (n->next_sibling_clone)
- n = n->next_sibling_clone;
- else
- {
- while (n != first_clone && !n->next_sibling_clone)
- n = n->clone_of;
- if (n != first_clone)
- n = n->next_sibling_clone;
- }
- }
-
- /* Copy the OLD_VERSION_NODE function tree to the new version. */
- tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL,
- NULL, NULL);
-
- DECL_EXTERNAL (first_clone->decl) = 0;
- DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
- TREE_PUBLIC (first_clone->decl) = 0;
- DECL_COMDAT (first_clone->decl) = 0;
- VEC_free (ipa_opt_pass, heap,
- first_clone->ipa_transforms_to_apply);
- first_clone->ipa_transforms_to_apply = NULL;
-
-#ifdef ENABLE_CHECKING
- verify_cgraph_node (first_clone);
-#endif
- return first_clone;
-}
-
/* Given virtual clone, turn it into actual clone. */
static void
cgraph_materialize_clone (struct cgraph_node *node)