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 *);
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),
};
{ "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 },
{ "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. */
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. */
@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
@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.
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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"
+
--- /dev/null
+/* { 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" } */
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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" } */
--- /dev/null
+/* 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 ();
+}
+++ /dev/null
-/* { 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 ();
-}
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 }
###############################
(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
|| (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.
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
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";
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";