/* Callgraph based interprocedural optimizations.
- Copyright (C) 2003, 2004, 2005, 2006 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.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
/* This module implements main driver of compilation process as well as
few basic interprocedural optimizers.
??? On the tree-ssa genericizing should take place here and we will avoid
need for these hooks (replacing them by genericizing hook)
- - expand_function callback
-
- This function is used to expand function and pass it into RTL back-end.
- Front-end should not make any assumptions about when this function can be
- called. In particular cgraph_assemble_pending_functions,
- varpool_assemble_pending_variables, cgraph_finalize_function,
- varpool_finalize_function, cgraph_optimize can cause arbitrarily
- previously finalized functions to be expanded.
-
We implement two compilation modes.
- unit-at-a-time: In this mode analyzing of all functions is deferred
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;
+
+/* 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;
+ }
+ DECL_INLINE (fndecl) = 1;
+ 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 (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, or (if not doing unit-at-a-time) to something we havn't
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
return true;
+ /* With -fkeep-inline-functions we are keeping all inline functions except
+ for extern inline ones. */
+ if (flag_keep_inline_functions
+ && DECL_DECLARED_INLINE_P (decl)
+ && !DECL_EXTERNAL (decl)
+ && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl)))
+ return true;
+
/* If we decided it was needed before, but at the time we didn't have
the body of the function available, then it's still needed. We have
to go back and re-check its dependencies now. */
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;
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);
+ node->local.self_insns = estimate_num_insns (fndecl,
+ &eni_inlining_weights);
node->local.disregard_inline_limits
- = lang_hooks.tree_inlining.disregard_inline_limits (fndecl);
+ |= DECL_DISREGARD_INLINE_LIMITS (fndecl);
/* Inlining characteristics are maintained by the
cgraph_mark_inline. */
node->global.insns = node->local.self_insns;
/* 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 ();
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;
if (node->local.finalized)
cgraph_reset_node (node);
+ 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);
if (node->nested)
lower_nested_functions (decl);
gcc_assert (!node->nested);
/* If not unit at a time, then we need to create the call graph
now, so that called functions can be queued and emitted now. */
if (!flag_unit_at_a_time)
- {
- cgraph_analyze_function (node);
- cgraph_decide_inlining_incrementally (node, false);
- }
+ cgraph_analyze_function (node);
if (decide_is_function_needed (node, decl))
cgraph_mark_needed_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)
{
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 (!e->inline_failed)
{
if (node->global.inlined_to
dump_cgraph_node (stderr, node);
internal_error ("verify_cgraph_node failed");
}
+ set_cfun (saved_cfun);
timevar_pop (TV_CGRAPH_VERIFY);
}
current_function_decl = decl;
push_cfun (DECL_STRUCT_FUNCTION (decl));
cgraph_lower_function (node);
+ node->analyzed = true;
- node->local.estimated_self_stack_size = estimated_stack_frame_size ();
- node->global.estimated_stack_size = node->local.estimated_self_stack_size;
- node->global.stack_frame_offset = 0;
- node->local.inlinable = tree_inlinable_function_p (decl);
- if (!flag_unit_at_a_time)
- node->local.self_insns = estimate_num_insns (decl);
- if (node->local.inlinable)
- node->local.disregard_inline_limits
- = lang_hooks.tree_inlining.disregard_inline_limits (decl);
- if (flag_really_no_inline && !node->local.disregard_inline_limits)
- node->local.inlinable = 0;
- /* Inlining characteristics are maintained by the cgraph_mark_inline. */
- node->global.insns = node->local.self_insns;
if (!flag_unit_at_a_time)
{
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);
}
- node->analyzed = true;
pop_cfun ();
current_function_decl = NULL;
}
}
}
-/* Analyze the whole compilation unit once it is parsed completely. */
+/* Process CGRAPH_NODES_NEEDED queue, analyze each function (and transitively
+ each reachable functions) and build cgraph.
+ The function can be called multiple times after inserting new nodes
+ into beginning of queue. Just the new part of queue is re-scanned then. */
-void
-cgraph_finalize_compilation_unit (void)
+static void
+cgraph_analyze_functions (void)
{
- struct cgraph_node *node, *next;
/* Keep track of already processed nodes when called multiple times for
intermodule optimization. */
static struct cgraph_node *first_analyzed;
struct cgraph_node *first_processed = first_analyzed;
static struct varpool_node *first_analyzed_var;
+ struct cgraph_node *node, *next;
- if (errorcount || sorrycount)
- return;
-
- finish_aliases_1 ();
-
- if (!flag_unit_at_a_time)
- {
- cgraph_output_pending_asms ();
- cgraph_assemble_pending_functions ();
- varpool_output_debug_info ();
- return;
- }
-
- if (!quiet_flag)
- {
- fprintf (stderr, "\nAnalyzing compilation unit\n");
- fflush (stderr);
- }
-
- timevar_push (TV_CGRAPH);
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");
}
+ cgraph_process_new_functions ();
/* Propagate reachability flag and lower representation of all reachable
functions. In the future, lowering will introduce new functions and
first_processed = cgraph_nodes;
first_analyzed_var = varpool_nodes;
varpool_analyze_pending_decls ();
+ cgraph_process_new_functions ();
}
/* Collect entry points to the unit. */
}
first_analyzed = cgraph_nodes;
ggc_collect ();
+}
+
+/* Analyze the whole compilation unit once it is parsed completely. */
+
+void
+cgraph_finalize_compilation_unit (void)
+{
+ if (errorcount || sorrycount)
+ return;
+
+ finish_aliases_1 ();
+
+ if (!flag_unit_at_a_time)
+ {
+ cgraph_output_pending_asms ();
+ cgraph_assemble_pending_functions ();
+ varpool_output_debug_info ();
+ return;
+ }
+
+ if (!quiet_flag)
+ {
+ fprintf (stderr, "\nAnalyzing compilation unit\n");
+ fflush (stderr);
+ }
+
+ timevar_push (TV_CGRAPH);
+ cgraph_analyze_functions ();
timevar_pop (TV_CGRAPH);
}
/* Figure out what functions we want to assemble. */
announce_function (decl);
gcc_assert (node->lowered);
- /*cgraph_lower_function (node);*/
/* Generate RTL for the body of DECL. */
- lang_hooks.callgraph.expand_function (decl);
+ if (lang_hooks.callgraph.emit_associated_thunks)
+ lang_hooks.callgraph.emit_associated_thunks (decl);
+ tree_rest_of_compilation (decl);
/* Make sure that BE didn't give up on compiling. */
/* ??? Can happen with nested function of extern inline. */
struct cgraph_node *node;
if (!cgraph_global_info_ready)
return (flag_really_no_inline
- ? lang_hooks.tree_inlining.disregard_inline_limits (decl)
+ ? DECL_DISREGARD_INLINE_LIMITS (decl)
: DECL_INLINE (decl));
/* Look if there is any clone around. */
for (node = cgraph_node (decl); node; node = node->next_clone)
static void
ipa_passes (void)
{
- cfun = NULL;
+ set_cfun (NULL);
current_function_decl = NULL;
tree_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
#ifdef ENABLE_CHECKING
verify_cgraph ();
#endif
+
+ /* Call functions declared with the "constructor" or "destructor"
+ attribute. */
+ cgraph_build_cdtor_fns ();
if (!flag_unit_at_a_time)
{
cgraph_assemble_pending_functions ();
/* Frontend may output common variables after the unit has been finalized.
It is safe to deal with them here as they are always zero initialized. */
varpool_analyze_pending_decls ();
- cgraph_process_new_functions ();
+ cgraph_analyze_functions ();
timevar_push (TV_CGRAPHOPT);
if (pre_ipa_mem_report)
}
#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 fot 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;
{
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 ();
cgraph_add_new_function (decl, false);
cgraph_mark_needed_node (cgraph_node (decl));
-
- if (targetm.have_ctors_dtors)
- {
- void (*fn) (rtx, int);
-
- if (which == 'I')
- fn = targetm.asm_out.constructor;
- else
- fn = targetm.asm_out.destructor;
- fn (XEXP (DECL_RTL (decl), 0), priority);
- }
+ set_cfun (NULL);
}
void
for (e = new_version->callers; e; e = e->next_caller)
/* Update the call expr on the edges
to call the new version. */
- TREE_OPERAND (TREE_OPERAND (get_call_expr_in (e->call_stmt), 0), 0) = new_version->decl;
+ TREE_OPERAND (CALL_EXPR_FN (get_call_expr_in (e->call_stmt)), 0) = new_version->decl;
}
also cloned. */
for (e = old_version->callees;e; e=e->next_callee)
{
- new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->loop_nest, true);
+ new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->frequency,
+ e->loop_nest, true);
new_e->count = e->count;
}
/* Fix recursive calls.
{
struct cgraph_edge *e;
- first_clone = cgraph_clone_node (node, node->count, 0, false);
+ first_clone = cgraph_clone_node (node, node->count, 0, CGRAPH_FREQ_BASE,
+ false);
first_clone->needed = 0;
first_clone->reachable = 1;
/* Recursively clone all bodies. */
#endif
return first_clone;
}
+
+#include "gt-cgraphunit.h"