OSDN Git Service

PR target/36684
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 2120b6b..2dcccc1 100644 (file)
@@ -1,5 +1,6 @@
 /* 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.
@@ -76,15 +77,6 @@ along with GCC; see the file COPYING3.  If not see
       ??? 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
@@ -127,9 +119,9 @@ along with GCC; see the file COPYING3.  If not see
        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"
@@ -167,8 +159,10 @@ static void cgraph_output_pending_asms (void);
 
 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
@@ -180,39 +174,120 @@ static GTY (()) tree static_dtors;
 static void
 record_cdtor_fn (tree fndecl)
 {
-  if (targetm.have_ctors_dtors)
+  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);
+      VEC_safe_push (tree, gc, static_ctors, fndecl);
       DECL_STATIC_CONSTRUCTOR (fndecl) = 0;
-      cgraph_mark_reachable_node (cgraph_node (fndecl));
     }
   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;
-      cgraph_mark_reachable_node (cgraph_node (fndecl));
     }
+  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.  */
+/* 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
@@ -222,23 +297,36 @@ build_cdtor (int method_type, tree cdtors)
 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
@@ -355,7 +443,6 @@ 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;
@@ -372,22 +459,13 @@ cgraph_process_new_functions (void)
            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
-           = lang_hooks.tree_inlining.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 ();
@@ -417,7 +495,7 @@ cgraph_assemble_pending_functions (void)
 {
   bool output = false;
 
-  if (flag_unit_at_a_time)
+  if (flag_unit_at_a_time || errorcount || sorrycount)
     return false;
 
   cgraph_output_pending_asms ();
@@ -486,8 +564,9 @@ cgraph_reset_node (struct cgraph_node *node)
   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;
 
@@ -523,7 +602,6 @@ cgraph_finalize_function (tree decl, bool nested)
 
   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);
@@ -562,6 +640,18 @@ cgraph_finalize_function (tree decl, bool nested)
     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)
@@ -569,6 +659,7 @@ 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;
@@ -577,6 +668,8 @@ verify_cgraph_node (struct cgraph_node *node)
     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)
       {
@@ -719,6 +812,7 @@ verify_cgraph_node (struct cgraph_node *node)
       dump_cgraph_node (stderr, node);
       internal_error ("verify_cgraph_node failed");
     }
+  set_cfun (saved_cfun);
   timevar_pop (TV_CGRAPH_VERIFY);
 }
 
@@ -761,11 +855,11 @@ cgraph_analyze_function (struct cgraph_node *node)
   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);
@@ -1048,8 +1142,6 @@ 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.  */
@@ -1060,29 +1152,17 @@ cgraph_expand_function (struct cgraph_node *node)
 
   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.  */
-  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.  */
-  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
@@ -1119,7 +1199,7 @@ cgraph_expand_all_functions (void)
 {
   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);
@@ -1247,7 +1327,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)
@@ -1259,7 +1339,7 @@ cgraph_preserve_function_body_p (tree decl)
 static void
 ipa_passes (void)
 {
-  cfun = NULL;
+  set_cfun (NULL);
   current_function_decl = NULL;
   tree_register_cfg_hooks ();
   bitmap_obstack_initialize (NULL);
@@ -1307,7 +1387,7 @@ cgraph_optimize (void)
   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 ();
@@ -1349,8 +1429,8 @@ cgraph_optimize (void)
       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;
 
@@ -1378,13 +1458,14 @@ cgraph_optimize (void)
            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)
@@ -1393,7 +1474,10 @@ 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,
@@ -1402,15 +1486,13 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
 
   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;
@@ -1440,6 +1522,7 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
 
   cgraph_add_new_function (decl, false);
   cgraph_mark_needed_node (cgraph_node (decl));
+  set_cfun (NULL);
 }
 
 void