OSDN Git Service

* ada/gcc-interface/Make-lang.in, alias.c, attribs.c, auto-inc-dec.c,
[pf3gnuchains/gcc-fork.git] / gcc / ipa.c
index dd0b76c..1cc7221 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -1,6 +1,6 @@
 /* Basic IPA optimizations and utilities.
-   Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation,
-   Inc.
+   Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -114,13 +114,14 @@ update_inlined_to_pointer (struct cgraph_node *node, struct cgraph_node *inlined
 
 /* Perform reachability analysis and reclaim all unreachable nodes.
    If BEFORE_INLINING_P is true this function is called before inlining
-   decisions has been made.  If BEFORE_INLINING_P is false this function also 
+   decisions has been made.  If BEFORE_INLINING_P is false this function also
    removes unneeded bodies of extern inline functions.  */
 
 bool
 cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
 {
   struct cgraph_node *first = (struct cgraph_node *) (void *) 1;
+  struct cgraph_node *processed = (struct cgraph_node *) (void *) 2;
   struct cgraph_node *node, *next;
   bool changed = false;
 
@@ -135,16 +136,20 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
 #endif
   for (node = cgraph_nodes; node; node = node->next)
     if (!cgraph_can_remove_if_no_direct_calls_p (node)
-       && ((!DECL_EXTERNAL (node->decl)) 
+       && ((!DECL_EXTERNAL (node->decl))
             || !node->analyzed
             || before_inlining_p))
       {
         gcc_assert (!node->global.inlined_to);
        node->aux = first;
        first = node;
+       node->reachable = true;
       }
     else
-      gcc_assert (!node->aux);
+      {
+        gcc_assert (!node->aux);
+       node->reachable = false;
+      }
 
   /* Perform reachability analysis.  As a special case do not consider
      extern inline functions not inlined as live because we won't output
@@ -154,22 +159,59 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
       struct cgraph_edge *e;
       node = first;
       first = (struct cgraph_node *) first->aux;
+      node->aux = processed;
+
+      if (node->reachable)
+        for (e = node->callees; e; e = e->next_callee)
+         if (!e->callee->reachable
+             && node->analyzed
+             && (!e->inline_failed || !e->callee->analyzed
+                 || (!DECL_EXTERNAL (e->callee->decl))
+                  || before_inlining_p))
+           {
+             bool prev_reachable = e->callee->reachable;
+             e->callee->reachable |= node->reachable;
+             if (!e->callee->aux
+                 || (e->callee->aux == processed
+                     && prev_reachable != e->callee->reachable))
+               {
+                 e->callee->aux = first;
+                 first = e->callee;
+               }
+           }
+
+      /* If any function in a comdat group is reachable, force
+        all other functions in the same comdat group to be
+        also reachable.  */
+      if (node->same_comdat_group
+         && node->reachable
+         && !node->global.inlined_to)
+       {
+         for (next = node->same_comdat_group;
+              next != node;
+              next = next->same_comdat_group)
+           if (!next->reachable)
+             {
+               next->aux = first;
+               first = next;
+               next->reachable = true;
+             }
+       }
 
-      for (e = node->callees; e; e = e->next_callee)
-       if (!e->callee->aux
-           && node->analyzed
-           && (!e->inline_failed || !e->callee->analyzed
-               || (!DECL_EXTERNAL (e->callee->decl))
-                || before_inlining_p))
-         {
-           e->callee->aux = first;
-           first = e->callee;
-         }
+      /* We can freely remove inline clones even if they are cloned, however if
+        function is clone of real clone, we must keep it around in order to
+        make materialize_clones produce function body with the changes
+        applied.  */
       while (node->clone_of && !node->clone_of->aux && !gimple_has_body_p (node->decl))
         {
+         bool noninline = node->clone_of->decl != node->decl;
          node = node->clone_of;
-         node->aux = first;
-         first = node;
+         if (noninline)
+           {
+             node->aux = first;
+             first = node;
+             break;
+           }
        }
     }
 
@@ -184,13 +226,18 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
   for (node = cgraph_nodes; node; node = next)
     {
       next = node->next;
+      if (node->aux && !node->reachable)
+        {
+         cgraph_node_remove_callees (node);
+         node->analyzed = false;
+         node->local.inlinable = false;
+       }
       if (!node->aux)
        {
           node->global.inlined_to = NULL;
          if (file)
            fprintf (file, " %s", cgraph_node_name (node));
-         if (!node->analyzed || !DECL_EXTERNAL (node->decl)
-             || before_inlining_p)
+         if (!node->analyzed || !DECL_EXTERNAL (node->decl) || before_inlining_p)
            cgraph_remove_node (node);
          else
            {
@@ -215,10 +262,19 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                  if (!clone)
                    {
                      cgraph_release_function_body (node);
-                     cgraph_node_remove_callees (node);
                      node->analyzed = false;
                      node->local.inlinable = false;
                    }
+                 cgraph_node_remove_callees (node);
+                 if (node->prev_sibling_clone)
+                   node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone;
+                 else if (node->clone_of)
+                   node->clone_of->clones = node->next_sibling_clone;
+                 if (node->next_sibling_clone)
+                   node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
+                 node->clone_of = NULL;
+                 node->next_sibling_clone = NULL;
+                 node->prev_sibling_clone = NULL;
                }
              else
                cgraph_remove_node (node);
@@ -261,11 +317,29 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program)
     return false;
   if (!whole_program)
     return true;
+  if (DECL_PRESERVE_P (node->decl))
+    return true;
   /* COMDAT functions must be shared only if they have address taken,
      otherwise we can produce our own private implementation with
      -fwhole-program.  */
-  if (DECL_COMDAT (node->decl) && (node->address_taken || !node->analyzed))
-    return true;
+  if (DECL_COMDAT (node->decl))
+    {
+      if (node->address_taken || !node->analyzed)
+       return true;
+      if (node->same_comdat_group)
+       {
+         struct cgraph_node *next;
+
+         /* If more than one function is in the same COMDAT group, it must
+            be shared even if just one function in the comdat group has
+            address taken.  */
+         for (next = node->same_comdat_group;
+              next != node;
+              next = next->same_comdat_group)
+           if (next->address_taken || !next->analyzed)
+             return true;
+       }
+    }
   if (MAIN_NAME_P (DECL_NAME (node->decl)))
     return true;
   if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl)))
@@ -292,6 +366,29 @@ function_and_variable_visibility (bool whole_program)
 
   for (node = cgraph_nodes; node; node = node->next)
     {
+      /* C++ FE on lack of COMDAT support create local COMDAT functions
+        (that ought to be shared but can not due to object format
+        limitations).  It is neccesary to keep the flag to make rest of C++ FE
+        happy.  Clear the flag here to avoid confusion in middle-end.  */
+      if (DECL_COMDAT (node->decl) && !TREE_PUBLIC (node->decl))
+        DECL_COMDAT (node->decl) = 0;
+      /* For external decls stop tracking same_comdat_group, it doesn't matter
+        what comdat group they are in when they won't be emitted in this TU,
+        and simplifies later passes.  */
+      if (node->same_comdat_group && DECL_EXTERNAL (node->decl))
+       {
+         struct cgraph_node *n = node, *next;
+         do
+           {
+             /* If at least one of same comdat group functions is external,
+                all of them have to be, otherwise it is a front-end bug.  */
+             gcc_assert (DECL_EXTERNAL (n->decl));
+             next = n->same_comdat_group;
+             n->same_comdat_group = NULL;
+             n = next;
+           }
+         while (n != node);
+       }
       gcc_assert ((!DECL_WEAK (node->decl) && !DECL_COMDAT (node->decl))
                  || TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl));
       if (cgraph_externally_visible_p (node, whole_program))
@@ -305,21 +402,45 @@ function_and_variable_visibility (bool whole_program)
          && !DECL_EXTERNAL (node->decl))
        {
          gcc_assert (whole_program || !TREE_PUBLIC (node->decl));
-         TREE_PUBLIC (node->decl) = 0;
-         DECL_COMDAT (node->decl) = 0;
-         DECL_WEAK (node->decl) = 0;
+         cgraph_make_decl_local (node->decl);
        }
       node->local.local = (cgraph_only_called_directly_p (node)
                           && node->analyzed
                           && !DECL_EXTERNAL (node->decl)
                           && !node->local.externally_visible);
     }
+  for (vnode = varpool_nodes; vnode; vnode = vnode->next)
+    {
+      /* weak flag makes no sense on local variables.  */
+      gcc_assert (!DECL_WEAK (vnode->decl)
+                 || TREE_PUBLIC (vnode->decl) || DECL_EXTERNAL (vnode->decl));
+      /* In several cases declarations can not be common:
+
+        - when declaration has initializer
+        - when it is in weak
+        - when it has specific section
+        - when it resides in non-generic address space.
+        - if declaration is local, it will get into .local common section
+          so common flag is not needed.  Frontends still produce these in
+          certain cases, such as for:
+
+            static int a __attribute__ ((common))
+
+        Canonicalize things here and clear the redundant flag.  */
+      if (DECL_COMMON (vnode->decl)
+         && (!(TREE_PUBLIC (vnode->decl) || DECL_EXTERNAL (vnode->decl))
+             || (DECL_INITIAL (vnode->decl)
+                 && DECL_INITIAL (vnode->decl) != error_mark_node)
+             || DECL_WEAK (vnode->decl)
+             || DECL_SECTION_NAME (vnode->decl) != NULL
+             || ! (ADDR_SPACE_GENERIC_P
+                   (TYPE_ADDR_SPACE (TREE_TYPE (vnode->decl))))))
+       DECL_COMMON (vnode->decl) = 0;
+    }
   for (vnode = varpool_nodes_queue; vnode; vnode = vnode->next_needed)
     {
       if (!vnode->finalized)
         continue;
-      gcc_assert ((!DECL_WEAK (vnode->decl) && !DECL_COMMON (vnode->decl))
-                 || TREE_PUBLIC (vnode->decl) || DECL_EXTERNAL (vnode->decl));
       if (vnode->needed
          && (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl))
          && (!whole_program
@@ -336,8 +457,7 @@ function_and_variable_visibility (bool whole_program)
       if (!vnode->externally_visible)
        {
          gcc_assert (whole_program || !TREE_PUBLIC (vnode->decl));
-         TREE_PUBLIC (vnode->decl) = 0;
-         DECL_COMMON (vnode->decl) = 0;
+         cgraph_make_decl_local (vnode->decl);
        }
      gcc_assert (TREE_STATIC (vnode->decl));
     }
@@ -373,7 +493,7 @@ local_function_and_variable_visibility (void)
   return function_and_variable_visibility (flag_whole_program && !flag_lto && !flag_whopr);
 }
 
-struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility = 
+struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility =
 {
  {
   SIMPLE_IPA_PASS,
@@ -557,7 +677,7 @@ cgraph_node_set_remove (cgraph_node_set set, struct cgraph_node *node)
       VEC_replace (cgraph_node_ptr, set->nodes, last_element->index,
                   last_node);
     }
-  
+
   /* Remove element from hash table.  */
   htab_clear_slot (set->hashtab, slot);
   ggc_free (element);