MaskRay updated this revision to Diff 325336.
MaskRay marked 5 inline comments as done.
MaskRay edited the summary of this revision.
MaskRay added a comment.

Comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D96838/new/

https://reviews.llvm.org/D96838

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/CodeGen/CodeGenModule.h
  clang/lib/Sema/SemaDecl.cpp
  clang/test/CodeGen/attr-retain.c
  clang/test/CodeGenCXX/attr-retain.cpp
  clang/test/CodeGenCXX/extern-c.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,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;
Index: clang/test/CodeGenCXX/extern-c.cpp
===================================================================
--- clang/test/CodeGenCXX/extern-c.cpp
+++ 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 {
Index: clang/test/CodeGenCXX/attr-retain.cpp
===================================================================
--- /dev/null
+++ 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 <typename T>
+struct S {
+  void __attribute__((used, retain)) f() {}
+};
+
+void test() {
+  // CHECK: define linkonce_odr{{.*}} void @_ZN18instantiate_member1SIiE1fEv({{.*}} !retain
+  S<int> a;
+}
+} // namespace instantiate_member
Index: clang/test/CodeGen/attr-retain.c
===================================================================
--- /dev/null
+++ 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]] = !{}
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))
@@ -13300,6 +13305,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.h
===================================================================
--- clang/lib/CodeGen/CodeGenModule.h
+++ clang/lib/CodeGen/CodeGenModule.h
@@ -1048,6 +1048,9 @@
   template<typename SomeDecl>
   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);
 
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -1886,6 +1886,8 @@
 
   if (D) {
     if (auto *GV = dyn_cast<llvm::GlobalVariable>(GO)) {
+      if (D->hasAttr<RetainAttr>())
+        setRetain(GV);
       if (auto *SA = D->getAttr<PragmaClangBSSSectionAttr>())
         GV->addAttribute("bss-section", SA->getName());
       if (auto *SA = D->getAttr<PragmaClangDataSectionAttr>())
@@ -1897,6 +1899,8 @@
     }
 
     if (auto *F = dyn_cast<llvm::Function>(GO)) {
+      if (D->hasAttr<RetainAttr>())
+        setRetain(F);
       if (auto *SA = D->getAttr<PragmaClangTextSectionAttr>())
         if (!D->getAttr<SectionAttr>())
           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<llvm::Function>(GV) || !GV->isDeclaration()) &&
          "Only globals with definition can force usage.");
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -443,6 +443,8 @@
 
   if (D.hasAttr<UsedAttr>())
     CGM.addUsedGlobal(var);
+  if (D.hasAttr<RetainAttr>())
+    CGM.setRetain(var);
 
   // We may have to cast the constant because of the initializer
   // mismatch above.
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ 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 = [{
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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to