OSDN Git Service

* config/xtensa/xtensa.h (TRAMPOLINE_TEMPLATE): Use "no-transform"
[pf3gnuchains/gcc-fork.git] / gcc / cgraph.c
index 5669edd..11953b4 100644 (file)
@@ -1,5 +1,5 @@
 /* Callgraph handling code.
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -44,7 +44,7 @@ The callgraph:
 
     The callgraph at the moment does not represent indirect calls or calls
     from other compilation unit.  Flag NEEDED is set for each node that may
-    be accessed in such a invisible way and it shall be considered an
+    be accessed in such an invisible way and it shall be considered an
     entry point to the callgraph.
 
     Intraprocedural information:
@@ -61,7 +61,7 @@ The callgraph:
       The function inlining information is decided in advance and maintained
       in the callgraph as so called inline plan.
       For each inlined call, the callee's node is cloned to represent the
-      new function copy produced by inlininer.
+      new function copy produced by inliner.
       Each inlined call gets a unique corresponding clone node of the callee
       and the data structure is updated while inlining is performed, so
       the clones are eliminated and their callee edges redirected to the
@@ -96,13 +96,13 @@ The varpool data structure:
 #include "output.h"
 #include "intl.h"
 
+static void cgraph_node_remove_callers (struct cgraph_node *node);
+static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
+static inline void cgraph_edge_remove_callee (struct cgraph_edge *e);
+
 /* Hash table used to convert declarations into nodes.  */
 static GTY((param_is (struct cgraph_node))) htab_t cgraph_hash;
 
-/* We destructively update callgraph during inlining and thus we need to
-   keep information on whether inlining happened separately.  */
-htab_t cgraph_inline_hash;
-
 /* The linked list of cgraph nodes.  */
 struct cgraph_node *cgraph_nodes;
 
@@ -124,9 +124,6 @@ static GTY((param_is (struct cgraph_varpool_node))) htab_t cgraph_varpool_hash;
 /* Queue of cgraph nodes scheduled to be lowered and output.  */
 struct cgraph_varpool_node *cgraph_varpool_nodes_queue;
 
-/* Number of nodes in existence.  */
-int cgraph_varpool_n_nodes;
-
 /* The linked list of cgraph varpool nodes.  */
 static GTY(())  struct cgraph_varpool_node *cgraph_varpool_nodes;
 
@@ -138,9 +135,8 @@ static int eq_node (const void *, const void *);
 static hashval_t
 hash_node (const void *p)
 {
-  return ((hashval_t)
-         IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME
-                                (((struct cgraph_node *) p)->decl)));
+  const struct cgraph_node *n = p;
+  return (hashval_t) DECL_UID (n->decl);
 }
 
 /* Returns nonzero if P1 and P2 are equal.  */
@@ -148,8 +144,8 @@ hash_node (const void *p)
 static int
 eq_node (const void *p1, const void *p2)
 {
-  return ((DECL_ASSEMBLER_NAME (((struct cgraph_node *) p1)->decl)) ==
-         (tree) p2);
+  const struct cgraph_node *n1 = p1, *n2 = p2;
+  return DECL_UID (n1->decl) == DECL_UID (n2->decl);
 }
 
 /* Allocate new callgraph node and insert it into basic data structures.  */
@@ -173,19 +169,17 @@ cgraph_create_node (void)
 struct cgraph_node *
 cgraph_node (tree decl)
 {
-  struct cgraph_node *node;
-  struct cgraph_node **slot;
+  struct cgraph_node key, *node, **slot;
 
-  if (TREE_CODE (decl) != FUNCTION_DECL)
-    abort ();
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
 
   if (!cgraph_hash)
     cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
 
-  slot = (struct cgraph_node **)
-    htab_find_slot_with_hash (cgraph_hash, DECL_ASSEMBLER_NAME (decl),
-                             IDENTIFIER_HASH_VALUE
-                               (DECL_ASSEMBLER_NAME (decl)), INSERT);
+  key.decl = decl;
+
+  slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+
   if (*slot)
     return *slot;
 
@@ -201,6 +195,56 @@ cgraph_node (tree decl)
   return node;
 }
 
+/* Compare ASMNAME with the DECL_ASSEMBLER_NAME of DECL.  */
+
+static bool
+decl_assembler_name_equal (tree decl, tree asmname)
+{
+  tree decl_asmname = DECL_ASSEMBLER_NAME (decl);
+
+  if (decl_asmname == asmname)
+    return true;
+
+  /* If the target assembler name was set by the user, things are trickier.
+     We have a leading '*' to begin with.  After that, it's arguable what
+     is the correct thing to do with -fleading-underscore.  Arguably, we've
+     historically been doing the wrong thing in assemble_alias by always
+     printing the leading underscore.  Since we're not changing that, make
+     sure user_label_prefix follows the '*' before matching.  */
+  if (IDENTIFIER_POINTER (decl_asmname)[0] == '*')
+    {
+      const char *decl_str = IDENTIFIER_POINTER (decl_asmname) + 1;
+      size_t ulp_len = strlen (user_label_prefix);
+
+      if (ulp_len == 0)
+       ;
+      else if (strncmp (decl_str, user_label_prefix, ulp_len) == 0)
+       decl_str += ulp_len;
+      else
+       return false;
+
+      return strcmp (decl_str, IDENTIFIER_POINTER (asmname)) == 0;
+    }
+
+  return false;
+}
+
+
+/* Return the cgraph node that has ASMNAME for its DECL_ASSEMBLER_NAME.
+   Return NULL if there's no such node.  */
+
+struct cgraph_node *
+cgraph_node_for_asm (tree asmname)
+{
+  struct cgraph_node *node;
+
+  for (node = cgraph_nodes; node ; node = node->next)
+    if (decl_assembler_name_equal (node->decl, asmname))
+      return node;
+
+  return NULL;
+}
+
 /* Return callgraph edge representing CALL_EXPR.  */
 struct cgraph_edge *
 cgraph_edge (struct cgraph_node *node, tree call_expr)
@@ -218,26 +262,6 @@ cgraph_edge (struct cgraph_node *node, tree call_expr)
   return e;
 }
 
-/* Try to find existing function for identifier ID.  */
-struct cgraph_node *
-cgraph_node_for_identifier (tree id)
-{
-  struct cgraph_node **slot;
-
-  if (TREE_CODE (id) != IDENTIFIER_NODE)
-    abort ();
-
-  if (!cgraph_hash)
-    return NULL;
-
-  slot = (struct cgraph_node **)
-    htab_find_slot_with_hash (cgraph_hash, id,
-                             IDENTIFIER_HASH_VALUE (id), NO_INSERT);
-  if (!slot)
-    return NULL;
-  return *slot;
-}
-
 /* Create edge from CALLER to CALLEE in the cgraph.  */
 
 struct cgraph_edge *
@@ -249,12 +273,10 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
   struct cgraph_edge *e;
 
   for (e = caller->callees; e; e = e->next_callee)
-    if (e->call_expr == call_expr)
-      abort ();
+    gcc_assert (e->call_expr != call_expr);
 #endif
 
-  if (TREE_CODE (call_expr) != CALL_EXPR)
-    abort ();
+  gcc_assert (TREE_CODE (call_expr) == CALL_EXPR);
 
   if (!DECL_SAVED_TREE (callee->decl))
     edge->inline_failed = N_("function body not available");
@@ -271,32 +293,55 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
   edge->caller = caller;
   edge->callee = callee;
   edge->call_expr = call_expr;
+  edge->prev_caller = NULL;
   edge->next_caller = callee->callers;
+  if (callee->callers)
+    callee->callers->prev_caller = edge;
+  edge->prev_callee = NULL;
   edge->next_callee = caller->callees;
+  if (caller->callees)
+    caller->callees->prev_callee = edge;
   caller->callees = edge;
   callee->callers = edge;
   return edge;
 }
 
-/* Remove the edge E the cgraph.  */
+/* Remove the edge E from the list of the callers of the callee.  */
+
+static inline void
+cgraph_edge_remove_callee (struct cgraph_edge *e)
+{
+  if (e->prev_caller)
+    e->prev_caller->next_caller = e->next_caller;
+  if (e->next_caller)
+    e->next_caller->prev_caller = e->prev_caller;
+  if (!e->prev_caller)
+    e->callee->callers = e->next_caller;
+}
+
+/* Remove the edge E from the list of the callees of the caller.  */
+
+static inline void
+cgraph_edge_remove_caller (struct cgraph_edge *e)
+{
+  if (e->prev_callee)
+    e->prev_callee->next_callee = e->next_callee;
+  if (e->next_callee)
+    e->next_callee->prev_callee = e->prev_callee;
+  if (!e->prev_callee)
+    e->caller->callees = e->next_callee;
+}
+
+/* Remove the edge E in the cgraph.  */
 
 void
 cgraph_remove_edge (struct cgraph_edge *e)
 {
-  struct cgraph_edge **edge, **edge2;
+  /* Remove from callers list of the callee.  */
+  cgraph_edge_remove_callee (e);
 
-  for (edge = &e->callee->callers; *edge && *edge != e;
-       edge = &((*edge)->next_caller))
-    continue;
-  if (!*edge)
-    abort ();
-  *edge = (*edge)->next_caller;
-  for (edge2 = &e->caller->callees; *edge2 && *edge2 != e;
-       edge2 = &(*edge2)->next_callee)
-    continue;
-  if (!*edge2)
-    abort ();
-  *edge2 = (*edge2)->next_callee;
+  /* Remove from callees list of the callers.  */
+  cgraph_edge_remove_caller (e);
 }
 
 /* Redirect callee of E to N.  The function does not update underlying
@@ -305,17 +350,46 @@ cgraph_remove_edge (struct cgraph_edge *e)
 void
 cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
 {
-  struct cgraph_edge **edge;
+  /* Remove from callers list of the current callee.  */
+  cgraph_edge_remove_callee (e);
 
-  for (edge = &e->callee->callers; *edge && *edge != e;
-       edge = &((*edge)->next_caller))
-    continue;
-  if (!*edge)
-    abort ();
-  *edge = (*edge)->next_caller;
-  e->callee = n;
+  /* Insert to callers list of the new callee.  */
+  e->prev_caller = NULL;
+  if (n->callers)
+    n->callers->prev_caller = e;
   e->next_caller = n->callers;
   n->callers = e;
+  e->callee = n;
+}
+
+/* Remove all callees from the node.  */
+
+void
+cgraph_node_remove_callees (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+
+  /* It is sufficient to remove the edges from the lists of callers of
+     the callees.  The callee list of the node can be zapped with one
+     assignment.  */
+  for (e = node->callees; e; e = e->next_callee)
+    cgraph_edge_remove_callee (e);
+  node->callees = NULL;
+}
+
+/* Remove all callers from the node.  */
+
+static void
+cgraph_node_remove_callers (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+
+  /* It is sufficient to remove the edges from the lists of callees of
+     the callers.  The caller list of the node can be zapped with one
+     assignment.  */
+  for (e = node->callers; e; e = e->next_caller)
+    cgraph_edge_remove_caller (e);
+  node->callers = NULL;
 }
 
 /* Remove the node from cgraph.  */
@@ -326,10 +400,8 @@ cgraph_remove_node (struct cgraph_node *node)
   void **slot;
   bool check_dead = 1;
 
-  while (node->callers)
-    cgraph_remove_edge (node->callers);
-  while (node->callees)
-    cgraph_remove_edge (node->callees);
+  cgraph_node_remove_callers (node);
+  cgraph_node_remove_callees (node);
   while (node->nested)
     cgraph_remove_node (node->nested);
   if (node->origin)
@@ -346,10 +418,7 @@ cgraph_remove_node (struct cgraph_node *node)
     cgraph_nodes = node->next;
   if (node->next)
     node->next->previous = node->previous;
-  slot = 
-    htab_find_slot_with_hash (cgraph_hash, DECL_ASSEMBLER_NAME (node->decl),
-                             IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME
-                                                    (node->decl)), NO_INSERT);
+  slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
   if (*slot == node)
     {
       if (node->next_clone)
@@ -357,7 +426,7 @@ cgraph_remove_node (struct cgraph_node *node)
       else
        {
           htab_clear_slot (cgraph_hash, slot);
-         if (!dump_enabled_p (TDI_all))
+         if (!dump_enabled_p (TDI_tree_all))
            {
               DECL_SAVED_TREE (node->decl) = NULL;
              DECL_STRUCT_FUNCTION (node->decl) = NULL;
@@ -385,10 +454,11 @@ cgraph_remove_node (struct cgraph_node *node)
            || (!n->global.inlined_to
                && !TREE_ASM_WRITTEN (n->decl) && !DECL_EXTERNAL (n->decl)))
          break;
-      if (!n && !dump_enabled_p (TDI_all))
+      if (!n && !dump_enabled_p (TDI_tree_all))
        {
          DECL_SAVED_TREE (node->decl) = NULL;
          DECL_STRUCT_FUNCTION (node->decl) = NULL;
+          DECL_INITIAL (node->decl) = error_mark_node;
        }
     }
   cgraph_n_nodes--;
@@ -420,29 +490,14 @@ cgraph_mark_needed_node (struct cgraph_node *node)
   cgraph_mark_reachable_node (node);
 }
 
-/* Return true when CALLER_DECL calls CALLEE_DECL.  */
-
-bool
-cgraph_calls_p (tree caller_decl, tree callee_decl)
-{
-  struct cgraph_node *caller = cgraph_node (caller_decl);
-  struct cgraph_node *callee = cgraph_node (callee_decl);
-  struct cgraph_edge *edge;
-
-  for (edge = callee->callers; edge && (edge)->caller != caller;
-       edge = (edge->next_caller))
-    continue;
-  return edge != NULL;
-}
-
 /* Return local info for the compiled function.  */
 
 struct cgraph_local_info *
 cgraph_local_info (tree decl)
 {
   struct cgraph_node *node;
-  if (TREE_CODE (decl) != FUNCTION_DECL)
-    abort ();
+  
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   node = cgraph_node (decl);
   return &node->local;
 }
@@ -453,8 +508,8 @@ struct cgraph_global_info *
 cgraph_global_info (tree decl)
 {
   struct cgraph_node *node;
-  if (TREE_CODE (decl) != FUNCTION_DECL || !cgraph_global_info_ready)
-    abort ();
+  
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL && cgraph_global_info_ready);
   node = cgraph_node (decl);
   return &node->global;
 }
@@ -465,8 +520,8 @@ struct cgraph_rtl_info *
 cgraph_rtl_info (tree decl)
 {
   struct cgraph_node *node;
-  if (TREE_CODE (decl) != FUNCTION_DECL)
-    abort ();
+  
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   node = cgraph_node (decl);
   if (decl != current_function_decl
       && !TREE_ASM_WRITTEN (node->decl))
@@ -505,7 +560,6 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
     fprintf (f, " tree");
   if (node->output)
     fprintf (f, " output");
-
   if (node->local.local)
     fprintf (f, " local");
   if (node->local.disregard_inline_limits)
@@ -550,57 +604,61 @@ dump_cgraph (FILE *f)
 /* Returns a hash code for P.  */
 
 static hashval_t
-cgraph_varpool_hash_node (const void *p)
+hash_varpool_node (const void *p)
 {
-  return ((hashval_t)
-         IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME
-                                (((struct cgraph_varpool_node *) p)->decl)));
+  const struct cgraph_varpool_node *n = p;
+  return (hashval_t) DECL_UID (n->decl);
 }
 
 /* Returns nonzero if P1 and P2 are equal.  */
 
 static int
-eq_cgraph_varpool_node (const void *p1, const void *p2)
+eq_varpool_node (const void *p1, const void *p2)
 {
-  return ((DECL_ASSEMBLER_NAME (((struct cgraph_varpool_node *) p1)->decl)) ==
-         (tree) p2);
+  const struct cgraph_varpool_node *n1 = p1, *n2 = p2;
+  return DECL_UID (n1->decl) == DECL_UID (n2->decl);
 }
 
 /* Return cgraph_varpool node assigned to DECL.  Create new one when needed.  */
 struct cgraph_varpool_node *
 cgraph_varpool_node (tree decl)
 {
-  struct cgraph_varpool_node *node;
-  struct cgraph_varpool_node **slot;
+  struct cgraph_varpool_node key, *node, **slot;
 
-  if (!DECL_P (decl) || TREE_CODE (decl) == FUNCTION_DECL)
-    abort ();
+  gcc_assert (DECL_P (decl) && TREE_CODE (decl) != FUNCTION_DECL);
 
   if (!cgraph_varpool_hash)
-    cgraph_varpool_hash = htab_create_ggc (10, cgraph_varpool_hash_node,
-                                          eq_cgraph_varpool_node, NULL);
+    cgraph_varpool_hash = htab_create_ggc (10, hash_varpool_node,
+                                          eq_varpool_node, NULL);
+  key.decl = decl;
   slot = (struct cgraph_varpool_node **)
-    htab_find_slot_with_hash (cgraph_varpool_hash, DECL_ASSEMBLER_NAME (decl),
-                             IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME (decl)),
-                             INSERT);
+    htab_find_slot (cgraph_varpool_hash, &key, INSERT);
   if (*slot)
     return *slot;
   node = ggc_alloc_cleared (sizeof (*node));
   node->decl = decl;
-  cgraph_varpool_n_nodes++;
+  node->next = cgraph_varpool_nodes;
   cgraph_varpool_nodes = node;
   *slot = node;
   return node;
 }
 
+struct cgraph_varpool_node *
+cgraph_varpool_node_for_asm (tree asmname)
+{
+  struct cgraph_varpool_node *node;
+
+  for (node = cgraph_varpool_nodes; node ; node = node->next)
+    if (decl_assembler_name_equal (node->decl, asmname))
+      return node;
+
+  return NULL;
+}
+
 /* Set the DECL_ASSEMBLER_NAME and update cgraph hashtables.  */
 void
 change_decl_assembler_name (tree decl, tree name)
 {
-  struct cgraph_node *node = NULL;
-  struct cgraph_varpool_node *vnode = NULL;
-  void **slot;
-
   if (!DECL_ASSEMBLER_NAME_SET_P (decl))
     {
       SET_DECL_ASSEMBLER_NAME (decl, name);
@@ -613,83 +671,7 @@ change_decl_assembler_name (tree decl, tree name)
       && DECL_RTL_SET_P (decl))
     warning ("%D renamed after being referenced in assembly", decl);
 
-  if (TREE_CODE (decl) == FUNCTION_DECL && cgraph_hash)
-    {
-      /* Take a look whether declaration is in the cgraph structure.  */
-      slot = 
-       htab_find_slot_with_hash (cgraph_hash, DECL_ASSEMBLER_NAME (decl),
-                                  IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME
-                                                         (decl)), NO_INSERT);
-      if (slot)
-       node = *slot;
-
-      /* It is, verify that we are the canonical node for this decl.  */
-      if (node && node->decl == decl)
-       {
-         node = *slot;
-         htab_clear_slot (cgraph_hash, slot);
-        }
-       else
-        node = NULL;
-    }
-  if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) && cgraph_varpool_hash)
-    {
-      /* Take a look whether declaration is in the cgraph structure.  */
-      slot = 
-       htab_find_slot_with_hash (cgraph_varpool_hash, DECL_ASSEMBLER_NAME (decl),
-                                  IDENTIFIER_HASH_VALUE (DECL_ASSEMBLER_NAME
-                                                         (decl)), NO_INSERT);
-      if (slot)
-       vnode = *slot;
-
-      /* It is, verify that we are the canonical vnode for this decl.  */
-      if (vnode && vnode->decl == decl)
-       {
-         vnode = *slot;
-         htab_clear_slot (cgraph_varpool_hash, slot);
-        }
-       else
-        vnode = NULL;
-    }
   SET_DECL_ASSEMBLER_NAME (decl, name);
-  if (node)
-    {
-      slot = 
-       htab_find_slot_with_hash (cgraph_hash, name,
-                                 IDENTIFIER_HASH_VALUE (name), INSERT);
-      if (*slot)
-       abort ();
-      *slot = node;
-    }
-  if (vnode)
-    {
-      slot = 
-       htab_find_slot_with_hash (cgraph_varpool_hash, name,
-                                 IDENTIFIER_HASH_VALUE (name), INSERT);
-      if (*slot)
-       abort ();
-      *slot = vnode;
-    }
-}
-
-/* Try to find existing function for identifier ID.  */
-struct cgraph_varpool_node *
-cgraph_varpool_node_for_identifier (tree id)
-{
-  struct cgraph_varpool_node **slot;
-
-  if (TREE_CODE (id) != IDENTIFIER_NODE)
-    abort ();
-
-  if (!cgraph_varpool_hash)
-    return NULL;
-
-  slot = (struct cgraph_varpool_node **)
-    htab_find_slot_with_hash (cgraph_varpool_hash, id,
-                             IDENTIFIER_HASH_VALUE (id), NO_INSERT);
-  if (!slot)
-    return NULL;
-  return *slot;
 }
 
 /* Notify finalize_compilation_unit that given node is reachable
@@ -765,10 +747,7 @@ cgraph_function_possibly_inlined_p (tree decl)
 {
   if (!cgraph_global_info_ready)
     return (DECL_INLINE (decl) && !flag_really_no_inline);
-  if (!cgraph_inline_hash)
-    return false;
-  return (htab_find_slot (cgraph_inline_hash, DECL_ASSEMBLER_NAME (decl),
-                         NO_INSERT) != NULL);
+  return DECL_POSSIBLY_INLINED (decl);
 }
 
 /* Create clone of E in the node N represented by CALL_EXPR the callgraph.  */
@@ -808,4 +787,17 @@ cgraph_clone_node (struct cgraph_node *n)
 
   return new;
 }
+
+/* NODE is no longer nested function; update cgraph accordingly.  */
+void
+cgraph_unnest_node (struct cgraph_node *node)
+{
+  struct cgraph_node **node2 = &node->origin->nested;
+  gcc_assert (node->origin);
+
+  while (*node2 != node)
+    node2 = &(*node2)->next_nested;
+  *node2 = node->next_nested;
+  node->origin = NULL;
+}
 #include "gt-cgraph.h"