/* Callgraph based interprocedural optimizations.
- Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+ Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
Functions are output early using call of
cgraph_assemble_pending_function from cgraph_finalize_function. The
decision on whether function is needed is made more conservative so
- uninlininable static functions are needed too. During the call-graph
+ uninlinable static functions are needed too. During the call-graph
construction the edge destinations are not marked as reachable and it
- is completely relied upn assemble_variable to mark them. */
+ is completely relied upon assemble_variable to mark them. */
#include "config.h"
static FILE *cgraph_dump_file;
-static GTY (()) tree static_ctors;
-static GTY (()) tree static_dtors;
+/* 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;
/* When target does not have ctors and dtors, we call all constructor
and destructor by special initialization/destruction function
if (DECL_STATIC_CONSTRUCTOR (fndecl))
{
- static_ctors = tree_cons (NULL_TREE, fndecl, static_ctors);
+ VEC_safe_push (tree, gc, static_ctors, fndecl);
DECL_STATIC_CONSTRUCTOR (fndecl) = 0;
}
if (DECL_STATIC_DESTRUCTOR (fndecl))
{
- static_dtors = tree_cons (NULL_TREE, fndecl, static_dtors);
+ VEC_safe_push (tree, gc, static_dtors, fndecl);
DECL_STATIC_DESTRUCTOR (fndecl) = 0;
}
DECL_INLINE (fndecl) = 1;
cgraph_mark_reachable_node (node);
}
-/* Synthesize a function which calls all the global ctors or global
- dtors in this file. This is only used for targets which do not
- support .ctors/.dtors sections. */
+/* 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 (int method_type, tree cdtors)
+build_cdtor (bool ctor_p, tree *cdtors, size_t len)
{
- tree body = 0;
+ size_t i;
- if (!cdtors)
- return;
+ 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 (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);
+ }
+}
- for (; cdtors; cdtors = TREE_CHAIN (cdtors))
- append_to_statement_list (build_function_call_expr (TREE_VALUE (cdtors), 0),
- &body);
+/* 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. */
- cgraph_build_static_cdtor (method_type, body, DEFAULT_INIT_PRIORITY);
+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
static void
cgraph_build_cdtor_fns (void)
{
- if (!targetm.have_ctors_dtors)
+ if (!VEC_empty (tree, static_ctors))
{
- build_cdtor ('I', static_ctors);
- static_ctors = NULL_TREE;
- build_cdtor ('D', static_dtors);
- static_dtors = NULL_TREE;
+ 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);
}
- else
+
+ if (!VEC_empty (tree, static_dtors))
{
- gcc_assert (!static_ctors);
- gcc_assert (!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, or (if not doing unit-at-a-time) to something we havn't
+ configury, or (if not doing unit-at-a-time) to something we haven't
seen yet. */
static bool
it into reachable functions list. */
node->next_needed = NULL;
- node->needed = node->reachable = false;
cgraph_finalize_function (fndecl, false);
cgraph_mark_reachable_node (node);
output = true;
cgraph_analyze_function (node);
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
current_function_decl = fndecl;
- node->local.inlinable = tree_inlinable_function_p (fndecl);
- node->local.self_insns = estimate_num_insns (fndecl,
- &eni_inlining_weights);
- node->local.disregard_inline_limits
- |= DECL_DISREGARD_INLINE_LIMITS (fndecl);
- /* Inlining characteristics are maintained by the
- cgraph_mark_inline. */
- node->global.insns = node->local.self_insns;
- if (flag_really_no_inline && !node->local.disregard_inline_limits)
- node->local.inlinable = 0;
+ 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.sub);
+ execute_pass_list (pass_early_local_passes.pass.sub);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
{
bool output = false;
- if (flag_unit_at_a_time)
+ if (flag_unit_at_a_time || errorcount || sorrycount)
return false;
cgraph_output_pending_asms ();
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. */
- if (node->reachable && !flag_unit_at_a_time)
+ we already proceeded it and ignored as not needed or got
+ a re-declaration in IMA mode. */
+ if (node->reachable)
{
struct cgraph_node *n;
node->pid = cgraph_max_pid ++;
notice_global_symbol (decl);
- node->decl = decl;
node->local.finalized = true;
node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
record_cdtor_fn (node->decl);
do_warn_unused_parameter (decl);
}
+/* C99 extern inline keywords allow changing of declaration after function
+ has been finalized. We need to re-decide if we want to mark the function as
+ needed then. */
+
+void
+cgraph_mark_if_needed (tree decl)
+{
+ struct cgraph_node *node = cgraph_node (decl);
+ if (node->local.finalized && decide_is_function_needed (node, decl))
+ cgraph_mark_needed_node (node);
+}
+
/* Verify cgraph nodes of given cgraph node. */
void
verify_cgraph_node (struct cgraph_node *node)
struct cgraph_edge *e;
struct cgraph_node *main_clone;
struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
+ struct function *saved_cfun = cfun;
basic_block this_block;
block_stmt_iterator bsi;
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)
{
dump_cgraph_node (stderr, node);
internal_error ("verify_cgraph_node failed");
}
+ set_cfun (saved_cfun);
timevar_pop (TV_CGRAPH_VERIFY);
}
cgraph_lower_function (node);
node->analyzed = true;
- if (!flag_unit_at_a_time)
+ if (!flag_unit_at_a_time && !sorrycount && !errorcount)
{
bitmap_obstack_initialize (NULL);
tree_register_cfg_hooks ();
- execute_pass_list (pass_early_local_passes.sub);
+ execute_pass_list (pass_early_local_passes.pass.sub);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
bitmap_obstack_release (NULL);
static void
cgraph_expand_function (struct cgraph_node *node)
{
- enum debug_info_type save_write_symbols = NO_DEBUG;
- const struct gcc_debug_hooks *save_debug_hooks = NULL;
tree decl = node->decl;
/* We ought to not compile any inline clones. */
gcc_assert (node->lowered);
- if (DECL_IGNORED_P (decl))
- {
- save_write_symbols = write_symbols;
- write_symbols = NO_DEBUG;
- save_debug_hooks = debug_hooks;
- debug_hooks = &do_nothing_debug_hooks;
- }
-
/* Generate RTL for the body of DECL. */
if (lang_hooks.callgraph.emit_associated_thunks)
lang_hooks.callgraph.emit_associated_thunks (decl);
/* Make sure that BE didn't give up on compiling. */
/* ??? Can happen with nested function of extern inline. */
- gcc_assert (TREE_ASM_WRITTEN (node->decl));
-
- if (DECL_IGNORED_P (decl))
- {
- write_symbols = save_write_symbols;
- debug_hooks = save_debug_hooks;
- }
+ gcc_assert (TREE_ASM_WRITTEN (decl));
current_function_decl = NULL;
- if (!cgraph_preserve_function_body_p (node->decl))
+ if (!cgraph_preserve_function_body_p (decl))
{
cgraph_release_function_body (node);
/* Eliminate all call edges. This is important so the call_expr no longer
{
struct cgraph_node *node;
struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
- int order_pos = 0, new_order_pos = 0;
+ int order_pos, new_order_pos = 0;
int i;
order_pos = cgraph_postorder (order);
if (!quiet_flag)
fprintf (stderr, "Performing interprocedural optimizations\n");
cgraph_state = CGRAPH_STATE_IPA;
-
+
/* Don't run the IPA passes if there was any error or sorry messages. */
if (errorcount == 0 && sorrycount == 0)
ipa_passes ();
varpool_remove_unreferenced_decls ();
varpool_assemble_pending_decls ();
- varpool_output_debug_info ();
}
+ varpool_output_debug_info ();
cgraph_process_new_functions ();
cgraph_state = CGRAPH_STATE_FINISHED;
dump_cgraph_node (stderr, node);
}
if (error_found)
- internal_error ("nodes with no released memory found");
+ internal_error ("nodes with unreleased memory found");
}
#endif
}
-/* Generate and emit a static constructor or destructor. WHICH must be
- one of 'I' or 'D'. BODY should be a STATEMENT_LIST containing
- GENERIC statements. */
+/* 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)
char which_buf[16];
tree decl, name, resdecl;
- sprintf (which_buf, "%c_%d", which, counter++);
+ /* 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 (FUNCTION_DECL, name,
resdecl = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
DECL_ARTIFICIAL (resdecl) = 1;
- DECL_IGNORED_P (resdecl) = 1;
DECL_RESULT (decl) = resdecl;
- allocate_struct_function (decl);
+ allocate_struct_function (decl, false);
TREE_STATIC (decl) = 1;
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
- DECL_IGNORED_P (decl) = 1;
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
DECL_SAVED_TREE (decl) = body;
TREE_PUBLIC (decl) = ! targetm.have_ctors_dtors;
cgraph_add_new_function (decl, false);
cgraph_mark_needed_node (cgraph_node (decl));
+ set_cfun (NULL);
}
void