re PR c++/41090 (Using static label reference in c++ class constructor produces wrong...
[platform/upstream/gcc.git] / gcc / cp / optimize.c
index 5a67431..40494f2 100644 (file)
@@ -1,6 +1,5 @@
 /* Perform optimizations on tree structure.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   Copyright (C) 1998-2013 Free Software Foundation, Inc.
    Written by Mark Michell (mark@codesourcery.com).
 
 This file is part of GCC.
@@ -24,13 +23,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
+#include "stringpool.h"
 #include "cp-tree.h"
-#include "rtl.h"
-#include "insn-config.h"
 #include "input.h"
-#include "integrate.h"
-#include "toplev.h"
-#include "varray.h"
 #include "params.h"
 #include "hashtab.h"
 #include "target.h"
@@ -38,9 +33,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-inline.h"
 #include "flags.h"
 #include "langhooks.h"
-#include "diagnostic.h"
-#include "tree-dump.h"
-#include "gimple.h"
+#include "diagnostic-core.h"
+#include "dumpfile.h"
+#include "pointer-set.h"
 #include "tree-iterator.h"
 #include "cgraph.h"
 
@@ -104,6 +99,20 @@ clone_body (tree clone, tree fn, void *arg_map)
 
   stmts = DECL_SAVED_TREE (fn);
   walk_tree (&stmts, copy_tree_body_r, &id, NULL);
+
+  /* Also remap the initializer of any static variables so that they (in
+     particular, any label addresses) correspond to the base variant rather
+     than the abstract one.  */
+  if (DECL_NAME (clone) == base_dtor_identifier
+      || DECL_NAME (clone) == base_ctor_identifier)
+    {
+      unsigned ix;
+      tree decl;
+
+      FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (fn), ix, decl)
+        walk_tree (&DECL_INITIAL (decl), copy_tree_body_r, &id, NULL);
+    }
+
   append_to_statement_list_force (stmts, &DECL_SAVED_TREE (clone));
 }
 
@@ -119,7 +128,8 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
 
   /* Call the corresponding complete destructor.  */
   gcc_assert (complete_dtor);
-  call_dtor = build_cxx_call (complete_dtor, 1, &parm);
+  call_dtor = build_cxx_call (complete_dtor, 1, &parm,
+                             tf_warning_or_error);
   add_stmt (call_dtor);
 
   add_stmt (build_stmt (0, LABEL_EXPR, cdtor_label));
@@ -129,7 +139,8 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
                                       virtual_size,
                                       /*global_p=*/false,
                                       /*placement=*/NULL_TREE,
-                                      /*alloc_fn=*/NULL_TREE);
+                                      /*alloc_fn=*/NULL_TREE,
+                                     tf_warning_or_error);
   add_stmt (call_delete);
 
   /* Return the address of the object.  */
@@ -182,29 +193,40 @@ cdtor_comdat_group (tree complete, tree base)
   return get_identifier (grp_name);
 }
 
-/* FN is a function that has a complete body.  Clone the body as
-   necessary.  Returns nonzero if there's no longer any need to
-   process the main body.  */
+/* Returns true iff we can make the base and complete [cd]tor aliases of
+   the same symbol rather than separate functions.  */
 
-bool
-maybe_clone_body (tree fn)
+static bool
+can_alias_cdtor (tree fn)
 {
-  tree comdat_group = NULL_TREE;
-  tree clone;
-  tree fns[3];
-  bool first = true;
-  bool in_charge_parm_used;
-  int idx;
+#ifndef ASM_OUTPUT_DEF
+  /* If aliases aren't supported by the assembler, fail.  */
+  return false;
+#endif
+  /* We can't use an alias if there are virtual bases.  */
+  if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fn)))
+    return false;
+  /* ??? Why not use aliases with -frepo?  */
+  if (flag_use_repository)
+    return false;
+  gcc_assert (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
+             || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn));
+  /* Don't use aliases for weak/linkonce definitions unless we can put both
+     symbols in the same COMDAT group.  */
+  return (DECL_INTERFACE_KNOWN (fn)
+         && (SUPPORTS_ONE_ONLY || !DECL_WEAK (fn))
+         && (!DECL_ONE_ONLY (fn)
+             || (HAVE_COMDAT_GROUP && DECL_WEAK (fn))));
+}
 
-  /* We only clone constructors and destructors.  */
-  if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
-      && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
-    return 0;
+/* FN is a [cd]tor, fns is a pointer to an array of length 3.  Fill fns
+   with pointers to the base, complete, and deleting variants.  */
 
-  /* Emit the DWARF1 abstract instance.  */
-  (*debug_hooks->deferred_inline_function) (fn);
+static void
+populate_clone_array (tree fn, tree *fns)
+{
+  tree clone;
 
-  in_charge_parm_used = CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fn)) != NULL;
   fns[0] = NULL_TREE;
   fns[1] = NULL_TREE;
   fns[2] = NULL_TREE;
@@ -222,6 +244,211 @@ maybe_clone_body (tree fn)
       fns[2] = clone;
     else
       gcc_unreachable ();
+}
+
+/* FN is a constructor or destructor, and there are FUNCTION_DECLs
+   cloned from it nearby.  Instead of cloning this body, leave it
+   alone and create tiny one-call bodies for the cloned
+   FUNCTION_DECLs.  These clones are sibcall candidates, and their
+   resulting code will be very thunk-esque.  */
+
+static bool
+maybe_thunk_body (tree fn, bool force)
+{
+  tree bind, block, call, clone, clone_result, fn_parm, fn_parm_typelist;
+  tree last_arg, modify, *args;
+  int parmno, vtt_parmno, max_parms;
+  tree fns[3];
+
+  if (!force && !flag_declone_ctor_dtor)
+    return 0;
+
+  /* If function accepts variable arguments, give up.  */
+  last_arg = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fn)));
+  if (last_arg != void_list_node)
+    return 0;
+
+  /* If we got this far, we've decided to turn the clones into thunks.  */
+
+  /* We're going to generate code for fn, so it is no longer "abstract."
+     Also make the unified ctor/dtor private to either the translation unit
+     (for non-vague linkage ctors) or the COMDAT group (otherwise).  */
+
+  populate_clone_array (fn, fns);
+  DECL_ABSTRACT (fn) = false;
+  if (!DECL_WEAK (fn))
+    {
+      TREE_PUBLIC (fn) = false;
+      DECL_EXTERNAL (fn) = false;
+      DECL_INTERFACE_KNOWN (fn) = true;
+    }
+  else if (HAVE_COMDAT_GROUP)
+    {
+      tree comdat_group = cdtor_comdat_group (fns[1], fns[0]);
+      DECL_COMDAT_GROUP (fns[0]) = comdat_group;
+      symtab_add_to_same_comdat_group (cgraph_get_create_node (fns[1]),
+                                      cgraph_get_create_node (fns[0]));
+      symtab_add_to_same_comdat_group (symtab_get_node (fn),
+                                      symtab_get_node (fns[0]));
+      if (fns[2])
+       /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
+          virtual, it goes into the same comdat group as well.  */
+       symtab_add_to_same_comdat_group (cgraph_get_create_node (fns[2]),
+                                        symtab_get_node (fns[0]));
+      TREE_PUBLIC (fn) = false;
+      DECL_EXTERNAL (fn) = false;
+      DECL_INTERFACE_KNOWN (fn) = true;
+      /* function_and_variable_visibility doesn't want !PUBLIC decls to
+        have these flags set.  */
+      DECL_WEAK (fn) = false;
+      DECL_COMDAT (fn) = false;
+    }
+
+  /* Find the vtt_parm, if present.  */
+  for (vtt_parmno = -1, parmno = 0, fn_parm = DECL_ARGUMENTS (fn);
+       fn_parm;
+       ++parmno, fn_parm = TREE_CHAIN (fn_parm))
+    {
+      if (DECL_ARTIFICIAL (fn_parm)
+         && DECL_NAME (fn_parm) == vtt_parm_identifier)
+       {
+         /* Compensate for removed in_charge parameter.  */
+         vtt_parmno = parmno;
+         break;
+       }
+    }
+
+  /* Allocate an argument buffer for build_cxx_call().
+     Make sure it is large enough for any of the clones.  */
+  max_parms = 0;
+  FOR_EACH_CLONE (clone, fn)
+    {
+      int length = list_length (DECL_ARGUMENTS (fn));
+      if (length > max_parms)
+        max_parms = length;
+    }
+  args = (tree *) alloca (max_parms * sizeof (tree));
+
+  /* We know that any clones immediately follow FN in TYPE_METHODS.  */
+  FOR_EACH_CLONE (clone, fn)
+    {
+      tree clone_parm;
+
+      /* If we've already generated a body for this clone, avoid
+        duplicating it.  (Is it possible for a clone-list to grow after we
+        first see it?)  */
+      if (DECL_SAVED_TREE (clone) || TREE_ASM_WRITTEN (clone))
+       continue;
+
+      /* Start processing the function.  */
+      start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);
+
+      if (clone == fns[2])
+       {
+         for (clone_parm = DECL_ARGUMENTS (clone); clone_parm;
+              clone_parm = TREE_CHAIN (clone_parm))
+           DECL_ABSTRACT_ORIGIN (clone_parm) = NULL_TREE;
+         /* Build the delete destructor by calling complete destructor and
+            delete function.  */
+         build_delete_destructor_body (clone, fns[1]);
+       }
+      else
+       {
+         /* Walk parameter lists together, creating parameter list for
+            call to original function.  */
+         for (parmno = 0,
+                fn_parm = DECL_ARGUMENTS (fn),
+                fn_parm_typelist = TYPE_ARG_TYPES (TREE_TYPE (fn)),
+                clone_parm = DECL_ARGUMENTS (clone);
+              fn_parm;
+              ++parmno,
+                fn_parm = TREE_CHAIN (fn_parm))
+           {
+             if (parmno == vtt_parmno && ! DECL_HAS_VTT_PARM_P (clone))
+               {
+                 gcc_assert (fn_parm_typelist);
+                 /* Clobber argument with formal parameter type.  */
+                 args[parmno]
+                   = convert (TREE_VALUE (fn_parm_typelist),
+                              null_pointer_node);
+               }
+             else if (parmno == 1 && DECL_HAS_IN_CHARGE_PARM_P (fn))
+               {
+                 tree in_charge
+                   = copy_node (in_charge_arg_for_name (DECL_NAME (clone)));
+                 args[parmno] = in_charge;
+               }
+             /* Map other parameters to their equivalents in the cloned
+                function.  */
+             else
+               {
+                 gcc_assert (clone_parm);
+                 DECL_ABSTRACT_ORIGIN (clone_parm) = NULL;
+                 args[parmno] = clone_parm;
+                 clone_parm = TREE_CHAIN (clone_parm);
+               }
+             if (fn_parm_typelist)
+               fn_parm_typelist = TREE_CHAIN (fn_parm_typelist);
+           }
+
+         /* We built this list backwards; fix now.  */
+         mark_used (fn);
+         call = build_cxx_call (fn, parmno, args, tf_warning_or_error);
+         /* Arguments passed to the thunk by invisible reference should
+            be transmitted to the callee unchanged.  Do not create a
+            temporary and invoke the copy constructor.  The thunking
+            transformation must not introduce any constructor calls.  */
+         CALL_FROM_THUNK_P (call) = 1;
+         block = make_node (BLOCK);
+         if (targetm.cxx.cdtor_returns_this ())
+           {
+             clone_result = DECL_RESULT (clone);
+             modify = build2 (MODIFY_EXPR, TREE_TYPE (clone_result),
+                              clone_result, call);
+             add_stmt (modify);
+             BLOCK_VARS (block) = clone_result;
+           }
+         else
+           {
+             add_stmt (call);
+           }
+         bind = c_build_bind_expr (DECL_SOURCE_LOCATION (clone),
+                                   block, cur_stmt_list);
+         DECL_SAVED_TREE (clone) = push_stmt_list ();
+         add_stmt (bind);
+       }
+
+      DECL_ABSTRACT_ORIGIN (clone) = NULL;
+      expand_or_defer_fn (finish_function (0));
+    }
+  return 1;
+}
+
+/* FN is a function that has a complete body.  Clone the body as
+   necessary.  Returns nonzero if there's no longer any need to
+   process the main body.  */
+
+bool
+maybe_clone_body (tree fn)
+{
+  tree comdat_group = NULL_TREE;
+  tree clone;
+  tree fns[3];
+  bool first = true;
+  int idx;
+  bool need_alias = false;
+
+  /* We only clone constructors and destructors.  */
+  if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
+      && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
+    return 0;
+
+  populate_clone_array (fn, fns);
+
+  /* Remember if we can't have multiple clones for some reason.  We need to
+     check this before we remap local static initializers in clone_body.  */
+  if (!tree_versionable_function_p (fn))
+    need_alias = true;
 
   /* We know that any clones immediately follow FN in the TYPE_METHODS
      list.  */
@@ -230,9 +457,6 @@ maybe_clone_body (tree fn)
     {
       tree parm;
       tree clone_parm;
-      int parmno;
-      bool alias = false;
-      struct pointer_map_t *decl_map;
 
       clone = fns[idx];
       if (!clone)
@@ -241,6 +465,7 @@ maybe_clone_body (tree fn)
       /* Update CLONE's source position information to match FN's.  */
       DECL_SOURCE_LOCATION (clone) = DECL_SOURCE_LOCATION (fn);
       DECL_DECLARED_INLINE_P (clone) = DECL_DECLARED_INLINE_P (fn);
+      DECL_DECLARED_CONSTEXPR_P (clone) = DECL_DECLARED_CONSTEXPR_P (fn);
       DECL_COMDAT (clone) = DECL_COMDAT (fn);
       DECL_WEAK (clone) = DECL_WEAK (fn);
 
@@ -266,39 +491,57 @@ maybe_clone_body (tree fn)
       clone_parm = DECL_ARGUMENTS (clone);
       /* Update the `this' parameter, which is always first.  */
       update_cloned_parm (parm, clone_parm, first);
-      parm = TREE_CHAIN (parm);
-      clone_parm = TREE_CHAIN (clone_parm);
+      parm = DECL_CHAIN (parm);
+      clone_parm = DECL_CHAIN (clone_parm);
       if (DECL_HAS_IN_CHARGE_PARM_P (fn))
-       parm = TREE_CHAIN (parm);
+       parm = DECL_CHAIN (parm);
       if (DECL_HAS_VTT_PARM_P (fn))
-       parm = TREE_CHAIN (parm);
+       parm = DECL_CHAIN (parm);
       if (DECL_HAS_VTT_PARM_P (clone))
-       clone_parm = TREE_CHAIN (clone_parm);
+       clone_parm = DECL_CHAIN (clone_parm);
       for (; parm;
-          parm = TREE_CHAIN (parm), clone_parm = TREE_CHAIN (clone_parm))
+          parm = DECL_CHAIN (parm), clone_parm = DECL_CHAIN (clone_parm))
        /* Update this parameter.  */
        update_cloned_parm (parm, clone_parm, first);
+    }
+
+  bool can_alias = can_alias_cdtor (fn);
+
+  /* If we decide to turn clones into thunks, they will branch to fn.
+     Must have original function available to call.  */
+  if (!can_alias && maybe_thunk_body (fn, need_alias))
+    {
+      pop_from_top_level ();
+      /* We still need to emit the original function.  */
+      return 0;
+    }
+
+  /* Emit the DWARF1 abstract instance.  */
+  (*debug_hooks->deferred_inline_function) (fn);
+
+  /* We know that any clones immediately follow FN in the TYPE_METHODS list. */
+  for (idx = 0; idx < 3; idx++)
+    {
+      tree parm;
+      tree clone_parm;
+      int parmno;
+      struct pointer_map_t *decl_map;
+      bool alias = false;
+
+      clone = fns[idx];
+      if (!clone)
+       continue;
 
       /* Start processing the function.  */
       start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);
 
       /* Tell cgraph if both ctors or both dtors are known to have
         the same body.  */
-      if (!in_charge_parm_used
+      if (can_alias
          && fns[0]
          && idx == 1
-         && !flag_use_repository
-         && DECL_INTERFACE_KNOWN (fns[0])
-         && (SUPPORTS_ONE_ONLY || !DECL_WEAK (fns[0]))
-         && (!DECL_ONE_ONLY (fns[0])
-             || (HAVE_COMDAT_GROUP
-                 && DECL_WEAK (fns[0])
-                 /* Don't optimize synthetized virtual dtors, because then
-                    the deleting and other dtors are emitted when needed
-                    and so it is not certain we would emit both
-                    deleting and complete/base dtors in the comdat group.  */
-                 && (fns[2] == NULL || !DECL_ARTIFICIAL (fn))))
-         && cgraph_same_body_alias (clone, fns[0]))
+         && cgraph_same_body_alias (cgraph_get_create_node (fns[0]),
+                                    clone, fns[0]))
        {
          alias = true;
          if (DECL_ONE_ONLY (fns[0]))
@@ -308,18 +551,38 @@ maybe_clone_body (tree fn)
                 *[CD][12]*.  */
              comdat_group = cdtor_comdat_group (fns[1], fns[0]);
              DECL_COMDAT_GROUP (fns[0]) = comdat_group;
+             symtab_add_to_same_comdat_group (symtab_get_node (clone),
+                                              symtab_get_node (fns[0]));
            }
-         emit_associated_thunks (clone);
        }
 
       /* Build the delete destructor by calling complete destructor
          and delete function.  */
       if (idx == 2)
-       build_delete_destructor_body (clone, fns[1]);
+       {
+         build_delete_destructor_body (clone, fns[1]);
+         /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
+            virtual, it goes into the same comdat group as well.  */
+         if (comdat_group)
+           symtab_add_to_same_comdat_group
+              (cgraph_get_create_node (clone),
+               symtab_get_node (fns[0]));
+       }
       else if (alias)
        /* No need to populate body.  */ ;
       else
        {
+         /* If we can't have multiple copies of FN (say, because there's a
+            static local initialized with the address of a label), we need
+            to use an alias for the complete variant.  */
+         if (idx == 1 && need_alias)
+           {
+             if (DECL_STRUCT_FUNCTION (fn)->cannot_be_copied_set)
+               sorry (DECL_STRUCT_FUNCTION (fn)->cannot_be_copied_reason, fn);
+             else
+               sorry ("making multiple clones of %qD", fn);
+           }
+
           /* Remap the parameters.  */
           decl_map = pointer_map_create ();
           for (parmno = 0,
@@ -327,7 +590,7 @@ maybe_clone_body (tree fn)
                 clone_parm = DECL_ARGUMENTS (clone);
               parm;
               ++parmno,
-                parm = TREE_CHAIN (parm))
+                parm = DECL_CHAIN (parm))
             {
               /* Map the in-charge parameter to an appropriate constant.  */
               if (DECL_HAS_IN_CHARGE_PARM_P (fn) && parmno == 1)
@@ -346,7 +609,7 @@ maybe_clone_body (tree fn)
                     {
                       DECL_ABSTRACT_ORIGIN (clone_parm) = parm;
                       *pointer_map_insert (decl_map, parm) = clone_parm;
-                      clone_parm = TREE_CHAIN (clone_parm);
+                      clone_parm = DECL_CHAIN (clone_parm);
                     }
                   /* Otherwise, map the VTT parameter to `NULL'.  */
                   else
@@ -358,7 +621,7 @@ maybe_clone_body (tree fn)
               else
                 {
                   *pointer_map_insert (decl_map, parm) = clone_parm;
-                  clone_parm = TREE_CHAIN (clone_parm);
+                  clone_parm = DECL_CHAIN (clone_parm);
                 }
             }
 
@@ -383,22 +646,16 @@ maybe_clone_body (tree fn)
       finish_function (0);
       BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
       if (alias)
-       expand_or_defer_fn_1 (clone);
+       {
+         if (expand_or_defer_fn_1 (clone))
+           emit_associated_thunks (clone);
+       }
       else
        expand_or_defer_fn (clone);
       first = false;
     }
   pop_from_top_level ();
 
-  if (comdat_group)
-    {
-      DECL_COMDAT_GROUP (fns[1]) = comdat_group;
-      if (fns[2])
-       /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
-          virtual, it goes into the same comdat group as well.  */
-       DECL_COMDAT_GROUP (fns[2]) = comdat_group;
-    }
-
   /* We don't need to process the original function any further.  */
   return 1;
 }