[committed] openmp: Add omp::decl attribute support [PR111392]

Message ID ZQqVzpk6dV9FP0LD@tucnak
State Unresolved
Headers
Series [committed] openmp: Add omp::decl attribute support [PR111392] |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Sept. 20, 2023, 6:48 a.m. UTC
  Hi!

This patch adds support for (so far C++) omp::decl attribute.  For
declare simd and declare variant directives it is essentially another
spelling of omp::decl, except per discussions it is not allowed inside
of omp::sequence attribute.  For threadprivate, declare target, allocate
and later groupprivate directives it should appertain to variable (or for
declare target also function definitions and) declarations and where in
normal syntax one specifies a list of variables (or variables and functions),
either as argument of the directive or clause argument, such argument is
not specified and implied to be the variable it applies to.

Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.

2023-09-20  Jakub Jelinek  <jakub@redhat.com>

	PR c++/111392
gcc/
	* attribs.cc (decl_attributes): Don't warn on omp::directive attribute
	on vars or function decls if -fopenmp or -fopenmp-simd.
gcc/c-family/
	* c-omp.cc (c_omp_directives): Add commented out groupprivate
	directive entry.
gcc/cp/
	* parser.h (struct cp_lexer): Add in_omp_decl_attribute member.
	* cp-tree.h (cp_maybe_parse_omp_decl): Declare.
	* parser.cc (cp_parser_handle_statement_omp_attributes): Diagnose
	omp::decl attribute on statements.  Adjust diagnostic wording for
	omp::decl.
	(cp_parser_omp_directive_args): Add DECL_P argument, set TREE_PUBLIC
	to it on the DEFERRED_PARSE tree.
	(cp_parser_omp_sequence_args): Adjust caller.
	(cp_parser_std_attribute): Handle omp::decl attribute.
	(cp_parser_omp_var_list): If parser->lexer->in_omp_decl_attribute
	don't expect any arguments, instead create clause or TREE_LIST for
	that decl.
	(cp_parser_late_parsing_omp_declare_simd): Adjust diagnostic wording
	for omp::decl.
	(cp_maybe_parse_omp_decl): New function.
	(cp_parser_omp_declare_target): If
	parser->lexer->in_omp_decl_attribute and first token isn't name or
	comma invoke cp_parser_omp_var_list.
	* decl2.cc (cplus_decl_attributes): Adjust diagnostic wording for
	omp::decl.  Handle omp::decl on declarations.
	* name-lookup.cc (finish_using_directive): Adjust diagnostic wording
	for omp::decl.
gcc/testsuite/
	* g++.dg/gomp/attrs-19.C: New test.
	* g++.dg/gomp/attrs-20.C: New test.
	* g++.dg/gomp/attrs-21.C: New test.
libgomp/
	* libgomp.texi: Mark decl attribute was added to the C++ attribute
	syntax as implemented.


	Jakub
  

Patch

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