OSDN Git Service

PR c++/55877
[pf3gnuchains/gcc-fork.git] / gcc / ipa.c
index b199796..4955408 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -1,5 +1,5 @@
 /* Basic IPA optimizations and utilities.
-   Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010
+   Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -57,8 +57,9 @@ cgraph_postorder (struct cgraph_node **order)
     for (node = cgraph_nodes; node; node = node->next)
       if (!node->aux
          && (pass
-             || (!cgraph_only_called_directly_p (node)
-                 && !node->address_taken)))
+             || (!node->address_taken
+                 && !node->global.inlined_to
+                 && !cgraph_only_called_directly_p (node))))
        {
          node2 = node;
          if (!node->callers)
@@ -169,12 +170,11 @@ process_references (struct ipa_ref_list *list,
        {
          struct cgraph_node *node = ipa_ref_node (ref);
          if (!node->reachable
+             && node->analyzed
              && (!DECL_EXTERNAL (node->decl)
                  || before_inlining_p))
-           {
-             node->reachable = true;
-             enqueue_cgraph_node (node, first);
-           }
+           node->reachable = true;
+         enqueue_cgraph_node (node, first);
        }
       else
        {
@@ -188,16 +188,6 @@ process_references (struct ipa_ref_list *list,
     }
 }
 
-/* Return true when function NODE can be removed from callgraph
-   if all direct calls are eliminated.  */
-
-static inline bool
-varpool_can_remove_if_no_refs (struct varpool_node *node)
-{
-  return (!node->force_output && !node->used_from_other_partition
-         && (DECL_COMDAT (node->decl) || !node->externally_visible));
-}
-
 /* Return true when function can be marked local.  */
 
 static bool
@@ -237,10 +227,22 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
     gcc_assert (!vnode->aux);
 #endif
   varpool_reset_queue ();
+  /* Mark functions whose bodies are obviously needed.
+     This is mostly when they can be referenced externally.  Inline clones
+     are special since their declarations are shared with master clone and thus
+     cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them.  */
   for (node = cgraph_nodes; node; node = node->next)
-    if (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
-       && ((!DECL_EXTERNAL (node->decl))
-            || before_inlining_p))
+    if (node->analyzed && !node->global.inlined_to
+       && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
+           /* Keep around virtual functions for possible devirtualization.  */
+           || (before_inlining_p
+               && DECL_VIRTUAL_P (node->decl)
+               && (DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl)))
+           /* Also external functions with address taken are better to stay
+              for indirect inlining.  */
+           || (before_inlining_p
+               && DECL_EXTERNAL (node->decl)
+               && node->address_taken)))
       {
         gcc_assert (!node->global.inlined_to);
        enqueue_cgraph_node (node, &first);
@@ -251,11 +253,14 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
         gcc_assert (!node->aux);
        node->reachable = false;
       }
+
+  /* Mark variables that are obviously needed.  */
   for (vnode = varpool_nodes; vnode; vnode = vnode->next)
     {
       vnode->next_needed = NULL;
       vnode->prev_needed = NULL;
-      if (!varpool_can_remove_if_no_refs (vnode))
+      if ((vnode->analyzed || vnode->force_output)
+         && !varpool_can_remove_if_no_refs (vnode))
        {
          vnode->needed = false;
          varpool_mark_needed_node (vnode);
@@ -289,15 +294,15 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
          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))
-                 {
+               {
+                 if (!e->callee->reachable
+                     && node->analyzed
+                     && (!e->inline_failed
+                         || !DECL_EXTERNAL (e->callee->decl)
+                         || before_inlining_p))
                    e->callee->reachable = true;
-                   enqueue_cgraph_node (e->callee, &first);
-                 }
+                 enqueue_cgraph_node (e->callee, &first);
+               }
              process_references (&node->ref_list, &first, &first_varpool, before_inlining_p);
            }
 
@@ -379,22 +384,31 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
        }
       if (!node->aux)
        {
+         struct cgraph_edge *e;
+         bool found = false;
+         int i;
+         struct ipa_ref *ref;
+
           node->global.inlined_to = NULL;
          if (file)
            fprintf (file, " %s", cgraph_node_name (node));
-         if (!node->analyzed || !DECL_EXTERNAL (node->decl) || before_inlining_p)
-           cgraph_remove_node (node);
-         else
+         /* See if there is reachable caller.  */
+         for (e = node->callers; e && !found; e = e->next_caller)
+           if (e->caller->reachable)
+             found = true;
+         for (i = 0; (ipa_ref_list_refering_iterate (&node->ref_list, i, ref)
+                      && !found); i++)
+           if (ref->refering_type == IPA_REF_CGRAPH
+               && ipa_ref_refering_node (ref)->reachable)
+             found = true;
+           else if (ref->refering_type == IPA_REF_VARPOOL
+                    && ipa_ref_refering_varpool_node (ref)->needed)
+             found = true;
+
+         /* If so, we need to keep node in the callgraph.  */
+         if (found)
            {
-             struct cgraph_edge *e;
-
-             /* See if there is reachable caller.  */
-             for (e = node->callers; e; e = e->next_caller)
-               if (e->caller->reachable)
-                 break;
-
-             /* If so, we need to keep node in the callgraph.  */
-             if (e || node->needed)
+             if (node->analyzed)
                {
                  struct cgraph_node *clone;
 
@@ -407,27 +421,32 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                  if (!clone)
                    {
                      cgraph_release_function_body (node);
-                     node->analyzed = false;
                      node->local.inlinable = false;
+                     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;
+                     if (node->clone_of)
+                       node->former_clone_of = node->clone_of->decl;
+                     node->clone_of = NULL;
+                     node->next_sibling_clone = NULL;
+                     node->prev_sibling_clone = NULL;
                    }
                  else
                    gcc_assert (!clone->in_other_partition);
+                 node->analyzed = false;
+                 changed = true;
                  cgraph_node_remove_callees (node);
                  ipa_remove_all_references (&node->ref_list);
-                 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);
            }
-         changed = true;
+         else
+           {
+             cgraph_remove_node (node);
+             changed = true;
+           }
        }
     }
   for (node = cgraph_nodes; node; node = node->next)
@@ -561,18 +580,68 @@ ipa_discover_readonly_nonaddressable_vars (void)
            if (dump_file)
              fprintf (dump_file, " %s (read-only)", varpool_node_name (vnode));
            TREE_READONLY (vnode->decl) = 1;
-           vnode->const_value_known |= varpool_decide_const_value_known (vnode);
          }
       }
   if (dump_file)
     fprintf (dump_file, "\n");
 }
 
+/* Return true when there is a reference to node and it is not vtable.  */
+static bool
+cgraph_address_taken_from_non_vtable_p (struct cgraph_node *node)
+{
+  int i;
+  struct ipa_ref *ref;
+  for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
+    {
+      struct varpool_node *node;
+      if (ref->refered_type == IPA_REF_CGRAPH)
+       return true;
+      node = ipa_ref_varpool_node (ref);
+      if (!DECL_VIRTUAL_P (node->decl))
+       return true;
+    }
+  return false;
+}
+
+/* COMDAT functions must be shared only if they have address taken,
+   otherwise we can produce our own private implementation with
+   -fwhole-program.  
+   Return true when turning COMDAT functoin static can not lead to wrong
+   code when the resulting object links with a library defining same COMDAT.
+
+   Virtual functions do have their addresses taken from the vtables,
+   but in C++ there is no way to compare their addresses for equality.  */
+
+bool
+cgraph_comdat_can_be_unshared_p (struct cgraph_node *node)
+{
+  if ((cgraph_address_taken_from_non_vtable_p (node)
+       && !DECL_VIRTUAL_P (node->decl))
+      || !node->analyzed)
+    return false;
+  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 (cgraph_address_taken_from_non_vtable_p (node)
+           && !DECL_VIRTUAL_P (next->decl))
+         return false;
+    }
+  return true;
+}
+
 /* Return true when function NODE should be considered externally visible.  */
 
 static bool
 cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool aliased)
 {
+  struct cgraph_node *alias;
   if (!node->local.finalized)
     return false;
   if (!DECL_COMDAT (node->decl)
@@ -584,42 +653,149 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool
   if (aliased)
     return true;
 
+  /* Do not try to localize built-in functions yet.  One of problems is that we
+     end up mangling their asm for WHOPR that makes it impossible to call them
+     using the implicit built-in declarations anymore.  Similarly this enables
+     us to remove them as unreachable before actual calls may appear during
+     expansion or folding.  */
+  if (DECL_BUILT_IN (node->decl))
+    return true;
+
+  /* FIXME: We get wrong symbols with asm aliases in callgraph and LTO.
+     This is because very little of code knows that assembler name needs to
+     mangled.  Avoid touching declarations with user asm name set to mask
+     some of the problems.  */
+  if (DECL_ASSEMBLER_NAME_SET_P (node->decl)
+      && IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl))[0]=='*')
+    return true;
+
+  /* If linker counts on us, we must preserve the function.  */
+  if (cgraph_used_from_object_file_p (node))
+    return true;
+  if (DECL_PRESERVE_P (node->decl))
+    return true;
+  if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl)))
+    return true;
+  if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+      && lookup_attribute ("dllexport", DECL_ATTRIBUTES (node->decl)))
+    return true;
+  /* When doing LTO or whole program, we can bring COMDAT functoins static.
+     This improves code quality and we know we will duplicate them at most twice
+     (in the case that we are not using plugin and link with object file
+      implementing same COMDAT)  */
+  if ((in_lto_p || whole_program)
+      && DECL_COMDAT (node->decl)
+      && cgraph_comdat_can_be_unshared_p (node))
+    return false;
+
+  /* See if we have linker information about symbol not being used or
+     if we need to make guess based on the declaration.
+
+     Even if the linker clams the symbol is unused, never bring internal
+     symbols that are declared by user as used or externally visible.
+     This is needed for i.e. references from asm statements.   */
+  for (alias = node->same_body; alias; alias = alias->next)
+    if (alias->resolution != LDPR_PREVAILING_DEF_IRONLY)
+      break;
+  if (!alias && node->resolution == LDPR_PREVAILING_DEF_IRONLY)
+    return false;
+
   /* When doing link time optimizations, hidden symbols become local.  */
-  if (in_lto_p && DECL_VISIBILITY (node->decl) == VISIBILITY_HIDDEN
+  if (in_lto_p
+      && (DECL_VISIBILITY (node->decl) == VISIBILITY_HIDDEN
+         || DECL_VISIBILITY (node->decl) == VISIBILITY_INTERNAL)
       /* Be sure that node is defined in IR file, not in other object
         file.  In that case we don't set used_from_other_object_file.  */
       && node->analyzed)
     ;
   else if (!whole_program)
     return true;
-  /* COMDAT functions must be shared only if they have address taken,
-     otherwise we can produce our own private implementation with
-     -fwhole-program.  */
-  else 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 (node->local.used_from_object_file)
+
+  if (MAIN_NAME_P (DECL_NAME (node->decl)))
     return true;
-  if (DECL_PRESERVE_P (node->decl))
+
+  return false;
+}
+
+/* Return true when variable VNODE should be considered externally visible.  */
+
+static bool
+varpool_externally_visible_p (struct varpool_node *vnode, bool aliased)
+{
+  struct varpool_node *alias;
+  if (!DECL_COMDAT (vnode->decl) && !TREE_PUBLIC (vnode->decl))
+    return false;
+
+  /* Do not even try to be smart about aliased nodes.  Until we properly
+     represent everything by same body alias, these are just evil.  */
+  if (aliased)
     return true;
-  if (MAIN_NAME_P (DECL_NAME (node->decl)))
+
+  /* If linker counts on us, we must preserve the function.  */
+  if (varpool_used_from_object_file_p (vnode))
     return true;
-  if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl)))
+
+  /* FIXME: We get wrong symbols with asm aliases in callgraph and LTO.
+     This is because very little of code knows that assembler name needs to
+     mangled.  Avoid touching declarations with user asm name set to mask
+     some of the problems.  */
+  if (DECL_ASSEMBLER_NAME_SET_P (vnode->decl)
+      && IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (vnode->decl))[0]=='*')
+    return true;
+
+  if (DECL_PRESERVE_P (vnode->decl))
+    return true;
+  if (lookup_attribute ("externally_visible",
+                       DECL_ATTRIBUTES (vnode->decl)))
+    return true;
+  if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+      && lookup_attribute ("dllexport",
+                          DECL_ATTRIBUTES (vnode->decl)))
+    return true;
+
+  /* See if we have linker information about symbol not being used or
+     if we need to make guess based on the declaration.
+
+     Even if the linker clams the symbol is unused, never bring internal
+     symbols that are declared by user as used or externally visible.
+     This is needed for i.e. references from asm statements.   */
+  if (varpool_used_from_object_file_p (vnode))
+    return true;
+  for (alias = vnode->extra_name; alias; alias = alias->next)
+    if (alias->resolution != LDPR_PREVAILING_DEF_IRONLY)
+      break;
+  if (!alias && vnode->resolution == LDPR_PREVAILING_DEF_IRONLY)
+    return false;
+
+  /* As a special case, the COMDAT virutal tables can be unshared.
+     In LTO mode turn vtables into static variables.  The variable is readonly,
+     so this does not enable more optimization, but referring static var
+     is faster for dynamic linking.  Also this match logic hidding vtables
+     from LTO symbol tables.  */
+  if ((in_lto_p || flag_whole_program)
+      && !vnode->force_output
+      && DECL_COMDAT (vnode->decl) && DECL_VIRTUAL_P (vnode->decl))
+    return false;
+
+  /* When doing link time optimizations, hidden symbols become local.  */
+  if (in_lto_p
+      && (DECL_VISIBILITY (vnode->decl) == VISIBILITY_HIDDEN
+         || DECL_VISIBILITY (vnode->decl) == VISIBILITY_INTERNAL)
+      /* Be sure that node is defined in IR file, not in other object
+        file.  In that case we don't set used_from_other_object_file.  */
+      && vnode->finalized)
+    ;
+  else if (!flag_whole_program)
+    return true;
+
+  /* Do not attempt to privatize COMDATS by default.
+     This would break linking with C++ libraries sharing
+     inline definitions.
+
+     FIXME: We can do so for readonly vars with no address taken and
+     possibly also for vtables since no direct pointer comparsion is done.
+     It might be interesting to do so to reduce linking overhead.  */
+  if (DECL_COMDAT (vnode->decl) || DECL_WEAK (vnode->decl))
     return true;
   return false;
 }
@@ -668,16 +844,34 @@ function_and_variable_visibility (bool whole_program)
                IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
                IDENTIFIER_POINTER (p->target));
                
-      if ((node = cgraph_node_for_asm (p->target)) != NULL)
+      if ((node = cgraph_node_for_asm (p->target)) != NULL
+         && !DECL_EXTERNAL (node->decl))
         {
+         if (!node->analyzed)
+           continue;
+         /* Weakrefs alias symbols from other compilation unit.  In the case
+            the destination of weakref became available because of LTO, we must
+            mark it as needed.  */
+         if (in_lto_p
+             && lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))
+             && !node->needed)
+           cgraph_mark_needed_node (node);
          gcc_assert (node->needed);
          pointer_set_insert (aliased_nodes, node);
          if (dump_file)
            fprintf (dump_file, "  node %s/%i",
                     cgraph_node_name (node), node->uid);
         }
-      else if ((vnode = varpool_node_for_asm (p->target)) != NULL)
+      else if ((vnode = varpool_node_for_asm (p->target)) != NULL
+              && !DECL_EXTERNAL (vnode->decl))
         {
+         /* Weakrefs alias symbols from other compilation unit.  In the case
+            the destination of weakref became available because of LTO, we must
+            mark it as needed.  */
+         if (in_lto_p
+             && lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))
+             && !vnode->needed)
+           varpool_mark_needed_node (vnode);
          gcc_assert (vnode->needed);
          pointer_set_insert (aliased_vnodes, vnode);
          if (dump_file)
@@ -690,6 +884,24 @@ function_and_variable_visibility (bool whole_program)
 
   for (node = cgraph_nodes; node; node = node->next)
     {
+      int flags = flags_from_decl_or_type (node->decl);
+
+      /* Optimize away PURE and CONST constructors and destructors.  */
+      if (optimize
+         && (flags & (ECF_CONST | ECF_PURE))
+         && !(flags & ECF_LOOPING_CONST_OR_PURE))
+       {
+         DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+         DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+       }
+
+      /* Frontends and alias code marks nodes as needed before parsing is finished.
+        We may end up marking as node external nodes where this flag is meaningless
+        strip it.  */
+      if (node->needed
+         && (DECL_EXTERNAL (node->decl) || !node->analyzed))
+       node->needed = 0;
+
       /* 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
@@ -730,6 +942,7 @@ function_and_variable_visibility (bool whole_program)
           struct cgraph_node *alias;
          gcc_assert (whole_program || in_lto_p || !TREE_PUBLIC (node->decl));
          cgraph_make_decl_local (node->decl);
+         node->resolution = LDPR_PREVAILING_DEF_IRONLY;
          for (alias = node->same_body; alias; alias = alias->next)
            cgraph_make_decl_local (alias->decl);
          if (node->same_comdat_group)
@@ -768,35 +981,15 @@ function_and_variable_visibility (bool whole_program)
              || ! (ADDR_SPACE_GENERIC_P
                    (TYPE_ADDR_SPACE (TREE_TYPE (vnode->decl))))))
        DECL_COMMON (vnode->decl) = 0;
-     /* Even extern variables might have initializers known.
-       See, for example testsuite/g++.dg/opt/static3.C  */
-     vnode->const_value_known |= varpool_decide_const_value_known (vnode);
     }
   for (vnode = varpool_nodes_queue; vnode; vnode = vnode->next_needed)
     {
       if (!vnode->finalized)
         continue;
       if (vnode->needed
-         && (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl))
-         && (((!whole_program
-               /* We can privatize comdat readonly variables whose address is
-                  not taken, but doing so is not going to bring us
-                  optimization oppurtunities until we start reordering
-                  datastructures.  */
-               || DECL_COMDAT (vnode->decl)
-               || DECL_WEAK (vnode->decl))
-              /* When doing linktime optimizations, all hidden symbols will
-                 become local.  */
-              && (!in_lto_p
-                  || DECL_VISIBILITY (vnode->decl) != VISIBILITY_HIDDEN
-                  /* We can get prevailing decision in other object file.
-                     In this case we do not sed used_from_object_file.  */
-                  || !vnode->finalized))
-             || DECL_PRESERVE_P (vnode->decl)
-              || vnode->used_from_object_file
-             || pointer_set_contains (aliased_vnodes, vnode)
-             || lookup_attribute ("externally_visible",
-                                  DECL_ATTRIBUTES (vnode->decl))))
+         && varpool_externally_visible_p
+             (vnode, 
+              pointer_set_contains (aliased_vnodes, vnode)))
        vnode->externally_visible = true;
       else
         vnode->externally_visible = false;
@@ -804,8 +997,8 @@ function_and_variable_visibility (bool whole_program)
        {
          gcc_assert (in_lto_p || whole_program || !TREE_PUBLIC (vnode->decl));
          cgraph_make_decl_local (vnode->decl);
+         vnode->resolution = LDPR_PREVAILING_DEF_IRONLY;
        }
-     vnode->const_value_known |= varpool_decide_const_value_known (vnode);
      gcc_assert (TREE_STATIC (vnode->decl));
     }
   pointer_set_destroy (aliased_nodes);
@@ -839,7 +1032,7 @@ function_and_variable_visibility (bool whole_program)
 static unsigned int
 local_function_and_variable_visibility (void)
 {
-  return function_and_variable_visibility (flag_whole_program && !flag_lto && !flag_whopr);
+  return function_and_variable_visibility (flag_whole_program && !flag_lto);
 }
 
 struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility =
@@ -1328,10 +1521,13 @@ struct ipa_opt_pass_d pass_ipa_profile =
 /* 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.  */
+   initialization priority for this constructor or destructor. 
 
-void
-cgraph_build_static_cdtor (char which, tree body, int priority)
+   FINAL specify whether the externally visible name for collect2 should
+   be produced. */
+
+static void
+cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
 {
   static int counter = 0;
   char which_buf[16];
@@ -1340,7 +1536,12 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
   /* 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++);
+  if (final)
+    sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++);
+  else
+  /* Proudce sane name but one not recognizable by collect2, just for the
+     case we fail to inline the function.  */
+    sprintf (which_buf, "sub_%c_%.5d_%d", which, priority, counter++);
   name = get_file_function_name (which_buf);
 
   decl = build_decl (input_location, FUNCTION_DECL, name,
@@ -1360,7 +1561,7 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
   DECL_ARTIFICIAL (decl) = 1;
   DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
   DECL_SAVED_TREE (decl) = body;
-  if (!targetm.have_ctors_dtors)
+  if (!targetm.have_ctors_dtors && final)
     {
       TREE_PUBLIC (decl) = 1;
       DECL_PRESERVE_P (decl) = 1;
@@ -1395,6 +1596,16 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
   current_function_decl = NULL;
 }
 
+/* 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)
+{
+  cgraph_build_static_cdtor_1 (which, body, priority, false);
+}
 
 /* A vector of FUNCTION_DECLs declared as static constructors.  */
 static VEC(tree, heap) *static_ctors;
@@ -1425,9 +1636,10 @@ record_cdtor_fn (struct cgraph_node *node)
    they are destructors.  */
 
 static void
-build_cdtor (bool ctor_p, tree *cdtors, size_t len)
+build_cdtor (bool ctor_p, VEC (tree, heap) *cdtors)
 {
   size_t i,j;
+  size_t len = VEC_length (tree, cdtors);
 
   i = 0;
   while (i < len)
@@ -1442,7 +1654,7 @@ build_cdtor (bool ctor_p, tree *cdtors, size_t len)
       do
        {
          priority_type p;
-         fn = cdtors[i];
+         fn = VEC_index (tree, cdtors, j);
          p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
          if (j == i)
            priority = p;
@@ -1452,7 +1664,7 @@ build_cdtor (bool ctor_p, tree *cdtors, size_t len)
        }
       while (j < len);
 
-      /* When there is only once constructor and target supports them, do nothing.  */
+      /* When there is only one cdtor and target supports them, do nothing.  */
       if (j == i + 1
          && targetm.have_ctors_dtors)
        {
@@ -1461,14 +1673,10 @@ build_cdtor (bool ctor_p, tree *cdtors, size_t len)
        }
       /* Find the next batch of constructors/destructors with the same
         initialization priority.  */
-      do
+      for (;i < j; i++)
        {
-         priority_type p;
          tree call;
-         fn = cdtors[i];
-         p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
-         if (p != priority)
-           break;
+         fn = VEC_index (tree, cdtors, i);
          call = build_call_expr (fn, 0);
          if (ctor_p)
            DECL_STATIC_CONSTRUCTOR (fn) = 0;
@@ -1479,13 +1687,11 @@ build_cdtor (bool ctor_p, tree *cdtors, size_t len)
             optimizing, we want user to be able to breakpoint in them.  */
          TREE_SIDE_EFFECTS (call) = 1;
          append_to_statement_list (call, &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);
+      cgraph_build_static_cdtor_1 (ctor_p ? 'I' : 'D', body, priority, true);
     }
 }
 
@@ -1552,27 +1758,15 @@ build_cdtor_fns (void)
   if (!VEC_empty (tree, static_ctors))
     {
       gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
-      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);
+      VEC_qsort (tree, static_ctors, compare_ctor);
+      build_cdtor (/*ctor_p=*/true, static_ctors);
     }
 
   if (!VEC_empty (tree, static_dtors))
     {
       gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
-      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);
+      VEC_qsort (tree, static_dtors, compare_dtor);
+      build_cdtor (/*ctor_p=*/false, static_dtors);
     }
 }