From 308af5a66ad9c23b75804a43a7692e1884ce271b Mon Sep 17 00:00:00 2001 From: Jozef Lawrynowicz Date: Tue, 8 Sep 2020 20:41:03 +0100 Subject: [PATCH] Add support for "retain" attribute --- gcc/c-family/c-attribs.c | 33 +++++++++++++ gcc/config/arm/arm.c | 2 + gcc/config/arm/unknown-elf.h | 5 +- gcc/config/elfos.h | 16 +++++++ gcc/config/i386/i386.c | 2 + gcc/config/msp430/msp430.c | 7 ++- gcc/defaults.h | 4 ++ gcc/doc/extend.texi | 23 +++++++++ gcc/testsuite/c-c++-common/attr-retain-1.c | 56 ++++++++++++++++++++++ gcc/testsuite/c-c++-common/attr-retain-2.c | 26 ++++++++++ gcc/testsuite/c-c++-common/attr-retain-3.c | 10 ++++ gcc/varasm.c | 19 +++++++- gcc/varasm.h | 3 ++ 13 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/attr-retain-1.c create mode 100644 gcc/testsuite/c-c++-common/attr-retain-2.c create mode 100644 gcc/testsuite/c-c++-common/attr-retain-3.c diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 37214831538..99839367e88 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -150,6 +150,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *); static tree handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *); static tree handle_copy_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) \ @@ -484,6 +485,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_noinit_attribute, attr_noinit_exclusions }, { "access", 1, 3, false, true, true, false, handle_access_attribute, NULL }, + { "retain", 0, 0, true, false, false, false, + handle_retain_attribute, NULL }, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; @@ -2420,6 +2423,36 @@ handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args, decl, is_alias ? "alias" : "ifunc"); } + return NULL_TREE; +} + +/* Handle a "retain" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_retain_attribute (tree * pnode, + tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + tree node = *pnode; + + /* FIXME: Maybe it would be useful to allow the attribute to be set on types + as well... */ + if (TREE_CODE (node) == FUNCTION_DECL + || (VAR_P (node) && TREE_STATIC (node))) + { + TREE_USED (node) = 1; + DECL_PRESERVE_P (node) = 1; + if (VAR_P (node)) + DECL_READ_P (node) = 1; + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } return NULL_TREE; } diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index dd78141519e..5e36644e4a7 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -21407,6 +21407,8 @@ arm_asm_declare_function_name (FILE *file, const char *name, tree decl) ARM_DECLARE_FUNCTION_NAME (file, name, decl); ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); + if (lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) + ASM_OUTPUT_RETAIN_DIRECTIVE (file, decl); ASM_DECLARE_RESULT (file, DECL_RESULT (decl)); ASM_OUTPUT_LABEL (file, name); diff --git a/gcc/config/arm/unknown-elf.h b/gcc/config/arm/unknown-elf.h index 9ad2947505f..d78d40cc00e 100644 --- a/gcc/config/arm/unknown-elf.h +++ b/gcc/config/arm/unknown-elf.h @@ -62,7 +62,7 @@ switch_to_section (get_named_section (DECL, NULL, 0)); \ else \ switch_to_section (bss_section); \ - \ + \ ASM_OUTPUT_ALIGN (FILE, floor_log2 (ALIGN / BITS_PER_UNIT)); \ \ last_assemble_variable_decl = DECL; \ @@ -80,6 +80,9 @@ else \ switch_to_section (bss_section); \ \ + if (DECL && lookup_attribute ("retain", DECL_ATTRIBUTES (DECL))) \ + ASM_OUTPUT_RETAIN_DIRECTIVE (FILE, DECL); \ + \ ASM_OUTPUT_ALIGN (FILE, floor_log2 (ALIGN / BITS_PER_UNIT)); \ ASM_OUTPUT_LABEL (FILE, NAME); \ fprintf (FILE, "\t.space\t%d\n", SIZE ? (int) SIZE : 1); \ diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h index 74a3eafda6b..9be1948c008 100644 --- a/gcc/config/elfos.h +++ b/gcc/config/elfos.h @@ -275,6 +275,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define ASM_DECLARE_RESULT(FILE, RESULT) #endif +#ifndef ASM_OUTPUT_RETAIN_DIRECTIVE +#define ASM_OUTPUT_RETAIN_DIRECTIVE(STREAM, DECL) \ + do \ + { \ + assemble_retain (STREAM, DECL); \ + } \ + while (0) +#endif + /* These macros generate the special .type and .size directives which are used to set the corresponding fields of the linker symbol table entries in an ELF object file under SVR4. These macros also output @@ -289,6 +298,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see do \ { \ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function"); \ + if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL))) \ + ASM_OUTPUT_RETAIN_DIRECTIVE (FILE, DECL); \ ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL); \ } \ @@ -305,6 +316,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see do \ { \ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function"); \ + if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL))) \ + ASM_OUTPUT_RETAIN_DIRECTIVE (FILE, DECL); \ ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL); \ } \ @@ -335,6 +348,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see else \ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "object"); \ \ + if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL))) \ + ASM_OUTPUT_RETAIN_DIRECTIVE (FILE, DECL); \ + \ size_directive_output = 0; \ if (!flag_inhibit_size_directive \ && (DECL) && DECL_SIZE (DECL)) \ diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index a15807d91da..062d68765d5 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -850,6 +850,8 @@ x86_elf_aligned_decl_common (FILE *file, tree decl, assemble_name (file, name); fprintf (file, "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n", size, align / BITS_PER_UNIT); + if (lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) + ASM_OUTPUT_RETAIN_DIRECTIVE (file, decl); } #endif diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c index a299ed7f9d1..dac3896e416 100644 --- a/gcc/config/msp430/msp430.c +++ b/gcc/config/msp430/msp430.c @@ -1746,6 +1746,8 @@ msp430_start_function (FILE *file, const char *name, tree decl) switch_to_section (function_section (decl)); ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); + if (lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) + ASM_OUTPUT_RETAIN_DIRECTIVE (file, decl); ASM_OUTPUT_FUNCTION_LABEL (file, name, decl); } @@ -2018,7 +2020,8 @@ msp430_output_aligned_decl_common (FILE * stream, && !has_attr (ATTR_LOWER, decl) && !has_attr (ATTR_UPPER, decl) && !has_attr (ATTR_PERSIST, decl) - && !has_attr (ATTR_NOINIT, decl)) + && !has_attr (ATTR_NOINIT, decl) + && !has_attr ("retain", decl)) { if (local) { @@ -2064,6 +2067,8 @@ msp430_output_aligned_decl_common (FILE * stream, ASM_OUTPUT_LABEL (stream, name); ASM_OUTPUT_SKIP (stream, size ? size : 1); } + if (decl && lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) + ASM_OUTPUT_RETAIN_DIRECTIVE (stream, decl); } #undef TARGET_ASM_FILE_END diff --git a/gcc/defaults.h b/gcc/defaults.h index f1a38626624..9899d4abce2 100644 --- a/gcc/defaults.h +++ b/gcc/defaults.h @@ -260,6 +260,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #endif #endif +#ifndef ASM_OUTPUT_RETAIN_DIRECTIVE +#define ASM_OUTPUT_RETAIN_DIRECTIVE(STREAM, DECL) hook_void_void +#endif + /* This determines whether or not we support weak symbols. SUPPORTS_WEAK must be a preprocessor constant. */ #ifndef SUPPORTS_WEAK diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 3b37aba5795..5342dd2801d 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3578,6 +3578,17 @@ diagnosed. Because a pure function cannot have any observable side effects it does not make sense for such a function to return @code{void}. Declaring such a function is diagnosed. +@item retain +@cindex @code{retain} function attribute +The @code{retain} attribute, attached to a function, means that function must +not be garbage collected by the linker, even if it appears unused. + +The section containing the function is marked with the SHF_GNU_RETAIN flag, +which is a GNU extension to the ELF standard. + +This attribute implies, and has the same restrictions as, the @code{used} +attribute. + @item returns_nonnull @cindex @code{returns_nonnull} function attribute The @code{returns_nonnull} attribute specifies that the function @@ -7168,6 +7179,18 @@ been fixed in GCC 4.4 but the change can lead to differences in the structure layout. See the documentation of @option{-Wpacked-bitfield-compat} for more information. +@item retain +@cindex @code{retain} variable attribute +The @code{retain} attribute, attached to a variable with static storage, means +that variable must not be garbage collected by the linker, even if it appears +unused. + +The section containing the variable is marked with the SHF_GNU_RETAIN flag, +which is a GNU extension to the ELF standard. + +This attribute implies, and has the same restrictions as, the @code{used} +attribute. + @item section ("@var{section-name}") @cindex @code{section} variable attribute Normally, the compiler places the objects it generates in sections like diff --git a/gcc/testsuite/c-c++-common/attr-retain-1.c b/gcc/testsuite/c-c++-common/attr-retain-1.c new file mode 100644 index 00000000000..2ed6f9b220c --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-retain-1.c @@ -0,0 +1,56 @@ +/* { dg-do compile } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.a1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.b1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.data\\.c1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.sa1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.sb1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.data\\.sc1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.text\\.foo1" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.lsa" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.bss\\.lsb" } } */ +/* { dg-final { scan-assembler "\\.retain\t\\.data\\.lsc" } } */ + +/* Test .retain directives are emitted for declarations using the "retain" + when they are each put in their own sections. */ + +#define RETAIN __attribute__((retain)) +#define SECTION(X) __attribute__((section(X))) + +/* This group of functions and data should be garbage collected by the + linker. */ +int SECTION(".bss.a0") a0; +int SECTION(".bss.b0") b0 = 0; +int SECTION(".data.c0") c0 = 1; +static int SECTION(".bss.sa0") sa0; +static int SECTION(".bss.sb0") sb0 = 0; +static int SECTION(".data.sc0") sc0 = 1; +void SECTION(".text.foo0") foo0 (void) {} + +/* The "retain" attribute set on this group of functions and data should protect + them from linker garbage collection. */ +int RETAIN SECTION(".bss.a1") a1; +int RETAIN SECTION(".bss.b1") b1 = 0; +int RETAIN SECTION(".data.c1") c1 = 1; +static int RETAIN SECTION(".bss.sa1") sa1; +static int RETAIN SECTION(".bss.sb1") sb1 = 0; +static int RETAIN SECTION(".data.sc1") sc1 = 1; +void RETAIN SECTION(".text.foo1") foo1 (void) {} + +/* .text.foo2 should be garbage collected, but the static variables defined + inside it have the retain attribute so should not be garbage collected. */ +void SECTION(".text.foo2") +foo2 (void) +{ + static int RETAIN SECTION(".bss.lsa") lsa; + static int RETAIN SECTION(".bss.lsb") lsb = 0; + static int RETAIN SECTION(".data.lsc") lsc = 1; + lsa++; + lsb++; + lsc++; +} + +int main (void) +{ + while (1); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/attr-retain-2.c b/gcc/testsuite/c-c++-common/attr-retain-2.c new file mode 100644 index 00000000000..cd906246cbf --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-retain-2.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-final { scan-assembler-times "\\.retain" 10 } } */ + +/* Test .retain directives are emitted for declarations using the "retain" + attribute, even when symbols don't have an explicit section. */ + +#define RETAIN __attribute__((retain)) + +int RETAIN a1; +int RETAIN b1 = 0; +int RETAIN c1 = 1; +static int RETAIN sa1; +static int RETAIN sb1 = 0; +static int RETAIN sc1 = 1; +void RETAIN foo1 (void) {} + +void +foo2 (void) +{ + static int RETAIN lsa; + static int RETAIN lsb = 0; + static int RETAIN lsc = 1; + lsa++; + lsb++; + lsc++; +} diff --git a/gcc/testsuite/c-c++-common/attr-retain-3.c b/gcc/testsuite/c-c++-common/attr-retain-3.c new file mode 100644 index 00000000000..a8c1917e38a --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-retain-3.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +typedef int int_retain __attribute__((retain)); /* { dg-warning "" "'retain' attribute ignored" } */ + +void +foo (void) +{ + int __attribute__((retain)) a; /* { dg-warning "" "'retain' attribute ignored" } */ + while(a++); +} diff --git a/gcc/varasm.c b/gcc/varasm.c index ea0b59cf44a..db67329fe74 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -483,11 +483,13 @@ resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED, support is localized here. */ static void -asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, +asm_output_aligned_bss (FILE *file, tree decl, const char *name, unsigned HOST_WIDE_INT size, int align) { switch_to_section (bss_section); + if (lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) + ASM_OUTPUT_RETAIN_DIRECTIVE (file, decl); ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); #ifdef ASM_DECLARE_OBJECT_NAME last_assemble_variable_decl = decl; @@ -6225,6 +6227,21 @@ assemble_alias (tree decl, tree target) } } +/* Emit a ".retain" assembler directive, which applies the SHF_GNU_RETAIN flag + to the specified section. This indicates that the linker should not garbage + collect the section, even if it appears unused. */ +void +assemble_retain (FILE *stream, tree decl) +{ + if (DECL_SECTION_NAME (decl)) + fprintf (stream, "\t.retain\t%s\n", DECL_SECTION_NAME (decl)); + else + /* If the section name isn't readily available, just output the bare + ".retain" directive, which indicates the current section should be + retained. */ + fprintf (stream, "\t.retain\n"); +} + /* Record and output a table of translations from original function to its transaction aware clone. Note that tm_pure functions are considered to be their own clone. */ diff --git a/gcc/varasm.h b/gcc/varasm.h index 1b715ab1736..97001d8975f 100644 --- a/gcc/varasm.h +++ b/gcc/varasm.h @@ -51,6 +51,9 @@ extern void merge_weak (tree, tree); /* Make one symbol an alias for another. */ extern void assemble_alias (tree, tree); +/* Emit the .retain directive. */ +void assemble_retain (FILE *stream, tree decl); + /* Return nonzero if VALUE is a valid constant-valued expression for use in initializing a static variable; one that can be an element of a "constant" initializer. -- 2.28.0