]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR c++/41090 (Using static label reference in c++ class constructor produces wrong...
authorJason Merrill <jason@gcc.gnu.org>
Mon, 23 Dec 2013 17:49:47 +0000 (12:49 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 23 Dec 2013 17:49:47 +0000 (12:49 -0500)
PR c++/41090
Add -fdeclone-ctor-dtor.
gcc/cp/
* optimize.c (can_alias_cdtor, populate_clone_array): Split out
from maybe_clone_body.
(maybe_thunk_body): New function.
(maybe_clone_body): Call it.
* mangle.c (write_mangled_name): Remove code to suppress
writing of mangled name for cloned constructor or destructor.
(write_special_name_constructor): Handle decloned constructor.
(write_special_name_destructor): Handle decloned destructor.
* method.c (trivial_fn_p): Handle decloning.
* semantics.c (expand_or_defer_fn_1): Clone after setting linkage.
gcc/c-family/
* c.opt: Add -fdeclone-ctor-dtor.
* c-opts.c (c_common_post_options): Default to on iff -Os.
gcc/
* cgraph.h (struct cgraph_node): Add calls_comdat_local.
(symtab_comdat_local_p, symtab_in_same_comdat_p): New.
* cif-code.def: Add USES_COMDAT_LOCAL.
* symtab.c (verify_symtab_base): Make sure we don't refer to a
comdat-local symbol from outside its comdat.
* cgraph.c (verify_cgraph_node): Likewise.
* cgraphunit.c (mark_functions_to_output): Don't mark comdat-locals.
* ipa.c (symtab_remove_unreachable_nodes): Likewise.
(function_and_variable_visibility): Handle comdat-local fns.
* ipa-cp.c (determine_versionability): Don't clone comdat-locals.
* ipa-inline-analysis.c (compute_inline_parameters): Update
calls_comdat_local.
* ipa-inline-transform.c (inline_call): Likewise.
(save_inline_function_body): Don't clear DECL_COMDAT_GROUP.
* ipa-inline.c (can_inline_edge_p): Check calls_comdat_local.
* lto-cgraph.c (input_overwrite_node): Read calls_comdat_local.
(lto_output_node): Write it.
* symtab.c (symtab_dissolve_same_comdat_group_list): Clear
DECL_COMDAT_GROUP for comdat-locals.
include/
* demangle.h (enum gnu_v3_ctor_kinds):
Added literal gnu_v3_unified_ctor.
(enum gnu_v3_ctor_kinds):
Added literal gnu_v3_unified_dtor.
libiberty/
* cp-demangle.c (cplus_demangle_fill_ctor,cplus_demangle_fill_dtor):
Handle unified ctor/dtor.
(d_ctor_dtor_name): Handle unified ctor/dtor.

From-SVN: r206182

28 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-opts.c
gcc/c-family/c.opt
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphunit.c
gcc/cif-code.def
gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/cp/mangle.c
gcc/cp/method.c
gcc/cp/optimize.c
gcc/cp/semantics.c
gcc/doc/invoke.texi
gcc/ipa-cp.c
gcc/ipa-inline-analysis.c
gcc/ipa-inline-transform.c
gcc/ipa-inline.c
gcc/ipa.c
gcc/lto-cgraph.c
gcc/symtab.c
gcc/testsuite/g++.dg/ext/label13a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/opt/declone1.C [new file with mode: 0644]
include/ChangeLog
include/demangle.h
libiberty/ChangeLog
libiberty/cp-demangle.c

index b2f9f6e1619b82e14425bccd42daa82ba9e3dbf5..ab6875e977f4de823b794d63e2039cf298409886 100644 (file)
@@ -1,3 +1,25 @@
+2013-12-23  Jason Merrill  <jason@redhat.com>
+
+       * cgraph.h (struct cgraph_node): Add calls_comdat_local.
+       (symtab_comdat_local_p, symtab_in_same_comdat_p): New.
+       * cif-code.def: Add USES_COMDAT_LOCAL.
+       * symtab.c (verify_symtab_base): Make sure we don't refer to a
+       comdat-local symbol from outside its comdat.
+       * cgraph.c (verify_cgraph_node): Likewise.
+       * cgraphunit.c (mark_functions_to_output): Don't mark comdat-locals.
+       * ipa.c (symtab_remove_unreachable_nodes): Likewise.
+       (function_and_variable_visibility): Handle comdat-local fns.
+       * ipa-cp.c (determine_versionability): Don't clone comdat-locals.
+       * ipa-inline-analysis.c (compute_inline_parameters): Update
+       calls_comdat_local.
+       * ipa-inline-transform.c (inline_call): Likewise.
+       (save_inline_function_body): Don't clear DECL_COMDAT_GROUP.
+       * ipa-inline.c (can_inline_edge_p): Check calls_comdat_local.
+       * lto-cgraph.c (input_overwrite_node): Read calls_comdat_local.
+       (lto_output_node): Write it.
+       * symtab.c (symtab_dissolve_same_comdat_group_list): Clear
+       DECL_COMDAT_GROUP for comdat-locals.
+
 2013-12-23   H.J. Lu  <hongjiu.lu@intel.com>
 
        * config/i386/i386.c (processor_target_table): Move Bonnell and
index ffd8eff13242a5dbfe52fc9695e21cb2c2f57a3b..1e0da89b347df69ecb5fd6dd82e0263467c9a3d8 100644 (file)
@@ -1,3 +1,10 @@
+2013-12-23  Stuart Hastings <stuart@apple.com>
+           Bill Maddox  <maddox@google.com>
+           Jason Merrill  <jason@redhat.com>
+
+       * c.opt: Add -fdeclone-ctor-dtor.
+       * c-opts.c (c_common_post_options): Default to on iff -Os.
+
 2013-12-18  Balaji V. Iyer  <balaji.v.iyer@intel.com>
 
        * c-common.c (c_common_attribute_table): Added "cilk simd function"
index f368cab289f804036919b12f95b378f53dd8d5e7..3576f7d57c1439379c57f0d38c2b225fbc75efa0 100644 (file)
@@ -899,6 +899,10 @@ c_common_post_options (const char **pfilename)
   if (warn_implicit_function_declaration == -1)
     warn_implicit_function_declaration = flag_isoc99;
 
+  /* Declone C++ 'structors if -Os.  */
+  if (flag_declone_ctor_dtor == -1)
+    flag_declone_ctor_dtor = optimize_size;
+
   if (cxx_dialect >= cxx11)
     {
       /* If we're allowing C++0x constructs, don't warn about C++98
index bfca1e0e6f612edffb9abd09a74e9b7035a2781d..d270f77ae6ba8ab9260a8ea8e5820defb83f9f23 100644 (file)
@@ -890,6 +890,10 @@ fdeduce-init-list
 C++ ObjC++ Var(flag_deduce_init_list) Init(0)
 -fdeduce-init-list     enable deduction of std::initializer_list for a template type parameter from a brace-enclosed initializer-list
 
+fdeclone-ctor-dtor
+C++ ObjC++ Var(flag_declone_ctor_dtor) Init(-1)
+Factor complex constructors and destructors to favor space over speed
+
 fdefault-inline
 C++ ObjC++ Ignore
 Does nothing.  Preserved for backward compatibility.
index 9501afa0c668c051ca4dbfc77b5faf33fb922ff8..ccd150c81450fba860e565f58aab38fa9b168607 100644 (file)
@@ -2666,10 +2666,18 @@ verify_cgraph_node (struct cgraph_node *node)
          error_found = true;
        }
     }
+  bool check_comdat = symtab_comdat_local_p (node);
   for (e = node->callers; e; e = e->next_caller)
     {
       if (verify_edge_count_and_frequency (e))
        error_found = true;
+      if (check_comdat
+         && !symtab_in_same_comdat_p (e->caller, node))
+       {
+         error ("comdat-local function called by %s outside its comdat",
+                identifier_to_locale (e->caller->name ()));
+         error_found = true;
+       }
       if (!e->inline_failed)
        {
          if (node->global.inlined_to
index 0a88da3889e3faece9fd248aa491a8901a429539..69b97a7e703df44f072204a76e93d2c76b9871f8 100644 (file)
@@ -428,6 +428,9 @@ public:
   unsigned tm_clone : 1;
   /* True if this decl is a dispatcher for function versions.  */
   unsigned dispatcher_function : 1;
+  /* True if this decl calls a COMDAT-local function.  This is set up in
+     compute_inline_parameters and inline_call.  */
+  unsigned calls_comdat_local : 1;
 };
 
 
@@ -1490,4 +1493,22 @@ symtab_can_be_discarded (symtab_node *node)
              && node->resolution != LDPR_PREVAILING_DEF_IRONLY
              && node->resolution != LDPR_PREVAILING_DEF_IRONLY_EXP));
 }
+
+/* Return true if NODE is local to a particular COMDAT group, and must not
+   be named from outside the COMDAT.  This is used for C++ decloned
+   constructors.  */
+
+static inline bool
+symtab_comdat_local_p (symtab_node *node)
+{
+  return (node->same_comdat_group && !TREE_PUBLIC (node->decl));
+}
+
+/* Return true if ONE and TWO are part of the same COMDAT group.  */
+
+static inline bool
+symtab_in_same_comdat_p (symtab_node *one, symtab_node *two)
+{
+  return DECL_COMDAT_GROUP (one->decl) == DECL_COMDAT_GROUP (two->decl);
+}
 #endif  /* GCC_CGRAPH_H  */
index 28f51162bba98f9b9862e8a55050b43160806304..679c9eca8184878238cb17d97e09e9c556a61573 100644 (file)
@@ -1244,7 +1244,8 @@ mark_functions_to_output (void)
              for (next = cgraph (node->same_comdat_group);
                   next != node;
                   next = cgraph (next->same_comdat_group))
-               if (!next->thunk.thunk_p && !next->alias)
+               if (!next->thunk.thunk_p && !next->alias
+                   && !symtab_comdat_local_p (next))
                  next->process = 1;
            }
        }
index e71123dc6092cd3f1691d2d3dda8cc6d7a356030..be4af6a0a5d6a892a972723c0cb3fef2cc2f55cc 100644 (file)
@@ -106,3 +106,6 @@ DEFCIFCODE(TARGET_OPTION_MISMATCH, N_("target specific option mismatch"))
 
 /* We can't inline because of mismatched optimization levels.  */
 DEFCIFCODE(OPTIMIZATION_MISMATCH, N_("optimization level attribute mismatch"))
+
+/* We can't inline because the callee refers to comdat-local symbols.  */
+DEFCIFCODE(USES_COMDAT_LOCAL, N_("callee refers to comdat-local symbols"))
index e4896d5c0fab429ef60db0dc8ffbf2706d560a9d..52400421d6419df7d1d982da0b2b0571f2866a53 100644 (file)
@@ -1,3 +1,19 @@
+2013-12-23  Stuart Hastings <stuart@apple.com>
+           Bill Maddox  <maddox@google.com>
+           Jason Merrill  <jason@redhat.com>
+
+       PR c++/41090
+       * optimize.c (can_alias_cdtor, populate_clone_array): Split out
+       from maybe_clone_body.
+       (maybe_thunk_body): New function.
+       (maybe_clone_body): Call it.
+       * mangle.c (write_mangled_name): Remove code to suppress
+       writing of mangled name for cloned constructor or destructor.
+       (write_special_name_constructor): Handle decloned constructor.
+       (write_special_name_destructor): Handle decloned destructor.
+       * method.c (trivial_fn_p): Handle decloning.
+       * semantics.c (expand_or_defer_fn_1): Clone after setting linkage.
+
 2013-12-23  Marek Polacek  <polacek@redhat.com>
 
        PR c++/59111
index 5d06b371865c3ca8f21c68598c7ebb48208651e1..a184dcc0c1bcb6b9588f3cc939132f1f75e08ee5 100644 (file)
@@ -10185,7 +10185,9 @@ grokdeclarator (const cp_declarator *declarator,
            /* The TYPE_DECL is "abstract" because there will be
               clones of this constructor/destructor, and there will
               be copies of this TYPE_DECL generated in those
-              clones.  */
+              clones.  The decloning optimization (for space) may
+               revert this subsequently if it determines that
+               the clones should share a common implementation.  */
            DECL_ABSTRACT (decl) = 1;
        }
       else if (current_class_type
index 8a24d6c8cfd93defe7ea8e3a58c087be7f0c075f..d99062dcb5ec4e3244c61aaa2bd0dc49e17582d0 100644 (file)
@@ -689,13 +689,6 @@ write_mangled_name (const tree decl, bool top_level)
     mangled_name:;
       write_string ("_Z");
       write_encoding (decl);
-      if (DECL_LANG_SPECIFIC (decl)
-         && (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (decl)
-             || DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl)))
-       /* We need a distinct mangled name for these entities, but
-          we should never actually output it.  So, we append some
-          characters the assembler won't like.  */
-       write_string (" *INTERNAL* ");
     }
 }
 
@@ -1653,25 +1646,21 @@ write_identifier (const char *identifier)
                    ::= C2   # base object constructor
                    ::= C3   # complete object allocating constructor
 
-   Currently, allocating constructors are never used.
-
-   We also need to provide mangled names for the maybe-in-charge
-   constructor, so we treat it here too.  mangle_decl_string will
-   append *INTERNAL* to that, to make sure we never emit it.  */
+   Currently, allocating constructors are never used.  */
 
 static void
 write_special_name_constructor (const tree ctor)
 {
   if (DECL_BASE_CONSTRUCTOR_P (ctor))
     write_string ("C2");
+  /* This is the old-style "[unified]" constructor.
+     In some cases, we may emit this function and call
+     it from the clones in order to share code and save space.  */
+  else if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (ctor))
+    write_string ("C4");
   else
     {
-      gcc_assert (DECL_COMPLETE_CONSTRUCTOR_P (ctor)
-                 /* Even though we don't ever emit a definition of
-                    the old-style destructor, we still have to
-                    consider entities (like static variables) nested
-                    inside it.  */
-                 || DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (ctor));
+      gcc_assert (DECL_COMPLETE_CONSTRUCTOR_P (ctor));
       write_string ("C1");
     }
 }
@@ -1681,11 +1670,7 @@ write_special_name_constructor (const tree ctor)
 
      <special-name> ::= D0 # deleting (in-charge) destructor
                    ::= D1 # complete object (in-charge) destructor
-                   ::= D2 # base object (not-in-charge) destructor
-
-   We also need to provide mangled names for the maybe-incharge
-   destructor, so we treat it here too.  mangle_decl_string will
-   append *INTERNAL* to that, to make sure we never emit it.  */
+                   ::= D2 # base object (not-in-charge) destructor  */
 
 static void
 write_special_name_destructor (const tree dtor)
@@ -1694,14 +1679,14 @@ write_special_name_destructor (const tree dtor)
     write_string ("D0");
   else if (DECL_BASE_DESTRUCTOR_P (dtor))
     write_string ("D2");
+  else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (dtor))
+    /* This is the old-style "[unified]" destructor.
+       In some cases, we may emit this function and call
+       it from the clones in order to share code and save space.  */
+    write_string ("D4");
   else
     {
-      gcc_assert (DECL_COMPLETE_DESTRUCTOR_P (dtor)
-                 /* Even though we don't ever emit a definition of
-                    the old-style destructor, we still have to
-                    consider entities (like static variables) nested
-                    inside it.  */
-                 || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (dtor));
+      gcc_assert (DECL_COMPLETE_DESTRUCTOR_P (dtor));
       write_string ("D1");
     }
 }
index 740536573cb64a36be8476c5d37fc16c061e0cf0..e79a9221a99e7eafff33b384462c7b77784b5287 100644 (file)
@@ -477,7 +477,8 @@ trivial_fn_p (tree fn)
     return false;
 
   /* If fn is a clone, get the primary variant.  */
-  fn = DECL_ORIGIN (fn);
+  if (tree prim = DECL_CLONED_FUNCTION (fn))
+    fn = prim;
   return type_has_trivial_fn (DECL_CONTEXT (fn), special_function_p (fn));
 }
 
index f1b09bfd55e5cdb7f706d9ab307a7ba4a98206ab..40494f27a5598eab51fb7a84cfec2a613061a955 100644 (file)
@@ -193,30 +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;
-  bool need_alias = false;
+#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;
@@ -234,6 +244,206 @@ 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.  */
@@ -247,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)
@@ -296,26 +503,44 @@ maybe_clone_body (tree fn)
           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])))
-         && !flag_syntax_only
-         /* Set linkage flags appropriately before
-            cgraph_create_function_alias looks at them.  */
-         && expand_or_defer_fn_1 (clone)
-         && cgraph_same_body_alias (cgraph_get_node (fns[0]),
+         && cgraph_same_body_alias (cgraph_get_create_node (fns[0]),
                                     clone, fns[0]))
        {
          alias = true;
index c08d53f48985b389705f1dca62ba8d0b082f836d..1a5948478b4968ab9533c636a1aef69df93fee96 100644 (file)
@@ -3901,20 +3901,6 @@ expand_or_defer_fn_1 (tree fn)
 
   gcc_assert (DECL_SAVED_TREE (fn));
 
-  /* If this is a constructor or destructor body, we have to clone
-     it.  */
-  if (maybe_clone_body (fn))
-    {
-      /* We don't want to process FN again, so pretend we've written
-        it out, even though we haven't.  */
-      TREE_ASM_WRITTEN (fn) = 1;
-      /* If this is an instantiation of a constexpr function, keep
-        DECL_SAVED_TREE for explain_invalid_constexpr_fn.  */
-      if (!is_instantiation_of_constexpr (fn))
-       DECL_SAVED_TREE (fn) = NULL_TREE;
-      return false;
-    }
-
   /* We make a decision about linkage for these functions at the end
      of the compilation.  Until that point, we do not want the back
      end to output them -- but we do want it to see the bodies of
@@ -3962,6 +3948,20 @@ expand_or_defer_fn_1 (tree fn)
        }
     }
 
+  /* If this is a constructor or destructor body, we have to clone
+     it.  */
+  if (maybe_clone_body (fn))
+    {
+      /* We don't want to process FN again, so pretend we've written
+        it out, even though we haven't.  */
+      TREE_ASM_WRITTEN (fn) = 1;
+      /* If this is an instantiation of a constexpr function, keep
+        DECL_SAVED_TREE for explain_invalid_constexpr_fn.  */
+      if (!is_instantiation_of_constexpr (fn))
+       DECL_SAVED_TREE (fn) = NULL_TREE;
+      return false;
+    }
+
   /* There's no reason to do any of the work here if we're only doing
      semantic analysis; this code just generates RTL.  */
   if (flag_syntax_only)
index a10b6f567452188447c5b6966fdc465637431b98..60675c585e010e6c5b3d5e50fbf494322e0971a2 100644 (file)
@@ -7339,6 +7339,18 @@ branch-less equivalents.
 
 Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
 
+@item -fdeclone-ctor-dtor
+@opindex fdeclone-ctor-dtor
+The C++ ABI requires multiple entry points for constructors and
+destructors: one for a base subobject, one for a complete object, and
+one for a virtual destructor that calls operator delete afterwards.
+For a hierarchy with virtual bases, the base and complete variants are
+clones, which means two copies of the function.  With this option, the
+base and complete variants are changed to be thunks that call a common
+implementation.
+
+Enabled by @option{-Os}.
+
 @item -fdelete-null-pointer-checks
 @opindex fdelete-null-pointer-checks
 Assume that programs cannot safely dereference null pointers, and that
index 305ad2d72dbd8860d1463fb55d53b8eee397eaa6..b484d053f163d9b42e0e339b53ccbbd7dc2d1672 100644 (file)
@@ -437,6 +437,10 @@ determine_versionability (struct cgraph_node *node)
         coexist, but that may not be worth the effort.  */
       reason = "function has SIMD clones";
     }
+  /* Don't clone decls local to a comdat group; it breaks and for C++
+     decloned constructors, inlining is always better anyway.  */
+  else if (symtab_comdat_local_p (node))
+    reason = "comdat-local function";
 
   if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
     fprintf (dump_file, "Function %s/%i is not versionable, reason: %s.\n",
index 9e9087f30db1fc982ddee33fec5a832f1f78b9ac..21e52a193ffaa43b3cbd54012905a1ec0d3365fd 100644 (file)
@@ -2796,6 +2796,11 @@ compute_inline_parameters (struct cgraph_node *node, bool early)
     }
   estimate_function_body_sizes (node, early);
 
+  for (e = node->callees; e; e = e->next_callee)
+    if (symtab_comdat_local_p (e->callee))
+      break;
+  node->calls_comdat_local = (e != NULL);
+
   /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
   info->time = info->self_time;
   info->size = info->self_size;
index 7fb4ab97fa4a09e14a3e3b495392eefbb5b1ef13..71d7800cea1245ad9eedf59d592f3306c99cb1e6 100644 (file)
@@ -272,6 +272,18 @@ inline_call (struct cgraph_edge *e, bool update_original,
    inline_update_overall_summary (to);
   new_size = inline_summary (to)->size;
 
+  if (callee->calls_comdat_local)
+    to->calls_comdat_local = true;
+  else if (to->calls_comdat_local && symtab_comdat_local_p (callee))
+    {
+      struct cgraph_edge *se = to->callees;
+      for (; se; se = se->next_callee)
+       if (se->inline_failed && symtab_comdat_local_p (se->callee))
+         break;
+      if (se == NULL)
+       to->calls_comdat_local = false;
+    }
+
 #ifdef ENABLE_CHECKING
   /* Verify that estimated growth match real growth.  Allow off-by-one
      error due to INLINE_SIZE_SCALE roudoff errors.  */
@@ -369,7 +381,6 @@ save_inline_function_body (struct cgraph_node *node)
   /* The function will be short lived and removed after we inline all the clones,
      but make it internal so we won't confuse ourself.  */
   DECL_EXTERNAL (first_clone->decl) = 0;
-  DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
   TREE_PUBLIC (first_clone->decl) = 0;
   DECL_COMDAT (first_clone->decl) = 0;
   first_clone->ipa_transforms_to_apply.release ();
index 9fd5d4183d0cf5586b5b934b72f625b2ea78c836..f6a26a882e694410111c597e569e4ced405f4d66 100644 (file)
@@ -241,7 +241,7 @@ report_inline_failed_reason (struct cgraph_edge *e)
 
    if REPORT is true, output reason to the dump file.  
 
-   if DISREGARD_LIMITES is true, ignore size limits.*/
+   if DISREGARD_LIMITS is true, ignore size limits.*/
 
 static bool
 can_inline_edge_p (struct cgraph_edge *e, bool report,
@@ -271,6 +271,11 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
       e->inline_failed = CIF_BODY_NOT_AVAILABLE;
       inlinable = false;
     }
+  else if (callee->calls_comdat_local)
+    {
+      e->inline_failed = CIF_USES_COMDAT_LOCAL;
+      inlinable = false;
+    }
   else if (!inline_summary (callee)->inlinable 
           || (caller_cfun && fn_contains_cilk_spawn_p (caller_cfun)))
     {
index 1ec4b5fc3b7e62cf61e241c1a55610384dc92ee7..3c19288f26cd7a2a55f4aaae145d24ab36326859 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -362,14 +362,17 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
              enqueue_node (origin_node, &first, reachable);
            }
          /* If any symbol in a comdat group is reachable, force
-            all other in the same comdat group to be also reachable.  */
+            all externally visible symbols in the same comdat
+            group to be reachable as well.  Comdat-local symbols
+            can be discarded if all uses were inlined.  */
          if (node->same_comdat_group)
            {
              symtab_node *next;
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
-               if (!pointer_set_insert (reachable, next))
+               if (!symtab_comdat_local_p (next)
+                   && !pointer_set_insert (reachable, next))
                  enqueue_node (next, &first, reachable);
            }
          /* Mark references as reachable.  */
@@ -969,14 +972,14 @@ function_and_variable_visibility (bool whole_program)
          node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
                                      || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
                                      && TREE_PUBLIC (node->decl));
-         symtab_make_decl_local (node->decl);
          node->resolution = LDPR_PREVAILING_DEF_IRONLY;
-         if (node->same_comdat_group)
+         if (node->same_comdat_group && TREE_PUBLIC (node->decl))
            /* cgraph_externally_visible_p has already checked all other nodes
               in the group and they will all be made local.  We need to
               dissolve the group at once so that the predicate does not
               segfault though. */
            symtab_dissolve_same_comdat_group_list (node);
+         symtab_make_decl_local (node->decl);
        }
 
       if (node->thunk.thunk_p
index 44cfa286fad93199d0a879813b0b4e637eec2d30..7834ed04160b97311bfc5b1721c41f3c21895812 100644 (file)
@@ -518,6 +518,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_startup, 1);
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
+  bp_pack_value (&bp, node->calls_comdat_local, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
                LDPR_NUM_KNOWN, node->resolution);
@@ -993,6 +994,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_startup = bp_unpack_value (bp, 1);
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
+  node->calls_comdat_local = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
                                     LDPR_NUM_KNOWN);
index dc700e7c73542d0ed34502b7b6eb3066c9afb424..8d36cae7b2712caae13214a2210e9f414b629e27 100644 (file)
@@ -538,6 +538,10 @@ symtab_dissolve_same_comdat_group_list (symtab_node *node)
     {
       next = n->same_comdat_group;
       n->same_comdat_group = NULL;
+      /* Clear DECL_COMDAT_GROUP for comdat locals, since
+         make_decl_local doesn't.  */
+      if (!TREE_PUBLIC (n->decl))
+       DECL_COMDAT_GROUP (n->decl) = NULL_TREE;
       n = next;
     }
   while (n != node);
@@ -844,6 +848,21 @@ verify_symtab_base (symtab_node *node)
          n = n->same_comdat_group;
        }
       while (n != node);
+      if (symtab_comdat_local_p (node))
+       {
+         struct ipa_ref_list *refs = &node->ref_list;
+         struct ipa_ref *ref;
+         for (int i = 0; ipa_ref_list_referring_iterate (refs, i, ref); ++i)
+           {
+             if (!symtab_in_same_comdat_p (ref->referring, node))
+               {
+                 error ("comdat-local symbol referred to by %s outside its "
+                        "comdat",
+                        identifier_to_locale (ref->referring->name()));
+                 error_found = true;
+               }
+           }
+       }
     }
   return error_found;
 }
@@ -911,6 +930,10 @@ symtab_make_decl_local (tree decl)
 {
   rtx rtl, symbol;
 
+  /* Avoid clearing DECL_COMDAT_GROUP on comdat-local decls.  */
+  if (TREE_PUBLIC (decl) == 0)
+    return;
+
   if (TREE_CODE (decl) == VAR_DECL)
     DECL_COMMON (decl) = 0;
   else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
diff --git a/gcc/testsuite/g++.dg/ext/label13a.C b/gcc/testsuite/g++.dg/ext/label13a.C
new file mode 100644 (file)
index 0000000..3be8e31
--- /dev/null
@@ -0,0 +1,25 @@
+// PR c++/41090
+// { dg-do run }
+// { dg-options "-save-temps" }
+// { dg-final { scan-assembler "_ZN1CC4Ev" } }
+// { dg-final cleanup-saved-temps }
+
+int i;
+struct A { A() {} };
+struct C: virtual A
+{
+  C();
+};
+
+C::C()
+{
+  static void *labelref = &&label;
+  goto *labelref;
+ label: i = 1;
+}
+
+int main()
+{
+  C c;
+  return (i != 1);
+}
diff --git a/gcc/testsuite/g++.dg/opt/declone1.C b/gcc/testsuite/g++.dg/opt/declone1.C
new file mode 100644 (file)
index 0000000..1f935b7
--- /dev/null
@@ -0,0 +1,21 @@
+// { dg-options "-fdeclone-ctor-dtor -O3" }
+
+struct V {};
+
+template <class T>
+struct A: virtual V {
+  static A* p;
+  A();
+};
+
+template <class T>
+A<T>::A()
+{
+  if (!p)
+    p = new A();
+}
+
+int main()
+{
+  A<int> a;
+}
index 9109c7694e69e09cf44101c543dc0fd0f2c70636..a06aec0e47834ae4a35d24838dab88e9483b5212 100644 (file)
@@ -1,3 +1,10 @@
+2013-12-23  Bill Maddox  <maddox@google.com>
+
+       * demangle.h (enum gnu_v3_ctor_kinds):
+       Added literal gnu_v3_unified_ctor.
+       (enum gnu_v3_ctor_kinds):
+       Added literal gnu_v3_unified_dtor.
+
 2013-12-04  Richard Sandiford  <rdsandiford@googlemail.com>
 
        * longlong.h: New file.
index 58bf547d5474abeca17da64345b3417936a69768..bbad71bd8c742519283ebeaf17e9686b71c2b140 100644 (file)
@@ -173,6 +173,10 @@ enum gnu_v3_ctor_kinds {
   gnu_v3_complete_object_ctor = 1,
   gnu_v3_base_object_ctor,
   gnu_v3_complete_object_allocating_ctor,
+  /* These are not part of the V3 ABI.  Unified constructors are generated
+     as a speed-for-space optimization when the -fdeclone-ctor-dtor option
+     is used, and are always internal symbols.  */
+  gnu_v3_unified_ctor,
   gnu_v3_object_ctor_group
 };
 
@@ -188,6 +192,10 @@ enum gnu_v3_dtor_kinds {
   gnu_v3_deleting_dtor = 1,
   gnu_v3_complete_object_dtor,
   gnu_v3_base_object_dtor,
+  /* These are not part of the V3 ABI.  Unified destructors are generated
+     as a speed-for-space optimization when the -fdeclone-ctor-dtor option
+     is used, and are always internal symbols.  */
+  gnu_v3_unified_dtor,
   gnu_v3_object_dtor_group
 };
 
index 825ddd2d1fa4bee4b20e575d0251ef4ce3b2b595..1e8d0fe66385de61011ef9006fc7a020fdce9417 100644 (file)
@@ -1,3 +1,9 @@
+2013-12-23  Bill Maddox  <maddox@google.com>
+
+       * cp-demangle.c (cplus_demangle_fill_ctor,cplus_demangle_fill_dtor):
+       Handle unified ctor/dtor.
+       (d_ctor_dtor_name): Handle unified ctor/dtor.
+
 2013-11-22  Cary Coutant  <ccoutant@google.com>
     
        PR other/59195
index 029151e320aadddd23642470434b5aa90761f9e1..7f51268e1151d262c55fb6735efeae73b60b7f32 100644 (file)
@@ -2107,6 +2107,9 @@ d_ctor_dtor_name (struct d_info *di)
          case '3':
            kind = gnu_v3_complete_object_allocating_ctor;
            break;
+          case '4':
+           kind = gnu_v3_unified_ctor;
+           break;
          case '5':
            kind = gnu_v3_object_ctor_group;
            break;
@@ -2132,6 +2135,10 @@ d_ctor_dtor_name (struct d_info *di)
          case '2':
            kind = gnu_v3_base_object_dtor;
            break;
+          /*  digit '3' is not used */
+         case '4':
+           kind = gnu_v3_unified_dtor;
+           break;
          case '5':
            kind = gnu_v3_object_dtor_group;
            break;