Implement the "persistent" attribute
authorJozef Lawrynowicz <jozef.l@mittosystems.com>
Mon, 23 Nov 2020 12:06:39 +0000 (12:06 +0000)
committerJozef Lawrynowicz <jozef.l@mittosystems.com>
Mon, 23 Nov 2020 12:15:51 +0000 (12:15 +0000)
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.

17 files changed:
gcc/c-family/c-attribs.c
gcc/doc/extend.texi
gcc/doc/sourcebuild.texi
gcc/testsuite/c-c++-common/torture/attr-noinit-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-noinit-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-noinit-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-persistent-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-persistent-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-persistent-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c [deleted file]
gcc/testsuite/lib/target-supports.exp
gcc/tree.h
gcc/varasm.c

index 1d2ab7c81eda254086fbb4e69cc699fe754c6270..6d515dca05ac8cf2fc022ec56061f09792bf0e86 100644 (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);
+  /* 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.  */
 
index f0269422ca21aeb95cf697e7fff87b161b8b7a42..6018347daed5d02c8c0f31ffffbadb6ed9ba21fa 100644 (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
index 566fda02c306b9b6232025de32986b8d990862f9..852eaa2e6765c53ce52d19de610cc1df6c828fac 100644 (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.
 
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 (file)
index 0000000..877e764
--- /dev/null
@@ -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 (file)
index 0000000..befa2a0
--- /dev/null
@@ -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 (file)
index 0000000..519e88a
--- /dev/null
@@ -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 (file)
index 0000000..c3b5fff
--- /dev/null
@@ -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 (file)
index 0000000..92cdb9b
--- /dev/null
@@ -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 (file)
index 0000000..72dc3c2
--- /dev/null
@@ -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 (file)
index 0000000..a7de0d5
--- /dev/null
@@ -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 (file)
index 0000000..3e4fd28
--- /dev/null
@@ -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 (file)
index 0000000..06d9f35
--- /dev/null
@@ -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 (file)
index 0000000..a442141
--- /dev/null
@@ -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 (file)
index c8fa22b..0000000
+++ /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 ();
-}
index 43ac526567f17dd7f64d28a53e9439ec5493ef0f..ff6bc5f4b9251d976db163fe5688542a7cf4cdf4 100644 (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 }
 ###############################
index 664449aa32991eb0469b053419242414952e2528..078919bce5deaaa1cb10b2774f78bd672b024499 100644 (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
index da7d0d7d91d15168784d4b21ba467e75a45b8cf0..b92da26fb820523aa1b48408bfbb5e53caec0008 100644 (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";