OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index f75d7b8..accb647 100644 (file)
@@ -1,12 +1,12 @@
 /* Callgraph based interprocedural optimizations.
-   Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 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
@@ -15,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 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.
@@ -168,6 +167,80 @@ static void cgraph_output_pending_asms (void);
 
 static FILE *cgraph_dump_file;
 
+static GTY (()) tree static_ctors;
+static GTY (()) tree 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))
+    {
+      static_ctors = tree_cons (NULL_TREE, fndecl, static_ctors);
+      DECL_STATIC_CONSTRUCTOR (fndecl) = 0;
+    }
+  if (DECL_STATIC_DESTRUCTOR (fndecl))
+    {
+      static_dtors = tree_cons (NULL_TREE, fndecl, static_dtors);
+      DECL_STATIC_DESTRUCTOR (fndecl) = 0;
+    }
+  DECL_INLINE (fndecl) = 1;
+  node = cgraph_node (fndecl);
+  node->local.disregard_inline_limits = 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.  */
+static void
+build_cdtor (int method_type, tree cdtors)
+{
+  tree body = 0;
+
+  if (!cdtors)
+    return;
+
+  for (; cdtors; cdtors = TREE_CHAIN (cdtors))
+    append_to_statement_list (build_function_call_expr (TREE_VALUE (cdtors), 0),
+                             &body);
+
+  cgraph_build_static_cdtor (method_type, body, DEFAULT_INIT_PRIORITY);
+}
+
+/* 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 (!targetm.have_ctors_dtors)
+    {
+      build_cdtor ('I', static_ctors); 
+      static_ctors = NULL_TREE;
+      build_cdtor ('D', static_dtors); 
+      static_dtors = NULL_TREE;
+    }
+  else
+    {
+      gcc_assert (!static_ctors);
+      gcc_assert (!static_dtors);
+    }
+}
+
 /* 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
@@ -198,6 +271,14 @@ decide_is_function_needed (struct cgraph_node *node, tree decl)
       && 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.  */
@@ -279,6 +360,7 @@ cgraph_process_new_functions (void)
             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;
@@ -296,9 +378,10 @@ cgraph_process_new_functions (void)
          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;
@@ -443,10 +526,12 @@ cgraph_finalize_function (tree decl, bool nested)
   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);
@@ -454,10 +539,7 @@ cgraph_finalize_function (tree decl, bool 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);
@@ -519,6 +601,16 @@ verify_cgraph_node (struct cgraph_node *node)
          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
@@ -672,20 +764,8 @@ cgraph_analyze_function (struct cgraph_node *node)
   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);
@@ -696,7 +776,6 @@ cgraph_analyze_function (struct cgraph_node *node)
       bitmap_obstack_release (NULL);
     }
 
-  node->analyzed = true;
   pop_cfun ();
   current_function_decl = NULL;
 }
@@ -780,38 +859,21 @@ process_function_and_variable_attributes (struct cgraph_node *first,
     }
 }
 
-/* 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;
@@ -825,6 +887,7 @@ cgraph_finalize_compilation_unit (void)
          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
@@ -864,6 +927,7 @@ cgraph_finalize_compilation_unit (void)
       first_processed = cgraph_nodes;
       first_analyzed_var = varpool_nodes;
       varpool_analyze_pending_decls ();
+      cgraph_process_new_functions ();
     }
 
   /* Collect entry points to the unit.  */
@@ -907,6 +971,34 @@ cgraph_finalize_compilation_unit (void)
     }
   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.  */
@@ -961,6 +1053,8 @@ cgraph_mark_functions_to_output (void)
 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.  */
@@ -970,7 +1064,14 @@ cgraph_expand_function (struct cgraph_node *node)
     announce_function (decl);
 
   gcc_assert (node->lowered);
-  /*cgraph_lower_function (node);*/
+
+  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.  */
   lang_hooks.callgraph.expand_function (decl);
@@ -979,6 +1080,12 @@ cgraph_expand_function (struct cgraph_node *node)
   /* ??? 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;
+    }
+
   current_function_decl = NULL;
   if (!cgraph_preserve_function_body_p (node->decl))
     {
@@ -1145,7 +1252,7 @@ cgraph_preserve_function_body_p (tree decl)
   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)
@@ -1176,6 +1283,10 @@ cgraph_optimize (void)
 #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 ();
@@ -1190,7 +1301,7 @@ cgraph_optimize (void)
   /* 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)
@@ -1320,9 +1431,11 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
     {
     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 ();
@@ -1332,17 +1445,6 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
 
   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);
-    }
 }
 
 void
@@ -1364,7 +1466,7 @@ update_call_expr (struct cgraph_node *new_version)
   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;
 }
 
 
@@ -1400,7 +1502,8 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
       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.
@@ -1499,7 +1602,8 @@ save_inline_function_body (struct cgraph_node *node)
     {
       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.  */
@@ -1533,3 +1637,5 @@ save_inline_function_body (struct cgraph_node *node)
 #endif
   return first_clone;
 }
+
+#include "gt-cgraphunit.h"