[clang] [Clang] Retain constructor/destructor variants when symbol must be kept (PR #226572)

Zaara Syeda via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 28 15:02:26 PDT 2026


https://github.com/syzaara updated https://github.com/llvm/llvm-project/pull/226572

>From fa0920dce6c6ed913593bceab00fb2ba983c6997 Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Fri, 25 Sep 2026 15:03:11 -0400
Subject: [PATCH 1/6] [Clang] Retain constructor/destructor variants when
 symbol must be kept

With -mconstructor-aliases, complete constructor and destructor variants
with discardable-if-unused linkage can be silently replaced in the IR
(RAUW) rather than emitted as distinct symbols. This prevents
-fkeep-inline-functions and __attribute__((used)) from retaining the
complete (C1/D1) variants.

Skip RAUW when the declaration requires its symbol to be kept by
introducing structorSymbolMustBeRetained(), which returns true when
__attribute__((used)) is present or -fkeep-inline-functions is active
for an inline definition that is not available_externally.

Assisted-by: IBM Bob
---
 clang/lib/CodeGen/ItaniumCXXABI.cpp           | 15 ++++++-
 clang/test/CodeGen/keep-inline-functions.cpp  | 26 ++++--------
 .../mconstructor-aliases-used-attr.cpp        | 41 +++++++++++++++++++
 3 files changed, 63 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/CodeGen/mconstructor-aliases-used-attr.cpp

diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 754511e6a7d41..1c739e5175d33 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -4735,7 +4735,19 @@ ItaniumCXXABI::RTTIUniquenessKind ItaniumCXXABI::classifyRTTIUniqueness(
 // Find out how to codegen the complete destructor and constructor
 namespace {
 enum class StructorCodegen { Emit, RAUW, Alias, COMDAT };
+} // namespace
+
+// Returns true if the complete constructor/destructor variant must be retained
+// as a distinct symbol rather than being silently replaced in the IR (RAUW).
+static bool
+structorSymbolMustBeRetained(CodeGenModule &CGM, const CXXMethodDecl *MD,
+                              llvm::GlobalValue::LinkageTypes Linkage) {
+  if (MD->hasAttr<UsedAttr>())
+    return true;
+  return CGM.getCodeGenOpts().KeepInlineFunctions && MD->isInlined() &&
+         Linkage != llvm::GlobalValue::AvailableExternallyLinkage;
 }
+
 static StructorCodegen getCodegenToUse(CodeGenModule &CGM,
                                        const CXXMethodDecl *MD) {
   if (!CGM.getCodeGenOpts().CXXCtorDtorAliases)
@@ -4755,7 +4767,8 @@ static StructorCodegen getCodegenToUse(CodeGenModule &CGM,
   }
   llvm::GlobalValue::LinkageTypes Linkage = CGM.getFunctionLinkage(AliasDecl);
 
-  if (llvm::GlobalValue::isDiscardableIfUnused(Linkage))
+  if (llvm::GlobalValue::isDiscardableIfUnused(Linkage) &&
+      !structorSymbolMustBeRetained(CGM, MD, Linkage))
     return StructorCodegen::RAUW;
 
   // FIXME: Should we allow available_externally aliases?
diff --git a/clang/test/CodeGen/keep-inline-functions.cpp b/clang/test/CodeGen/keep-inline-functions.cpp
index 4c56fabaecc00..1c64a1d083895 100644
--- a/clang/test/CodeGen/keep-inline-functions.cpp
+++ b/clang/test/CodeGen/keep-inline-functions.cpp
@@ -5,12 +5,18 @@
 // RUN: %clang_cc1 -O2 -fkeep-inline-functions -emit-llvm %s -o - -triple x86_64-pc-windows-msvc | FileCheck %s --check-prefix=MSVC
 // RUN: %clang_cc1 -O2 -fkeep-inline-functions -emit-llvm %s -o - -triple powerpc64-ibm-aix-xcoff | FileCheck %s
 // RUN: %clang_cc1 -O0 -fkeep-inline-functions -emit-llvm %s -o - -triple powerpc64-ibm-aix-xcoff | FileCheck %s
-// RUN: %clang_cc1 -O0 -mconstructor-aliases -fkeep-inline-functions -emit-llvm %s -o - -triple powerpc64-ibm-aix-xcoff | FileCheck %s --check-prefix=CONSTRUCTOR-ALIASES
+
+// The driver enables -mconstructor-aliases by default for this target, but
+// -cc1 does not, so pass it explicitly here.
+// With -fkeep-inline-functions, C1/D1 must be emitted instead of being
+// silently replaced in the IR (RAUW), so they remain available in the used
+// list.
+// RUN: %clang_cc1 -O0 -mconstructor-aliases -fkeep-inline-functions -emit-llvm %s -o - -triple powerpc64-ibm-aix-xcoff | FileCheck %s
+// RUN: %clang_cc1 -O2 -mconstructor-aliases -fkeep-inline-functions -emit-llvm %s -o - -triple powerpc64-ibm-aix-xcoff | FileCheck %s
 
 // -fkeep-inline-functions retains inline function definitions available in
 // this translation unit. Definitions emitted with available_externally
 // linkage are excluded.
-
 // Retained:
 //   f1  explicit inline and referenced
 //   f2  static inline
@@ -77,22 +83,6 @@ int use(S s) {
 // CHECK-DAG: define {{.*}}@_ZN12TestCtorDtorC2Ev
 // CHECK-DAG: define {{.*}}@_ZN12TestCtorDtorD2Ev
 
-// FIXME: -mconstructor-aliases is enabled by default by the driver for this
-// target, but is not enabled by default for -cc1. With -mconstructor-aliases,
-// the C1/D1 constructor and destructor variants are not emitted as separate
-// definitions.
-
-// CONSTRUCTOR-ALIASES: @llvm{{(\.compiler)?}}.used = appending global [8 x ptr]
-
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_Z2f1i
-// CONSTRUCTOR-ALIASES-DAG: define internal {{.*}}@_ZL2f2i
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_Z2f3i
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_ZN1S2f4Ev
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_Z2f7i
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_Z2f8IiET_S0_
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_ZN12TestCtorDtorC2Ev
-// CONSTRUCTOR-ALIASES-DAG: define {{.*}}@_ZN12TestCtorDtorD2Ev
-
 // MSVC: @llvm{{(\.compiler)?}}.used = appending global [8 x ptr]
 
 // MSVC-DAG: define {{.*}}@"?f1@@YAHH at Z"
diff --git a/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp b/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
new file mode 100644
index 0000000000000..fdff81cb587f0
--- /dev/null
+++ b/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
@@ -0,0 +1,41 @@
+// Test that __attribute__((used)) on a constructor/destructor retains the
+// C1/D1 complete variants when -mconstructor-aliases is active.
+//
+// Without the fix, -mconstructor-aliases causes C1/D1 to be silently replaced
+// in the IR (RAUW) before SetCommonAttributes can add them to llvm.used, so
+// __attribute__((used)) does not work as expected.
+
+// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
+// RUN: %clang_cc1 -O2 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
+// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple x86_64-unknown-linux-gnu \
+// RUN:   | FileCheck %s --check-prefixes=ELF,USED
+
+// Without -mconstructor-aliases the complete variants are always emitted as
+// separate definitions and the normal path handles __attribute__((used)).
+// RUN: %clang_cc1 -O0 -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
+
+struct Foo {
+  __attribute__((used)) Foo() {}
+  __attribute__((used)) ~Foo() {}
+};
+// All four variants must appear in llvm.used/llvm.compiler.used.
+// USED: @llvm{{(\.compiler)?}}.used = appending global [4 x ptr]
+
+// On XCOFF, C1/D1 are full definitions
+// XCOFF-DAG: define {{.*}}@_ZN3FooC1Ev
+// XCOFF-DAG: define {{.*}}@_ZN3FooC2Ev
+// XCOFF-DAG: define {{.*}}@_ZN3FooD1Ev
+// XCOFF-DAG: define {{.*}}@_ZN3FooD2Ev
+
+// On ELF, C1/D1 are aliases to C2/D2
+// ELF-DAG: @_ZN3FooC1Ev = {{.*}}alias{{.*}}@_ZN3FooC2Ev
+// ELF-DAG: @_ZN3FooD1Ev = {{.*}}alias{{.*}}@_ZN3FooD2Ev
+// ELF-DAG: define {{.*}}@_ZN3FooC2Ev
+// ELF-DAG: define {{.*}}@_ZN3FooD2Ev

>From 3c28cfafcfc8053c387f100e7b43c926c0bb85b8 Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Fri, 25 Sep 2026 15:10:12 -0400
Subject: [PATCH 2/6] clang-format

---
 clang/lib/CodeGen/ItaniumCXXABI.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 1c739e5175d33..460950b35c41e 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -4741,7 +4741,7 @@ enum class StructorCodegen { Emit, RAUW, Alias, COMDAT };
 // as a distinct symbol rather than being silently replaced in the IR (RAUW).
 static bool
 structorSymbolMustBeRetained(CodeGenModule &CGM, const CXXMethodDecl *MD,
-                              llvm::GlobalValue::LinkageTypes Linkage) {
+                             llvm::GlobalValue::LinkageTypes Linkage) {
   if (MD->hasAttr<UsedAttr>())
     return true;
   return CGM.getCodeGenOpts().KeepInlineFunctions && MD->isInlined() &&

>From b543fea65f33f054d02d88e4b0c407d3fdc679fa Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Mon, 28 Sep 2026 16:00:00 -0400
Subject: [PATCH 3/6] Address review comments

---
 .../mconstructor-aliases-used-attr.cpp        | 45 +++++++++++++++----
 1 file changed, 36 insertions(+), 9 deletions(-)

diff --git a/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp b/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
index fdff81cb587f0..bf02a91f530fd 100644
--- a/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
+++ b/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
@@ -19,23 +19,50 @@
 // separate definitions and the normal path handles __attribute__((used)).
 // RUN: %clang_cc1 -O0 -emit-llvm %s -o - \
 // RUN:   -triple powerpc64-ibm-aix-xcoff \
-// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
+// RUN:   | FileCheck %s --check-prefixes=XCOFF-NOALIAS,USED
 
 struct Foo {
   __attribute__((used)) Foo() {}
   __attribute__((used)) ~Foo() {}
 };
-// All four variants must appear in llvm.used/llvm.compiler.used.
-// USED: @llvm{{(\.compiler)?}}.used = appending global [4 x ptr]
 
-// On XCOFF, C1/D1 are full definitions
-// XCOFF-DAG: define {{.*}}@_ZN3FooC1Ev
-// XCOFF-DAG: define {{.*}}@_ZN3FooC2Ev
-// XCOFF-DAG: define {{.*}}@_ZN3FooD1Ev
-// XCOFF-DAG: define {{.*}}@_ZN3FooD2Ev
+namespace {
+struct Bar {
+  __attribute__((used)) Bar() {}
+  __attribute__((used)) ~Bar() {}
+};
+}
+
+// All eight variants (four for Foo, four for Bar) must appear in
+// llvm.used/llvm.compiler.used.
+// USED: @llvm{{(\.compiler)?}}.used = appending global [8 x ptr]
+
+// On XCOFF, C1/D1 are full definitions for externally visible Foo,
+// while they are aliases to C2/D2 for internal-linkage Bar.
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
+// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC1Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD1Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
 
-// On ELF, C1/D1 are aliases to C2/D2
+// On ELF, C1/D1 are aliases to C2/D2 for both Foo and Bar.
 // ELF-DAG: @_ZN3FooC1Ev = {{.*}}alias{{.*}}@_ZN3FooC2Ev
 // ELF-DAG: @_ZN3FooD1Ev = {{.*}}alias{{.*}}@_ZN3FooD2Ev
 // ELF-DAG: define {{.*}}@_ZN3FooC2Ev
 // ELF-DAG: define {{.*}}@_ZN3FooD2Ev
+// ELF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// ELF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev

>From daa09fd578c77f7d2589606b858c7bbce7affa36 Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Mon, 28 Sep 2026 16:39:42 -0400
Subject: [PATCH 4/6] Address review comments

---
 .../mconstructor-aliases-used-attr.cpp        | 68 -------------------
 clang/test/CodeGenCXX/attr-used.cpp           | 63 +++++++++++++++++
 2 files changed, 63 insertions(+), 68 deletions(-)
 delete mode 100644 clang/test/CodeGen/mconstructor-aliases-used-attr.cpp

diff --git a/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp b/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
deleted file mode 100644
index bf02a91f530fd..0000000000000
--- a/clang/test/CodeGen/mconstructor-aliases-used-attr.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Test that __attribute__((used)) on a constructor/destructor retains the
-// C1/D1 complete variants when -mconstructor-aliases is active.
-//
-// Without the fix, -mconstructor-aliases causes C1/D1 to be silently replaced
-// in the IR (RAUW) before SetCommonAttributes can add them to llvm.used, so
-// __attribute__((used)) does not work as expected.
-
-// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
-// RUN:   -triple powerpc64-ibm-aix-xcoff \
-// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
-// RUN: %clang_cc1 -O2 -mconstructor-aliases -emit-llvm %s -o - \
-// RUN:   -triple powerpc64-ibm-aix-xcoff \
-// RUN:   | FileCheck %s --check-prefixes=XCOFF,USED
-// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
-// RUN:   -triple x86_64-unknown-linux-gnu \
-// RUN:   | FileCheck %s --check-prefixes=ELF,USED
-
-// Without -mconstructor-aliases the complete variants are always emitted as
-// separate definitions and the normal path handles __attribute__((used)).
-// RUN: %clang_cc1 -O0 -emit-llvm %s -o - \
-// RUN:   -triple powerpc64-ibm-aix-xcoff \
-// RUN:   | FileCheck %s --check-prefixes=XCOFF-NOALIAS,USED
-
-struct Foo {
-  __attribute__((used)) Foo() {}
-  __attribute__((used)) ~Foo() {}
-};
-
-namespace {
-struct Bar {
-  __attribute__((used)) Bar() {}
-  __attribute__((used)) ~Bar() {}
-};
-}
-
-// All eight variants (four for Foo, four for Bar) must appear in
-// llvm.used/llvm.compiler.used.
-// USED: @llvm{{(\.compiler)?}}.used = appending global [8 x ptr]
-
-// On XCOFF, C1/D1 are full definitions for externally visible Foo,
-// while they are aliases to C2/D2 for internal-linkage Bar.
-// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
-// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
-// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
-// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
-// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
-// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
-// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
-// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
-
-// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
-// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
-// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
-// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
-// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC1Ev
-// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
-// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD1Ev
-// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
-
-// On ELF, C1/D1 are aliases to C2/D2 for both Foo and Bar.
-// ELF-DAG: @_ZN3FooC1Ev = {{.*}}alias{{.*}}@_ZN3FooC2Ev
-// ELF-DAG: @_ZN3FooD1Ev = {{.*}}alias{{.*}}@_ZN3FooD2Ev
-// ELF-DAG: define {{.*}}@_ZN3FooC2Ev
-// ELF-DAG: define {{.*}}@_ZN3FooD2Ev
-// ELF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
-// ELF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
-// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
-// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
diff --git a/clang/test/CodeGenCXX/attr-used.cpp b/clang/test/CodeGenCXX/attr-used.cpp
index 8ca37f6194ec2..dd0dc18b6d62c 100644
--- a/clang/test/CodeGenCXX/attr-used.cpp
+++ b/clang/test/CodeGenCXX/attr-used.cpp
@@ -1,4 +1,16 @@
 // RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s
+// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF
+// RUN: %clang_cc1 -O2 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF
+// RUN: %clang_cc1 -O0 -mconstructor-aliases -emit-llvm %s -o - \
+// RUN:   -triple x86_64-unknown-linux-gnu \
+// RUN:   | FileCheck %s --check-prefixes=ELF
+// RUN: %clang_cc1 -O0 -emit-llvm %s -o - \
+// RUN:   -triple powerpc64-ibm-aix-xcoff \
+// RUN:   | FileCheck %s --check-prefixes=XCOFF-NOALIAS
 
 // clang++ not respecting __attribute__((used)) on destructors
 struct X0 {
@@ -25,3 +37,54 @@ struct X2 {
   // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X23barEv
   // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X23fooEv
 };
+
+// Test that __attribute__((used)) on a constructor/destructor retains the
+// C1/D1 complete variants when -mconstructor-aliases is active.
+//
+// Without the fix, -mconstructor-aliases causes C1/D1 to be silently replaced
+// in the IR (RAUW) before SetCommonAttributes can add them to llvm.used, so
+// __attribute__((used)) does not work as expected.
+
+struct Foo {
+  __attribute__((used)) Foo() {}
+  __attribute__((used)) ~Foo() {}
+};
+
+namespace {
+struct Bar {
+  __attribute__((used)) Bar() {}
+  __attribute__((used)) ~Bar() {}
+};
+} // namespace
+
+
+// On XCOFF, C1/D1 are full definitions for externally visible Foo,
+// while they are aliases to C2/D2 for internal-linkage Bar.
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
+// XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
+// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+
+// XCOFF without -mconstructor-aliases: all variants are full definitions.
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev
+// XCOFF-NOALIAS-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC1Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD1Ev
+// XCOFF-NOALIAS-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+
+// On ELF, C1/D1 are aliases to C2/D2 for both Foo and Bar.
+// ELF-DAG: @_ZN3FooC1Ev = {{.*}}alias{{.*}}@_ZN3FooC2Ev
+// ELF-DAG: @_ZN3FooD1Ev = {{.*}}alias{{.*}}@_ZN3FooD2Ev
+// ELF-DAG: define {{.*}}@_ZN3FooC2Ev
+// ELF-DAG: define {{.*}}@_ZN3FooD2Ev
+// ELF-DAG: @_ZN12_GLOBAL__N_13BarC1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// ELF-DAG: @_ZN12_GLOBAL__N_13BarD1Ev = internal {{.*}}alias{{.*}}@_ZN12_GLOBAL__N_13BarD2Ev
+// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev
+// ELF-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD2Ev

>From 3844ea683a9bdaa1c6869181c675be0fc0eefe9e Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Mon, 28 Sep 2026 17:14:50 -0400
Subject: [PATCH 5/6] Address review comments

---
 clang/test/CodeGenCXX/attr-used.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang/test/CodeGenCXX/attr-used.cpp b/clang/test/CodeGenCXX/attr-used.cpp
index dd0dc18b6d62c..f1bb63d798988 100644
--- a/clang/test/CodeGenCXX/attr-used.cpp
+++ b/clang/test/CodeGenCXX/attr-used.cpp
@@ -58,8 +58,11 @@ struct Bar {
 } // namespace
 
 
-// On XCOFF, C1/D1 are full definitions for externally visible Foo,
-// while they are aliases to C2/D2 for internal-linkage Bar.
+// On XCOFF, Foo's constructors/destructors have linkonce_odr linkage. C1/D1
+// are emitted as full definitions since linkonce_odr does not guarantee that
+// an alias and its target will be retained from the same translation unit.
+// Bar's C1/D1 have internal linkage, so they are confined to the translation
+// unit and can safely be aliases to C2/D2.
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev

>From 361c1e8f3bd603a7da39bd0964e3c26e739835ad Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at ca.ibm.com>
Date: Mon, 28 Sep 2026 18:02:02 -0400
Subject: [PATCH 6/6] Address review comments

---
 clang/test/CodeGenCXX/attr-used.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/test/CodeGenCXX/attr-used.cpp b/clang/test/CodeGenCXX/attr-used.cpp
index f1bb63d798988..ced13cd2cbeac 100644
--- a/clang/test/CodeGenCXX/attr-used.cpp
+++ b/clang/test/CodeGenCXX/attr-used.cpp
@@ -58,11 +58,11 @@ struct Bar {
 } // namespace
 
 
-// On XCOFF, Foo's constructors/destructors have linkonce_odr linkage. C1/D1
-// are emitted as full definitions since linkonce_odr does not guarantee that
-// an alias and its target will be retained from the same translation unit.
-// Bar's C1/D1 have internal linkage, so they are confined to the translation
-// unit and can safely be aliases to C2/D2.
+// C1/D1 are aliases to C2/D2 when the linkage is not weak-for-linker.
+// Bar has internal linkage (not weak-for-linker), so C1/D1 are aliases.
+// Foo has linkonce_odr linkage (weak-for-linker): on ELF this produces a
+// COMDAT, but XCOFF does not support COMDAT so C1/D1 are emitted as full
+// definitions instead.
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev
 // XCOFF-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev



More information about the cfe-commits mailing list