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.
This commit is contained in:
Jozef Lawrynowicz 2020-11-23 12:06:39 +00:00
parent fb8309d4ab
commit 762ca20364
16 changed files with 307 additions and 73 deletions

View File

@ -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);
if (TREE_CODE (*node) != VAR_DECL)
message = G_("%qE attribute only applies to variables");
/* 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");
else if (!targetm.have_switchable_bss_sections)
message = G_("%qE attribute is specific to ELF targets");
if (message)
/* First perform generic validation common to "noinit" and "persistent"
attributes. */
if (!targetm_common.have_named_sections)
{
warning (OPT_Wattributes, message, name);
*no_add_attrs = true;
error_at (DECL_SOURCE_LOCATION (decl),
"section attributes are not supported for this target");
goto fail;
}
else
if (!VAR_P (decl))
{
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))
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"ignoring %qE attribute not set on a variable",
name);
goto fail;
}
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;
}
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 (!targetm.have_switchable_bss_sections)
{
error ("%qE attribute is specific to ELF targets", name);
goto fail;
}
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))
{
/* 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 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)
{
if (DECL_COMMON (decl) || DECL_INITIAL (decl) == NULL_TREE)
{
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. */

View File

@ -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

View File

@ -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.

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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" } */

View File

@ -1,16 +1,16 @@
/* { 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.
/* 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. */
forever, so it must be skipped for those targets. */
#ifdef __cplusplus
extern "C" {
#endif
extern void _start (void) __attribute__ ((noreturn));
extern void abort (void) __attribute__ ((noreturn));
extern void exit (int) __attribute__ ((noreturn));
#ifdef __cplusplus
}
#endif
int var_common;
int var_zero = 0;
@ -18,24 +18,19 @@ 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 ();
__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)
abort ();
__builtin_abort ();
switch (var_init)
{
@ -45,19 +40,19 @@ main (void)
break;
case 3:
/* Second time through - make sure that d has not been reset. */
/* Second time through - make sure that var_noinit has not been reset. */
if (var_noinit != 3)
abort ();
exit (0);
__builtin_abort ();
__builtin_exit (0);
default:
/* Any other value for var_init is an error. */
abort ();
__builtin_abort ();
}
/* Simulate a processor reset by calling the C startup code. */
_start ();
/* Should never reach here. */
abort ();
__builtin_abort ();
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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" } */

View File

@ -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 ();
}

View File

@ -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 }
###############################

View File

@ -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

View File

@ -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";