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"
+#include "lto-streamer.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;
the name later after finalizing the function and the fact is noticed
in assemble_name then. This is arguably a bug. */
if (DECL_ASSEMBLER_NAME_SET_P (decl)
+ && (!node->thunk.thunk_p && !node->same_body_alias)
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
return true;
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;
memset (&node->global, 0, sizeof (node->global));
memset (&node->rtl, 0, sizeof (node->rtl));
node->analyzed = false;
- node->local.redefined_extern_inline = true;
node->local.finalized = false;
cgraph_node_remove_callees (node);
-
- /* We may need to re-queue the node for assembling in case
- we already proceeded it and ignored as not needed or got
- a re-declaration in IMA mode. */
- if (node->reachable)
- {
- struct cgraph_node *n;
-
- for (n = cgraph_nodes_queue; n; n = n->next_needed)
- if (n == node)
- break;
- if (!n)
- node->reachable = 0;
- }
}
static void
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);
+ {
+ cgraph_reset_node (node);
+ node->local.redefined_extern_inline = true;
+ }
- 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. */
- || (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. */
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);
}
static bool
clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
{
+ node = cgraph_function_or_thunk_node (node, NULL);
+ node2 = cgraph_function_or_thunk_node (node2, NULL);
while (node != node2 && node2)
node2 = node2->clone_of;
return node2 != NULL;
}
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
+ && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec)
+ <= (unsigned) e->uid)
+ || !inline_edge_summary (e)->predicate)))
&& (e->frequency
!= 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 that call graph edge E corresponds to DECL from the associated
+ statement. Return true if the verification should fail. */
+
+static bool
+verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
+{
+ struct cgraph_node *node;
+
+ if (!decl || e->callee->global.inlined_to)
+ return false;
+ node = cgraph_get_node (decl);
+
+ /* We do not know if a node from a different partition is an alias or what it
+ aliases and therefore cannot do the former_clone_of check reliably. */
+ if (!node || node->in_other_partition)
+ return false;
+ node = cgraph_function_or_thunk_node (node, NULL);
+
+ if ((e->callee->former_clone_of != node->decl)
+ /* IPA-CP sometimes redirect edge to clone and then back to the former
+ function. This ping-pong has to go, eventaully. */
+ && (node != cgraph_function_or_thunk_node (e->callee, NULL))
+ && !clone_of_p (node, e->callee))
+ return true;
+ else
+ return false;
+}
+
/* 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;
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->alias)
+ {
+ bool ref_found = false;
+ int i;
+ struct ipa_ref *ref;
+
+ if (node->callees)
+ {
+ error ("Alias has call edges");
+ error_found = true;
+ }
+ for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
+ if (ref->use != IPA_REF_ALIAS)
+ {
+ error ("Alias has non-alias refernece");
+ error_found = true;
+ }
+ else if (ref_found)
+ {
+ error ("Alias has more than one alias reference");
+ error_found = true;
+ }
+ else
+ ref_found = true;
+ if (!ref_found)
+ {
+ error ("Analyzed alias has no reference");
+ error_found = true;
+ }
+ }
+ 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->aux)
{
error ("shared call_stmt:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
error_found = true;
}
if (!e->indirect_unknown_callee)
{
- struct cgraph_node *n;
-
- if (e->callee->same_body_alias)
- {
- error ("edge points to same body alias:");
- debug_tree (e->callee->decl);
- error_found = true;
- }
- else if (!e->callee->global.inlined_to
- && decl
- && cgraph_get_node (decl)
- && (e->callee->former_clone_of
- != cgraph_get_node (decl)->decl)
- && !clone_of_p (cgraph_node (decl),
- e->callee))
+ if (verify_edge_corresponds_to_fndecl (e, decl))
{
error ("edge points to wrong declaration:");
debug_tree (e->callee->decl);
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:");
- debug_gimple_stmt (stmt);
- }
}
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;
tree decl = node->decl;
- current_function_decl = decl;
- push_cfun (DECL_STRUCT_FUNCTION (decl));
+ if (node->alias && node->thunk.alias)
+ {
+ struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
+ if (!VEC_length (ipa_ref_t, node->ref_list.references))
+ ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
+ if (node->same_body_alias)
+ {
+ DECL_VIRTUAL_P (node->decl) = DECL_VIRTUAL_P (node->thunk.alias);
+ DECL_DECLARED_INLINE_P (node->decl)
+ = DECL_DECLARED_INLINE_P (node->thunk.alias);
+ DECL_DISREGARD_INLINE_LIMITS (node->decl)
+ = DECL_DISREGARD_INLINE_LIMITS (node->thunk.alias);
+ }
+
+ /* Fixup visibility nonsences C++ frontend produce on same body aliases. */
+ if (TREE_PUBLIC (node->decl) && node->same_body_alias)
+ {
+ DECL_EXTERNAL (node->decl) = DECL_EXTERNAL (node->thunk.alias);
+ if (DECL_ONE_ONLY (node->thunk.alias))
+ {
+ DECL_COMDAT (node->decl) = DECL_COMDAT (node->thunk.alias);
+ DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (node->thunk.alias);
+ if (DECL_ONE_ONLY (node->thunk.alias) && !node->same_comdat_group)
+ {
+ struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
+ node->same_comdat_group = tgt;
+ if (!tgt->same_comdat_group)
+ tgt->same_comdat_group = node;
+ else
+ {
+ struct cgraph_node *n;
+ for (n = tgt->same_comdat_group;
+ n->same_comdat_group != tgt;
+ n = n->same_comdat_group)
+ ;
+ n->same_comdat_group = node;
+ }
+ }
+ }
+ }
+ cgraph_mark_reachable_node (cgraph_alias_aliased_node (node));
+ if (node->address_taken)
+ cgraph_mark_address_taken_node (cgraph_alias_aliased_node (node));
+ if (cgraph_decide_is_function_needed (node, node->decl))
+ cgraph_mark_needed_node (node);
+ }
+ else 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;
}
+/* C++ frontend produce same body aliases all over the place, even before PCH
+ gets streamed out. It relies on us linking the aliases with their function
+ in order to do the fixups, but ipa-ref is not PCH safe. Consequentely we
+ first produce aliases without links, but once C++ FE is sure he won't sream
+ PCH we build the links via this function. */
+
+void
+cgraph_process_same_body_aliases (void)
+{
+ struct cgraph_node *node;
+ for (node = cgraph_nodes; node; node = node->next)
+ if (node->same_body_alias
+ && !VEC_length (ipa_ref_t, node->ref_list.references))
+ {
+ struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
+ ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
+ }
+ same_body_aliases_done = true;
+}
+
/* Process attributes common for vars and functions. */
static void
"%<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 && !node->alias))
+ {
+ 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));
+ }
+
+ if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl))
+ && !DECL_DECLARED_INLINE_P (decl)
+ /* redefining extern inline function makes it DECL_UNINLINABLE. */
+ && !DECL_UNINLINABLE (decl))
+ warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+ "always_inline function might not be inlinable");
+
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);
}
}
/* ??? 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->alias || !node->thunk.alias)
+ && !node->thunk.thunk_p)
{
cgraph_reset_node (node);
+ node->local.redefined_extern_inline = true;
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)
{
/* 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;
}
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->alias || !node->thunk.alias)
+ && !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
+ || (node->alias && node->thunk.alias)))
{
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
+ || node->alias
+ || gimple_has_body_p (decl));
gcc_assert (node->analyzed == node->local.finalized);
}
if (cgraph_dump_file)
ggc_collect ();
}
+/* Translate the ugly representation of aliases as alias pairs into nice
+ representation in callgraph. We don't handle all cases yet,
+ unforutnately. */
+
+static void
+handle_alias_pairs (void)
+{
+ alias_pair *p;
+ unsigned i;
+ struct cgraph_node *target_node;
+ struct cgraph_node *src_node;
+ struct varpool_node *target_vnode;
+
+ for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);)
+ {
+ if (TREE_CODE (p->decl) == FUNCTION_DECL
+ && (target_node = cgraph_node_for_asm (p->target)) != NULL)
+ {
+ src_node = cgraph_get_node (p->decl);
+ if (src_node && src_node->local.finalized)
+ cgraph_reset_node (src_node);
+ /* Normally EXTERNAL flag is used to mark external inlines,
+ however for aliases it seems to be allowed to use it w/o
+ any meaning. See gcc.dg/attr-alias-3.c
+ However for weakref we insist on EXTERNAL flag being set.
+ See gcc.dg/attr-alias-5.c */
+ if (DECL_EXTERNAL (p->decl))
+ DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
+ DECL_ATTRIBUTES (p->decl)) != NULL;
+ cgraph_create_function_alias (p->decl, target_node->decl);
+ VEC_unordered_remove (alias_pair, alias_pairs, i);
+ }
+ else if (TREE_CODE (p->decl) == VAR_DECL
+ && (target_vnode = varpool_node_for_asm (p->target)) != NULL)
+ {
+ /* Normally EXTERNAL flag is used to mark external inlines,
+ however for aliases it seems to be allowed to use it w/o
+ any meaning. See gcc.dg/attr-alias-3.c
+ However for weakref we insist on EXTERNAL flag being set.
+ See gcc.dg/attr-alias-5.c */
+ if (DECL_EXTERNAL (p->decl))
+ DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
+ DECL_ATTRIBUTES (p->decl)) != NULL;
+ varpool_create_variable_alias (p->decl, target_vnode->decl);
+ VEC_unordered_remove (alias_pair, alias_pairs, i);
+ }
+ /* Weakrefs with target not defined in current unit are easy to handle; they
+ behave just as external variables except we need to note the alias flag
+ to later output the weakref pseudo op into asm file. */
+ else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL
+ && (TREE_CODE (p->decl) == FUNCTION_DECL
+ ? (varpool_node_for_asm (p->target) == NULL)
+ : (cgraph_node_for_asm (p->target) == NULL)))
+ {
+ if (TREE_CODE (p->decl) == FUNCTION_DECL)
+ cgraph_get_create_node (p->decl)->alias = true;
+ else
+ varpool_get_node (p->decl)->alias = true;
+ DECL_EXTERNAL (p->decl) = 1;
+ VEC_unordered_remove (alias_pair, alias_pairs, i);
+ }
+ else
+ {
+ if (dump_file)
+ fprintf (dump_file, "Unhandled alias %s->%s\n",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
+ IDENTIFIER_POINTER (p->target));
+
+ i++;
+ }
+ }
+}
+
/* Analyze the whole compilation unit once it is parsed completely. */
{
timevar_push (TV_CGRAPH);
+ /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE. */
+ if (flag_lto)
+ lto_streamer_hooks_init ();
+
+ /* 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. */
/* Mark alias targets necessary and emit diagnostics. */
finish_aliases_1 ();
+ handle_alias_pairs ();
if (!quiet_flag)
{
fflush (stderr);
}
+ if (flag_dump_passes)
+ dump_passes ();
+
/* Gimplify and lower all functions, compute reachability and
remove unreachable nodes. */
cgraph_analyze_functions ();
/* Mark alias targets necessary and emit diagnostics. */
finish_aliases_1 ();
+ handle_alias_pairs ();
/* Gimplify and lower thunks. */
cgraph_analyze_functions ();
always inlined, as well as those that are reachable from
outside the current compilation unit. */
if (node->analyzed
+ && !node->thunk.thunk_p
+ && !node->alias
&& !node->global.inlined_to
&& (!cgraph_only_called_directly_p (node)
- || (e && node->reachable))
+ || ((e || ipa_ref_has_aliases_p (&node->ref_list))
+ && node->reachable))
&& !TREE_ASM_WRITTEN (decl)
&& !DECL_EXTERNAL (decl))
{
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
- next->process = 1;
+ if (!next->thunk.thunk_p && !next->alias)
+ next->process = 1;
}
}
else if (node->same_comdat_group)
are inside partition, we can end up not removing the body since we no longer
have analyzed node pointing to it. */
&& !node->in_other_partition
+ && !node->alias
&& !DECL_EXTERNAL (decl))
{
dump_cgraph_node (stderr, node);
&& !DECL_EXTERNAL (decl))
{
dump_cgraph_node (stderr, node);
- internal_error ("failed to reclaim unneeded function");
+ internal_error ("failed to reclaim unneeded functionin same comdat group");
}
}
#endif
DECL_SAVED_TREE (decl) = error_mark_node;
cfun->curr_properties |=
(PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
- PROP_ssa);
+ PROP_ssa | PROP_gimple_any);
/* Create BB for body of the function and connect it properly. */
bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
if (this_adjusting
&& fixed_offset != 0)
{
- stmt = gimple_build_assign (ptr,
- fold_build2_loc (input_location,
- POINTER_PLUS_EXPR,
- TREE_TYPE (ptr), ptr,
- size_int (fixed_offset)));
+ stmt = gimple_build_assign
+ (ptr, fold_build_pointer_plus_hwi_loc (input_location,
+ ptr,
+ fixed_offset));
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
}
tree vtabletmp;
tree vtabletmp2;
tree vtabletmp3;
- tree offsettmp;
if (!vtable_entry_type)
{
/* Find the entry with the vcall offset. */
stmt = gimple_build_assign (vtabletmp2,
- fold_build2_loc (input_location,
- POINTER_PLUS_EXPR,
- TREE_TYPE (vtabletmp2),
- vtabletmp2,
- fold_convert (sizetype,
- virtual_offset)));
+ fold_build_pointer_plus_loc (input_location,
+ vtabletmp2,
+ virtual_offset));
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
/* Get the offset itself. */
mark_symbols_for_renaming (stmt);
find_referenced_vars_in (stmt);
- /* Cast to sizetype. */
- offsettmp = create_tmp_var (sizetype, "offset");
- stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3));
- gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
- mark_symbols_for_renaming (stmt);
- find_referenced_vars_in (stmt);
-
/* Adjust the `this' pointer. */
- ptr = fold_build2_loc (input_location,
- POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- offsettmp);
+ ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
+ ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
+ GSI_CONTINUE_LINKING);
}
if (!this_adjusting
mark_symbols_for_renaming (stmt);
find_referenced_vars_in (stmt);
}
- ptr = fold_build2_loc (input_location,
- POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp,
- size_int (fixed_offset));
+ ptr = fold_build_pointer_plus_hwi_loc (input_location,
+ ptrtmp, fixed_offset);
}
/* Emit the statement and gimplify the adjustment expression. */
{
const char *fnname;
tree fn_block;
+ tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
DECL_RESULT (thunk_fndecl)
= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
- RESULT_DECL, 0, integer_type_node);
+ RESULT_DECL, 0, restype);
fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
/* The back end expects DECL_INITIAL to contain a BLOCK, so we
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 and aliases asociated to NODE. */
+
+static void
+assemble_thunks_and_aliases (struct cgraph_node *node)
+{
+ struct cgraph_edge *e;
+ int i;
+ struct ipa_ref *ref;
+
+ for (e = node->callers; e;)
+ if (e->caller->thunk.thunk_p)
+ {
+ struct cgraph_node *thunk = e->caller;
+
+ e = e->next_caller;
+ assemble_thunks_and_aliases (thunk);
+ assemble_thunk (thunk);
+ }
+ else
+ e = e->next_caller;
+ for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++)
+ if (ref->use == IPA_REF_ALIAS)
+ {
+ struct cgraph_node *alias = ipa_ref_refering_node (ref);
+ bool saved_written = TREE_ASM_WRITTEN (alias->thunk.alias);
+
+ /* Force assemble_alias to really output the alias this time instead
+ of buffering it in same alias pairs. */
+ TREE_ASM_WRITTEN (alias->thunk.alias) = 1;
+ assemble_alias (alias->decl,
+ DECL_ASSEMBLER_NAME (alias->thunk.alias));
+ assemble_thunks_and_aliases (alias);
+ TREE_ASM_WRITTEN (alias->thunk.alias) = saved_written;
+ }
+}
+
/* Expand function specified by NODE. */
static void
announce_function (decl);
node->process = 0;
- if (node->same_body)
- {
- struct cgraph_node *alias, *next;
- bool saved_alias = node->alias;
- for (alias = node->same_body;
- 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. */
- for (; alias; alias = next)
- {
- next = alias->previous;
- 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 ();
- }
-
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));
+
+ /* It would make a lot more sense to output thunks before function body to get more
+ forward and lest backwarding jumps. This is however would need solving problem
+ with comdats. See PR48668. Also aliases must come after function itself to
+ make one pass assemblers, like one on AIX happy. See PR 50689.
+ FIXME: Perhaps thunks should be move before function IFF they are not in comdat
+ groups. */
+ assemble_thunks_and_aliases (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 && !pf->alias)
{
i = pf->order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
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->alias && !node->thunk.thunk_p);
+
/* Look if there is any clone around. */
- node = cgraph_node (decl);
if (node->clones)
return true;
return false;
return;
}
+ /* We never run removal of unreachable nodes after early passes. This is
+ because TODO is run before the subpasses. It is important to remove
+ the unreachable functions to save works at IPA level and to get LTO
+ symbol tables right. */
+ cgraph_remove_unreachable_nodes (true, cgraph_dump_file);
+
/* If pass_all_early_optimizations was not scheduled, the state of
the cgraph will not be properly updated. Update it now. */
if (cgraph_state < CGRAPH_STATE_IPA_SSA)
if (flag_generate_lto)
targetm.asm_out.lto_end ();
- if (!flag_ltrans)
+ if (!flag_ltrans && (in_lto_p || !flag_lto || flag_fat_lto_objects))
execute_ipa_pass_list (all_regular_ipa_passes);
invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);
}
+/* Return string alias is alias of. */
+
+static tree
+get_alias_symbol (tree decl)
+{
+ tree alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+ return get_identifier (TREE_STRING_POINTER
+ (TREE_VALUE (TREE_VALUE (alias))));
+}
+
+
+/* Weakrefs may be associated to external decls and thus not output
+ at expansion time. Emit all neccesary aliases. */
+
+static void
+output_weakrefs (void)
+{
+ struct cgraph_node *node;
+ struct varpool_node *vnode;
+ for (node = cgraph_nodes; node; node = node->next)
+ if (node->alias && DECL_EXTERNAL (node->decl)
+ && !TREE_ASM_WRITTEN (node->decl))
+ assemble_alias (node->decl,
+ node->thunk.alias ? DECL_ASSEMBLER_NAME (node->thunk.alias)
+ : get_alias_symbol (node->decl));
+ for (vnode = varpool_nodes; vnode; vnode = vnode->next)
+ if (vnode->alias && DECL_EXTERNAL (vnode->decl)
+ && !TREE_ASM_WRITTEN (vnode->decl))
+ assemble_alias (vnode->decl,
+ vnode->alias_of ? DECL_ASSEMBLER_NAME (vnode->alias_of)
+ : get_alias_symbol (vnode->decl));
+}
+
+
/* Perform simple optimizations based on callgraph. */
void
if (!seen_error ())
ipa_passes ();
- /* Do nothing else if any IPA pass found errors. */
- if (seen_error ())
+ /* Do nothing else if any IPA pass found errors or if we are just streaming LTO. */
+ if (seen_error ()
+ || (!in_lto_p && flag_lto && !flag_fat_lto_objects))
{
timevar_pop (TV_CGRAPHOPT);
return;
#endif
cgraph_materialize_all_clones ();
+ bitmap_obstack_initialize (NULL);
+ execute_ipa_pass_list (all_late_ipa_passes);
+ cgraph_remove_unreachable_nodes (true, dump_file);
+#ifdef ENABLE_CHECKING
+ verify_cgraph ();
+#endif
+ bitmap_obstack_release (NULL);
cgraph_mark_functions_to_output ();
cgraph_state = CGRAPH_STATE_EXPANSION;
varpool_assemble_pending_decls ();
}
+
+ output_weakrefs ();
cgraph_process_new_functions ();
cgraph_state = CGRAPH_STATE_FINISHED;
was copied to prevent duplications of calls that are dead
in the clone. */
-static struct cgraph_node *
+struct cgraph_node *
cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
tree new_decl,
VEC(cgraph_edge_p,heap) *redirect_callers,
gcc_assert (old_version);
- new_version = cgraph_node (new_decl);
+ new_version = cgraph_create_node (new_decl);
- new_version->analyzed = true;
+ new_version->analyzed = old_version->analyzed;
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)
SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
SET_DECL_RTL (new_decl, NULL);
+ /* When the old decl was a con-/destructor make sure the clone isn't. */
+ DECL_STATIC_CONSTRUCTOR(new_decl) = 0;
+ DECL_STATIC_DESTRUCTOR(new_decl) = 0;
+
/* Create the new version's call-graph node.
and update the edges of the new node. */
new_version_node =
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)
tree decl = gimple_call_fndecl (e->call_stmt);
gimple new_stmt;
gimple_stmt_iterator gsi;
- bool gsi_computed = false;
#ifdef ENABLE_CHECKING
struct cgraph_node *node;
#endif
if (e->indirect_unknown_callee
- || decl == e->callee->decl
- /* Don't update call from same body alias to the real function. */
- || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)))
+ || decl == e->callee->decl)
return e->call_stmt;
#ifdef ENABLE_CHECKING
}
}
- if (e->indirect_info &&
- e->indirect_info->thunk_delta != 0
- && (!e->callee->clone.combined_args_to_skip
- || !bitmap_bit_p (e->callee->clone.combined_args_to_skip, 0)))
- {
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, " Thunk delta is "
- HOST_WIDE_INT_PRINT_DEC "\n", e->indirect_info->thunk_delta);
- gsi = gsi_for_stmt (e->call_stmt);
- gsi_computed = true;
- gimple_adjust_this_by_delta (&gsi,
- build_int_cst (sizetype,
- e->indirect_info->thunk_delta));
- e->indirect_info->thunk_delta = 0;
- }
-
if (e->callee->clone.combined_args_to_skip)
{
int lp_nr;
&& TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
- if (!gsi_computed)
- gsi = gsi_for_stmt (e->call_stmt);
+ gsi = gsi_for_stmt (e->call_stmt);
gsi_replace (&gsi, new_stmt, false);
/* We need to defer cleaning EH info on the new statement to
fixup-cfg. We may not have dominator information at this point
{
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)