MaskRay updated this revision to Diff 326513.
MaskRay marked 2 inline comments as done.
MaskRay added a comment.
Improve documentation
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D97447/new/
https://reviews.llvm.org/D97447
Files:
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
clang/test/CodeGen/attr-retain.c
clang/test/CodeGenCXX/attr-retain.cpp
clang/test/Sema/attr-retain.c
Index: clang/test/Sema/attr-retain.c
===================================================================
--- /dev/null
+++ 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;
Index: clang/test/CodeGenCXX/attr-retain.cpp
===================================================================
--- /dev/null
+++ 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
Index: clang/test/CodeGen/attr-retain.c
===================================================================
--- /dev/null
+++ 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) {}
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -2831,6 +2831,11 @@
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 @@
}
// Already handled.
- if (isa<UsedAttr>(I))
+ if (isa<UsedAttr>(I) || isa<RetainAttr>(I))
continue;
if (mergeDeclAttribute(*this, New, I, LocalAMK))
@@ -13314,6 +13319,13 @@
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
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -1896,6 +1896,8 @@
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 @@
}
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());
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -441,7 +441,9 @@
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
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -57,6 +57,54 @@
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 only has an effect on ELF targets; other targets prevent the
+removal of definitions by the linker when using the ``used`` attribute alone.
+There is no harm to using this attribute on non-ELF 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 = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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;
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits