diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -441,8 +441,10 @@ if (const SectionAttr *SA = D.getAttr()) var->setSection(SA->getName()); - if (D.hasAttr()) + if (D.hasAttr()) { CGM.addUsedGlobal(var); + CGM.maybeSetRetain(var); + } // We may have to cast the constant because of the initializer // mismatch above. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1048,6 +1048,9 @@ template void MaybeHandleStaticInExternC(const SomeDecl *D, llvm::GlobalValue *GV); + /// Set !retain metadata if the target supports. + void maybeSetRetain(llvm::GlobalObject *GO); + /// Add a global to a list to be added to the llvm.used metadata. void addUsedGlobal(llvm::GlobalValue *GV); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1886,6 +1886,8 @@ if (D) { if (auto *GV = dyn_cast(GO)) { + if (D->hasAttr()) + maybeSetRetain(GV); if (auto *SA = D->getAttr()) GV->addAttribute("bss-section", SA->getName()); if (auto *SA = D->getAttr()) @@ -1897,6 +1899,8 @@ } if (auto *F = dyn_cast(GO)) { + if (D->hasAttr()) + maybeSetRetain(F); if (auto *SA = D->getAttr()) if (!D->getAttr()) F->addFnAttr("implicit-section-name", SA->getName()); @@ -2065,6 +2069,13 @@ } } +void CodeGenModule::maybeSetRetain(llvm::GlobalObject *GO) { + const llvm::Triple &T = getTarget().getTriple(); + if (T.isOSFreeBSD() || T.isOSFuchsia() || T.isOSLinux()) + GO->setMetadata(llvm::LLVMContext::MD_retain, + llvm::MDNode::get(getLLVMContext(), None)); +} + void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) { assert((isa(GV) || !GV->isDeclaration()) && "Only globals with definition can force usage."); diff --git a/clang/test/CodeGen/attr-used.c b/clang/test/CodeGen/attr-used.c --- a/clang/test/CodeGen/attr-used.c +++ b/clang/test/CodeGen/attr-used.c @@ -1,10 +1,25 @@ -// RUN: %clang_cc1 -emit-llvm -o %t %s -// RUN: grep '@llvm.used = .*@a0' %t -// RUN: grep '@llvm.used = .*@g0' %t -// RUN: grep '@llvm.used = .*@f0' %t -// RUN: grep '@llvm.used = .*@f1.l0' %t +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux %s -o - | FileCheck %s --check-prefixes=CHECK,RETAIN +// RUN: %clang_cc1 -emit-llvm -triple powerpc64-freebsd %s -o - | FileCheck %s --check-prefixes=CHECK,RETAIN +// RUN: %clang_cc1 -emit-llvm -triple aarch64-fuchsia %s -o - | FileCheck %s --check-prefixes=CHECK,RETAIN +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin %s -o - | FileCheck %s --check-prefixes=CHECK,NORETAIN +// RETAIN: @r0 ={{.*}} constant i32 {{.*}} !retain ![[#EMPTY:]] +// RETAIN: @f1.l0 = internal global i32 {{.*}} !retain ![[#EMPTY]] +// RETAIN: @a0 ={{.*}} global i32 {{.*}} !retain ![[#EMPTY]] +// RETAIN: @g0 ={{.*}} global i32 {{.*}} !retain ![[#EMPTY]] + +/// Set !retain only on Linux and FreeBSD. +// NORETAIN-NOT: !retain + +// CHECK: @llvm.used ={{.*}} @f0 +// CHECK-SAME: @f1.l0 +// CHECK-SAME: @g0 +// CHECK-SAME: @a0 + +// RETAIN: [[#EMPTY]] = !{} + +const int r0 __attribute__((used)) = 42; int g0 __attribute__((used)); static void __attribute__((used)) f0(void) { diff --git a/clang/test/CodeGenCXX/attr-used.cpp b/clang/test/CodeGenCXX/attr-used.cpp --- a/clang/test/CodeGenCXX/attr-used.cpp +++ b/clang/test/CodeGenCXX/attr-used.cpp @@ -1,10 +1,13 @@ // RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux -o - %s | FileCheck %s --check-prefix=RETAIN // : clang++ not respecting __attribute__((used)) on destructors struct X0 { // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X0C1Ev + // RETAIN-DAG: define linkonce_odr {{.*}} @_ZN2X0C1Ev({{.*}} !retain __attribute__((used)) X0() {} // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X0D1Ev + // RETAIN-DAG: define linkonce_odr {{.*}} @_ZN2X0D1Ev({{.*}} !retain __attribute__((used)) ~X0() {} }; @@ -12,6 +15,7 @@ struct X1 { struct Nested { // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X16Nested1fEv + // RETAIN-DAG: define linkonce_odr {{.*}} @_ZN2X16Nested1fEv({{.*}} !retain void __attribute__((used)) f() {} }; }; @@ -24,4 +28,5 @@ // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X23barEv // CHECK-DAG: define linkonce_odr {{.*}} @_ZN2X23fooEv + // RETAIN-DAG: define linkonce_odr {{.*}} @_ZN2X23barEv({{.*}} !retain }; diff --git a/clang/test/CodeGenCXX/extern-c.cpp b/clang/test/CodeGenCXX/extern-c.cpp --- a/clang/test/CodeGenCXX/extern-c.cpp +++ b/clang/test/CodeGenCXX/extern-c.cpp @@ -75,11 +75,14 @@ // CHECK-NOT: @unused // CHECK-NOT: @duplicate_internal // CHECK: @internal_var = internal alias i32, i32* @_ZL12internal_var + // CHECK-NOT: !retain // CHECK-NOT: @unused // CHECK-NOT: @duplicate_internal // CHECK: @internal_fn = internal alias i32 (), i32 ()* @_ZL11internal_fnv + // CHECK-NOT: !retain // CHECK-NOT: @unused // CHECK-NOT: @duplicate_internal + // CHECK: define internal i32 @_ZL11internal_fnv() } namespace PR19411 {