diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2648,7 +2648,14 @@ def Used : InheritableAttr { let Spellings = [GCC<"used">]; let Subjects = SubjectList<[NonLocalVar, Function, ObjCMethod]>; - let Documentation = [Undocumented]; + let Documentation = [UsedDocs]; + let SimpleHandler = 1; +} + +def Retain : InheritableAttr { + let Spellings = [GCC<"retain">]; + let Subjects = SubjectList<[NonLocalVar, Function, ObjCMethod]>; + let Documentation = [RetainDocs]; let SimpleHandler = 1; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -57,6 +57,41 @@ let Heading = "section, __declspec(allocate)"; } +def UsedDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The attribute, when attached to a function definition, causes the function to +be emitted even if it appears that the function is not referenced and can +otherwise be omitted. + +The attribute, when attached to a variable definition with static storage, +causes the variable to be emitted even if it appears that the variable is not +referenced. + +On macOS and Windows, this attribute prevents the defining section from being +garbage collected by the linker. + +On ELF targets, this attribute does not prevent garbage collection. You need to +additionally specify the ``retain`` attribute to emit the function or variable +in a unique section which prevents garbage collection by supported linkers. + }]; +} + +def RetainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute only has effects on ELF targets that support SHF_GNU_RETAIN. +The attribute, when attached to a function or variable definition, if the +function or variable is emitted, causes the function or variable to be emitted +in a unique section with SHF_GNU_RETAIN flag. This ELF section flag prevents +garbage collection of the section by supported linkers (GNU ld and gold from +binutils 2.36 onwards, LLD 13 or newer). + +Note: this attribute is usually used together with the ``used`` attribute +because ``used`` is not implied. + }]; +} + def InitPriorityDocs : Documentation { let Category = DocCatVariable; let Content = [{ 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 @@ -443,6 +443,8 @@ if (D.hasAttr()) CGM.addUsedGlobal(var); + if (D.hasAttr()) + CGM.setRetain(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 the !retain metadata. + void setRetain(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()) + setRetain(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()) + setRetain(F); if (auto *SA = D->getAttr()) if (!D->getAttr()) F->addFnAttr("implicit-section-name", SA->getName()); @@ -2065,6 +2069,11 @@ } } +void CodeGenModule::setRetain(llvm::GlobalObject *GO) { + 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/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2831,6 +2831,11 @@ NewAttr->setInherited(true); New->addAttr(NewAttr); } + if (RetainAttr *OldAttr = Old->getMostRecentDecl()->getAttr()) { + RetainAttr *NewAttr = OldAttr->clone(Context); + NewAttr->setInherited(true); + New->addAttr(NewAttr); + } if (!Old->hasAttrs() && !New->hasAttrs()) return; @@ -2953,7 +2958,7 @@ } // Already handled. - if (isa(I)) + if (isa(I) || isa(I)) continue; if (mergeDeclAttribute(*this, New, I, LocalAMK)) @@ -13300,6 +13305,13 @@ VD->dropAttr(); } } + if (RetainAttr *Attr = VD->getAttr()) { + if (!Attr->isInherited() && !VD->isThisDeclarationADefinition()) { + Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition) + << Attr; + VD->dropAttr(); + } + } const DeclContext *DC = VD->getDeclContext(); // If there's a #pragma GCC visibility in scope, and this isn't a class diff --git a/clang/test/CodeGen/attr-retain.c b/clang/test/CodeGen/attr-retain.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-retain.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +/// Set !retain regardless of the target. The backend will lower !retain to +/// SHF_GNU_RETAIN on ELF and ignore the metadata for other binary formats. +// CHECK: @c0 ={{.*}} constant i32 {{.*}} !retain ![[#EMPTY:]] +// CHECK: @foo.l0 = internal global i32 {{.*}} !retain ![[#EMPTY]] +// CHECK: @g0 ={{.*}} global i32 {{.*}} !retain ![[#EMPTY]] +// CHECK-NEXT: @g1 ={{.*}} global i32 {{.*}} !retain ![[#EMPTY]] +// CHECK-NEXT: @g3 = internal global i32 {{.*}} !retain ![[#EMPTY]] +// CHECK-NEXT: @g4 = internal global i32 0, section ".data.g"{{.*}} !retain ![[#EMPTY]] + +const int c0 __attribute__((retain)) = 42; + +void foo() { + static int l0 __attribute__((retain)) = 2; +} + +__attribute__((retain)) int g0; +int g1 __attribute__((retain)); +__attribute__((retain)) static int g2; +__attribute__((used, retain)) static int g3; +__attribute__((used, retain, section(".data.g"))) static int g4; + +// CHECK: define dso_local void @f0(){{.*}} !retain ![[#EMPTY]] +// CHECK-NOT: @f1 +// CHECK: define internal void @f2(){{.*}} !retain ![[#EMPTY]] + +void __attribute__((retain)) f0(void) {} +static void __attribute__((retain)) f1(void) {} +static void __attribute__((used, retain)) f2(void) {} + +// CHECK: [[#EMPTY]] = !{} diff --git a/clang/test/CodeGenCXX/attr-retain.cpp b/clang/test/CodeGenCXX/attr-retain.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-retain.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -Werror %s -o - | FileCheck %s + +struct X0 { + // RETAIN: define linkonce_odr{{.*}} @_ZN2X0C1Ev({{.*}} !retain + __attribute__((used, retain)) X0() {} + // RETAIN: define linkonce_odr{{.*}} @_ZN2X0D1Ev({{.*}} !retain + __attribute__((used, retain)) ~X0() {} +}; + +struct X1 { + struct Nested { + // RETAIN-NOT: 2f0Ev + // RETAIN: define linkonce_odr{{.*}} @_ZN2X16Nested2f1Ev({{.*}} !retain + void __attribute__((retain)) f0() {} + void __attribute__((used, retain)) f1() {} + }; +}; + +// CHECK: define internal void @_ZN10merge_declL4funcEv(){{.*}} !retain +namespace merge_decl { +static void func(); +void bar() { void func() __attribute__((used, retain)); } +static void func() {} +} // namespace merge_decl + +namespace instantiate_member { +template +struct S { + void __attribute__((used, retain)) f() {} +}; + +void test() { + // CHECK: define linkonce_odr{{.*}} void @_ZN18instantiate_member1SIiE1fEv({{.*}} !retain + S a; +} +} // namespace instantiate_member 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 @@ -70,16 +70,19 @@ __attribute__((used)) static int duplicate_internal_fn() { return 0; } } - // CHECK: @llvm.used = appending global {{.*}} @internal_var {{.*}} @internal_fn + // CHECK: @llvm.used = appending global {{.*}} @internal_var {{.*}} @internal_fn // 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 { diff --git a/clang/test/Sema/attr-retain.c b/clang/test/Sema/attr-retain.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-retain.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +/// We allow 'retain' on non-ELF targets because 'retain' is often used together +/// with 'used'. 'used' has GC root semantics on macOS and Windows. We want +/// users to just write retain,used and don't need to dispatch on binary formats. + +extern char test1[] __attribute__((retain)); // expected-warning {{'retain' attribute ignored on a non-definition declaration}} +extern const char test2[] __attribute__((retain)); // expected-warning {{'retain' attribute ignored on a non-definition declaration}} +const char test3[] __attribute__((retain)) = ""; + +struct __attribute__((retain)) s { // expected-warning {{'retain' attribute only applies to variables with non-local storage, functions, and Objective-C methods}} +}; + +int g0 __attribute__((retain(0))); // expected-error {{'retain' attribute takes no arguments}} + +void foo() { + static int a __attribute__((retain)); + int b __attribute__((retain)); // expected-warning {{'retain' attribute only applies to variables with non-local storage, functions, and Objective-C methods}} +} + +__attribute__((used)) static void f0(); + +/// Test attribute merging. +int tentative; +int tentative __attribute__((retain)); +extern int tentative; +int tentative = 0;