OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / cgraph.c
index 64a9c65..63fed49 100644 (file)
@@ -94,8 +94,10 @@ The callgraph:
 #include "tree-flow.h"
 #include "value-prof.h"
 #include "except.h"
-#include "diagnostic.h"
+#include "diagnostic-core.h"
 #include "rtl.h"
+#include "ipa-utils.h"
+#include "lto-streamer.h"
 
 static void cgraph_node_remove_callers (struct cgraph_node *node);
 static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
@@ -441,7 +443,7 @@ cgraph_allocate_node (void)
     }
   else
     {
-      node = GGC_CNEW (struct cgraph_node);
+      node = ggc_alloc_cleared_cgraph_node ();
       node->uid = cgraph_max_uid++;
     }
 
@@ -603,6 +605,29 @@ cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
    is assigned.  */
 
 struct cgraph_node *
+cgraph_get_node_or_alias (tree decl)
+{
+  struct cgraph_node key, *node = NULL, **slot;
+
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+  if (!cgraph_hash)
+    return NULL;
+
+  key.decl = decl;
+
+  slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
+                                                NO_INSERT);
+
+  if (slot && *slot)
+    node = *slot;
+  return node;
+}
+
+/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
+   is assigned.  */
+
+struct cgraph_node *
 cgraph_get_node (tree decl)
 {
   struct cgraph_node key, *node = NULL, **slot;
@@ -969,7 +994,7 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
     }
   else
     {
-      edge = GGC_NEW (struct cgraph_edge);
+      edge = ggc_alloc_cgraph_edge ();
       edge->uid = cgraph_edge_max_uid++;
     }
 
@@ -1035,6 +1060,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
 
 struct cgraph_edge *
 cgraph_create_indirect_edge (struct cgraph_node *caller, gimple call_stmt,
+                            int ecf_flags,
                             gcov_type count, int freq, int nest)
 {
   struct cgraph_edge *edge = cgraph_create_edge_1 (caller, NULL, call_stmt,
@@ -1043,8 +1069,9 @@ cgraph_create_indirect_edge (struct cgraph_node *caller, gimple call_stmt,
   edge->indirect_unknown_callee = 1;
   initialize_inline_failed (edge);
 
-  edge->indirect_info = GGC_NEW (struct cgraph_indirect_call_info);
+  edge->indirect_info = ggc_alloc_cleared_cgraph_indirect_call_info ();
   edge->indirect_info->param_index = -1;
+  edge->indirect_info->ecf_flags = ecf_flags;
 
   edge->next_callee = caller->indirect_calls;
   if (caller->indirect_calls)
@@ -1291,6 +1318,15 @@ cgraph_node_remove_callees (struct cgraph_node *node)
        cgraph_edge_remove_callee (e);
       cgraph_free_edge (e);
     }
+  for (e = node->indirect_calls; e; e = f)
+    {
+      f = e->next_callee;
+      cgraph_call_edge_removal_hooks (e);
+      if (!e->indirect_unknown_callee)
+       cgraph_edge_remove_callee (e);
+      cgraph_free_edge (e);
+    }
+  node->indirect_calls = NULL;
   node->callees = NULL;
   if (node->call_site_hash)
     {
@@ -1642,9 +1678,17 @@ cgraph_mark_reachable_node (struct cgraph_node *node)
 {
   if (!node->reachable && node->local.finalized)
     {
-      notice_global_symbol (node->decl);
+      if (cgraph_global_info_ready)
+        {
+         /* Verify that function does not appear to be needed out of blue
+            during the optimization process.  This can happen for extern
+            inlines when bodies was removed after inlining.  */
+         gcc_assert ((node->analyzed || node->in_other_partition
+                      || DECL_EXTERNAL (node->decl)));
+       }
+      else
+        notice_global_symbol (node->decl);
       node->reachable = 1;
-      gcc_assert (!cgraph_global_info_ready);
 
       node->next_needed = cgraph_nodes_queue;
       cgraph_nodes_queue = node;
@@ -1667,8 +1711,8 @@ cgraph_mark_needed_node (struct cgraph_node *node)
 void
 cgraph_mark_address_taken_node (struct cgraph_node *node)
 {
+  cgraph_mark_reachable_node (node);
   node->address_taken = 1;
-  cgraph_mark_needed_node (node);
 }
 
 /* Return local info for the compiled function.  */
@@ -1805,12 +1849,16 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
     fprintf (f, " local");
   if (node->local.externally_visible)
     fprintf (f, " externally_visible");
+  if (node->local.used_from_object_file)
+    fprintf (f, " used_from_object_file");
   if (node->local.finalized)
     fprintf (f, " finalized");
   if (node->local.disregard_inline_limits)
     fprintf (f, " always_inline");
   else if (node->local.inlinable)
     fprintf (f, " inlinable");
+  else if (node->local.versionable)
+    fprintf (f, " versionable");
   if (node->local.redefined_extern_inline)
     fprintf (f, " redefined_extern_inline");
   if (TREE_ASM_WRITTEN (node->decl))
@@ -1894,7 +1942,7 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
 
 /* Dump call graph node NODE to stderr.  */
 
-void
+DEBUG_FUNCTION void
 debug_cgraph_node (struct cgraph_node *node)
 {
   dump_cgraph_node (stderr, node);
@@ -1916,7 +1964,7 @@ dump_cgraph (FILE *f)
 
 /* Dump the call graph to stderr.  */
 
-void
+DEBUG_FUNCTION void
 debug_cgraph (void)
 {
   dump_cgraph (stderr);
@@ -1928,20 +1976,43 @@ debug_cgraph (void)
 void
 change_decl_assembler_name (tree decl, tree name)
 {
-  gcc_assert (!assembler_name_hash);
+  struct cgraph_node *node;
+  void **slot;
   if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+    SET_DECL_ASSEMBLER_NAME (decl, name);
+  else
     {
-      SET_DECL_ASSEMBLER_NAME (decl, name);
-      return;
-    }
-  if (name == DECL_ASSEMBLER_NAME (decl))
-    return;
+      if (name == DECL_ASSEMBLER_NAME (decl))
+       return;
 
-  if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
-      && DECL_RTL_SET_P (decl))
-    warning (0, "%D renamed after being referenced in assembly", decl);
+      if (assembler_name_hash
+         && TREE_CODE (decl) == FUNCTION_DECL
+         && (node = cgraph_get_node_or_alias (decl)) != NULL)
+       {
+         tree old_name = DECL_ASSEMBLER_NAME (decl);
+         slot = htab_find_slot_with_hash (assembler_name_hash, old_name,
+                                          decl_assembler_name_hash (old_name),
+                                          NO_INSERT);
+         /* Inline clones are not hashed.  */
+         if (slot && *slot == node)
+           htab_clear_slot (assembler_name_hash, slot);
+       }
+      if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+         && DECL_RTL_SET_P (decl))
+       warning (0, "%D renamed after being referenced in assembly", decl);
 
-  SET_DECL_ASSEMBLER_NAME (decl, name);
+      SET_DECL_ASSEMBLER_NAME (decl, name);
+    }
+  if (assembler_name_hash
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && (node = cgraph_get_node_or_alias (decl)) != NULL)
+    {
+      slot = htab_find_slot_with_hash (assembler_name_hash, name,
+                                      decl_assembler_name_hash (name),
+                                      INSERT);
+      gcc_assert (!*slot);
+      *slot = node;
+    }
 }
 
 /* Add a top-level asm statement to the list.  */
@@ -1951,7 +2022,7 @@ cgraph_add_asm_node (tree asm_str)
 {
   struct cgraph_asm_node *node;
 
-  node = GGC_CNEW (struct cgraph_asm_node);
+  node = ggc_alloc_cleared_cgraph_asm_node ();
   node->asm_str = asm_str;
   node->order = cgraph_order++;
   node->next = NULL;
@@ -2001,9 +2072,11 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
        }
       else
        {
-         new_edge = cgraph_create_indirect_edge (n, call_stmt, count, freq,
+         new_edge = cgraph_create_indirect_edge (n, call_stmt,
+                                                 e->indirect_info->ecf_flags,
+                                                 count, freq,
                                                  e->loop_nest + loop_nest);
-         new_edge->indirect_info->param_index = e->indirect_info->param_index;
+         *new_edge->indirect_info = *e->indirect_info;
        }
     }
   else
@@ -2025,12 +2098,14 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
 
 /* Create node representing clone of N executed COUNT times.  Decrease
    the execution counts from original node too.
+   The new clone will have decl set to DECL that may or may not be the same
+   as decl of N.
 
    When UPDATE_ORIGINAL is true, the counts are subtracted from the original
    function's profile to reflect the fact that part of execution is handled
    by node.  */
 struct cgraph_node *
-cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
+cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
                   int loop_nest, bool update_original,
                   VEC(cgraph_edge_p,heap) *redirect_callers)
 {
@@ -2039,7 +2114,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
   gcov_type count_scale;
   unsigned i;
 
-  new_node->decl = n->decl;
+  new_node->decl = decl;
   new_node->origin = n->origin;
   if (new_node->origin)
     {
@@ -2049,6 +2124,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
   new_node->analyzed = n->analyzed;
   new_node->local = n->local;
   new_node->local.externally_visible = false;
+  new_node->local.used_from_object_file = false;
   new_node->local.local = true;
   new_node->local.vtable_method = false;
   new_node->global = n->global;
@@ -2097,27 +2173,47 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
   new_node->clone_of = n;
 
   cgraph_call_node_duplication_hooks (n, new_node);
+  if (n->decl != decl)
+    {
+      struct cgraph_node **slot;
+      slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, new_node, INSERT);
+      gcc_assert (!*slot);
+      *slot = new_node;
+      if (assembler_name_hash)
+       {
+         void **aslot;
+         tree name = DECL_ASSEMBLER_NAME (decl);
+
+         aslot = htab_find_slot_with_hash (assembler_name_hash, name,
+                                           decl_assembler_name_hash (name),
+                                           INSERT);
+         gcc_assert (!*aslot);
+         *aslot = new_node;
+       }
+    }
   return new_node;
 }
 
-/* Create a new name for omp child function.  Returns an identifier.  */
+/* Create a new name for clone of DECL, add SUFFIX.  Returns an identifier.  */
 
 static GTY(()) unsigned int clone_fn_id_num;
 
-static tree
-clone_function_name (tree decl)
+tree
+clone_function_name (tree decl, const char *suffix)
 {
   tree name = DECL_ASSEMBLER_NAME (decl);
   size_t len = IDENTIFIER_LENGTH (name);
   char *tmp_name, *prefix;
 
-  prefix = XALLOCAVEC (char, len + strlen ("_clone") + 1);
+  prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
   memcpy (prefix, IDENTIFIER_POINTER (name), len);
-  strcpy (prefix + len, "_clone");
+  strcpy (prefix + len + 1, suffix);
 #ifndef NO_DOT_IN_LABEL
   prefix[len] = '.';
 #elif !defined NO_DOLLAR_IN_LABEL
   prefix[len] = '$';
+#else
+  prefix[len] = '_';
 #endif
   ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
   return get_identifier (tmp_name);
@@ -2133,14 +2229,19 @@ struct cgraph_node *
 cgraph_create_virtual_clone (struct cgraph_node *old_node,
                             VEC(cgraph_edge_p,heap) *redirect_callers,
                             VEC(ipa_replace_map_p,gc) *tree_map,
-                            bitmap args_to_skip)
+                            bitmap args_to_skip,
+                            const char * suffix)
 {
   tree old_decl = old_node->decl;
   struct cgraph_node *new_node = NULL;
   tree new_decl;
-  struct cgraph_node key, **slot;
+  size_t i;
+  struct ipa_replace_map *map;
 
-  gcc_assert  (tree_versionable_function_p (old_decl));
+#ifdef ENABLE_CHECKING
+  if (!flag_wpa)
+    gcc_assert  (tree_versionable_function_p (old_decl));
+#endif
 
   /* Make a new FUNCTION_DECL tree node */
   if (!args_to_skip)
@@ -2150,26 +2251,47 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
   DECL_STRUCT_FUNCTION (new_decl) = NULL;
 
   /* Generate a new name for the new version. */
-  DECL_NAME (new_decl) = clone_function_name (old_decl);
+  DECL_NAME (new_decl) = clone_function_name (old_decl, suffix);
   SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
   SET_DECL_RTL (new_decl, NULL);
 
-  new_node = cgraph_clone_node (old_node, old_node->count,
+  new_node = cgraph_clone_node (old_node, new_decl, old_node->count,
                                CGRAPH_FREQ_BASE, 0, false,
                                redirect_callers);
-  new_node->decl = new_decl;
   /* Update the properties.
      Make clone visible only within this translation unit.  Make sure
      that is not weak also.
      ??? We cannot use COMDAT linkage because there is no
      ABI support for this.  */
   DECL_EXTERNAL (new_node->decl) = 0;
+  if (DECL_ONE_ONLY (old_decl))
+    DECL_SECTION_NAME (new_node->decl) = NULL;
   DECL_COMDAT_GROUP (new_node->decl) = 0;
   TREE_PUBLIC (new_node->decl) = 0;
   DECL_COMDAT (new_node->decl) = 0;
   DECL_WEAK (new_node->decl) = 0;
   new_node->clone.tree_map = tree_map;
   new_node->clone.args_to_skip = args_to_skip;
+  for (i = 0; VEC_iterate (ipa_replace_map_p, tree_map, i, map); i++)
+    {
+      tree var = map->new_tree;
+
+      STRIP_NOPS (var);
+      if (TREE_CODE (var) != ADDR_EXPR)
+       continue;
+      var = get_base_var (var);
+      if (!var)
+       continue;
+
+      /* Record references of the future statement initializing the constant
+        argument.  */
+      if (TREE_CODE (var) == FUNCTION_DECL)
+       ipa_record_reference (new_node, NULL, cgraph_node (var),
+                             NULL, IPA_REF_ADDR, NULL);
+      else if (TREE_CODE (var) == VAR_DECL)
+       ipa_record_reference (new_node, NULL, NULL, varpool_node (var),
+                             IPA_REF_ADDR, NULL);
+    }
   if (!args_to_skip)
     new_node->clone.combined_args_to_skip = old_node->clone.combined_args_to_skip;
   else if (old_node->clone.combined_args_to_skip)
@@ -2180,7 +2302,7 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
       struct cgraph_node *orig_node;
       for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of)
         ;
-      for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = TREE_CHAIN (arg), oldi++)
+      for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = DECL_CHAIN (arg), oldi++)
        {
          if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi))
            {
@@ -2196,25 +2318,11 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
   else
     new_node->clone.combined_args_to_skip = args_to_skip;
   new_node->local.externally_visible = 0;
+  new_node->local.used_from_object_file = 0;
   new_node->local.local = 1;
   new_node->lowered = true;
   new_node->reachable = true;
 
-  key.decl = new_decl;
-  slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
-  gcc_assert (!*slot);
-  *slot = new_node;
-  if (assembler_name_hash)
-    {
-      void **aslot;
-      tree name = DECL_ASSEMBLER_NAME (new_decl);
-
-      aslot = htab_find_slot_with_hash (assembler_name_hash, name,
-                                       decl_assembler_name_hash (name),
-                                       INSERT);
-      gcc_assert (!*aslot);
-      *aslot = new_node;
-    }
 
   return new_node;
 }
@@ -2357,7 +2465,7 @@ cgraph_add_new_function (tree fndecl, bool lowered)
 bool
 cgraph_node_can_be_local_p (struct cgraph_node *node)
 {
-  return (!node->needed
+  return (!node->needed && !node->address_taken
          && ((DECL_COMDAT (node->decl) && !node->same_comdat_group)
              || !node->local.externally_visible));
 }
@@ -2371,15 +2479,50 @@ cgraph_make_decl_local (tree decl)
 
   if (TREE_CODE (decl) == VAR_DECL)
     DECL_COMMON (decl) = 0;
-  else if (TREE_CODE (decl) == FUNCTION_DECL)
+  else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+  if (DECL_COMDAT (decl))
     {
+      /* It is possible that we are linking against library defining same COMDAT
+        function.  To avoid conflict we need to rename our local name of the
+        function just in the case WHOPR partitioning decide to make it hidden
+        to avoid cross partition references.  */
+      if (flag_wpa)
+       {
+         const char *old_name;
+
+         old_name  = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+         if (TREE_CODE (decl) == FUNCTION_DECL)
+           {
+             struct cgraph_node *node = cgraph_get_node_or_alias (decl);
+             change_decl_assembler_name (decl,
+                                         clone_function_name (decl, "local"));
+             if (node->local.lto_file_data)
+               lto_record_renamed_decl (node->local.lto_file_data,
+                                        old_name,
+                                        IDENTIFIER_POINTER
+                                          (DECL_ASSEMBLER_NAME (decl)));
+           }
+         else if (TREE_CODE (decl) == VAR_DECL)
+           {
+             struct varpool_node *vnode = varpool_get_node (decl);
+             /* change_decl_assembler_name will warn here on vtables because
+                C++ frontend still sets TREE_SYMBOL_REFERENCED on them.  */
+             SET_DECL_ASSEMBLER_NAME (decl,
+                                      clone_function_name (decl, "local"));
+             if (vnode->lto_file_data)
+               lto_record_renamed_decl (vnode->lto_file_data,
+                                        old_name,
+                                        IDENTIFIER_POINTER
+                                          (DECL_ASSEMBLER_NAME (decl)));
+           }
+       }
+      DECL_SECTION_NAME (decl) = 0;
       DECL_COMDAT (decl) = 0;
-      DECL_COMDAT_GROUP (decl) = 0;
-      DECL_WEAK (decl) = 0;
-      DECL_EXTERNAL (decl) = 0;
     }
-  else
-    gcc_unreachable ();
+  DECL_COMDAT_GROUP (decl) = 0;
+  DECL_WEAK (decl) = 0;
+  DECL_EXTERNAL (decl) = 0;
   TREE_PUBLIC (decl) = 0;
   if (!DECL_RTL_SET_P (decl))
     return;
@@ -2529,4 +2672,66 @@ cgraph_propagate_frequency (struct cgraph_node *node)
    return false;
 }
 
+/* Return true when NODE can not return or throw and thus
+   it is safe to ignore its side effects for IPA analysis.  */
+
+bool
+cgraph_node_cannot_return (struct cgraph_node *node)
+{
+  int flags = flags_from_decl_or_type (node->decl);
+  if (!flag_exceptions)
+    return (flags & ECF_NORETURN) != 0;
+  else
+    return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+            == (ECF_NORETURN | ECF_NOTHROW));
+}
+
+/* Return true when call of E can not lead to return from caller
+   and thus it is safe to ignore its side effects for IPA analysis
+   when computing side effects of the caller.
+   FIXME: We could actually mark all edges that have no reaching
+   patch to EXIT_BLOCK_PTR or throw to get better results.  */
+bool
+cgraph_edge_cannot_lead_to_return (struct cgraph_edge *e)
+{
+  if (cgraph_node_cannot_return (e->caller))
+    return true;
+  if (e->indirect_unknown_callee)
+    {
+      int flags = e->indirect_info->ecf_flags;
+      if (!flag_exceptions)
+       return (flags & ECF_NORETURN) != 0;
+      else
+       return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+                == (ECF_NORETURN | ECF_NOTHROW));
+    }
+  else
+    return cgraph_node_cannot_return (e->callee);
+}
+
+/* Return true when function NODE can be excpected to be removed
+   from program when direct calls in this compilation unit are removed.
+
+   As a special case COMDAT functions are
+   cgraph_can_remove_if_no_direct_calls_p while the are not
+   cgraph_only_called_directly_p (it is possible they are called from other
+   unit)
+
+   This function behaves as cgraph_only_called_directly_p because eliminating
+   all uses of COMDAT function does not make it neccesarily disappear from
+   the program unless we are compiling whole program or we do LTO.  In this
+   case we know we win since dynamic linking will not really discard the
+   linkonce section.  */
+
+bool
+cgraph_will_be_removed_from_program_if_no_direct_calls (struct cgraph_node *node)
+{
+  if (node->local.used_from_object_file)
+    return false;
+  if (!in_lto_p && !flag_whole_program)
+    return cgraph_only_called_directly_p (node);
+  else
+    return cgraph_can_remove_if_no_direct_calls_p (node);
+}
+
 #include "gt-cgraph.h"