[3/3] Implement the "persistent" attribute

Message ID 20201115220629.tqoyd6ttvqdjtfo5@jozef-acer-manjaro
State New
Headers show
Series
  • [1/3] Fix "noinit" attribute being ignored for -O0 and -fdata-sections
Related show

Commit Message

Jozef Lawrynowicz Nov. 15, 2020, 10:06 p.m.
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.

Successfully regtested for arm-none-eabi.

Ok for trunk?
From c67b1bb6f46a69916c7de74617f4301b95c894d8 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

Date: Sun, 15 Nov 2020 21:44:10 +0000
Subject: [PATCH 3/3] 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: Document the "persistent" variable attribute.
	* 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.
---
 gcc/c-family/c-attribs.c                      | 141 ++++++++++++------
 gcc/doc/extend.texi                           |  20 ++-
 .../c-c++-common/torture/attr-noinit-1.c      |   7 +
 .../c-c++-common/torture/attr-noinit-2.c      |   8 +
 .../c-c++-common/torture/attr-noinit-3.c      |  11 ++
 .../torture/attr-noinit-invalid.c             |  12 ++
 .../torture/attr-noinit-main.inc}             |  37 ++---
 .../c-c++-common/torture/attr-persistent-1.c  |   8 +
 .../c-c++-common/torture/attr-persistent-2.c  |   8 +
 .../c-c++-common/torture/attr-persistent-3.c  |  10 ++
 .../torture/attr-persistent-invalid.c         |  11 ++
 .../torture/attr-persistent-main.inc          |  58 +++++++
 gcc/testsuite/lib/target-supports.exp         |  13 ++
 gcc/tree.h                                    |   7 +
 gcc/varasm.c                                  |  19 ++-
 15 files changed, 301 insertions(+), 69 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c
 rename gcc/testsuite/{gcc.c-torture/execute/noinit-attribute.c => c-c++-common/torture/attr-noinit-main.inc} (55%)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc

-- 
2.29.2

Comments

Aldy Hernandez via Gcc-patches Nov. 21, 2020, 4:43 p.m. | #1
On 11/15/20 3:06 PM, Jozef Lawrynowicz wrote:
> 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.

>

> Successfully regtested for arm-none-eabi.

>

> Ok for trunk?

>

> 0003-Implement-the-persistent-attribute.patch

>

> From c67b1bb6f46a69916c7de74617f4301b95c894d8 Mon Sep 17 00:00:00 2001

> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

> Date: Sun, 15 Nov 2020 21:44:10 +0000

> Subject: [PATCH 3/3] 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: Document the "persistent" variable attribute.

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

I think you need to document the new effective target check in
sourcebuild.texi.  With that change this is OK

jeff

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index f1680820ecd..6b26f43c298 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 *);
@@ -246,9 +246,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),
 };
@@ -337,7 +340,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 },
@@ -507,7 +510,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.  */
@@ -2381,64 +2386,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 5f1e3bf8a2e..295f5b8d822 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7420,9 +7420,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.
 
 @end table
 
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/gcc.c-torture/execute/noinit-attribute.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
similarity index 55%
rename from gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
rename to gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
index c8fa22bf38b..92cdb9b8534 100644
--- a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
@@ -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 ();
 }
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/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 49c65b50109..38cdbc58655 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -380,6 +380,19 @@  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 f342731ae59..b52f0e3ae1f 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2666,6 +2666,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 3c902059069..73109dceb1e 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -1024,7 +1024,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.
@@ -6647,6 +6651,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
@@ -6990,6 +6997,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";
@@ -7060,6 +7072,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";