Author: Zaara Syeda Date: 2026-09-30T13:58:56-04:00 New Revision: 7d812d2b8aad397ed65a664ca2304500ba311b3f
URL: https://github.com/llvm/llvm-project/commit/7d812d2b8aad397ed65a664ca2304500ba311b3f DIFF: https://github.com/llvm/llvm-project/commit/7d812d2b8aad397ed65a664ca2304500ba311b3f.diff LOG: [Clang] Retain constructor/destructor variants when symbol must be kept (#226572) 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 Added: Modified: clang/docs/ReleaseNotes.md clang/lib/CodeGen/ItaniumCXXABI.cpp clang/test/CodeGen/keep-inline-functions.cpp clang/test/CodeGenCXX/attr-used.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index ca0846e917ac4..81e4d1fc8360f 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -271,6 +271,11 @@ features cannot lower the translation-unit ABI level; path share one module cache, and is only sound when no module needs the path -- a lookup that would have resolved through an ignored path simply fails. +- Added support for `-fkeep-inline-functions` in Clang. This option forces + inline function definitions that are available in the current translation + unit to be emitted into the object file, even when they are inlined into all + callers or are otherwise unused. + ### Deprecated Compiler Flags ### Modified Compiler Flags @@ -779,6 +784,13 @@ features cannot lower the translation-unit ABI level; a new-expression is now correctly rejected in a constant expression because it does not select a replaceable global allocation function. (#GH212211) +- Fixed `__attribute__((used))` and `-fkeep-inline-functions` being + incorrectly ignored for complete constructors and destructors (C1/D1 variants) + when `-mconstructor-aliases` is active. C1/D1 variants were previously + replaced with their base variants (C2/D2) in the IR before retention could + take effect, causing them to be dropped from llvm.used and omitted from + the object file. (#GH226572) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 699649046d244..35964b8e0d0b1 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -4737,7 +4737,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) @@ -4757,7 +4769,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@Z" diff --git a/clang/test/CodeGenCXX/attr-used.cpp b/clang/test/CodeGenCXX/attr-used.cpp index 8ca37f6194ec2..26fe86a82a223 100644 --- a/clang/test/CodeGenCXX/attr-used.cpp +++ b/clang/test/CodeGenCXX/attr-used.cpp @@ -1,12 +1,13 @@ // RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s - -// clang++ not respecting __attribute__((used)) on destructors -struct X0 { - // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X0C1Ev - __attribute__((used)) X0() {} - // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X0D1Ev - __attribute__((used)) ~X0() {} -}; +// 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 // PR19743: not emitting __attribute__((used)) inline methods in nested classes. struct X1 { @@ -25,3 +26,57 @@ 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 + + +// 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 +// 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 + +// Without -mconstructor-aliases: all variants are full definitions. +// CHECK-DAG: define linkonce_odr {{.*}}@_ZN3FooC1Ev +// CHECK-DAG: define linkonce_odr {{.*}}@_ZN3FooC2Ev +// CHECK-DAG: define linkonce_odr {{.*}}@_ZN3FooD1Ev +// CHECK-DAG: define linkonce_odr {{.*}}@_ZN3FooD2Ev +// CHECK-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC1Ev +// CHECK-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarC2Ev +// CHECK-DAG: define internal {{.*}}@_ZN12_GLOBAL__N_13BarD1Ev +// CHECK-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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
