--- gcc/attribs.cc.jj	2023-09-18 10:37:49.560050817 +0200
+++ gcc/attribs.cc	2023-09-19 12:29:01.495789400 +0200
@@ -719,6 +719,12 @@ decl_attributes (tree *node, tree attrib
 	      if (ns == NULL_TREE || !cxx11_attr_p)
 		warning (OPT_Wattributes, "%qE attribute directive ignored",
 			 name);
+	      else if ((flag_openmp || flag_openmp_simd)
+		       && is_attribute_p ("omp", ns)
+		       && is_attribute_p ("directive", name)
+		       && (VAR_P (*node)
+			   || TREE_CODE (*node) == FUNCTION_DECL))
+		continue;
 	      else
 		warning (OPT_Wattributes,
 			 "%<%E::%E%> scoped attribute directive ignored",
--- gcc/c-family/c-omp.cc.jj	2023-09-18 10:37:49.577050592 +0200
+++ gcc/c-family/c-omp.cc	2023-09-19 12:29:01.507789239 +0200
@@ -3306,6 +3306,8 @@ const struct c_omp_directive c_omp_direc
     C_OMP_DIR_STANDALONE, false },
   { "for", nullptr, nullptr, PRAGMA_OMP_FOR,
     C_OMP_DIR_CONSTRUCT, true },
+  /* { "groupprivate", nullptr, nullptr, PRAGMA_OMP_GROUPPRIVATE,
+    C_OMP_DIR_DECLARATIVE, false },  */
   /* { "interop", nullptr, nullptr, PRAGMA_OMP_INTEROP,
     C_OMP_DIR_STANDALONE, false },  */
   { "loop", nullptr, nullptr, PRAGMA_OMP_LOOP,
--- gcc/cp/parser.h.jj	2023-09-18 10:37:49.694049040 +0200
+++ gcc/cp/parser.h	2023-09-19 12:29:01.447790044 +0200
@@ -107,6 +107,10 @@ struct GTY (()) cp_lexer {
   /* The next lexer in a linked list of lexers.  */
   struct cp_lexer *next;
 
+  /* Set for omp::decl attribute parsing to the decl to which it
+     appertains.  */
+  tree in_omp_decl_attribute;
+
   /* True if we should output debugging information.  */
   bool debugging_p;
 
--- gcc/cp/cp-tree.h.jj	2023-09-19 09:24:19.505901636 +0200
+++ gcc/cp/cp-tree.h	2023-09-19 12:29:01.450790003 +0200
@@ -7317,6 +7317,7 @@ extern tree cp_convert_range_for (tree,
 extern void cp_convert_omp_range_for (tree &, tree &, tree &,
 				      tree &, tree &, tree &, tree &, tree &);
 extern void cp_finish_omp_range_for (tree, tree);
+extern bool cp_maybe_parse_omp_decl (tree, tree);
 extern bool parsing_nsdmi (void);
 extern bool parsing_function_declarator ();
 extern bool parsing_default_capturing_generic_lambda_in_template (void);
--- gcc/cp/parser.cc.jj	2023-09-19 09:24:19.692899089 +0200
+++ gcc/cp/parser.cc	2023-09-19 14:13:03.619970965 +0200
@@ -12001,6 +12001,12 @@ cp_parser_handle_statement_omp_attribute
 		parser->omp_attrs_forbidden_p = false;
 		bad = true;
 	      }
+	    else if (TREE_PUBLIC (d))
+	      {
+		error_at (first->location,
+			  "OpenMP %<omp::decl%> attribute on a statement");
+		bad = true;
+	      }
 	    const char *directive[3] = {};
 	    for (int i = 0; i < 3; i++)
 	      {
@@ -12022,8 +12028,9 @@ cp_parser_handle_statement_omp_attribute
 	    if (dir == NULL)
 	      {
 		error_at (first->location,
-			  "unknown OpenMP directive name in %<omp::directive%>"
-			  " attribute argument");
+			  "unknown OpenMP directive name in %qs attribute "
+			  "argument",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
 		continue;
 	      }
 	    c_omp_directive_kind kind = dir->kind;
@@ -29366,7 +29373,7 @@ cp_parser_gnu_attribute_list (cp_parser*
    parsing.  */
 
 static void
-cp_parser_omp_directive_args (cp_parser *parser, tree attribute)
+cp_parser_omp_directive_args (cp_parser *parser, tree attribute, bool decl_p)
 {
   cp_token *first = cp_lexer_peek_nth_token (parser->lexer, 2);
   if (first->type == CPP_CLOSE_PAREN)
@@ -29393,6 +29400,8 @@ cp_parser_omp_directive_args (cp_parser
   tree arg = make_node (DEFERRED_PARSE);
   DEFPARSE_TOKENS (arg) = cp_token_cache_new (first, last);
   DEFPARSE_INSTANTIATIONS (arg) = nullptr;
+  if (decl_p)
+    TREE_PUBLIC (arg) = 1;
   TREE_VALUE (attribute) = tree_cons (NULL_TREE, arg, TREE_VALUE (attribute));
 }
 
@@ -29440,7 +29449,7 @@ cp_parser_omp_sequence_args (cp_parser *
 	cp_parser_required_error (parser, RT_OPEN_PAREN, false,
 				  UNKNOWN_LOCATION);
       else if (directive)
-	cp_parser_omp_directive_args (parser, attribute);
+	cp_parser_omp_directive_args (parser, attribute, false);
       else
 	cp_parser_omp_sequence_args (parser, attribute);
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
@@ -29592,7 +29601,8 @@ cp_parser_std_attribute (cp_parser *pars
       if ((flag_openmp || flag_openmp_simd)
 	  && attr_ns == omp_identifier
 	  && (is_attribute_p ("directive", attr_id)
-	      || is_attribute_p ("sequence", attr_id)))
+	      || is_attribute_p ("sequence", attr_id)
+	      || is_attribute_p ("decl", attr_id)))
 	{
 	  error_at (token->location, "%<omp::%E%> attribute requires argument",
 		    attr_id);
@@ -29636,7 +29646,14 @@ cp_parser_std_attribute (cp_parser *pars
 	  {
 	    if (is_attribute_p ("directive", attr_id))
 	      {
-		cp_parser_omp_directive_args (parser, attribute);
+		cp_parser_omp_directive_args (parser, attribute, false);
+		return attribute;
+	      }
+	    else if (is_attribute_p ("decl", attr_id))
+	      {
+		TREE_VALUE (TREE_PURPOSE (attribute))
+		  = get_identifier ("directive");
+		cp_parser_omp_directive_args (parser, attribute, true);
 		return attribute;
 	      }
 	    else if (is_attribute_p ("sequence", attr_id))
@@ -37912,6 +37929,21 @@ static tree
 cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list,
 			bool allow_deref = false)
 {
+  if (parser->lexer->in_omp_decl_attribute)
+    {
+      if (kind)
+	{
+	  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+	  tree u = build_omp_clause (loc, kind);
+	  OMP_CLAUSE_DECL (u) = parser->lexer->in_omp_decl_attribute;
+	  OMP_CLAUSE_CHAIN (u) = list;
+	  return u;
+	}
+      else
+	return tree_cons (parser->lexer->in_omp_decl_attribute, NULL_TREE,
+			  list);
+    }
+
   if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return cp_parser_omp_var_list_no_open (parser, kind, list, NULL,
 					   allow_deref);
@@ -47843,7 +47875,9 @@ cp_parser_late_parsing_omp_declare_simd
 		  {
 		    error_at (first->location,
 			      "unknown OpenMP directive name in "
-			      "%<omp::directive%> attribute argument");
+			      "%qs attribute argument",
+			      TREE_PUBLIC (d)
+			      ? "omp::decl" : "omp::directive");
 		    continue;
 		  }
 		if (dir->id != PRAGMA_OMP_DECLARE
@@ -47949,6 +47983,89 @@ cp_parser_late_parsing_omp_declare_simd
   return attrs;
 }
 
+/* D should be DEFERRED_PARSE from omp::decl attribute.  If it contains
+   a threadprivate, groupprivate, allocate or declare target directive,
+   return true and parse it for DECL.  */
+
+bool
+cp_maybe_parse_omp_decl (tree decl, tree d)
+{
+  gcc_assert (TREE_CODE (d) == DEFERRED_PARSE);
+  cp_token *first = DEFPARSE_TOKENS (d)->first;
+  cp_token *last = DEFPARSE_TOKENS (d)->last;
+  const char *directive[3] = {};
+  for (int j = 0; j < 3; j++)
+    {
+      tree id = NULL_TREE;
+      if (first + j == last)
+	break;
+      if (first[j].type == CPP_NAME)
+	id = first[j].u.value;
+      else if (first[j].type == CPP_KEYWORD)
+	id = ridpointers[(int) first[j].keyword];
+      else
+	break;
+      directive[j] = IDENTIFIER_POINTER (id);
+    }
+  const c_omp_directive *dir = NULL;
+  if (directive[0])
+    dir = c_omp_categorize_directive (directive[0], directive[1],
+				      directive[2]);
+  if (dir == NULL)
+    {
+      error_at (first->location,
+		"unknown OpenMP directive name in "
+		"%qs attribute argument", "omp::decl");
+      return false;
+    }
+  if (dir->id != PRAGMA_OMP_THREADPRIVATE
+      /* && dir->id != PRAGMA_OMP_GROUPPRIVATE */
+      && dir->id != PRAGMA_OMP_ALLOCATE
+      && (dir->id != PRAGMA_OMP_DECLARE
+	  || strcmp (directive[1], "target") != 0))
+    return false;
+
+  if (!flag_openmp && !dir->simd)
+    return true;
+
+  cp_parser *parser = the_parser;
+  cp_lexer *lexer = cp_lexer_alloc ();
+  lexer->debugging_p = parser->lexer->debugging_p;
+  lexer->in_omp_decl_attribute = decl;
+  vec_safe_reserve (lexer->buffer, last - first + 3, true);
+  cp_token tok = {};
+  tok.type = CPP_PRAGMA;
+  tok.keyword = RID_MAX;
+  tok.u.value = build_int_cst (NULL, dir->id);
+  tok.location = first->location;
+  lexer->buffer->quick_push (tok);
+  while (++first < last)
+    lexer->buffer->quick_push (*first);
+  tok = {};
+  tok.type = CPP_PRAGMA_EOL;
+  tok.keyword = RID_MAX;
+  tok.location = last->location;
+  lexer->buffer->quick_push (tok);
+  tok = {};
+  tok.type = CPP_EOF;
+  tok.keyword = RID_MAX;
+  tok.location = last->location;
+  lexer->buffer->quick_push (tok);
+  lexer->next = parser->lexer;
+  lexer->next_token = lexer->buffer->address ();
+  lexer->last_token = lexer->next_token
+		      + lexer->buffer->length ()
+		      - 1;
+  lexer->in_omp_attribute_pragma = true;
+  parser->lexer = lexer;
+  /* Move the current source position to that of the first token in the
+     new lexer.  */
+  cp_lexer_set_source_position_from_token (lexer->next_token);
+  cp_parser_pragma (parser, pragma_external, NULL);
+
+  return true;
+}
+
 /* Helper for cp_parser_omp_declare_target, handle one to or link clause
    on #pragma omp declare target.  Return false if errors were reported.  */
 
@@ -48048,7 +48165,8 @@ cp_parser_omp_declare_target (cp_parser
     clauses
       = cp_parser_omp_all_clauses (parser, OMP_DECLARE_TARGET_CLAUSE_MASK,
 				   "#pragma omp declare target", pragma_tok);
-  else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+  else if (parser->lexer->in_omp_decl_attribute
+	   || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_ENTER,
 					clauses);
--- gcc/cp/decl2.cc.jj	2023-09-18 10:37:49.634049836 +0200
+++ gcc/cp/decl2.cc	2023-09-19 12:29:01.495789400 +0200
@@ -1738,16 +1738,34 @@ cplus_decl_attributes (tree *decl, tree
 	    {
 	      tree name = get_attribute_name (*pa);
 	      if (is_attribute_p ("directive", name)
-		  || is_attribute_p ("sequence", name))
+		  || is_attribute_p ("sequence", name)
+		  || is_attribute_p ("decl", name))
 		{
-		  if (!diagnosed)
+		  const char *p = NULL;
+		  if (TREE_VALUE (*pa) == NULL_TREE)
+		    p = IDENTIFIER_POINTER (name);
+		  for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a))
 		    {
-		      error ("%<omp::%E%> not allowed to be specified in this "
-			     "context", name);
+		      tree d = TREE_VALUE (a);
+		      gcc_assert (TREE_CODE (d) == DEFERRED_PARSE);
+		      if (TREE_PUBLIC (d)
+			  && (VAR_P (*decl)
+			      || TREE_CODE (*decl) == FUNCTION_DECL)
+			  && cp_maybe_parse_omp_decl (*decl, d))
+			continue;
+		      p = TREE_PUBLIC (d) ? "decl" : "directive";
+		    }
+		  if (p && !diagnosed)
+		    {
+		      error ("%<omp::%s%> not allowed to be specified in "
+			     "this context", p);
 		      diagnosed = true;
 		    }
-		  *pa = TREE_CHAIN (*pa);
-		  continue;
+		  if (p)
+		    {
+		      *pa = TREE_CHAIN (*pa);
+		      continue;
+		    }
 		}
 	    }
 	  pa = &TREE_CHAIN (*pa);
--- gcc/cp/name-lookup.cc.jj	2023-09-19 09:24:19.632899906 +0200
+++ gcc/cp/name-lookup.cc	2023-09-19 12:29:01.451789990 +0200
@@ -8402,12 +8402,24 @@ finish_using_directive (tree target, tre
 	else if ((flag_openmp || flag_openmp_simd)
 		 && get_attribute_namespace (a) == omp_identifier
 		 && (is_attribute_p ("directive", name)
-		     || is_attribute_p ("sequence", name)))
+		     || is_attribute_p ("sequence", name)
+		     || is_attribute_p ("decl", name)))
 	  {
 	    if (!diagnosed)
-	      error ("%<omp::%E%> not allowed to be specified in this "
-		     "context", name);
-	    diagnosed = true;
+	      {
+		if (tree ar = TREE_VALUE (a))
+		  {
+		    tree d = TREE_VALUE (ar);
+		    gcc_assert (TREE_CODE (d) == DEFERRED_PARSE);
+		    error ("%<omp::%s%> not allowed to be specified in "
+			   "this context",
+			   TREE_PUBLIC (d) ? "decl" : "directive");
+		  }
+		else
+		  error ("%<omp::%E%> not allowed to be specified in this "
+			 "context", name);
+		diagnosed = true;
+	      }
 	  }
 	else
 	  warning (OPT_Wattributes, "%qD attribute directive ignored", name);
--- gcc/testsuite/g++.dg/gomp/attrs-19.C.jj	2023-09-19 13:46:37.616293306 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-19.C	2023-09-19 15:48:01.254979254 +0200
@@ -0,0 +1,68 @@
+// { dg-do compile { target c++11 } }
+
+void foo1 ();
+
+void
+foo ()
+{
+  [[omp::decl (declare variant (foo1) match (construct={parallel,for}))]]
+  extern void foo2 ();
+  [[omp::sequence (directive (parallel), directive (for))]]
+  for (int i = 0; i < 5; i++)
+    foo2 ();
+  [[omp::decl (declare simd simdlen(4) linear(l) aligned(p:4) uniform(p) inbranch),
+    omp::directive (declare simd simdlen(8) notinbranch)]]
+  extern int foo3 (int l, int *p);
+  [[omp::directive (declare simd simdlen(4) linear(l) aligned(p:4) uniform(p) inbranch),
+    omp::decl (declare simd simdlen(8) notinbranch)]]
+  extern int foo4 (int l, int *p);
+  [[omp::decl (declare simd simdlen(4) linear(l) aligned(p:4) uniform(p) inbranch),
+    omp::decl (declare simd simdlen(8) notinbranch)]]
+  extern int foo5 (int l, int *p);
+}
+
+void bar1 ();
+
+void
+bar ()
+{
+  [[using omp : decl (declare variant (bar1), match (construct={parallel,for}))]]	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+  extern void bar2 ();
+  [[using omp : sequence (directive (parallel), directive (for))]]			// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+  for (int i = 0; i < 5; i++)
+    bar2 ();
+  [[omp::decl (declare simd, simdlen(4), linear(l), aligned(p:4),uniform(p),inbranch),
+    omp::directive (declare simd simdlen(8) notinbranch)]]
+  extern int bar3 (int l, int *p);
+  [[using omp : directive (declare simd,simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+    decl (declare simd, simdlen(8), notinbranch)]]
+  extern int bar4 (int l, int *p);
+  [[omp::decl (declare simd, simdlen(4), linear(l), aligned(p:4), uniform(p), inbranch),
+    omp::decl (declare simd, simdlen(8), notinbranch)]]
+  extern int bar5 (int l, int *p);
+}
+
+struct S { S (); ~S (); int s; };
+
+[[omp::decl (threadprivate)]] int t1, t2;
+int x1, t3 [[omp::decl (threadprivate)]], x2, t4 [[omp::decl (threadprivate)]] [5];
+[[maybe_unused, omp::decl (threadprivate)]] int t5, t6;
+[[using omp : decl (threadprivate)]] S t7, t8;	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+[[using omp : decl (declare target enter device_type (host))]] int d1, d2, d3 (int, int), d4;	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+int x3, d5 [[omp::decl (declare target, enter, device_type (any))]], d6 [[omp::decl (declare target link)]], x4;
+int d7 [[omp::decl (declare target)]];
+[[using omp : decl (declare target), decl (declare target)]] int d8, d9;	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+
+void
+baz ()
+{
+  [[omp::decl (threadprivate)]] static int t1, t2;
+  static int x1, t3 [[omp::decl (threadprivate)]], x2, t4 [[omp::decl (threadprivate)]] [5];
+  [[maybe_unused, omp::decl (threadprivate)]] extern int t5, t6;
+  [[using omp : decl (declare target enter)]] extern int d1, d2, d3 (int, int), d4;		// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+  static int x3, d5 [[omp::decl (declare target, enter, device_type (any))]], d6 [[omp::decl (declare target link)]], x4;
+  ++t1; ++t2;
+  ++t3; ++t4[2];
+  ++t5; ++t6;
+  ++d1;
+}
--- gcc/testsuite/g++.dg/gomp/attrs-20.C.jj	2023-09-19 13:48:33.998732461 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-20.C	2023-09-19 13:57:54.018211210 +0200
@@ -0,0 +1,240 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fopenmp -ffat-lto-objects -fdump-tree-gimple" }
+
+extern "C" void abort ();
+
+[[omp::decl (declare simd, linear (l))]] extern int f1 (int l);
+extern int f2 (int), f3 [[omp::decl (declare simd, uniform (m))]] (int m), f4 (int), z;
+[[omp::decl (declare simd, linear (l), simdlen(4))]] extern int f5 [[omp::decl (declare simd uniform (l) simdlen (8) notinbranch)]] (int l);
+
+int
+f1 (int l)
+{
+  return l;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM16l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN16l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f2 (int l)
+{
+  return l + 1;
+}
+
+// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f2i:" { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f3 (int l)
+{
+  return l + 2;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM16u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN16u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f4 (int l)
+{
+  return l + 3;
+}
+
+// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f4i:" { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f5 (int l)
+{	// { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-1 }
+  return l + 4;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-not "_ZGV\[bcde]M8u__Z2f5i:" { target { i?86-*-* x86_64-*-* } } } }
+
+[[omp::decl (declare simd, linear (l), simdlen(4), notinbranch),
+  omp::decl (declare simd, uniform (l), simdlen(4), inbranch)]]
+int
+f6 [[using omp : decl (declare simd uniform (l) simdlen (8), notinbranch),	// { dg-warning "attribute using prefix only available with" "" { target c++14_down } }
+     decl (declare simd, linear (l), simdlen (8), inbranch)]] (int l)
+{	// { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-2 }
+  return l + 5;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-not "_ZGV\[bcde]M4l__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-not "_ZGV\[bcde]N4u__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-not "_ZGV\[bcde]M8u__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-not "_ZGV\[bcde]N8l__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f7 (int l)
+{
+  return l + 6;
+}
+
+// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f7i:" { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f8 (int l)
+{
+  return l + 7;
+}
+
+// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f8i:" { target { i?86-*-* x86_64-*-* } } } }
+
+[[omp::decl (declare variant (f7), match (construct={parallel})),
+  omp::decl (declare simd uniform (l), simdlen(4))]]
+int
+f9 [[omp::decl (declare simd uniform (l) simdlen (8)),
+     omp::decl (declare variant (f8) match (construct={parallel,for}))]] (int l)
+{	// { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-2 }
+  return l + 8;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+int z;
+
+void
+test ()
+{
+  [[omp::directive (parallel)]]
+  if (f9 (3) != 9)
+    abort ();
+  [[omp::directive (parallel for)]]
+  for (int i = 0; i < 1; i++)
+    if (f9 (4) != 11)
+      abort ();
+  if (f9 (5) != 13)
+    abort ();
+}
+
+// { dg-final { scan-tree-dump-times " = f7 \\\(3\\\);" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times " = f8 \\\(4\\\);" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times " = f9 \\\(5\\\);" 1 "gimple" } }
+
+template <int N>
+int
+f10 (int x)
+{
+  return x + N;
+}
+
+template [[omp::decl (declare simd, notinbranch)]] int f10<0> (int);
+
+// { dg-final { scan-assembler-times "_ZGVbN4v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN16v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+template  int f10<1> [[omp::decl (declare simd inbranch linear(x))]] (int x);
+
+// { dg-final { scan-assembler-times "_ZGVbM4l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM16l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+template <int N>
+int f11 (int);
+
+template <> [[omp::decl (declare simd, inbranch)]] int
+f11<0> (int x)
+{
+  return x;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM16v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+template <> int
+f11<1> [[omp::decl (declare simd, notinbranch, linear (y))]] (int y)
+{
+  return y;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbN4l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN16l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+struct S
+{
+  [[omp::decl (declare simd, inbranch, uniform (this))]] int f12 (int x);
+  int f13 [[gnu::noinline, omp::decl (declare simd notinbranch uniform (this) linear (y))]] (int y) { return y; }
+};
+
+int
+S::f12 (int x)
+{
+  return x;
+}
+
+// { dg-final { scan-assembler-times "_ZGVbM4uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcM4uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdM8uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeM16uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+// { dg-final { scan-assembler-times "_ZGVbN4ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVcN4ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVdN8ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler-times "_ZGVeN16ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } }
+
+int
+f14 (S &p, int x)
+{
+  return p.f13 (x);
+}
--- gcc/testsuite/g++.dg/gomp/attrs-21.C.jj	2023-09-19 14:53:50.901137744 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-21.C	2023-09-19 16:30:26.031973827 +0200
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+  [[omp::decl]] int v1;						// { dg-error "'omp::decl' attribute requires argument" }
+  [[omp::decl ()]] int v2;					// { dg-error "expected OpenMP directive name" }
+								// { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 }
+  [[omp::decl (nonexistent foobar)]] int v3;			// { dg-error "unknown OpenMP directive name in 'omp::decl' attribute argument" }
+								// { dg-error "'omp::decl' not allowed to be specified in this context" "" { target *-*-* } .-1 }
+  [[omp::sequence(decl(threadprivate))]] int v4;		// { dg-error "expected 'directive' or 'sequence'" }
+								// { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 }
+  [[omp::sequence(omp::decl(threadprivate))]] int v5;		// { dg-error "expected 'directive' or 'sequence'" }
+								// { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 }
+  [[omp::decl (barrier)]];					// { dg-error "OpenMP 'omp::decl' attribute on a statement" }
+  [[omp::decl (parallel)]] {};					// { dg-error "OpenMP 'omp::decl' attribute on a statement" }
+  extern int [[omp::decl (threadprivate)]] *v6;			// { dg-warning "attribute ignored" }
+  [[omp::decl (threadprivate (v5))]] static int v7;		// { dg-error "expected end of line before '\\\(' token" }
+  extern int v8;
+  [[omp::decl (declare target (v8))]] static int v9;		// { dg-error "expected end of line before '\\\(' token" }
+  [[omp::decl (declare target enter (v8))]] static int v10;	// { dg-error "expected an OpenMP clause before '\\\(' token" }
+  [[omp::decl (declare target, link (v9))]] static int v11;	// { dg-error "expected an OpenMP clause before '\\\(' token" }
+  [[omp::decl (declare target device_type (any))]] static int v12;	// { dg-error "directive with only 'device_type' clause" }
+}
+
+int i;
+[[omp::decl (assume (i < 42))]];				// { dg-error "OpenMP 'omp::decl' attribute on a statement" }
--- libgomp/libgomp.texi.jj	2023-09-11 11:05:47.590726871 +0200
+++ libgomp/libgomp.texi	2023-09-19 17:41:42.002500222 +0200
@@ -444,7 +444,7 @@ Technical Report (TR) 11 is the first pr
 @item Features deprecated in versions 5.2, 5.1 and 5.0 were removed
       @tab N/A @tab Backward compatibility
 @item The @code{decl} attribute was added to the C++ attribute syntax
-      @tab N @tab
+      @tab Y @tab
 @item @code{_ALL} suffix to the device-scope environment variables
       @tab P @tab Host device number wrongly accepted
 @item For Fortran, @emph{locator list} can be also function reference with
