/* Callgraph based interprocedural optimizations.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
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 "target.h"
#include "cgraph.h"
#include "diagnostic.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
#include "timevar.h"
#include "params.h"
#include "fibheap.h"
#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 *);
-static FILE *cgraph_dump_file;
-
-/* A vector of FUNCTION_DECLs declared as static constructors. */
-static GTY (()) VEC(tree, gc) *static_ctors;
-/* A vector of FUNCTION_DECLs declared as static destructors. */
-static GTY (()) VEC(tree, gc) *static_dtors;
+FILE *cgraph_dump_file;
/* Used for vtable lookup in thunk adjusting. */
static GTY (()) tree vtable_entry_type;
-/* When target does not have ctors and dtors, we call all constructor
- and destructor by special initialization/destruction function
- recognized by collect2.
-
- When we are going to build this function, collect all constructors and
- destructors and turn them into normal functions. */
-
-static void
-record_cdtor_fn (tree fndecl)
-{
- struct cgraph_node *node;
- if (targetm.have_ctors_dtors
- || (!DECL_STATIC_CONSTRUCTOR (fndecl)
- && !DECL_STATIC_DESTRUCTOR (fndecl)))
- return;
-
- if (DECL_STATIC_CONSTRUCTOR (fndecl))
- {
- VEC_safe_push (tree, gc, static_ctors, fndecl);
- DECL_STATIC_CONSTRUCTOR (fndecl) = 0;
- }
- if (DECL_STATIC_DESTRUCTOR (fndecl))
- {
- VEC_safe_push (tree, gc, static_dtors, fndecl);
- DECL_STATIC_DESTRUCTOR (fndecl) = 0;
- }
- node = cgraph_node (fndecl);
- node->local.disregard_inline_limits = 1;
- cgraph_mark_reachable_node (node);
-}
-
-/* Define global constructors/destructor functions for the CDTORS, of
- which they are LEN. The CDTORS are sorted by initialization
- priority. If CTOR_P is true, these are constructors; otherwise,
- they are destructors. */
-
-static void
-build_cdtor (bool ctor_p, tree *cdtors, size_t len)
-{
- size_t i;
-
- i = 0;
- while (i < len)
- {
- tree body;
- tree fn;
- priority_type priority;
-
- priority = 0;
- body = NULL_TREE;
- /* Find the next batch of constructors/destructors with the same
- initialization priority. */
- do
- {
- priority_type p;
- fn = cdtors[i];
- p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
- if (!body)
- priority = p;
- else if (p != priority)
- break;
- append_to_statement_list (build_function_call_expr (UNKNOWN_LOCATION,
- fn, 0),
- &body);
- ++i;
- }
- while (i < len);
- gcc_assert (body != NULL_TREE);
- /* Generate a function to call all the function of like
- priority. */
- cgraph_build_static_cdtor (ctor_p ? 'I' : 'D', body, priority);
- }
-}
-
-/* Comparison function for qsort. P1 and P2 are actually of type
- "tree *" and point to static constructors. DECL_INIT_PRIORITY is
- used to determine the sort order. */
-
-static int
-compare_ctor (const void *p1, const void *p2)
-{
- tree f1;
- tree f2;
- int priority1;
- int priority2;
-
- f1 = *(const tree *)p1;
- f2 = *(const tree *)p2;
- priority1 = DECL_INIT_PRIORITY (f1);
- priority2 = DECL_INIT_PRIORITY (f2);
-
- if (priority1 < priority2)
- return -1;
- else if (priority1 > priority2)
- return 1;
- else
- /* Ensure a stable sort. */
- return (const tree *)p1 - (const tree *)p2;
-}
-
-/* Comparison function for qsort. P1 and P2 are actually of type
- "tree *" and point to static destructors. DECL_FINI_PRIORITY is
- used to determine the sort order. */
-
-static int
-compare_dtor (const void *p1, const void *p2)
-{
- tree f1;
- tree f2;
- int priority1;
- int priority2;
-
- f1 = *(const tree *)p1;
- f2 = *(const tree *)p2;
- priority1 = DECL_FINI_PRIORITY (f1);
- priority2 = DECL_FINI_PRIORITY (f2);
-
- if (priority1 < priority2)
- return -1;
- else if (priority1 > priority2)
- return 1;
- else
- /* Ensure a stable sort. */
- return (const tree *)p1 - (const tree *)p2;
-}
-
-/* Generate functions to call static constructors and destructors
- for targets that do not support .ctors/.dtors sections. These
- functions have magic names which are detected by collect2. */
-
-static void
-cgraph_build_cdtor_fns (void)
-{
- if (!VEC_empty (tree, static_ctors))
- {
- gcc_assert (!targetm.have_ctors_dtors);
- qsort (VEC_address (tree, static_ctors),
- VEC_length (tree, static_ctors),
- sizeof (tree),
- compare_ctor);
- build_cdtor (/*ctor_p=*/true,
- VEC_address (tree, static_ctors),
- VEC_length (tree, static_ctors));
- VEC_truncate (tree, static_ctors, 0);
- }
-
- if (!VEC_empty (tree, static_dtors))
- {
- gcc_assert (!targetm.have_ctors_dtors);
- qsort (VEC_address (tree, static_dtors),
- VEC_length (tree, static_dtors),
- sizeof (tree),
- compare_dtor);
- build_cdtor (/*ctor_p=*/false,
- VEC_address (tree, static_dtors),
- VEC_length (tree, static_dtors));
- VEC_truncate (tree, static_dtors, 0);
- }
-}
-
/* Determine if function DECL is needed. That is, visible to something
either outside this translation unit, something magic in the system
configury. */
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
When not optimizing, also output the static functions. (see
PR24561), but don't do so for always_inline functions, functions
- declared inline and nested functions. These was optimized out
+ declared inline and nested functions. These were optimized out
in the original implementation and it is unclear whether we want
to change the behavior here. */
if (((TREE_PUBLIC (decl)
- || (!optimize && !node->local.disregard_inline_limits
+ || (!optimize
+ && !DECL_DISREGARD_INLINE_LIMITS (decl)
&& !DECL_DECLARED_INLINE_P (decl)
- && !node->origin))
+ && !(DECL_CONTEXT (decl)
+ && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
&& !flag_whole_program
- && !flag_lto
- && !flag_whopr)
+ && !flag_lto)
&& !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
return true;
- /* Constructors and destructors are reachable from the runtime by
- some mechanism. */
- if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl))
- return true;
-
return false;
}
tree fndecl;
struct cgraph_node *node;
+ varpool_analyze_pending_decls ();
/* Note that this queue may grow as its being processed, as the new
functions may generate new ones. */
while (cgraph_new_nodes)
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;
- record_cdtor_fn (node->decl);
if (cgraph_decide_is_function_needed (node, decl))
cgraph_mark_needed_node (node);
/* Since we reclaim unreachable nodes at the end of every language
level unit, we need to be conservative about possible entry points
there. */
- if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)))
+ if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+ || DECL_STATIC_CONSTRUCTOR (decl)
+ || DECL_STATIC_DESTRUCTOR (decl)
+ /* COMDAT virtual functions may be referenced by vtable from
+ 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)
+ && 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;
}
+/* Verify edge E count and frequency. */
+
+static bool
+verify_edge_count_and_frequency (struct cgraph_edge *e)
+{
+ bool error_found = false;
+ if (e->count < 0)
+ {
+ error ("caller edge count is negative");
+ error_found = true;
+ }
+ if (e->frequency < 0)
+ {
+ error ("caller edge frequency is negative");
+ error_found = true;
+ }
+ if (e->frequency > CGRAPH_FREQ_MAX)
+ {
+ error ("caller edge frequency is too large");
+ error_found = true;
+ }
+ 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 frequency %i",
+ e->frequency,
+ compute_call_stmt_bb_frequency (e->caller->decl,
+ gimple_bb (e->call_stmt)));
+ error_found = true;
+ }
+ 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. */
-void
+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;
- if (errorcount || sorrycount)
+ if (seen_error ())
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)
{
}
if (node->count < 0)
{
- error ("Execution count is negative");
+ error ("execution count is negative");
error_found = true;
}
if (node->global.inlined_to && node->local.externally_visible)
{
- error ("Externally visible inline clone");
+ error ("externally visible inline clone");
error_found = true;
}
if (node->global.inlined_to && node->address_taken)
{
- error ("Inline clone with address taken");
+ error ("inline clone with address taken");
error_found = true;
}
if (node->global.inlined_to && node->needed)
{
- error ("Inline clone is needed");
+ error ("inline clone is needed");
error_found = true;
}
- for (e = node->callers; e; e = e->next_caller)
+ for (e = node->indirect_calls; e; e = e->next_callee)
{
- if (e->count < 0)
- {
- error ("caller edge count is negative");
- error_found = true;
- }
- if (e->frequency < 0)
+ if (e->aux)
{
- error ("caller edge frequency is negative");
+ error ("aux field set for indirect edge from %s",
+ identifier_to_locale (cgraph_node_name (e->caller)));
error_found = true;
}
- if (e->frequency > CGRAPH_FREQ_MAX)
+ if (!e->indirect_unknown_callee
+ || !e->indirect_info)
{
- error ("caller edge frequency is too large");
- error_found = true;
- }
- if (gimple_has_body_p (e->caller->decl)
- && !e->caller->global.inlined_to
- && (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",
- e->frequency,
- compute_call_stmt_bb_frequency (e->caller->decl,
- gimple_bb (e->call_stmt)));
+ 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)));
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
error_found = true;
}
+ }
+ for (e = node->callers; e; e = e->next_caller)
+ {
+ if (verify_edge_count_and_frequency (e))
+ error_found = true;
if (!e->inline_failed)
{
if (node->global.inlined_to
error_found = true;
}
}
+ for (e = node->indirect_calls; e; e = e->next_callee)
+ if (verify_edge_count_and_frequency (e))
+ error_found = true;
if (!node->callers && node->global.inlined_to)
{
error ("inlined_to pointer is set but no predecessors found");
error_found = true;
}
- if (!cgraph_node (node->decl))
+ if (!cgraph_get_node (node->decl))
{
error ("node not found in cgraph_hash");
error_found = true;
error ("double linked list of clones corrupted");
error_found = true;
}
+ if (node->same_comdat_group)
+ {
+ struct cgraph_node *n = node->same_comdat_group;
- 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 (!DECL_ONE_ONLY (node->decl))
+ {
+ error ("non-DECL_ONE_ONLY node in a same_comdat_group list");
+ error_found = true;
+ }
+ if (n == node)
+ {
+ error ("node is alone in a comdat group");
+ error_found = true;
+ }
+ do
+ {
+ if (!n->same_comdat_group)
+ {
+ error ("same_comdat_group is not a circular list");
+ error_found = true;
+ break;
+ }
+ n = n->same_comdat_group;
+ }
+ while (n != node);
+ }
+
+ 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)
{
gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
- tree decl;
- if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt)))
+ if (is_gimple_call (stmt))
{
struct cgraph_edge *e = cgraph_edge (node, stmt);
+ tree decl = gimple_call_fndecl (stmt);
if (e)
{
if (e->aux)
{
error ("shared call_stmt:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
error_found = true;
}
- if (e->callee->same_body_alias)
+ if (!e->indirect_unknown_callee)
{
- error ("edge points to same body alias:");
- debug_tree (e->callee->decl);
- error_found = true;
+ if (verify_edge_corresponds_to_fndecl (e, decl))
+ {
+ error ("edge points to wrong declaration:");
+ debug_tree (e->callee->decl);
+ fprintf (stderr," Instead of:");
+ debug_tree (decl);
+ error_found = true;
+ }
}
- else if (!node->global.inlined_to
- && !e->callee->global.inlined_to
- && !clone_of_p (cgraph_node (decl), e->callee))
+ else if (decl)
{
- error ("edge points to wrong declaration:");
- debug_tree (e->callee->decl);
- fprintf (stderr," Instead of:");
- debug_tree (decl);
+ error ("an indirect edge with unknown callee "
+ "corresponding to a call_stmt with "
+ "a known declaration:");
error_found = true;
+ cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
}
e->aux = (void *)1;
}
- else
+ else if (decl)
{
error ("missing callgraph edge for call stmt:");
- debug_gimple_stmt (stmt);
+ cgraph_debug_gimple_stmt (this_cfun, stmt);
error_found = true;
}
}
for (e = node->callees; e; e = e->next_callee)
{
- if (!e->aux && !e->indirect_call)
+ if (!e->aux)
{
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;
+ }
+ for (e = node->indirect_calls; e; e = e->next_callee)
+ {
+ if (!e->aux)
+ {
+ error ("an indirect edge from %s has no corresponding call_stmt",
+ identifier_to_locale (cgraph_node_name (e->caller)));
+ 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);
}
/* Verify whole cgraph structure. */
-void
+DEBUG_FUNCTION void
verify_cgraph (void)
{
struct cgraph_node *node;
- if (sorrycount || errorcount)
+ if (seen_error ())
return;
for (node = cgraph_nodes; node; node = node->next)
{
struct cgraph_asm_node *can;
- if (errorcount || sorrycount)
+ if (seen_error ())
return;
for (can = cgraph_asm_nodes; can; can = can->next)
}
/* 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);
+ }
- assign_assembler_name_if_neeeded (node->decl);
+ /* 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);
- /* 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
+process_common_attributes (tree decl)
+{
+ tree weakref = lookup_attribute ("weakref", DECL_ATTRIBUTES (decl));
+
+ if (weakref && !lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
+ {
+ warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+ "%<weakref%> attribute should be accompanied with"
+ " an %<alias%> attribute");
+ DECL_WEAK (decl) = 0;
+ DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+ DECL_ATTRIBUTES (decl));
+ }
+}
+
/* Look for externally_visible and used attributes and mark cgraph nodes
accordingly.
{
tree decl = node->decl;
if (DECL_PRESERVE_P (decl))
+ cgraph_mark_needed_node (node);
+ if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+ && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
+ && TREE_PUBLIC (node->decl))
{
- mark_decl_referenced (decl);
if (node->local.finalized)
- cgraph_mark_needed_node (node);
+ cgraph_mark_needed_node (node);
}
- if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
+ else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (node->decl))
warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
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)
{
tree decl = vnode->decl;
if (DECL_PRESERVE_P (decl))
{
- mark_decl_referenced (decl);
vnode->force_output = true;
if (vnode->finalized)
varpool_mark_needed_node (vnode);
}
- if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
+ if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+ && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
+ && TREE_PUBLIC (vnode->decl))
+ {
+ if (vnode->finalized)
+ varpool_mark_needed_node (vnode);
+ }
+ else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (vnode->decl))
warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
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);
}
}
static struct varpool_node *first_analyzed_var;
struct cgraph_node *node, *next;
+ bitmap_obstack_initialize (NULL);
process_function_and_variable_attributes (first_processed,
first_analyzed_var);
first_processed = cgraph_nodes;
/* ??? 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;
}
fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
fprintf (cgraph_dump_file, "\n\nInitial ");
dump_cgraph (cgraph_dump_file);
+ dump_varpool (cgraph_dump_file);
}
if (cgraph_dump_file)
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)
{
fprintf (cgraph_dump_file, "\n\nReclaimed ");
dump_cgraph (cgraph_dump_file);
+ dump_varpool (cgraph_dump_file);
}
+ bitmap_obstack_release (NULL);
first_analyzed = cgraph_nodes;
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
+ && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->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) = 0;
+ 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
+ && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->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) = 0;
+ varpool_create_variable_alias (p->decl, target_vnode->decl);
+ 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. */
/* Emit size functions we didn't inline. */
finalize_size_functions ();
- /* Call functions declared with the "constructor" or "destructor"
- attribute. */
- cgraph_build_cdtor_fns ();
-
/* 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
- && (node->needed || node->reachable_from_other_partition
- || (e && node->reachable))
+ && (!cgraph_only_called_directly_p (node)
+ || ((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)
{
vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
"vtableaddr");
stmt = gimple_build_assign (vtabletmp2,
- build1 (INDIRECT_REF,
- TREE_TYPE (vtabletmp2), vtabletmp));
+ build_simple_mem_ref (vtabletmp));
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
mark_symbols_for_renaming (stmt);
find_referenced_vars_in (stmt);
/* 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. */
vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
"vcalloffset");
stmt = gimple_build_assign (vtabletmp3,
- build1 (INDIRECT_REF,
- TREE_TYPE (vtabletmp3),
- vtabletmp2));
- gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
- 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));
+ build_simple_mem_ref (vtabletmp2));
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. */
current_function_decl = thunk_fndecl;
+ /* Ensure thunks are emitted in their correct sections. */
+ resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+
if (this_adjusting
&& targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
virtual_value, alias))
{
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
{
if (!is_gimple_reg_type (restype))
{
restmp = resdecl;
- cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls);
+ add_local_decl (cfun, restmp);
BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
}
else
restmp = create_tmp_var_raw (restype, "retval");
}
- for (arg = a; arg; arg = TREE_CHAIN (arg))
+ for (arg = a; arg; arg = DECL_CHAIN (arg))
nargs++;
vargs = VEC_alloc (tree, heap, nargs);
if (this_adjusting)
virtual_offset));
else
VEC_quick_push (tree, vargs, a);
- for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg))
+ for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
VEC_quick_push (tree, vargs, arg);
call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
VEC_free (tree, heap, vargs);
remove_edge (single_succ_edge (bb));
true_label = gimple_block_label (then_bb);
stmt = gimple_build_cond (NE_EXPR, restmp,
- fold_convert (TREE_TYPE (restmp),
- integer_zero_node),
+ build_zero_cst (TREE_TYPE (restmp)),
NULL_TREE, NULL_TREE);
gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
make_edge (bb, then_bb, EDGE_TRUE_VALUE);
{
gimple stmt;
bsi = gsi_last_bb (else_bb);
- stmt = gimple_build_assign (restmp, fold_convert (TREE_TYPE (restmp),
- integer_zero_node));
+ stmt = gimple_build_assign (restmp,
+ build_zero_cst (TREE_TYPE (restmp)));
gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
bsi = gsi_last_bb (return_bb);
}
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. */
- mark_decl_referenced (thunk_fndecl);
+ 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;
-
+ assemble_thunks_and_aliases (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;
- 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;
- }
- 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 && !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;
invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_START, NULL);
if (!in_lto_p)
- execute_ipa_pass_list (all_small_ipa_passes);
+ {
+ execute_ipa_pass_list (all_small_ipa_passes);
+ if (seen_error ())
+ 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. */
execute_ipa_summary_passes
((struct ipa_opt_pass_d *) all_regular_ipa_passes);
}
+
+ /* Some targets need to handle LTO assembler output specially. */
+ if (flag_generate_lto)
+ targetm.asm_out.lto_start ();
+
execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes);
if (!in_lto_p)
ipa_write_summaries ();
- if (!flag_ltrans)
+ if (flag_generate_lto)
+ targetm.asm_out.lto_end ();
+
+ 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);
void
cgraph_optimize (void)
{
- if (errorcount || sorrycount)
+ if (seen_error ())
return;
#ifdef ENABLE_CHECKING
cgraph_state = CGRAPH_STATE_IPA;
/* Don't run the IPA passes if there was any error or sorry messages. */
- if (errorcount == 0 && sorrycount == 0)
+ if (!seen_error ())
ipa_passes ();
- /* Do nothing else if any IPA pass found errors. */
- if (errorcount || sorrycount)
+ /* 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;
{
fprintf (cgraph_dump_file, "\nFinal ");
dump_cgraph (cgraph_dump_file);
+ dump_varpool (cgraph_dump_file);
}
#ifdef ENABLE_CHECKING
verify_cgraph ();
/* Double check that all inline clones are gone and that all
function bodies have been released from memory. */
- if (!(sorrycount || errorcount))
+ if (!seen_error ())
{
struct cgraph_node *node;
bool error_found = false;
#endif
}
-
-/* Generate and emit a static constructor or destructor. WHICH must
- be one of 'I' (for a constructor) or 'D' (for a destructor). BODY
- is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the
- initialization priority for this constructor or destructor. */
-
-void
-cgraph_build_static_cdtor (char which, tree body, int priority)
-{
- static int counter = 0;
- char which_buf[16];
- tree decl, name, resdecl;
-
- /* The priority is encoded in the constructor or destructor name.
- collect2 will sort the names and arrange that they are called at
- program startup. */
- sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++);
- name = get_file_function_name (which_buf);
-
- decl = build_decl (input_location, FUNCTION_DECL, name,
- build_function_type (void_type_node, void_list_node));
- current_function_decl = decl;
-
- resdecl = build_decl (input_location,
- RESULT_DECL, NULL_TREE, void_type_node);
- DECL_ARTIFICIAL (resdecl) = 1;
- DECL_RESULT (decl) = resdecl;
- DECL_CONTEXT (resdecl) = decl;
-
- allocate_struct_function (decl, false);
-
- TREE_STATIC (decl) = 1;
- TREE_USED (decl) = 1;
- DECL_ARTIFICIAL (decl) = 1;
- DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
- DECL_SAVED_TREE (decl) = body;
- if (!targetm.have_ctors_dtors)
- {
- TREE_PUBLIC (decl) = 1;
- DECL_PRESERVE_P (decl) = 1;
- }
- DECL_UNINLINABLE (decl) = 1;
-
- DECL_INITIAL (decl) = make_node (BLOCK);
- TREE_USED (DECL_INITIAL (decl)) = 1;
-
- DECL_SOURCE_LOCATION (decl) = input_location;
- cfun->function_end_locus = input_location;
-
- switch (which)
- {
- case 'I':
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- decl_init_priority_insert (decl, priority);
- break;
- case 'D':
- DECL_STATIC_DESTRUCTOR (decl) = 1;
- decl_fini_priority_insert (decl, priority);
- break;
- default:
- gcc_unreachable ();
- }
-
- gimplify_function_tree (decl);
-
- cgraph_add_new_function (decl, false);
- cgraph_mark_needed_node (cgraph_node (decl));
- set_cfun (NULL);
-}
-
void
init_cgraph (void)
{
- cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
+ if (!cgraph_dump_file)
+ cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
}
/* The edges representing the callers of the NEW_VERSION node were
edges which should be redirected to point to
NEW_VERSION. ALL the callees edges of OLD_VERSION
are cloned to the new version node. Return the new
- version node. */
+ version node.
+
+ If non-NULL BLOCK_TO_COPY determine what basic blocks
+ was copied to prevent duplications of calls that are dead
+ in the clone. */
static struct cgraph_node *
cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
tree new_decl,
- VEC(cgraph_edge_p,heap) *redirect_callers)
+ VEC(cgraph_edge_p,heap) *redirect_callers,
+ bitmap bbs_to_copy)
{
struct cgraph_node *new_version;
struct cgraph_edge *e;
- struct cgraph_edge *next_callee;
unsigned i;
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->global = old_version->global;
- new_version->rtl = new_version->rtl;
+ new_version->rtl = old_version->rtl;
new_version->reachable = true;
new_version->count = old_version->count;
- /* Clone the old node callees. Recursive calls are
- also cloned. */
- for (e = old_version->callees;e; e=e->next_callee)
- {
+ for (e = old_version->callees; 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);
- }
- /* Fix recursive calls.
- If OLD_VERSION has a recursive call after the
- previous edge cloning, the new version will have an edge
- pointing to the old version, which is wrong;
- Redirect it to point to the new version. */
- for (e = new_version->callees ; e; e = next_callee)
- {
- next_callee = e->next_callee;
- if (e->callee == old_version)
- cgraph_redirect_edge_callee (e, new_version);
-
- if (!next_callee)
- break;
- }
- for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+ 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,
+ true);
+ FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
{
/* Redirect calls to the old version node to point to its new
version. */
new ones (according to results of prior analysis).
OLD_VERSION_NODE is the node that is versioned.
It returns the new version's cgraph node.
- ARGS_TO_SKIP lists arguments to be omitted from functions
- */
+ If non-NULL ARGS_TO_SKIP determine function parameters to remove
+ from new version.
+ If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
+ If non_NULL NEW_ENTRY determine new entry BB of the clone. */
struct cgraph_node *
cgraph_function_versioning (struct cgraph_node *old_version_node,
VEC(cgraph_edge_p,heap) *redirect_callers,
VEC (ipa_replace_map_p,gc)* tree_map,
- bitmap args_to_skip)
+ bitmap args_to_skip,
+ bitmap bbs_to_copy,
+ basic_block new_entry_block,
+ const char *clone_name)
{
tree old_decl = old_version_node->decl;
struct cgraph_node *new_version_node = NULL;
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)
else
new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
+ /* Generate a new name for the new version. */
+ DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
+ SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+ SET_DECL_RTL (new_decl, NULL);
+
/* Create the new version's call-graph node.
and update the edges of the new node. */
new_version_node =
cgraph_copy_node_for_versioning (old_version_node, new_decl,
- redirect_callers);
+ redirect_callers, bbs_to_copy);
/* Copy the OLD_VERSION_NODE function tree to the new version. */
- tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip);
+ tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
+ bbs_to_copy, new_entry_block);
/* Update the new version's properties.
Make The new version visible only within this translation unit. Make sure
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);
-
- 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)
{
bitmap_obstack_initialize (NULL);
+ node->former_clone_of = node->clone_of->decl;
+ if (node->clone_of->former_clone_of)
+ node->former_clone_of = node->clone_of->former_clone_of;
/* Copy the OLD_VERSION_NODE function tree to the new version. */
tree_function_versioning (node->clone_of->decl, node->decl,
node->clone.tree_map, true,
- node->clone.args_to_skip);
+ node->clone.args_to_skip, NULL, NULL);
if (cgraph_dump_file)
{
dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
node->next_sibling_clone = NULL;
node->prev_sibling_clone = NULL;
if (!node->clone_of->analyzed && !node->clone_of->clones)
- cgraph_remove_node (node->clone_of);
+ {
+ cgraph_release_function_body (node->clone_of);
+ cgraph_node_remove_callees (node->clone_of);
+ ipa_remove_all_references (&node->clone_of->ref_list);
+ }
node->clone_of = NULL;
bitmap_obstack_release (NULL);
}
tree decl = gimple_call_fndecl (e->call_stmt);
gimple new_stmt;
gimple_stmt_iterator gsi;
+#ifdef ENABLE_CHECKING
+ struct cgraph_node *node;
+#endif
- if (!decl || decl == e->callee->decl
- /* Don't update call from same body alias to the real function. */
- || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl))
+ if (e->indirect_unknown_callee
+ || decl == e->callee->decl)
return e->call_stmt;
+#ifdef ENABLE_CHECKING
+ if (decl)
+ {
+ node = cgraph_get_node (decl);
+ gcc_assert (!node || !node->clone.combined_args_to_skip);
+ }
+#endif
+
if (cgraph_dump_file)
{
fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ",
cgraph_node_name (e->caller), e->caller->uid,
cgraph_node_name (e->callee), e->callee->uid);
print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
+ if (e->callee->clone.combined_args_to_skip)
+ {
+ fprintf (cgraph_dump_file, " combined args to skip: ");
+ dump_bitmap (cgraph_dump_file,
+ e->callee->clone.combined_args_to_skip);
+ }
}
if (e->callee->clone.combined_args_to_skip)
- new_stmt = gimple_call_copy_skip_args (e->call_stmt,
- e->callee->clone.combined_args_to_skip);
+ {
+ int lp_nr;
+
+ new_stmt
+ = gimple_call_copy_skip_args (e->call_stmt,
+ e->callee->clone.combined_args_to_skip);
+ gimple_call_set_fndecl (new_stmt, e->callee->decl);
+
+ if (gimple_vdef (new_stmt)
+ && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+ SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_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
+ and thus would end up with unreachable blocks and have no way
+ to communicate that we need to run CFG cleanup then. */
+ lp_nr = lookup_stmt_eh_lp (e->call_stmt);
+ if (lp_nr != 0)
+ {
+ remove_stmt_from_eh_lp (e->call_stmt);
+ add_stmt_to_eh_lp (new_stmt, lp_nr);
+ }
+ }
else
- new_stmt = e->call_stmt;
- if (gimple_vdef (new_stmt)
- && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
- SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
- gimple_call_set_fndecl (new_stmt, e->callee->decl);
-
- gsi = gsi_for_stmt (e->call_stmt);
- gsi_replace (&gsi, new_stmt, true);
- update_stmt (new_stmt);
-
- /* Update EH information too, just in case. */
- maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt);
+ {
+ new_stmt = e->call_stmt;
+ gimple_call_set_fndecl (new_stmt, e->callee->decl);
+ update_stmt (new_stmt);
+ }
cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
{
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)
}
}
cgraph_materialize_clone (node);
+ stabilized = false;
}
- else
- stabilized = false;
}
}
}
if (!node->analyzed && node->callees)
cgraph_node_remove_callees (node);
if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Updating call sites\n");
- for (node = cgraph_nodes; node; node = node->next)
- if (node->analyzed && !node->clone_of
- && gimple_has_body_p (node->decl))
- {
- struct cgraph_edge *e;
-
- current_function_decl = node->decl;
- push_cfun (DECL_STRUCT_FUNCTION (node->decl));
- for (e = node->callees; e; e = e->next_callee)
- cgraph_redirect_edge_call_stmt_to_callee (e);
- gcc_assert (!need_ssa_update_p (cfun));
- pop_cfun ();
- current_function_decl = NULL;
-#ifdef ENABLE_CHECKING
- verify_cgraph_node (node);
-#endif
- }
- if (cgraph_dump_file)
fprintf (cgraph_dump_file, "Materialization Call site updates done.\n");
- /* All changes to parameters have been performed. In order not to
- incorrectly repeat them, we simply dispose of the bitmaps that drive the
- changes. */
- for (node = cgraph_nodes; node; node = node->next)
- node->clone.combined_args_to_skip = NULL;
#ifdef ENABLE_CHECKING
verify_cgraph ();
#endif