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,55 @@ let Heading = "section, __declspec(allocate)"; } +def UsedDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute, when attached to a function or variable definition, indicates +that there may be references to the entity which are not apparent in the source +code. For example, it may be referenced from inline ``asm``, or it may be +found through a dynamic symbol or section lookup. + +The compiler must emit the definition even if it appears to be unused, and it +must not apply optimizations which depend on fully understanding how the entity +is used. + +Whether this attribute has any effect on the linker depends on the target and +the linker. Most linkers support the feature of section garbage collection +(``--gc-sections``), also known as "dead stripping" (``ld64 -dead_strip``) or +discarding unreferenced sections (``link.exe /OPT:REF``). On COFF and Mach-O +targets (Windows and Apple platforms), the `used` attribute prevents symbols +from being removed by linker section GC. On ELF targets, it has no effect on its +own, and the linker may remove the definition if it is not otherwise referenced. +This linker GC can be avoided by also adding the ``retain`` attribute. Note +that ``retain`` requires special support from the linker; see that attribute's +documentation for further information. + }]; +} + +def RetainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute, when attached to a function or variable definition, prevents +section garbage collection in the linker. It does not prevent other discard +mechanisms, such as archive member selection, and COMDAT group resolution. + +If the compiler does not emit the definition, e.g. because it was not used in +the translation unit or the compiler was able to eliminate all of the uses, +this attribute has no effect. This attribute is typically combined with the +``used`` attribute to force the definition to be emitted and preserved into the +final linked image. + +This attribute is only necessary on ELF targets; other targets prevent section +garbage collection by the linker when using the ``used`` attribute alone. +Using the attributes together should result in consistent behavior across +targets. + +This attribute requires the linker to support the ``SHF_GNU_RETAIN`` extension. +This support is available in GNU ``ld`` and ``gold`` as of binutils 2.36, as +well as in ``ld.lld`` 13. + }]; +} + 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 @@ -441,7 +441,9 @@ if (const SectionAttr *SA = D.getAttr()) var->setSection(SA->getName()); - if (D.hasAttr()) + if (D.hasAttr()) + CGM.addUsedGlobal(var); + else if (D.hasAttr()) CGM.addUsedOrCompilerUsedGlobal(var); // We may have to cast the constant because of the initializer 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 @@ -1896,6 +1896,8 @@ if (D) { if (auto *GV = dyn_cast(GO)) { + if (D->hasAttr()) + addUsedGlobal(GV); if (auto *SA = D->getAttr()) GV->addAttribute("bss-section", SA->getName()); if (auto *SA = D->getAttr()) @@ -1907,6 +1909,8 @@ } if (auto *F = dyn_cast(GO)) { + if (D->hasAttr()) + addUsedGlobal(F); if (auto *SA = D->getAttr()) if (!D->getAttr()) F->addFnAttr("implicit-section-name", SA->getName()); 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)) @@ -13304,6 +13309,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,29 @@ +// 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 {{.*}} +// CHECK: @foo.l0 = internal global i32 {{.*}} +// CHECK: @g0 ={{.*}} global i32 {{.*}} +// CHECK-NEXT: @g1 ={{.*}} global i32 {{.*}} +// CHECK-NEXT: @g3 = internal global i32 {{.*}} +// CHECK-NEXT: @g4 = internal global i32 0, section ".data.g"{{.*}} + +// CHECK: @llvm.used = appending global [8 x i8*] [i8* bitcast (i32* @c0 to i8*), i8* bitcast (i32* @foo.l0 to i8*), i8* bitcast (void ()* @f0 to i8*), i8* bitcast (void ()* @f2 to i8*), i8* bitcast (i32* @g0 to i8*), i8* bitcast (i32* @g1 to i8*), i8* bitcast (i32* @g3 to i8*), i8* bitcast (i32* @g4 to i8*)], section "llvm.metadata" +// CHECK: @llvm.compiler.used = appending global [3 x i8*] [i8* bitcast (void ()* @f2 to i8*), i8* bitcast (i32* @g3 to i8*), i8* bitcast (i32* @g4 to i8*)], section "llvm.metadata" + +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; + +void __attribute__((retain)) f0(void) {} +static void __attribute__((retain)) f1(void) {} +static void __attribute__((used, retain)) f2(void) {} 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,45 @@ +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -Werror %s -o - | FileCheck %s + +// CHECK: @llvm.used = appending global [7 x i8*] +// CHECK-SAME: @_ZN2X0C2Ev +// CHECK-SAME: @_ZN2X0C1Ev +// CHECK-SAME: @_ZN2X0D2Ev +// CHECK-SAME: @_ZN2X0D1Ev +// CHECK-SAME: @_ZN2X16Nested2f1Ev +// CHECK-SAME: @_ZN10merge_declL4funcEv +// CHECK-SAME: @_ZN18instantiate_member1SIiE1fEv + +struct X0 { + // CHECK: define linkonce_odr{{.*}} @_ZN2X0C1Ev({{.*}} + __attribute__((used, retain)) X0() {} + // CHECK: define linkonce_odr{{.*}} @_ZN2X0D1Ev({{.*}} + __attribute__((used, retain)) ~X0() {} +}; + +struct X1 { + struct Nested { + // CHECK-NOT: 2f0Ev + // CHECK: define linkonce_odr{{.*}} @_ZN2X16Nested2f1Ev({{.*}} + void __attribute__((retain)) f0() {} + void __attribute__((used, retain)) f1() {} + }; +}; + +// CHECK: define internal void @_ZN10merge_declL4funcEv(){{.*}} +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({{.*}} + S a; +} +} // namespace instantiate_member 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,29 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wunused-function + +/// 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}} +}; + +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}} + (void)a; + (void)b; +} + +__attribute__((retain,used)) static void f0() {} +__attribute__((retain)) static void f1() {} // expected-warning {{unused function 'f1'}} +__attribute__((retain)) void f2() {} + +/// Test attribute merging. +int tentative; +int tentative __attribute__((retain)); +extern int tentative; +int tentative = 0;