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;
-
/* 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. */
&& !(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;
}
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 compilatoin 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))))
cgraph_mark_reachable_node (node);
/* If we've not yet emitted decl, tell the debug info about it. */
cgraph_mark_needed_node (node);
}
-#ifdef ENABLE_CHECKING
/* Return TRUE if NODE2 is equivalent to NODE or its clone. */
static bool
clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
node2 = node2->clone_of;
return node2 != NULL;
}
-#endif
+
+/* 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
+ && (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_found = true;
+ }
+ return error_found;
+}
/* Verify cgraph nodes of given cgraph node. */
DEBUG_FUNCTION void
}
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->indirect_calls; e; e = e->next_callee)
}
for (e = node->callers; e; e = e->next_caller)
{
- 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
- && (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_found = true;
- }
+ 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");
debug_tree (e->callee->decl);
error_found = true;
}
-#ifdef ENABLE_CHECKING
else if (!e->callee->global.inlined_to
&& decl
&& cgraph_get_node (decl)
debug_tree (decl);
error_found = true;
}
-#endif
}
else if (decl)
{
tree decl = node->decl;
if (DECL_PRESERVE_P (decl))
cgraph_mark_needed_node (node);
- if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
+ if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+ && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
+ && TREE_PUBLIC (node->decl))
+ {
+ if (node->local.finalized)
+ cgraph_mark_needed_node (node);
+ }
+ else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (node->decl))
warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
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 (node->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,
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;
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)
{
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 ();
}
/* 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 ();
outside the current compilation unit. */
if (node->analyzed
&& !node->global.inlined_to
- && (node->needed || node->reachable_from_other_partition
- || node->address_taken
+ && (!cgraph_only_called_directly_p (node)
|| (e && node->reachable))
&& !TREE_ASM_WRITTEN (decl)
&& !DECL_EXTERNAL (decl))
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);
}
announce_function (decl);
node->process = 0;
-
- gcc_assert (node->lowered);
-
- /* Generate RTL for the body of DECL. */
- tree_rest_of_compilation (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;
assemble_thunk (alias);
}
node->alias = saved_alias;
+ cgraph_process_new_functions ();
}
+
+ gcc_assert (node->lowered);
+
+ /* Generate RTL for the body of DECL. */
+ tree_rest_of_compilation (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));
cgraph_release_function_body (node);
/* Eliminate all call edges. This is important so the GIMPLE_CALL no longer
{
fprintf (cgraph_dump_file, "\nFinal ");
dump_cgraph (cgraph_dump_file);
+ dump_varpool (cgraph_dump_file);
}
#ifdef ENABLE_CHECKING
verify_cgraph ();
#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
e->lto_stmt_uid, REG_BR_PROB_BASE,
CGRAPH_FREQ_BASE,
e->loop_nest, true);
- for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+ FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
{
/* Redirect calls to the old version node to point to its new
version. */
cgraph_materialize_clone (struct cgraph_node *node)
{
bitmap_obstack_initialize (NULL);
-#ifdef ENABLE_CHECKING
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;
-#endif
/* 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,
struct cgraph_node *node;
#endif
- if (!decl || decl == e->callee->decl
+ if (e->indirect_unknown_callee
+ || 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))
+ || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)))
return e->call_stmt;
#ifdef ENABLE_CHECKING
- node = cgraph_get_node (decl);
- gcc_assert (!node || !node->clone.combined_args_to_skip);
+ if (decl)
+ {
+ node = cgraph_get_node (decl);
+ gcc_assert (!node || !node->clone.combined_args_to_skip);
+ }
#endif
if (cgraph_dump_file)
if (e->callee->clone.combined_args_to_skip)
{
gimple_stmt_iterator gsi;
+ 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, true);
+ 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;
-
- gimple_call_set_fndecl (new_stmt, e->callee->decl);
- update_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);