[2/2] Implement the "persistent" attribute

Message ID 20201027114631.v5rmk4m2vxzn2fk6@jozef-acer-manjaro
State Superseded
Headers show
Series
  • [1/2] Fix "noinit" attribute being ignored for -O0 and -fdata-sections
Related show

Commit Message

Jozef Lawrynowicz Oct. 27, 2020, 11:46 a.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 bootstrapped/regtested x86_64-pc-linux-gnu and regtested
for arm-none-eabi.

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

Date: Mon, 26 Oct 2020 17:00:31 +0000
Subject: [PATCH 2/2] 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:

	* cgraph.h (symtab_node): Adjust comment for noinit flag.
	* doc/extend.texi: Document the "persistent" variable attribute.
	* tree.h (DECL_NOINIT_P): Adjust comment.
	* varasm.c (bss_initializer_p): Return false for a DECL_NOINIT_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                      | 150 ++++++++++++------
 gcc/cgraph.h                                  |   4 +-
 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                                    |   5 +-
 gcc/varasm.c                                  |  19 ++-
 16 files changed, 304 insertions(+), 77 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.28.0

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 6f8288326ee..d96bd3d87c9 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -92,10 +92,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 *);
@@ -241,9 +241,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),
 };
@@ -320,7 +323,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 },
@@ -488,7 +491,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 },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
@@ -2343,68 +2348,117 @@  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 (!VAR_P (decl))
+    {
+      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 (TREE_CODE (*node) != VAR_DECL)
-    message = G_("%qE attribute only applies to variables");
+  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;
+    }
 
-  /* 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 (!targetm.have_switchable_bss_sections)
+    {
+      error ("%qE attribute is specific to ELF targets", name);
+      goto fail;
+    }
 
-  else if (!targetm.have_switchable_bss_sections)
-    message = G_("%qE attribute is specific to ELF targets");
+  if (TREE_READONLY (decl))
+    {
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		  "ignoring %qE attribute set on const variable",
+		  name);
+      goto fail;
+    }
 
-  if (message)
+  /* Now validate noinit/persistent individually.  */
+  if (strcmp (IDENTIFIER_POINTER (name), "noinit") == 0)
     {
-      warning (OPT_Wattributes, message, name);
-      *no_add_attrs = true;
+      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
+  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;
-
-	  /* Set DECL_NOINIT_P to indicate the decaration should not be
-	     initialized by the startup code.  */
-	  DECL_NOINIT_P (*node) = 1;
+	  warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		      "ignoring %qE attribute set on uninitialized variable",
+		      name);
+	  goto fail;
 	}
     }
+  else
+    gcc_unreachable ();
 
+  /* Set DECL_NOINIT_P to indicate the declaration should not be initialized
+     by the startup code.  "noinit" and "persistent" decls can be later
+     differentiated between attributes by examining DECL_INITIAL.  */
+  DECL_NOINIT_P (decl) = 1;
+
+  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/cgraph.h b/gcc/cgraph.h
index 4176f761482..e127a31ce0c 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -577,8 +577,8 @@  public:
   /* Set when symbol is an IFUNC resolver.  */
   unsigned ifunc_resolver : 1;
 
-  /* Set when the symbol is decorated with the "noinit" attribute,
-     which indicates it should not be initialized by the runtime
+  /* Set when the symbol is decorated with the "noinit" or "persistent"
+     attributes, which indicate it should not be initialized by the runtime
      startup code.  */
   unsigned noinit : 1;
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 62549b02452..61ba1e0c601 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7318,9 +7318,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 4122285cc1d..6988fa162e5 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 ac73f3c3af9..a06a24a009a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2648,8 +2648,9 @@  extern tree vector_element_bits_tree (const_tree);
 #define DECL_PRESERVE_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.preserve_flag
 
-/* Nonzero for a decl that is decorated with the "noinit" attribute.
-   decls with this attribute are placed into a special section so they are not
+/* Nonzero for a decl that is decorated with the "noinit" or "persistent"
+   attributes.
+   decls with these attributes are placed into special sections so they are not
    initialized by the target's startup code.  */
 #define DECL_NOINIT_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.noinit_flag
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 0d44342e813..64de7a7faab 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -1019,7 +1019,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_NOINIT_P (decl))));
 }
 
 /* Compute the alignment of variable specified by DECL.
@@ -6643,6 +6647,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
@@ -6986,6 +6993,11 @@  default_elf_select_section (tree decl, int reloc,
       sname = ".sdata2";
       break;
     case SECCAT_DATA:
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
+	{
+	  sname = ".persistent";
+	  break;
+	}
       return data_section;
     case SECCAT_DATA_REL:
       sname = ".data.rel";
@@ -7056,6 +7068,11 @@  default_unique_section (tree decl, int reloc)
       break;
     case SECCAT_DATA:
       prefix = one_only ? ".d" : ".data";
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
+	{
+	  prefix = one_only ? ".p" : ".persistent";
+	  break;
+	}
       break;
     case SECCAT_DATA_REL:
       prefix = one_only ? ".d.rel" : ".data.rel";