public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-10132] c++: Retry the aliasing of base/complete cdtor optimization at import_export_decl time [PR113208]
@ 2024-04-25 18:39 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2024-04-25 18:39 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:c39654e7a431992773b48d61f804494b0d70855f

commit r14-10132-gc39654e7a431992773b48d61f804494b0d70855f
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Apr 25 20:37:10 2024 +0200

    c++: Retry the aliasing of base/complete cdtor optimization at import_export_decl time [PR113208]
    
    When expand_or_defer_fn is called at_eof time, it calls import_export_decl
    and then maybe_clone_body, which uses DECL_ONE_ONLY and comdat name in a
    couple of places to try to optimize cdtors which are known to have the
    same body by making the complete cdtor an alias to base cdtor (and in
    that case also uses *[CD]5* as comdat group name instead of the normal
    comdat group names specific to each mangled name).
    Now, this optimization depends on DECL_ONE_ONLY and DECL_INTERFACE_KNOWN,
    maybe_clone_body and can_alias_cdtor use:
          if (DECL_ONE_ONLY (fn))
            cgraph_node::get_create (clone)->set_comdat_group (cxx_comdat_group (clone));
    ...
      bool can_alias = can_alias_cdtor (fn);
    ...
          /* Tell cgraph if both ctors or both dtors are known to have
             the same body.  */
          if (can_alias
              && fns[0]
              && idx == 1
              && cgraph_node::get_create (fns[0])->create_same_body_alias
                   (clone, fns[0]))
            {
              alias = true;
              if (DECL_ONE_ONLY (fns[0]))
                {
                  /* For comdat base and complete cdtors put them
                     into the same, *[CD]5* comdat group instead of
                     *[CD][12]*.  */
                  comdat_group = cdtor_comdat_group (fns[1], fns[0]);
                  cgraph_node::get_create (fns[0])->set_comdat_group (comdat_group);
                  if (symtab_node::get (clone)->same_comdat_group)
                    symtab_node::get (clone)->remove_from_same_comdat_group ();
                  symtab_node::get (clone)->add_to_same_comdat_group
                    (symtab_node::get (fns[0]));
                }
            }
    and
      /* 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))));
    The following testcase regressed with Marek's r14-5979 change,
    when pr113208_0.C is compiled where the ctor is marked constexpr,
    we no longer perform this optimization, where
    _ZN6vectorI12QualityValueEC2ERKS1_ was emitted in the
    _ZN6vectorI12QualityValueEC5ERKS1_ comdat group and
    _ZN6vectorI12QualityValueEC1ERKS1_ was made an alias to it,
    instead we emit _ZN6vectorI12QualityValueEC2ERKS1_ in
    _ZN6vectorI12QualityValueEC2ERKS1_ comdat group and the same
    content _ZN6vectorI12QualityValueEC1ERKS1_ as separate symbol in
    _ZN6vectorI12QualityValueEC1ERKS1_ comdat group.
    Now, the linker seems to somehow cope with that, eventhough it
    probably keeps both copies of the ctor, but seems LTO can't cope
    with that and Honza doesn't know what it should do in that case
    (linker decides that the prevailing symbol is
    _ZN6vectorI12QualityValueEC2ERKS1_ (from the
    _ZN6vectorI12QualityValueEC2ERKS1_ comdat group) and
    _ZN6vectorI12QualityValueEC1ERKS1_ alias (from the other TU,
    from _ZN6vectorI12QualityValueEC5ERKS1_ comdat group)).
    
    Note, the case where some constructor is marked constexpr in one
    TU and not in another one happens pretty often in libstdc++ when
    one mixes -std= flags used to compile different compilation units.
    
    The reason the optimization doesn't trigger when the constructor is
    constexpr is that expand_or_defer_fn is called in that case much earlier
    than when it is not constexpr; in the former case it is called when we
    try to constant evaluate that constructor.  But DECL_INTERFACE_KNOWN
    is false in that case and comdat_linkage hasn't been called either
    (I think it is desirable, because comdat group is stored in the cgraph
    node and am not sure it is a good idea to create cgraph nodes for
    something that might not be needed later on at all), so maybe_clone_body
    clones the bodies, but doesn't make them as aliases.
    
    The following patch is an attempt to redo this optimization when
    import_export_decl is called at_eof time on the base/complete cdtor
    (or deleting dtor).  It will not do anything if maybe_clone_body
    hasn't been called uyet (the TREE_ASM_WRITTEN check on the
    DECL_MAYBE_IN_CHARGE_CDTOR_P), or when one or both of the base/complete
    cdtors have been lowered already, or when maybe_clone_body called
    maybe_thunk_body and it was successful.  Otherwise retries the
    can_alias_cdtor check and makes the complete cdtor alias to the
    base cdtor with adjustments to the comdat group.
    
    2024-04-25  Jakub Jelinek  <jakub@redhat.com>
    
            PR lto/113208
            * cp-tree.h (maybe_optimize_cdtor): Declare.
            * decl2.cc (import_export_decl): Call it for cloned cdtors.
            * optimize.cc (maybe_optimize_cdtor): New function.
    
            * g++.dg/abi/comdat2.C: New test.
            * g++.dg/abi/comdat5.C: New test.
            * g++.dg/lto/pr113208_0.C: New test.
            * g++.dg/lto/pr113208_1.C: New file.
            * g++.dg/lto/pr113208.h: New file.

Diff:
---
 gcc/cp/cp-tree.h                      |  1 +
 gcc/cp/decl2.cc                       |  3 ++
 gcc/cp/optimize.cc                    | 55 +++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/abi/comdat2.C    | 26 +++++++++++++++++
 gcc/testsuite/g++.dg/abi/comdat5.C    | 28 ++++++++++++++++++
 gcc/testsuite/g++.dg/lto/pr113208.h   | 10 +++++++
 gcc/testsuite/g++.dg/lto/pr113208_0.C | 13 +++++++++
 gcc/testsuite/g++.dg/lto/pr113208_1.C |  6 ++++
 8 files changed, 142 insertions(+)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1dbb577a38d..bafdf63dc63 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7447,6 +7447,7 @@ extern bool handle_module_option (unsigned opt, const char *arg, int value);
 /* In optimize.cc */
 extern tree clone_attrs				(tree);
 extern bool maybe_clone_body			(tree);
+extern void maybe_optimize_cdtor		(tree);
 
 /* In parser.cc */
 extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 1339f210dde..806a2a4bc69 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -3568,6 +3568,9 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  if (DECL_CLONED_FUNCTION_P (decl))
+    maybe_optimize_cdtor (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index ff4fb5be5bd..7147f5e00f1 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -723,3 +723,58 @@ maybe_clone_body (tree fn)
   /* We don't need to process the original function any further.  */
   return 1;
 }
+
+/* If maybe_clone_body is called while the cdtor is still tentative,
+   DECL_ONE_ONLY will be false and so will be can_alias_cdtor (fn).
+   In that case we wouldn't try to optimize using an alias and instead
+   would emit separate base and complete cdtor.  The following function
+   attempts to still optimize that case when we import_export_decl
+   is called first time on one of the clones.  */
+
+void
+maybe_optimize_cdtor (tree orig_decl)
+{
+  tree fns[3];
+  tree fn = DECL_CLONED_FUNCTION (orig_decl);
+  gcc_checking_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (fn));
+  if (DECL_INTERFACE_KNOWN (fn)
+      || !TREE_ASM_WRITTEN (fn)
+      || !DECL_ONE_ONLY (orig_decl)
+      || symtab->global_info_ready)
+    return;
+
+  populate_clone_array (fn, fns);
+
+  if (!fns[0] || !fns[1])
+    return;
+  for (int i = 2 - !fns[2]; i >= 0; --i)
+    if (fns[i] != orig_decl && DECL_INTERFACE_KNOWN (fns[i]))
+      return;
+  DECL_INTERFACE_KNOWN (fn) = 1;
+  comdat_linkage (fn);
+  if (!can_alias_cdtor (fn))
+    return;
+  /* For comdat base and complete cdtors put them into the same,
+     *[CD]5* comdat group instead of *[CD][12]*.  */
+  auto n0 = cgraph_node::get_create (fns[0]);
+  auto n1 = cgraph_node::get_create (fns[1]);
+  auto n2 = fns[2] ? cgraph_node::get_create (fns[1]) : NULL;
+  if (n0->lowered || n1->lowered || (n2 && n2->lowered))
+    return;
+  import_export_decl (fns[0]);
+  n1->definition = false;
+  if (!n0->create_same_body_alias (fns[1], fns[0]))
+    return;
+
+  tree comdat_group = cdtor_comdat_group (fns[1], fns[0]);
+  n1 = cgraph_node::get (fns[1]);
+  n0->set_comdat_group (comdat_group);
+  if (n1->same_comdat_group)
+    n1->remove_from_same_comdat_group ();
+  n1->add_to_same_comdat_group (n0);
+  if (fns[2])
+    n2->add_to_same_comdat_group (n0);
+  import_export_decl (fns[1]);
+  /* Remove the body now that it is an alias.  */
+  release_function_body (fns[1]);
+}
diff --git a/gcc/testsuite/g++.dg/abi/comdat2.C b/gcc/testsuite/g++.dg/abi/comdat2.C
new file mode 100644
index 00000000000..502d00c0582
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/comdat2.C
@@ -0,0 +1,26 @@
+// PR lto/113208
+// { dg-do compile { target { c++11 && { *-*-*gnu* } } } }
+// { dg-additional-options "-O2 -fkeep-inline-functions" }
+// { dg-final { scan-assembler "_ZN1BI1CEC5ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC1ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC2ERKS1_,comdat" } }
+
+template <typename T>
+struct A {
+  int foo () const;
+  A (int, int);
+};
+template <typename T>
+struct B : A<T> {
+  constexpr B (const B &x) : A<T> (1, x.foo ()) {}
+  B () : A<T> (1, 2) {}
+};
+struct C;
+struct D : B<C> {};
+void bar (D);
+
+void
+baz (D x)
+{
+  bar (x);
+}
diff --git a/gcc/testsuite/g++.dg/abi/comdat5.C b/gcc/testsuite/g++.dg/abi/comdat5.C
new file mode 100644
index 00000000000..f6542aa0d4c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/comdat5.C
@@ -0,0 +1,28 @@
+// PR lto/113208
+// { dg-do compile { target { c++11 && { *-*-*gnu* } } } }
+// { dg-additional-options "-O2" }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC5ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC1ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC2ERKS1_,comdat" } }
+
+template <typename T>
+struct A {
+  int foo () const;
+  A (int, int);
+};
+template <typename T>
+struct B : A<T> {
+  constexpr B (const B &x) : A<T> (1, x.foo ()) {}
+  B () : A<T> (1, 2) {}
+};
+struct C;
+struct D : B<C> {};
+void bar (D);
+
+void
+baz (D x)
+{
+  bar (x);
+}
+
+extern template struct B<C>;
diff --git a/gcc/testsuite/g++.dg/lto/pr113208.h b/gcc/testsuite/g++.dg/lto/pr113208.h
new file mode 100644
index 00000000000..7901ede7e92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr113208.h
@@ -0,0 +1,10 @@
+template <typename _Tp> struct _Vector_base {
+  int g() const;
+  _Vector_base(int, int);
+};
+template <typename _Tp>
+struct vector : _Vector_base<_Tp> {
+  CONSTEXPR vector(const vector &__x)
+      : _Vector_base<_Tp>(1, __x.g()) {}
+ vector() : _Vector_base<_Tp>(1, 2) {}
+};
diff --git a/gcc/testsuite/g++.dg/lto/pr113208_0.C b/gcc/testsuite/g++.dg/lto/pr113208_0.C
new file mode 100644
index 00000000000..dbaca558d31
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr113208_0.C
@@ -0,0 +1,13 @@
+// { dg-lto-do link }
+// { dg-lto-options { {-O1 -std=c++20 -flto}} }
+// { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" }
+// { dg-require-linker-plugin "" }
+
+#define CONSTEXPR constexpr
+#include "pr113208.h"
+
+struct QualityValue;
+struct k : vector<QualityValue> {};
+
+void m(k);
+void n(k i) { m(i); }
diff --git a/gcc/testsuite/g++.dg/lto/pr113208_1.C b/gcc/testsuite/g++.dg/lto/pr113208_1.C
new file mode 100644
index 00000000000..f925086b527
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr113208_1.C
@@ -0,0 +1,6 @@
+#define CONSTEXPR 
+#include "pr113208.h"
+
+struct QualityValue;
+vector<QualityValue> values1;
+vector<QualityValue> values{values1};

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-04-25 18:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-25 18:39 [gcc r14-10132] c++: Retry the aliasing of base/complete cdtor optimization at import_export_decl time [PR113208] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).