forked from OSchip/llvm-project
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
This commit is contained in:
parent
233ba2709b
commit
8afdacba9d
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = [{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
|
@ -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
|
|
@ -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;
|
Loading…
Reference in New Issue