[clang] 8afdacb - Add GNU attribute 'retain'
Fangrui Song via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 26 16:38:50 PST 2021
Author: Fangrui Song
Date: 2021-02-26T16:37:50-08:00
New Revision: 8afdacba9dcd36fc838eb86fca86f7f903040030
URL: https://github.com/llvm/llvm-project/commit/8afdacba9dcd36fc838eb86fca86f7f903040030
DIFF: https://github.com/llvm/llvm-project/commit/8afdacba9dcd36fc838eb86fca86f7f903040030.diff
LOG: Add GNU attribute 'retain'
For ELF targets, GCC 11 will set SHF_GNU_RETAIN on the section of a
`__attribute__((retain))` function/variable to prevent linker garbage
collection. (See AttrDocs.td for the linker support).
This patch adds `retain` functions/variables to the `llvm.used` list, which has
the desired linker GC semantics. Note: `retain` does not imply `used`,
so an unused function/variable can be dropped by Sema.
Before 'retain' was introduced, previous ELF solutions require inline asm or
linker tricks, e.g. `asm volatile(".reloc 0, R_X86_64_NONE, target");`
(architecture dependent) or define a non-local symbol in the section and use
`ld -u`. There was no elegant source-level solution.
With D97448, `__attribute__((retain))` will set `SHF_GNU_RETAIN` on ELF targets.
Differential Revision: https://reviews.llvm.org/D97447
Added:
clang/test/CodeGen/attr-retain.c
clang/test/CodeGenCXX/attr-retain.cpp
clang/test/Sema/attr-retain.c
Modified:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Sema/SemaDecl.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index bc2d8ceeeb6c..8afa676c133f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2648,7 +2648,14 @@ def Unused : InheritableAttr {
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
index b88b18d37477..deda68b64f90 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -57,6 +57,55 @@ global variable or function should be in after translation.
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
index ecf79dbbaffc..5a691ee303e4 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -441,7 +441,9 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
if (const SectionAttr *SA = D.getAttr<SectionAttr>())
var->setSection(SA->getName());
- if (D.hasAttr<UsedAttr>())
+ if (D.hasAttr<RetainAttr>())
+ CGM.addUsedGlobal(var);
+ else if (D.hasAttr<UsedAttr>())
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
index 564b07582376..0d499a564039 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1896,6 +1896,8 @@ void CodeGenModule::setNonAliasAttributes(GlobalDecl GD,
if (D) {
if (auto *GV = dyn_cast<llvm::GlobalVariable>(GO)) {
+ if (D->hasAttr<RetainAttr>())
+ addUsedGlobal(GV);
if (auto *SA = D->getAttr<PragmaClangBSSSectionAttr>())
GV->addAttribute("bss-section", SA->getName());
if (auto *SA = D->getAttr<PragmaClangDataSectionAttr>())
@@ -1907,6 +1909,8 @@ void CodeGenModule::setNonAliasAttributes(GlobalDecl GD,
}
if (auto *F = dyn_cast<llvm::Function>(GO)) {
+ if (D->hasAttr<RetainAttr>())
+ addUsedGlobal(F);
if (auto *SA = D->getAttr<PragmaClangTextSectionAttr>())
if (!D->getAttr<SectionAttr>())
F->addFnAttr("implicit-section-name", SA->getName());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 50a29e29eb54..64321f4880c3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2831,6 +2831,11 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
NewAttr->setInherited(true);
New->addAttr(NewAttr);
}
+ if (RetainAttr *OldAttr = Old->getMostRecentDecl()->getAttr<RetainAttr>()) {
+ RetainAttr *NewAttr = OldAttr->clone(Context);
+ NewAttr->setInherited(true);
+ New->addAttr(NewAttr);
+ }
if (!Old->hasAttrs() && !New->hasAttrs())
return;
@@ -2953,7 +2958,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
}
// Already handled.
- if (isa<UsedAttr>(I))
+ if (isa<UsedAttr>(I) || isa<RetainAttr>(I))
continue;
if (mergeDeclAttribute(*this, New, I, LocalAMK))
@@ -13304,6 +13309,13 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
VD->dropAttr<UsedAttr>();
}
}
+ if (RetainAttr *Attr = VD->getAttr<RetainAttr>()) {
+ if (!Attr->isInherited() && !VD->isThisDeclarationADefinition()) {
+ Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition)
+ << Attr;
+ VD->dropAttr<RetainAttr>();
+ }
+ }
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
index 000000000000..871065d4fe75
--- /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
index 000000000000..0bc1a4c95abb
--- /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 <typename T>
+struct S {
+ void __attribute__((used, retain)) f() {}
+};
+
+void test() {
+ // CHECK: define linkonce_odr{{.*}} void @_ZN18instantiate_member1SIiE1fEv({{.*}}
+ S<int> a;
+}
+} // namespace instantiate_member
diff --git a/clang/test/Sema/attr-retain.c b/clang/test/Sema/attr-retain.c
new file mode 100644
index 000000000000..478059b5fcc3
--- /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;
More information about the cfe-commits
mailing list