This function is called once (source level) compilation unit is finalized
and it will no longer change.
- In the the call-graph construction and local function
- analysis takes place here. Bodies of unreachable functions are released
- to conserve memory usage.
+ In the call-graph construction and local function analysis takes
+ place here. Bodies of unreachable functions are released to
+ conserve memory usage.
The function can be called multiple times when multiple source level
compilation units are combined (such as in C frontend)
#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);
static void cgraph_expand_function (struct cgraph_node *);
static void cgraph_output_pending_asms (void);
-static void cgraph_analyze_function (struct cgraph_node *);
FILE *cgraph_dump_file;
if (flag_keep_inline_functions
&& DECL_DECLARED_INLINE_P (decl)
&& !DECL_EXTERNAL (decl)
- && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl)))
+ && !DECL_DISREGARD_INLINE_LIMITS (decl))
return true;
/* If we decided it was needed before, but at the time we didn't have
to change the behavior here. */
if (((TREE_PUBLIC (decl)
|| (!optimize
- && !node->local.disregard_inline_limits
+ && !DECL_DISREGARD_INLINE_LIMITS (decl)
&& !DECL_DECLARED_INLINE_P (decl)
&& !(DECL_CONTEXT (decl)
&& TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
cgraph_finalize_function (fndecl, false);
cgraph_mark_reachable_node (node);
output = true;
+ cgraph_call_function_insertion_hooks (node);
break;
case CGRAPH_STATE_IPA:
cgraph_analyze_function (node);
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
current_function_decl = fndecl;
- compute_inline_parameters (node);
if ((cgraph_state == CGRAPH_STATE_IPA_SSA
&& !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
/* When not optimizing, be sure we run early local passes anyway
to expand OMP. */
|| !optimize)
execute_pass_list (pass_early_local_passes.pass.sub);
+ else
+ 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;
void
cgraph_finalize_function (tree decl, bool nested)
{
- struct cgraph_node *node = cgraph_node (decl);
+ struct cgraph_node *node = cgraph_get_create_node (decl);
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;
- node->finalized_by_frontend = true;
if (cgraph_decide_is_function_needed (node, decl))
cgraph_mark_needed_node (node);
|| DECL_STATIC_CONSTRUCTOR (decl)
|| DECL_STATIC_DESTRUCTOR (decl)
/* COMDAT virtual functions may be referenced by vtable from
- other compilatoin unit. Still we want to devirtualize calls
+ other compilation unit. Still we want to devirtualize calls
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. */
void
cgraph_mark_if_needed (tree decl)
{
- struct cgraph_node *node = cgraph_node (decl);
+ struct cgraph_node *node = cgraph_get_node (decl);
if (node->local.finalized && cgraph_decide_is_function_needed (node, decl))
cgraph_mark_needed_node (node);
}
!= compute_call_stmt_bb_frequency (e->caller->decl,
gimple_bb (e->call_stmt))))
{
- error ("caller edge frequency %i does not match BB freqency %i",
+ error ("caller edge frequency %i does not match BB frequency %i",
e->frequency,
compute_call_stmt_bb_frequency (e->caller->decl,
gimple_bb (e->call_stmt)));
return error_found;
}
+/* Switch to THIS_CFUN if needed and print STMT to stderr. */
+static void
+cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt)
+{
+ /* debug_gimple_stmt needs correct cfun */
+ if (cfun != this_cfun)
+ set_cfun (this_cfun);
+ debug_gimple_stmt (stmt);
+}
+
/* Verify cgraph nodes of given cgraph node. */
DEBUG_FUNCTION void
verify_cgraph_node (struct cgraph_node *node)
{
struct cgraph_edge *e;
struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
- struct function *saved_cfun = cfun;
basic_block this_block;
gimple_stmt_iterator gsi;
bool error_found = false;
return;
timevar_push (TV_CGRAPH_VERIFY);
- /* debug_generic_stmt needs correct cfun */
- set_cfun (this_cfun);
for (e = node->callees; e; e = e->next_callee)
if (e->aux)
{
error ("An indirect edge from %s is not marked as indirect or has "
"associated indirect_info, the corresponding statement is: ",
identifier_to_locale (cgraph_node_name (e->caller)));
- debug_gimple_stmt (e->call_stmt);
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
error_found = true;
}
}
error_found = true;
}
- if (!cgraph_node (node->decl))
+ if (!cgraph_get_node (node->decl))
{
error ("node not found in cgraph_hash");
error_found = true;
if (e->aux)
{
error ("shared call_stmt:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
error_found = true;
}
if (!e->indirect_unknown_callee)
&& cgraph_get_node (decl)
&& (e->callee->former_clone_of
!= cgraph_get_node (decl)->decl)
- && !clone_of_p (cgraph_node (decl),
+ && !clone_of_p (cgraph_get_node (decl),
e->callee))
{
error ("edge points to wrong declaration:");
{
error ("a call to thunk improperly represented "
"in the call graph:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
+ error_found = true;
}
}
else if (decl)
"corresponding to a call_stmt with "
"a known declaration:");
error_found = true;
- debug_gimple_stmt (e->call_stmt);
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
}
e->aux = (void *)1;
}
else if (decl)
{
error ("missing callgraph edge for call stmt:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
error_found = true;
}
}
error ("edge %s->%s has no corresponding call_stmt",
identifier_to_locale (cgraph_node_name (e->caller)),
identifier_to_locale (cgraph_node_name (e->callee)));
- debug_gimple_stmt (e->call_stmt);
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
error_found = true;
}
e->aux = 0;
{
error ("an indirect edge from %s has no corresponding call_stmt",
identifier_to_locale (cgraph_node_name (e->caller)));
- debug_gimple_stmt (e->call_stmt);
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
error_found = true;
}
e->aux = 0;
dump_cgraph_node (stderr, node);
internal_error ("verify_cgraph_node failed");
}
- set_cfun (saved_cfun);
timevar_pop (TV_CGRAPH_VERIFY);
}
}
/* Analyze the function scheduled to be output. */
-static void
+void
cgraph_analyze_function (struct cgraph_node *node)
{
tree save = current_function_decl;
"%<weakref%> attribute should be accompanied with"
" an %<alias%> attribute");
DECL_WEAK (decl) = 0;
- remove_attribute ("weakref", DECL_ATTRIBUTES (decl));
+ DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+ DECL_ATTRIBUTES (decl));
}
}
else if (node->local.finalized)
cgraph_mark_needed_node (node);
}
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
+ && node->local.finalized)
+ {
+ warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
+ "%<weakref%> attribute ignored"
+ " because function is defined");
+ DECL_WEAK (decl) = 0;
+ DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+ DECL_ATTRIBUTES (decl));
+ }
process_common_attributes (decl);
}
for (vnode = varpool_nodes; vnode != first_var; vnode = vnode->next)
else if (vnode->finalized)
varpool_mark_needed_node (vnode);
}
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
+ && vnode->finalized
+ && DECL_INITIAL (decl))
+ {
+ warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
+ "%<weakref%> attribute ignored"
+ " because variable is initialized");
+ DECL_WEAK (decl) = 0;
+ DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+ DECL_ATTRIBUTES (decl));
+ }
process_common_attributes (decl);
}
}
/* If decl is a clone of an abstract function, mark that abstract
function so that we don't release its body. The DECL_INITIAL() of that
- abstract function declaration will be later needed to output debug info. */
+ abstract function declaration will be later needed to output debug
+ info. */
if (DECL_ABSTRACT_ORIGIN (decl))
{
- struct cgraph_node *origin_node = cgraph_node (DECL_ABSTRACT_ORIGIN (decl));
+ struct cgraph_node *origin_node;
+ origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
origin_node->abstract_and_needed = true;
}
{
timevar_push (TV_CGRAPH);
+ /* If we're here there's no current function anymore. Some frontends
+ are lazy in clearing these. */
+ current_function_decl = NULL;
+ set_cfun (NULL);
+
/* Do not skip analyzing the functions if there were errors, we
miss diagnostics for following functions otherwise. */
alias && alias->next; alias = alias->next)
;
/* Walk aliases in the order they were created; it is possible that
- thunks reffers to the aliases made earlier. */
+ thunks refers to the aliases made earlier. */
for (; alias; alias = next)
{
next = alias->previous;
/* 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
varpool_empty_needed_queue ();
for (i = 0; i < max; ++i)
+ if (nodes[i].kind == ORDER_VAR)
+ varpool_finalize_named_section_flags (nodes[i].u.v);
+
+ for (i = 0; i < max; ++i)
{
switch (nodes[i].kind)
{
/* 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_node (decl);
if (node->clones)
return true;
return false;
gcc_assert (old_version);
- new_version = cgraph_node (new_decl);
+ new_version = cgraph_create_node (new_decl);
new_version->analyzed = true;
new_version->local = old_version->local;
new_version->local.externally_visible = false;
new_version->local.local = true;
- new_version->local.vtable_method = false;
new_version->global = old_version->global;
new_version->rtl = old_version->rtl;
new_version->reachable = true;
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
if (!tree_versionable_function_p (old_decl))
return NULL;
+ gcc_assert (old_version_node->local.can_change_signature || !args_to_skip);
+
/* Make a new FUNCTION_DECL tree node for the
new version. */
if (!args_to_skip)
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_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_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)
{
if (cgraph_dump_file)
{
- fprintf (cgraph_dump_file, "clonning %s to %s\n",
+ fprintf (cgraph_dump_file, "cloning %s to %s\n",
cgraph_node_name (node->clone_of),
cgraph_node_name (node));
if (node->clone.tree_map)