From: Jozef Lawrynowicz Date: Mon, 23 Nov 2020 12:06:39 +0000 (+0000) Subject: Implement the "persistent" attribute X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=762ca20364a590be2cb9c79c0101ccbff74b5de1;p=gcc.git Implement the "persistent" attribute The "persistent" attribute is used for variables that are initialized by the program loader, but are not initialized by the runtime startup code. "persistent" variables are placed in a non-volatile area of memory, which allows their value to "persist" between processor resets. gcc/c-family/ChangeLog: * c-attribs.c (handle_special_var_sec_attribute): New. (handle_noinit_attribute): Remove. (attr_noinit_exclusions): Rename to... (attr_section_exclusions): ...this, and add "persistent" attribute exclusion. (c_common_attribute_table): Add "persistent" attribute. gcc/ChangeLog: * doc/extend.texi (Common Variable Attributes): Document the "persistent" variable attribute. * doc/sourcebuild.texi (Effective-Target Keywords): Document the "persistent" effective target keyword. * tree.h (DECL_PERSISTENT_P): Define. * varasm.c (bss_initializer_p): Return false for a DECL_PERSISTENT_P decl initialized to zero. (default_section_type_flags): Handle the ".persistent" section. (default_elf_select_section): Likewise. (default_unique_section): Likewise. gcc/testsuite/ChangeLog: * gcc.c-torture/execute/noinit-attribute.c: Moved to... * c-c++-common/torture/attr-noinit-main.inc: ...here. * lib/target-supports.exp (check_effective_target_persistent): New. * c-c++-common/torture/attr-noinit-1.c: New test. * c-c++-common/torture/attr-noinit-2.c: New test. * c-c++-common/torture/attr-noinit-3.c: New test. * c-c++-common/torture/attr-noinit-invalid.c: New test. * c-c++-common/torture/attr-persistent-1.c: New test. * c-c++-common/torture/attr-persistent-2.c: New test. * c-c++-common/torture/attr-persistent-3.c: New test. * c-c++-common/torture/attr-persistent-invalid.c: New test. * c-c++-common/torture/attr-persistent-main.inc: New test. --- diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 1d2ab7c81ed..6d515dca05a 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -94,10 +94,10 @@ static tree handle_constructor_attribute (tree *, tree, tree, int, bool *); static tree handle_destructor_attribute (tree *, tree, tree, int, bool *); static tree handle_mode_attribute (tree *, tree, tree, int, bool *); static tree handle_section_attribute (tree *, tree, tree, int, bool *); +static tree handle_special_var_sec_attribute (tree *, tree, tree, int, bool *); static tree handle_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, int, bool *); -static tree handle_noinit_attribute (tree *, tree, tree, int, bool *); static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); @@ -248,9 +248,12 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] = ATTR_EXCL (NULL, false, false, false) }; -static const struct attribute_spec::exclusions attr_noinit_exclusions[] = +/* Exclusions that apply to attributes that put declarations in specific + sections. */ +static const struct attribute_spec::exclusions attr_section_exclusions[] = { ATTR_EXCL ("noinit", true, true, true), + ATTR_EXCL ("persistent", true, true, true), ATTR_EXCL ("section", true, true, true), ATTR_EXCL (NULL, false, false, false), }; @@ -339,7 +342,7 @@ const struct attribute_spec c_common_attribute_table[] = { "mode", 1, 1, false, true, false, false, handle_mode_attribute, NULL }, { "section", 1, 1, true, false, false, false, - handle_section_attribute, attr_noinit_exclusions }, + handle_section_attribute, attr_section_exclusions }, { "aligned", 0, 1, false, false, false, false, handle_aligned_attribute, attr_aligned_exclusions }, @@ -509,7 +512,9 @@ const struct attribute_spec c_common_attribute_table[] = { "copy", 1, 1, false, false, false, false, handle_copy_attribute, NULL }, { "noinit", 0, 0, true, false, false, false, - handle_noinit_attribute, attr_noinit_exclusions }, + handle_special_var_sec_attribute, attr_section_exclusions }, + { "persistent", 0, 0, true, false, false, false, + handle_special_var_sec_attribute, attr_section_exclusions }, { "access", 1, 3, false, true, true, false, handle_access_attribute, NULL }, /* Attributes used by Objective-C. */ @@ -2387,64 +2392,112 @@ handle_weak_attribute (tree *node, tree name, return NULL_TREE; } -/* Handle a "noinit" attribute; arguments as in struct - attribute_spec.handler. Check whether the attribute is allowed - here and add the attribute to the variable decl tree or otherwise - issue a diagnostic. This function checks NODE is of the expected - type and issues diagnostics otherwise using NAME. If it is not of - the expected type *NO_ADD_ATTRS will be set to true. */ - +/* Handle a "noinit" or "persistent" attribute; arguments as in + struct attribute_spec.handler. + This generic handler is used for "special variable sections" that allow the + section name to be set using a dedicated attribute. Additional validation + is performed for the specific properties of the section corresponding to the + attribute. + The ".noinit" section *is not* loaded by the program loader, and is not + initialized by the runtime startup code. + The ".persistent" section *is* loaded by the program loader, but is not + initialized by the runtime startup code. */ static tree -handle_noinit_attribute (tree * node, - tree name, - tree args, - int flags ATTRIBUTE_UNUSED, - bool *no_add_attrs) +handle_special_var_sec_attribute (tree *node, tree name, tree args, + int flags, bool *no_add_attrs) { - const char *message = NULL; + tree decl = *node; tree res = NULL_TREE; - gcc_assert (DECL_P (*node)); - gcc_assert (args == NULL); + /* First perform generic validation common to "noinit" and "persistent" + attributes. */ + if (!targetm_common.have_named_sections) + { + error_at (DECL_SOURCE_LOCATION (decl), + "section attributes are not supported for this target"); + goto fail; + } - if (TREE_CODE (*node) != VAR_DECL) - message = G_("%qE attribute only applies to variables"); + if (!VAR_P (decl)) + { + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes, + "ignoring %qE attribute not set on a variable", + name); + goto fail; + } - /* Check that it's possible for the variable to have a section. */ - else if ((TREE_STATIC (*node) || DECL_EXTERNAL (*node) || in_lto_p) - && DECL_SECTION_NAME (*node)) - message = G_("%qE attribute cannot be applied to variables " - "with specific sections"); + if (VAR_P (decl) + && current_function_decl != NULL_TREE + && !TREE_STATIC (decl)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute cannot be specified for local variables", + name); + goto fail; + } - else if (!targetm.have_switchable_bss_sections) - message = G_("%qE attribute is specific to ELF targets"); + if (VAR_P (decl) + && !targetm.have_tls && targetm.emutls.tmpl_section + && DECL_THREAD_LOCAL_P (decl)) + { + error ("section of %q+D cannot be overridden", decl); + goto fail; + } - if (message) + if (!targetm.have_switchable_bss_sections) { - warning (OPT_Wattributes, message, name); - *no_add_attrs = true; + error ("%qE attribute is specific to ELF targets", name); + goto fail; } - else + + if (TREE_READONLY (decl)) + { + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes, + "ignoring %qE attribute set on const variable", + name); + goto fail; + } + + /* Now validate noinit/persistent individually. */ + if (strcmp (IDENTIFIER_POINTER (name), "noinit") == 0) + { + if (DECL_INITIAL (decl)) + { + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes, + "ignoring %qE attribute set on initialized variable", + name); + goto fail; + } + /* If this var is thought to be common, then change this. "noinit" + variables must be placed in an explicit ".noinit" section. */ + DECL_COMMON (decl) = 0; + } + else if (strcmp (IDENTIFIER_POINTER (name), "persistent") == 0) { - res = targetm.handle_generic_attribute (node, name, args, flags, - no_add_attrs); - /* If the back end confirms the attribute can be added then continue onto - final processing. */ - if (!(*no_add_attrs)) + if (DECL_COMMON (decl) || DECL_INITIAL (decl) == NULL_TREE) { - /* If this var is thought to be common, then change this. Common - variables are assigned to sections before the backend has a - chance to process them. Do this only if the attribute is - valid. */ - if (DECL_COMMON (*node)) - DECL_COMMON (*node) = 0; + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes, + "ignoring %qE attribute set on uninitialized variable", + name); + goto fail; } } + else + gcc_unreachable (); + + res = targetm.handle_generic_attribute (node, name, args, flags, + no_add_attrs); + + /* If the back end confirms the attribute can be added then continue onto + final processing. */ + if (!(*no_add_attrs)) + return res; +fail: + *no_add_attrs = true; return res; } - /* Handle a "noplt" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index f0269422ca2..6018347daed 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -7426,9 +7426,23 @@ The @code{weak} attribute is described in @cindex @code{noinit} variable attribute Any data with the @code{noinit} attribute will not be initialized by the C runtime startup code, or the program loader. Not initializing -data in this way can reduce program startup times. This attribute is -specific to ELF targets and relies on the linker to place such data in -the right location +data in this way can reduce program startup times. + +This attribute is specific to ELF targets and relies on the linker +script to place sections with the @code{.noinit} prefix in the right +location. + +@item persistent +@cindex @code{persistent} variable attribute +Any data with the @code{persistent} attribute will not be initialized by +the C runtime startup code, but will be initialized by the program +loader. This enables the value of the variable to @samp{persist} +between processor resets. + +This attribute is specific to ELF targets and relies on the linker +script to place the sections with the @code{.persistent} prefix in the +right location. Specifically, some type of non-volatile, writeable +memory is required. @item objc_nullability (@var{nullability kind}) @r{(Objective-C and Objective-C++ only)} @cindex @code{objc_nullability} variable attribute diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi index 566fda02c30..852eaa2e676 100644 --- a/gcc/doc/sourcebuild.texi +++ b/gcc/doc/sourcebuild.texi @@ -2548,6 +2548,9 @@ Target does not generate PIC by default. @item offload_gcn Target has been configured for OpenACC/OpenMP offloading on AMD GCN. +@item persistent +Target supports the @code{persistent} variable attribute. + @item pie_enabled Target generates PIE by default. diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c new file mode 100644 index 00000000000..877e7647ac9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-require-effective-target noinit } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-options "-save-temps" } */ +/* { dg-final { scan-assembler ".section\t.noinit,\"aw\"\n" } } */ + +#include "attr-noinit-main.inc" diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c new file mode 100644 index 00000000000..befa2a0bd52 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-require-effective-target noinit } */ +/* { dg-options "-fdata-sections -save-temps" } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-final { scan-assembler ".section\t.noinit.var_noinit,\"aw\"\n" } } */ + +/* Test the "noinit" attribute with -fdata-sections. */ +#include "attr-noinit-main.inc" diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c new file mode 100644 index 00000000000..519e88a59a6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-require-effective-target noinit } */ +/* { dg-options "-flto -save-temps" } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-final { scan-file attr-noinit-3.ltrans0.ltrans.s ".section\t\.noinit,\"aw\"\n" } } */ + +/* Test the "noinit" attribute with -flto. Specifically examine the + final LTO assembly file, to ensure the "noinit" setting on the variable + hasn't been lost. */ +#include "attr-noinit-main.inc" + diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c new file mode 100644 index 00000000000..c3b5fffd166 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target noinit } */ +/* { dg-options "-Wattributes" } */ + +/* Check warning/error messages for "noinit" attribute misuse. */ +int __attribute__((noinit)) noinit_fn (void); /* { dg-warning "ignoring 'noinit' attribute not set on a variable" } */ +int __attribute__((section ("mysection"), noinit)) noinit_section1; /* { dg-warning "because it conflicts with attribute" } */ +int __attribute__((noinit, section ("mysection"))) noinit_section2; /* { dg-warning "because it conflicts with attribute" } */ +const int __attribute__((noinit)) noinit_const; /* { dg-warning "ignoring 'noinit' attribute set on const variable" } */ +/* { dg-error "uninitialized 'const noinit_const'" "" { target c++ } .-1 } */ +int __attribute__((noinit)) noinit_init = 42; /* { dg-warning "ignoring 'noinit' attribute set on initialized variable" } */ +void foo (void) { int __attribute__((noinit)) local_noinit; } /* { dg-error "'noinit' attribute cannot be specified for local variables" } */ diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc new file mode 100644 index 00000000000..92cdb9b8534 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc @@ -0,0 +1,58 @@ +/* This test checks that data marked with the "noinit" attribute is handled + correctly. + If data LMA != VMA (e.g. for simulating the copy of data from ROM to RAM), + then var_init will always be re-initialized to 2 and this test will loop + forever, so it must be skipped for those targets. */ + +#ifdef __cplusplus +extern "C" { +#endif +extern void _start (void) __attribute__ ((noreturn)); +#ifdef __cplusplus +} +#endif + +int var_common; +int var_zero = 0; +int var_one = 1; +int __attribute__((noinit)) var_noinit; +int var_init = 2; + +int +main (void) +{ + /* Make sure that the C startup code has correctly initialized the ordinary variables. */ + if (var_common != 0) + __builtin_abort (); + + /* Initialized variables are not re-initialized during startup, so + check their original values only during the first run of this + test. */ + if (var_init == 2) + if (var_zero != 0 || var_one != 1) + __builtin_abort (); + + switch (var_init) + { + case 2: + /* First time through - change all the values. */ + var_common = var_zero = var_one = var_noinit = var_init = 3; + break; + + case 3: + /* Second time through - make sure that var_noinit has not been reset. */ + if (var_noinit != 3) + __builtin_abort (); + __builtin_exit (0); + + default: + /* Any other value for var_init is an error. */ + __builtin_abort (); + } + + /* Simulate a processor reset by calling the C startup code. */ + _start (); + + /* Should never reach here. */ + __builtin_abort (); +} diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c new file mode 100644 index 00000000000..72dc3c27192 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-require-effective-target persistent } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-options "-save-temps" } */ +/* { dg-final { scan-assembler ".section\t.persistent,\"aw\"\n" } } */ + +/* Test the "persistent" attribute. */ +#include "attr-persistent-main.inc" diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c new file mode 100644 index 00000000000..a7de0d5d38b --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target persistent } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-options "-fdata-sections -save-temps" } */ +/* { dg-final { scan-assembler ".section\t.persistent.var_persistent,\"aw\"\n" } } */ + +/* Test the "persistent" attribute with -fdata-sections. */ +#include "attr-persistent-main.inc" diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c new file mode 100644 index 00000000000..3e4fd28618d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-require-effective-target persistent } */ +/* { dg-options "-flto -save-temps" } */ +/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ +/* { dg-final { scan-file attr-persistent-3.ltrans0.ltrans.s ".section\t\.persistent,\"aw\"\n" } } */ + +/* Test the "persistent" attribute with -flto. Specifically examine the + final LTO assembly file, to ensure the "persistent" setting on the variable + hasn't been lost. */ +#include "attr-persistent-main.inc" diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c new file mode 100644 index 00000000000..06d9f353629 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target persistent } */ +/* { dg-options "-Wattributes" } */ + +/* Check warning/error messages for "persistent" attribute misuse. */ +int __attribute__((persistent)) persistent_fn (void); /* { dg-warning "ignoring 'persistent' attribute not set on a variable" } */ +int __attribute__((section ("mysection"), persistent)) persistent_section1 = 1; /* { dg-warning "because it conflicts with attribute" } */ +int __attribute__((persistent, section ("mysection"))) persistent_section2 = 2; /* { dg-warning "because it conflicts with attribute" } */ +const int __attribute__((persistent)) persistent_const = 3; /* { dg-warning "ignoring 'persistent' attribute set on const variable" } */ +int __attribute__((persistent)) persistent_init; /* { dg-warning "ignoring 'persistent' attribute set on uninitialized variable" } */ +void foo (void) { int __attribute__((persistent)) local_persistent = 4; } /* { dg-error "'persistent' attribute cannot be specified for local variables" } */ diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc b/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc new file mode 100644 index 00000000000..a442141e55c --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc @@ -0,0 +1,58 @@ +/* This test checks that data marked with the "persistent" attribute is handled + correctly. + If data LMA != VMA (e.g. for simulating the copy of data from ROM to RAM), + then var_init will always be re-initialized to 2 and this test will loop + forever, so it must be skipped for those targets. */ + +#ifdef __cplusplus +extern "C" { +#endif +extern void _start (void) __attribute__ ((noreturn)); +#ifdef __cplusplus +} +#endif + +int var_common; +int var_zero = 0; +int var_one = 1; +int __attribute__((persistent)) var_persistent = 2; +int var_init = 2; + +int +main (void) +{ + /* Make sure that the C startup code has correctly initialized the ordinary variables. */ + if (var_common != 0) + __builtin_abort (); + + /* Initialized variables are not re-initialized during startup, so + check their original values only during the first run of this + test. */ + if (var_init == 2) + if (var_zero != 0 || var_one != 1 || var_persistent != 2) + __builtin_abort (); + + switch (var_init) + { + case 2: + /* First time through - change all the values. */ + var_common = var_zero = var_one = var_persistent = var_init = 3; + break; + + case 3: + /* Second time through - make sure that var_persistent has not been reset. */ + if (var_persistent != 3) + __builtin_abort (); + __builtin_exit (0); + + default: + /* Any other value for var_init is an error. */ + __builtin_abort (); + } + + /* Simulate a processor reset by calling the C startup code. */ + _start (); + + /* Should never reach here. */ + __builtin_abort (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c b/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c deleted file mode 100644 index c8fa22bf38b..00000000000 --- a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c +++ /dev/null @@ -1,63 +0,0 @@ -/* { dg-do run } */ -/* { dg-require-effective-target noinit } */ -/* { dg-options "-Wattributes" } */ -/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */ - -/* This test checks that noinit data is handled correctly. - If data LMA != VMA (e.g. for simulating the copy of data from ROM to RAM), - then var_init will always be re-initialized to 2 and this test will loop - forever. */ - -extern void _start (void) __attribute__ ((noreturn)); -extern void abort (void) __attribute__ ((noreturn)); -extern void exit (int) __attribute__ ((noreturn)); - -int var_common; -int var_zero = 0; -int var_one = 1; -int __attribute__((noinit)) var_noinit; -int var_init = 2; - -int __attribute__((noinit)) func(); /* { dg-warning "attribute only applies to variables" } */ -int __attribute__((section ("mysection"), noinit)) var_section1; /* { dg-warning "because it conflicts with attribute" } */ -int __attribute__((noinit, section ("mysection"))) var_section2; /* { dg-warning "because it conflicts with attribute" } */ - - -int -main (void) -{ - /* Make sure that the C startup code has correctly initialized the ordinary variables. */ - if (var_common != 0) - abort (); - - /* Initialized variables are not re-initialized during startup, so - check their original values only during the first run of this - test. */ - if (var_init == 2) - if (var_zero != 0 || var_one != 1) - abort (); - - switch (var_init) - { - case 2: - /* First time through - change all the values. */ - var_common = var_zero = var_one = var_noinit = var_init = 3; - break; - - case 3: - /* Second time through - make sure that d has not been reset. */ - if (var_noinit != 3) - abort (); - exit (0); - - default: - /* Any other value for var_init is an error. */ - abort (); - } - - /* Simulate a processor reset by calling the C startup code. */ - _start (); - - /* Should never reach here. */ - abort (); -} diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 43ac526567f..ff6bc5f4b92 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -380,6 +380,18 @@ proc check_effective_target_noinit { } { return 0 } +# The "persistent" attribute is only supported by some targets. +# This proc returns 1 if it's supported, 0 if it's not. + +proc check_effective_target_persistent { } { + if { [istarget arm*-*-eabi] + || [istarget msp430-*-*] } { + return 1 + } + + return 0 +} + ############################### # proc check_visibility_available { what_kind } ############################### diff --git a/gcc/tree.h b/gcc/tree.h index 664449aa329..078919bce5d 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2669,6 +2669,13 @@ extern tree vector_element_bits_tree (const_tree); (DECL_P (DECL) \ && (lookup_attribute ("noinit", DECL_ATTRIBUTES (DECL)) != NULL_TREE)) +/* Nonzero for a decl that is decorated with the "persistent" attribute. + decls with this attribute are placed into the ".persistent" section, so they + are not initialized by the target's startup code. */ +#define DECL_PERSISTENT_P(DECL) \ + (DECL_P (DECL) \ + && (lookup_attribute ("persistent", DECL_ATTRIBUTES (DECL)) != NULL_TREE)) + /* For function local variables of COMPLEX and VECTOR types, indicates that the variable is not aliased, and that all modifications to the variable have been adjusted so that diff --git a/gcc/varasm.c b/gcc/varasm.c index da7d0d7d91d..b92da26fb82 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -1057,7 +1057,11 @@ bss_initializer_p (const_tree decl, bool named) || (DECL_INITIAL (decl) == error_mark_node && !in_lto_p) || (flag_zero_initialized_in_bss - && initializer_zerop (DECL_INITIAL (decl))))); + && initializer_zerop (DECL_INITIAL (decl)) + /* A decl with the "persistent" attribute applied and + explicitly initialized to 0 should not be treated as a BSS + variable. */ + && !DECL_PERSISTENT_P (decl)))); } /* Compute the alignment of variable specified by DECL. @@ -6680,6 +6684,9 @@ default_section_type_flags (tree decl, const char *name, int reloc) if (strcmp (name, ".noinit") == 0) flags |= SECTION_WRITE | SECTION_BSS | SECTION_NOTYPE; + if (strcmp (name, ".persistent") == 0) + flags |= SECTION_WRITE | SECTION_NOTYPE; + /* Various sections have special ELF types that the assembler will assign by default based on the name. They are neither SHT_PROGBITS nor SHT_NOBITS, so when changing sections we don't want to print a @@ -7023,6 +7030,11 @@ default_elf_select_section (tree decl, int reloc, sname = ".sdata2"; break; case SECCAT_DATA: + if (DECL_P (decl) && DECL_PERSISTENT_P (decl)) + { + sname = ".persistent"; + break; + } return data_section; case SECCAT_DATA_REL: sname = ".data.rel"; @@ -7093,6 +7105,11 @@ default_unique_section (tree decl, int reloc) break; case SECCAT_DATA: prefix = one_only ? ".d" : ".data"; + if (DECL_P (decl) && DECL_PERSISTENT_P (decl)) + { + prefix = one_only ? ".p" : ".persistent"; + break; + } break; case SECCAT_DATA_REL: prefix = one_only ? ".d.rel" : ".data.rel";