OSDN Git Service

2009-12-11 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
[pf3gnuchains/gcc-fork.git] / gcc / cgraph.c
index 840cf29..0ed097a 100644 (file)
@@ -85,6 +85,7 @@ The callgraph:
 #include "tree-flow.h"
 #include "value-prof.h"
 #include "except.h"
+#include "diagnostic.h"
 
 static void cgraph_node_remove_callers (struct cgraph_node *node);
 static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
@@ -406,32 +407,6 @@ hash_node (const void *p)
 }
 
 
-/* Return the cgraph node associated with function DECL.  If none
-   exists, return NULL.  */
-
-struct cgraph_node *
-cgraph_node_for_decl (tree decl)
-{
-  struct cgraph_node *node;
-  void **slot;
-
-  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
-
-  node = NULL;
-  if (cgraph_hash)
-    {
-      struct cgraph_node key;
-
-      key.decl = decl;
-      slot = htab_find_slot (cgraph_hash, &key, NO_INSERT);
-      if (slot && *slot)
-       node = (struct cgraph_node *) *slot;
-    }
-
-  return node;
-}
-
-
 /* Returns nonzero if P1 and P2 are equal.  */
 
 static int
@@ -442,10 +417,10 @@ eq_node (const void *p1, const void *p2)
   return DECL_UID (n1->decl) == DECL_UID (n2->decl);
 }
 
-/* Allocate new callgraph node and insert it into basic data structures.  */
+/* Allocate new callgraph node.  */
 
-static struct cgraph_node *
-cgraph_create_node (void)
+static inline struct cgraph_node *
+cgraph_allocate_node (void)
 {
   struct cgraph_node *node;
 
@@ -460,6 +435,16 @@ cgraph_create_node (void)
       node->uid = cgraph_max_uid++;
     }
 
+  return node;
+}
+
+/* Allocate new callgraph node and insert it into basic data structures.  */
+
+static struct cgraph_node *
+cgraph_create_node (void)
+{
+  struct cgraph_node *node = cgraph_allocate_node ();
+
   node->next = cgraph_nodes;
   node->pid = -1;
   node->order = cgraph_order++;
@@ -491,6 +476,8 @@ cgraph_node (tree decl)
   if (*slot)
     {
       node = *slot;
+      if (node->same_body_alias)
+       node = node->same_body;
       return node;
     }
 
@@ -521,6 +508,85 @@ cgraph_node (tree decl)
   return node;
 }
 
+/* Mark ALIAS as an alias to DECL.  */
+
+static struct cgraph_node *
+cgraph_same_body_alias_1 (tree alias, tree decl)
+{
+  struct cgraph_node key, *alias_node, *decl_node, **slot;
+
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+  gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
+  decl_node = cgraph_node (decl);
+
+  key.decl = alias;
+
+  slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+
+  /* If the cgraph_node has been already created, fail.  */
+  if (*slot)
+    return NULL;
+
+  alias_node = cgraph_allocate_node ();
+  alias_node->decl = alias;
+  alias_node->same_body_alias = 1;
+  alias_node->same_body = decl_node;
+  alias_node->previous = NULL;
+  if (decl_node->same_body)
+    decl_node->same_body->previous = alias_node;
+  alias_node->next = decl_node->same_body;
+  alias_node->thunk.alias = decl;
+  decl_node->same_body = alias_node;
+  *slot = alias_node;
+  return alias_node;
+}
+
+/* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
+   Same body aliases are output whenever the body of DECL is output,
+   and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).   */
+
+bool
+cgraph_same_body_alias (tree alias, tree decl)
+{
+#ifndef ASM_OUTPUT_DEF
+  /* If aliases aren't supported by the assembler, fail.  */
+  return false;
+#endif
+
+  /*gcc_assert (!assembler_name_hash);*/
+
+  return cgraph_same_body_alias_1 (alias, decl) != NULL;
+}
+
+void
+cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
+                 HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
+                 tree virtual_offset,
+                 tree real_alias)
+{
+  struct cgraph_node *node = cgraph_get_node (alias);
+
+  if (node)
+    {
+      gcc_assert (node->local.finalized);
+      gcc_assert (!node->same_body);
+      cgraph_remove_node (node);
+    }
+  
+  node = cgraph_same_body_alias_1 (alias, decl);
+  gcc_assert (node);
+#ifdef ENABLE_CHECKING
+  gcc_assert (!virtual_offset
+             || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
+#endif
+  node->thunk.fixed_offset = fixed_offset;
+  node->thunk.this_adjusting = this_adjusting;
+  node->thunk.virtual_value = virtual_value;
+  node->thunk.virtual_offset_p = virtual_offset != NULL;
+  node->thunk.alias = real_alias;
+  node->thunk.thunk_p = true;
+}
+
 /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
    is assigned.  */
 
@@ -532,7 +598,7 @@ cgraph_get_node (tree decl)
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
 
   if (!cgraph_hash)
-    cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
+    return NULL;
 
   key.decl = decl;
 
@@ -540,7 +606,11 @@ cgraph_get_node (tree decl)
                                                 NO_INSERT);
 
   if (slot && *slot)
-    node = *slot;
+    {
+      node = *slot;
+      if (node->same_body_alias)
+       node = node->same_body;
+    }
   return node;
 }
 
@@ -601,9 +671,23 @@ cgraph_node_for_asm (tree asmname)
               it is __builtin_strlen and strlen, for instance.  Do we need to
               record them all?  Original implementation marked just first one
               so lets hope for the best.  */
-           if (*slot)
-             continue;
-           *slot = node;
+           if (!*slot)
+             *slot = node;
+           if (node->same_body)
+             {
+               struct cgraph_node *alias;
+
+               for (alias = node->same_body; alias; alias = alias->next)
+                 {
+                   hashval_t hash;
+                   name = DECL_ASSEMBLER_NAME (alias->decl);
+                   hash = decl_assembler_name_hash (name);
+                   slot = htab_find_slot_with_hash (assembler_name_hash, name,
+                                                    hash,  INSERT);
+                   if (!*slot)
+                     *slot = alias;
+                 }
+             }
          }
     }
 
@@ -612,7 +696,12 @@ cgraph_node_for_asm (tree asmname)
                                   NO_INSERT);
 
   if (slot)
-    return (struct cgraph_node *) *slot;
+    {
+      node = (struct cgraph_node *) *slot;
+      if (node->same_body_alias)
+       node = node->same_body;
+      return node;
+    }
   return NULL;
 }
 
@@ -740,14 +829,16 @@ cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
 }
 
 /* Like cgraph_create_edge walk the clone tree and update all clones sharing
-   same function body.  
-   
+   same function body.  If clones already have edge for OLD_STMT; only
+   update the edge same way as cgraph_set_call_stmt_including_clones does.
+
    TODO: COUNT and LOOP_DEPTH should be properly distributed based on relative
    frequencies of the clones.  */
 
 void
 cgraph_create_edge_including_clones (struct cgraph_node *orig,
                                     struct cgraph_node *callee,
+                                    gimple old_stmt,
                                     gimple stmt, gcov_type count,
                                     int freq, int loop_depth,
                                     cgraph_inline_failed_t reason)
@@ -765,9 +856,15 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
   if (node)
     while (node != orig)
       {
-        /* It is possible that we already constant propagated into the clone
-          and turned indirect call into dirrect call.  */
-        if (!cgraph_edge (node, stmt))
+       struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
+
+        /* It is possible that clones already contain the edge while
+          master didn't.  Either we promoted indirect call into direct
+          call in the clone or we are processing clones of unreachable
+          master where edges has been rmeoved.  */
+       if (edge)
+         cgraph_set_call_stmt (edge, stmt);
+       else if (!cgraph_edge (node, stmt))
          {
            edge = cgraph_create_edge (node, callee, stmt, count,
                                       freq, loop_depth);
@@ -1000,10 +1097,10 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
          /* Otherwise remove edge and create new one; we can't simply redirect
             since function has changed, so inline plan and other information
             attached to edge is invalid.  */
-         cgraph_remove_edge (e);
          count = e->count;
          frequency = e->frequency;
          loop_nest = e->loop_nest;
+         cgraph_remove_edge (e);
        }
       else
        {
@@ -1146,6 +1243,44 @@ cgraph_release_function_body (struct cgraph_node *node)
     DECL_INITIAL (node->decl) = error_mark_node;
 }
 
+/* Remove same body alias node.  */
+
+void
+cgraph_remove_same_body_alias (struct cgraph_node *node)
+{
+  void **slot;
+  int uid = node->uid;
+
+  gcc_assert (node->same_body_alias);
+  if (node->previous)
+    node->previous->next = node->next;
+  else
+    node->same_body->same_body = node->next;
+  if (node->next)
+    node->next->previous = node->previous;
+  node->next = NULL;
+  node->previous = NULL;
+  slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
+  if (*slot == node)
+    htab_clear_slot (cgraph_hash, slot);
+  if (assembler_name_hash)
+    {
+      tree name = DECL_ASSEMBLER_NAME (node->decl);
+      slot = htab_find_slot_with_hash (assembler_name_hash, name,
+                                      decl_assembler_name_hash (name),
+                                      NO_INSERT);
+      if (slot && *slot == node)
+       htab_clear_slot (assembler_name_hash, slot);
+    }
+
+  /* Clear out the node to NULL all pointers and add the node to the free
+     list.  */
+  memset (node, 0, sizeof(*node));
+  node->uid = uid;
+  NEXT_FREE_NODE (node) = free_nodes;
+  free_nodes = node;
+}
+
 /* Remove the node from cgraph.  */
 
 void
@@ -1210,11 +1345,15 @@ cgraph_remove_node (struct cgraph_node *node)
              = next_inline_clone->prev_sibling_clone;
          if (next_inline_clone->prev_sibling_clone)
            {
+             gcc_assert (node->clones != next_inline_clone);
              next_inline_clone->prev_sibling_clone->next_sibling_clone
                = next_inline_clone->next_sibling_clone;
            }
          else
-          node->clones = next_inline_clone->next_sibling_clone;
+           {
+             gcc_assert (node->clones == next_inline_clone);
+             node->clones = next_inline_clone->next_sibling_clone;
+           }
 
          new_clones = node->clones;
          node->clones = NULL;
@@ -1228,6 +1367,8 @@ cgraph_remove_node (struct cgraph_node *node)
          next_inline_clone->next_sibling_clone = NULL;
          if (node->clone_of)
            {
+             if (node->clone_of->clones)
+               node->clone_of->clones->prev_sibling_clone = next_inline_clone;
              next_inline_clone->next_sibling_clone = node->clone_of->clones;
              node->clone_of->clones = next_inline_clone;
            }
@@ -1262,8 +1403,6 @@ cgraph_remove_node (struct cgraph_node *node)
        }
 
     }
-  else
-    gcc_assert (node->clone_of);
   if (node->prev_sibling_clone)
     node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone;
   else if (node->clone_of)
@@ -1272,15 +1411,50 @@ cgraph_remove_node (struct cgraph_node *node)
     node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
   if (node->clones)
     {
-      struct cgraph_node *n;
+      struct cgraph_node *n, *next;
+
+      if (node->clone_of)
+        {
+         for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone)
+           n->clone_of = node->clone_of;
+         n->clone_of = node->clone_of;
+         n->next_sibling_clone = node->clone_of->clones;
+         if (node->clone_of->clones)
+           node->clone_of->clones->prev_sibling_clone = n;
+         node->clone_of->clones = node->clones;
+       }
+      else
+        {
+         /* We are removing node with clones.  this makes clones inconsistent,
+            but assume they will be removed subsequently and just keep clone
+            tree intact.  This can happen in unreachable function removal since
+            we remove unreachable functions in random order, not by bottom-up
+            walk of clone trees.  */
+         for (n = node->clones; n; n = next)
+           {
+              next = n->next_sibling_clone;
+              n->next_sibling_clone = NULL;
+              n->prev_sibling_clone = NULL;
+              n->clone_of = NULL;
+           }
+       }
+    }
+
+  while (node->same_body)
+    cgraph_remove_same_body_alias (node->same_body);
 
-      for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone)
-       n->clone_of = node->clone_of;
-      n->clone_of = node->clone_of;
-      n->next_sibling_clone = node->clone_of->clones;
-      if (node->clone_of->clones)
-       node->clone_of->clones->prev_sibling_clone = n;
-      node->clone_of->clones = node->clones;
+  if (node->same_comdat_group)
+    {
+      struct cgraph_node *prev;
+      for (prev = node->same_comdat_group;
+          prev->same_comdat_group != node;
+          prev = prev->same_comdat_group)
+       ;
+      if (node->same_comdat_group == prev)
+       prev->same_comdat_group = NULL;
+      else
+       prev->same_comdat_group = node->same_comdat_group;
+      node->same_comdat_group = NULL;
     }
 
   /* While all the clones are removed after being proceeded, the function
@@ -1550,6 +1724,27 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
        fprintf(f, "(can throw external) ");
     }
   fprintf (f, "\n");
+
+  if (node->same_body)
+    {
+      struct cgraph_node *n;
+      fprintf (f, "  aliases & thunks:");
+      for (n = node->same_body; n; n = n->next)
+        {
+          fprintf (f, " %s/%i", cgraph_node_name (n), n->uid);
+         if (n->thunk.thunk_p)
+           {
+             fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has "
+                      "virtual offset %i",
+                      lang_hooks.decl_printable_name (n->thunk.alias, 2),
+                      (int)n->thunk.fixed_offset,
+                      (int)n->thunk.virtual_value,
+                      (int)n->thunk.virtual_offset_p);
+             fprintf (f, ")");
+           }
+       }
+      fprintf (f, "\n");
+    }
 }
 
 
@@ -1641,8 +1836,12 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
 {
   struct cgraph_edge *new_edge;
   gcov_type count = e->count * count_scale / REG_BR_PROB_BASE;
-  gcov_type freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
+  gcov_type freq;
 
+  /* We do not want to ignore loop nest after frequency drops to 0.  */
+  if (!freq_scale)
+    freq_scale = 1;
+  freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
   if (freq > CGRAPH_FREQ_MAX)
     freq = CGRAPH_FREQ_MAX;
   new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
@@ -1753,7 +1952,7 @@ clone_function_name (tree decl)
 }
 
 /* Create callgraph node clone with new declaration.  The actual body will
-   be copied later at compilation stage.  
+   be copied later at compilation stage.
 
    TODO: after merging in ipa-sra use function call notes instead of args_to_skip
    bitmap interface.
@@ -1904,7 +2103,7 @@ cgraph_function_body_availability (struct cgraph_node *node)
    GIMPLE.
 
    The function is assumed to be reachable and have address taken (so no
-   API breaking optimizations are performed on it).  
+   API breaking optimizations are performed on it).
 
    Main work done by this function is to enqueue the function for later
    processing to avoid need the passes to be re-entrant.  */
@@ -1987,7 +2186,8 @@ bool
 cgraph_node_can_be_local_p (struct cgraph_node *node)
 {
   return (!node->needed
-         && (DECL_COMDAT (node->decl) || !node->local.externally_visible));
+         && ((DECL_COMDAT (node->decl) && !node->same_comdat_group)
+             || !node->local.externally_visible));
 }
 
 /* Bring NODE local.  */