On 9/27/26 11:01 AM, Iain Sandoe wrote:
While chasing two testsuite fails for -Os this wrong-code bug surfaced.
Fix tested on x86_64-linux/darwin.  OK for trunk / backports?
thanks
Iain

--- 8< ---

For Darwin, at least, when -Os is selected we attempt to declone cdtors
since the platform does not support strong symbol aliases the clones
are emitted verbatim, leading to larger binaries.

The unified cdtor is a private implementational detail local to a TU
and so we should not emit a thunk if that will not be emitted.

Hmm, isn't the unified ctor linkonce when the separate ctors are?

Likewise, a  thunk and the unified body must be selected together at
link time; for a vague-linkage cdtor COMDAT groups support this but,
without COMDAT, there is no guarantee and so we have to decline the
declone and emit ordinary clones instead.

It's desirable that they are selected together, but is it really necessary? As long as one is selected, I'd think they should work interchangeably.

gcc/cp/ChangeLog:

        * optimize.cc (maybe_thunk_body): Decline declone for targets
        without COMDAT when the item is vague-linkage.

gcc/testsuite/ChangeLog:

        * g++.dg/opt/declone4.C: New test.
        * g++.dg/opt/declone5.C: New test.

Signed-off-by: Iain Sandoe <[email protected]>
---
  gcc/cp/optimize.cc                  |  8 +++++++
  gcc/testsuite/g++.dg/opt/declone4.C | 37 +++++++++++++++++++++++++++++
  gcc/testsuite/g++.dg/opt/declone5.C | 33 +++++++++++++++++++++++++
  3 files changed, 78 insertions(+)
  create mode 100644 gcc/testsuite/g++.dg/opt/declone4.C
  create mode 100644 gcc/testsuite/g++.dg/opt/declone5.C

diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 0afeefd5704..d81e8e4ba15 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -287,6 +287,14 @@ maybe_thunk_body (tree fn, bool force)
    if (ctor_omit_inherited_parms (fns[0]))
      return 0;
+ /* A thunk and the unified body must be selected together at link time;
+     for a vague-linkage cdtor COMDAT groups support this.  Without COMDAT
+     there is no guarantee and so we have to decline and emit ordinary clones
+     instead.  FORCE means the body must not be copied at all, so it is still
+     honoured.  */
+  if (!force && DECL_WEAK (fn) && !HAVE_COMDAT_GROUP)
+    return 0;
+
    /* Don't diagnose deprecated or unavailable cdtors just because they
       have thunks emitted for them.  */
    auto du = make_temp_override (deprecated_state,
diff --git a/gcc/testsuite/g++.dg/opt/declone4.C 
b/gcc/testsuite/g++.dg/opt/declone4.C
new file mode 100644
index 00000000000..1d3f87d3126
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/declone4.C
@@ -0,0 +1,37 @@
+// { dg-do run }
+// { dg-options "-fdeclone-ctor-dtor -save-temps" }
+
+// A cdtor whose unified body this translation unit does emit should be
+// decloned.  The C1/C2 clones become thunks and the unified C4 body is
+// emitted either private to the translation unit here, or to the COMDAT
+// group on targets that support them.
+
+struct V { int v; V (); };
+V::V () : v (1) {}
+
+struct B : virtual V
+{
+  int n;
+  B (int);
+};
+
+B::B (int x) : n (x * 3 + 1) {}
+
+struct D : B                           // uses B::C2
+{
+  D (int x) : B (x) {}
+};
+
+B *p;
+D *q;
+
+int
+main ()
+{
+  p = new B (2);                       // uses B::C1
+  q = new D (5);
+  return !(p->n == 7 && q->n == 16 && p->v == 1 && q->v == 1);
+}
+
+// { dg-final { scan-assembler "_ZN1BC4Ei" } }
+// { dg-final { scan-assembler-not "globl\[^\n\]*_ZN1BC4Ei" } }
diff --git a/gcc/testsuite/g++.dg/opt/declone5.C 
b/gcc/testsuite/g++.dg/opt/declone5.C
new file mode 100644
index 00000000000..d714f99de40
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/declone5.C
@@ -0,0 +1,33 @@
+// { dg-do run }
+// { dg-options "-fdeclone-ctor-dtor -save-temps" }
+
+// We cannot emit a thunk to a unified cdtor on targets without comdat
+// support, since the thunk and the target must be selected together for
+// vague linkage.
+
+struct V { int v; V () : v (1) {} };
+
+template <typename T>
+struct B : virtual V
+{
+  T n;
+  B (T x) : n (x * 3 + 1) {}
+};
+
+struct D : B<int>                        // uses B<int>::C2
+{
+  D (int x) : B<int> (x) {}
+};
+
+B<int> *p;
+D *q;
+
+int
+main ()
+{
+  p = new B<int> (2);                    // uses B<int>::C1
+  q = new D (5);
+  return !(p->n == 7 && q->n == 16 && p->v == 1 && q->v == 1);
+}
+
+// { dg-final { scan-assembler-not "_ZN1BIiEC4Ei" { target { ! comdat_group } 
} } }

Reply via email to