static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *);
static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int,
bool *);
+static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
/* Helper to define attribute exclusions. */
#define ATTR_EXCL(name, function, type, variable) \
handle_used_attribute, NULL },
{ "unused", 0, 0, false, false, false, false,
handle_unused_attribute, NULL },
+ { "retain", 0, 0, true, false, false, false,
+ handle_retain_attribute, NULL },
{ "externally_visible", 0, 0, true, false, false, false,
handle_externally_visible_attribute, NULL },
{ "no_reorder", 0, 0, true, false, false, false,
return NULL_TREE;
}
+/* Handle a "retain" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ tree node = *pnode;
+
+ if (SUPPORTS_SHF_GNU_RETAIN
+ && (TREE_CODE (node) == FUNCTION_DECL
+ || (VAR_P (node) && TREE_STATIC (node))))
+ ;
+ else
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
/* Handle a "externally_visible" attribute; arguments as in
struct attribute_spec.handler. */
attribute also means that the function is instantiated if the
class itself is instantiated.
+@item retain
+@cindex @code{retain} function attribute
For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
-will also save the function from linker garbage collection. To support
+will save the function from linker garbage collection. To support
this behavior, functions that have not been placed in specific sections
(e.g. by the @code{section} attribute, or the @code{-ffunction-sections}
option), will be placed in new, unique sections.
attribute also means that the member is instantiated if the
class itself is instantiated.
+@item retain
+@cindex @code{retain} variable attribute
For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
-will also save the variable from linker garbage collection. To support
+will save the variable from linker garbage collection. To support
this behavior, variables that have not been placed in specific sections
(e.g. by the @code{section} attribute, or the @code{-fdata-sections} option),
will be placed in new, unique sections.
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-O3" } */
+
+static void function_declaration_before(void)
+ __attribute__((__used__, __retain__));
+
+static void function_declaration_before(void) {}
+
+static void function_declaration_after(void) {}
+
+static void function_declaration_after(void)
+ __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "function_declaration_before" } } */
+/* { dg-final { scan-assembler "function_declaration_after" } } */
+/* { dg-final { scan-assembler "\.text.*,\"axR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2" } */
+
+static int xyzzy __attribute__((__used__, __retain__)) = 1;
+
+void foo()
+{
+ int x __attribute__((__retain__)); /* { dg-warning "attribute ignored|unused variable" } */
+}
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler "\.data.*,\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2 -fcommon" } */
+
+static int xyzzy __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2 -fcommon" } */
+
+int xyzzy __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
+/* { dg-options "-Wall -O2" } */
+
+struct dtv_slotinfo_list
+{
+ struct dtv_slotinfo_list *next;
+};
+
+extern struct dtv_slotinfo_list *list;
+
+static int __attribute__ ((section ("__libc_freeres_fn")))
+free_slotinfo (struct dtv_slotinfo_list **elemp)
+/* { dg-warning "'.*' without 'retain' attribute and '.*' with 'retain' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
+{
+ if (!free_slotinfo (&(*elemp)->next))
+ return 0;
+ return 1;
+}
+
+__attribute__ ((used, retain, section ("__libc_freeres_fn")))
+static void free_mem (void)
+{
+ free_slotinfo (&list);
+}
+
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
+/* { dg-options "-Wall -O2" } */
+
+struct dtv_slotinfo_list
+{
+ struct dtv_slotinfo_list *next;
+};
+
+extern struct dtv_slotinfo_list *list;
+
+static int __attribute__ ((used, retain, section ("__libc_freeres_fn")))
+free_slotinfo (struct dtv_slotinfo_list **elemp)
+{
+ if (!free_slotinfo (&(*elemp)->next))
+ return 0;
+ return 1;
+}
+
+__attribute__ ((section ("__libc_freeres_fn")))
+void free_mem (void)
+/* { dg-warning "'.*' without 'retain' attribute and '.*' with 'retain' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
+{
+ free_slotinfo (&list);
+}
+
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
+/* { dg-options "-Wall -O2" } */
+
+int __attribute__((used,retain,section(".data.foo"))) foo2 = 2;
+int __attribute__((section(".data.foo"))) foo1 = 1;
+/* { dg-warning "'.*' without 'retain' attribute and '.*' with 'retain' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
+
+/* { dg-final { scan-assembler ".data.foo,\"aw\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler ".data.foo,\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
+/* { dg-options "-Wall -O2" } */
+
+int __attribute__((section(".data.foo"))) foo1 = 1;
+/* { dg-warning "'.*' without 'retain' attribute and '.*' with 'retain' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
+int __attribute__((used,retain,section(".data.foo"))) foo2 = 2;
+
+/* { dg-final { scan-assembler ".data.foo,\"aw\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler ".data.foo,\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
+/* { dg-options "-Wall -O2" } */
+
+struct dtv_slotinfo_list
+{
+ struct dtv_slotinfo_list *next;
+};
+
+extern struct dtv_slotinfo_list *list;
+
+static int __attribute__ ((used, retain, section ("__libc_freeres_fn")))
+free_slotinfo (struct dtv_slotinfo_list **elemp)
+{
+ if (!free_slotinfo (&(*elemp)->next))
+ return 0;
+ return 1;
+}
+
+__attribute__ ((section ("__libc_freeres_fn")))
+static void free_mem (void)
+/* { dg-warning "defined but not used" "" { target *-*-* } .-1 } */
+{
+ free_slotinfo (&list);
+}
+
+/* { dg-final { scan-assembler-not "__libc_freeres_fn\n" } } */
+/* { dg-final { scan-assembler-not "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
}
/* { dg-final { scan-assembler "xyzzy" } } */
-/* { dg-final { scan-assembler "\.data.*,\"awR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not "\.data.*,\"awR\"" { target R_flag_in_section } } } */
static int xyzzy __attribute__((__used__));
/* { dg-final { scan-assembler "xyzzy" } } */
-/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not ",\"awR\"" { target R_flag_in_section } } } */
int xyzzy __attribute__((__used__));
/* { dg-final { scan-assembler "xyzzy" } } */
-/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not ",\"awR\"" { target R_flag_in_section } } } */
static int __attribute__ ((section ("__libc_freeres_fn")))
free_slotinfo (struct dtv_slotinfo_list **elemp)
-/* { dg-warning "'.*' without 'used' attribute and '.*' with 'used' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
{
if (!free_slotinfo (&(*elemp)->next))
return 0;
}
/* { dg-final { scan-assembler "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
-/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
__attribute__ ((section ("__libc_freeres_fn")))
void free_mem (void)
-/* { dg-warning "'.*' without 'used' attribute and '.*' with 'used' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
{
free_slotinfo (&list);
}
/* { dg-final { scan-assembler "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
-/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
int __attribute__((used,section(".data.foo"))) foo2 = 2;
int __attribute__((section(".data.foo"))) foo1 = 1;
-/* { dg-warning "'.*' without 'used' attribute and '.*' with 'used' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
/* { dg-final { scan-assembler ".data.foo,\"aw\"" { target R_flag_in_section } } } */
-/* { dg-final { scan-assembler ".data.foo,\"awR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not ".data.foo,\"awR\"" { target R_flag_in_section } } } */
/* { dg-options "-Wall -O2" } */
int __attribute__((section(".data.foo"))) foo1 = 1;
-/* { dg-warning "'.*' without 'used' attribute and '.*' with 'used' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
int __attribute__((used,section(".data.foo"))) foo2 = 2;
/* { dg-final { scan-assembler ".data.foo,\"aw\"" { target R_flag_in_section } } } */
-/* { dg-final { scan-assembler ".data.foo,\"awR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not ".data.foo,\"awR\"" { target R_flag_in_section } } } */
}
/* { dg-final { scan-assembler-not "__libc_freeres_fn\n" } } */
-/* { dg-final { scan-assembler-not "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
-/* { dg-final { scan-assembler "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler "__libc_freeres_fn,\"ax\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not "__libc_freeres_fn,\"axR\"" { target R_flag_in_section } } } */
/* { dg-final { scan-assembler "function_declaration_before" } } */
/* { dg-final { scan-assembler "function_declaration_after" } } */
-/* { dg-final { scan-assembler "\.text.*,\"axR\"" { target R_flag_in_section } } } */
+/* { dg-final { scan-assembler-not "\.text.*,\"axR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-Wall -O2" } */
+
+static int xyzzy __attribute__((__used__)) = 1;
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler-not "\.data.*,\"awR\"" { target R_flag_in_section } } } */
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-final { scan-assembler ".text.*,\"axR\"" } } */
+/* { dg-final { scan-assembler ".bss.*,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.*,\"awR\"" } } */
+/* { dg-final { scan-assembler ".rodata.*,\"aR\"" } } */
+/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+
+void __attribute__((used,retain)) used_fn (void) { }
+void unused_fn (void) { }
+void __attribute__((hot,used,retain)) used_hot_fn (void) { }
+void __attribute__((hot)) unused_hot_fn (void) { }
+void __attribute__((cold,used,retain)) used_cold_fn (void) { }
+void __attribute__((cold)) unused_cold_fn (void) { }
+int __attribute__((used,retain)) used_bss = 0;
+int __attribute__((used,retain)) used_data = 1;
+const int __attribute__((used,retain)) used_rodata = 2;
+int __attribute__((used,retain)) used_comm;
+static int __attribute__((used,retain)) used_lcomm;
+
+int unused_bss = 0;
+int unused_data = 1;
+const int unused_rodata = 2;
+int unused_comm;
+static int unused_lcomm;
+
+/* Test switching back to the used,retained sections. */
+void __attribute__((used,retain)) used_fn2 (void) { }
+int __attribute__((used,retain)) used_bss2 = 0;
+int __attribute__((used,retain)) used_data2 = 1;
+const int __attribute__((used,retain)) used_rodata2 = 2;
+int __attribute__((used,retain)) used_comm2;
+static int __attribute__((used,retain)) used_lcomm2;
+
+int __attribute__((used,retain,section(".data.used_foo_sec"))) used_foo = 2;
--- /dev/null
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-final { scan-assembler ".text.used_fn,\"axR\"" } } */
+/* { dg-final { scan-assembler ".text.used_fn2,\"axR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_bss,\"awR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_bss2,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.used_data,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.used_data2,\"awR\"" } } */
+/* { dg-final { scan-assembler ".rodata.used_rodata,\"aR\"" } } */
+/* { dg-final { scan-assembler ".rodata.used_rodata2,\"aR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_lcomm,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler ".bss.used_lcomm2,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+/* { dg-options "-ffunction-sections -fdata-sections" } */
+
+#include "attr-retain-1.c"
/* { dg-do compile } */
/* { dg-require-effective-target R_flag_in_section } */
-/* { dg-final { scan-assembler ".text.*,\"axR\"" } } */
-/* { dg-final { scan-assembler ".bss.*,\"awR\"" } } */
-/* { dg-final { scan-assembler ".data.*,\"awR\"" } } */
-/* { dg-final { scan-assembler ".rodata.*,\"aR\"" } } */
-/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".text.*,\"axR\"" } } */
+/* { dg-final { scan-assembler-not ".bss.*,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".data.*,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".rodata.*,\"aR\"" } } */
+/* { dg-final { scan-assembler-not ".data.used_foo_sec,\"awR\"" } } */
void __attribute__((used)) used_fn (void) { }
void unused_fn (void) { }
/* { dg-do compile } */
/* { dg-require-effective-target R_flag_in_section } */
-/* { dg-final { scan-assembler ".text.used_fn,\"axR\"" } } */
-/* { dg-final { scan-assembler ".text.used_fn2,\"axR\"" } } */
-/* { dg-final { scan-assembler ".bss.used_bss,\"awR\"" } } */
-/* { dg-final { scan-assembler ".bss.used_bss2,\"awR\"" } } */
-/* { dg-final { scan-assembler ".data.used_data,\"awR\"" } } */
-/* { dg-final { scan-assembler ".data.used_data2,\"awR\"" } } */
-/* { dg-final { scan-assembler ".rodata.used_rodata,\"aR\"" } } */
-/* { dg-final { scan-assembler ".rodata.used_rodata2,\"aR\"" } } */
-/* { dg-final { scan-assembler ".bss.used_lcomm,\"awR\"" { target arm-*-* } } } */
-/* { dg-final { scan-assembler ".bss.used_lcomm2,\"awR\"" { target arm-*-* } } } */
-/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".text.used_fn,\"axR\"" } } */
+/* { dg-final { scan-assembler-not ".text.used_fn2,\"axR\"" } } */
+/* { dg-final { scan-assembler-not ".bss.used_bss,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".bss.used_bss2,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".data.used_data,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".data.used_data2,\"awR\"" } } */
+/* { dg-final { scan-assembler-not ".rodata.used_rodata,\"aR\"" } } */
+/* { dg-final { scan-assembler-not ".rodata.used_rodata2,\"aR\"" } } */
+/* { dg-final { scan-assembler-not ".bss.used_lcomm,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler-not ".bss.used_lcomm2,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler-not ".data.used_foo_sec,\"awR\"" } } */
/* { dg-options "-ffunction-sections -fdata-sections" } */
#include "attr-used-retain-1.c"
slot = section_htab->find_slot_with_hash (name, htab_hash_string (name),
INSERT);
flags |= SECTION_NAMED;
- if (SUPPORTS_SHF_GNU_RETAIN
- && decl != nullptr
+ if (decl != nullptr
&& DECL_P (decl)
- && DECL_PRESERVE_P (decl))
+ && lookup_attribute ("retain", DECL_ATTRIBUTES (decl)))
flags |= SECTION_RETAIN;
if (*slot == NULL)
{
if (DECL_SECTION_NAME (decl) == NULL
&& targetm_common.have_named_sections
&& (flag_function_or_data_sections
- || (SUPPORTS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl))
+ || lookup_attribute ("retain", DECL_ATTRIBUTES (decl))
|| DECL_COMDAT_GROUP (decl)))
{
targetm.asm_out.unique_section (decl, reloc);
vnode->get_constructor ();
if (DECL_COMMON (decl)
- && !(SUPPORTS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl)))
+ && !lookup_attribute ("retain", DECL_ATTRIBUTES (decl)))
{
/* If the decl has been given an explicit section name, or it resides
in a non-generic address space, then it isn't common, and shouldn't
{
if (in_section == new_section)
{
- if (SUPPORTS_SHF_GNU_RETAIN
- && (new_section->common.flags & SECTION_NAMED)
+ bool retain_p;
+ if ((new_section->common.flags & SECTION_NAMED)
&& decl != nullptr
&& DECL_P (decl)
- && (!!DECL_PRESERVE_P (decl)
+ && ((retain_p = !!lookup_attribute ("retain",
+ DECL_ATTRIBUTES (decl)))
!= !!(new_section->common.flags & SECTION_RETAIN)))
{
/* If the SECTION_RETAIN bit doesn't match, switch to a new
section. */
tree used_decl, no_used_decl;
- if (DECL_PRESERVE_P (decl))
+ if (retain_p)
{
new_section->common.flags |= SECTION_RETAIN;
used_decl = decl;
no_used_decl = decl;
}
warning (OPT_Wattributes,
- "%+qD without %<used%> attribute and %qD with "
- "%<used%> attribute are placed in a section with "
+ "%+qD without %<retain%> attribute and %qD with "
+ "%<retain%> attribute are placed in a section with "
"the same name", no_used_decl, used_decl);
inform (DECL_SOURCE_LOCATION (used_decl),
"%qD was declared here", used_decl);