[committed] openmp: Add omp::decl support for C2X

Message ID ZUX99pqtT+0bL/8t@tucnak
State Unresolved
Headers
Series [committed] openmp: Add omp::decl support for C2X |

Checks

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

Commit Message

Jakub Jelinek Nov. 4, 2023, 8:16 a.m. UTC
  Hi!

This patch adds omp::decl support which has been added recently for
C++ also to C.

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

2023-11-04  Jakub Jelinek  <jakub@redhat.com>

	* c-parser.h (c_maybe_parse_omp_decl): Declare.
	* c-parser.cc (struct c_parser): Add in_omp_decl_attribute member.
	(c_parser_std_attribute): Uncoment omp::decl handling.
	(c_parser_omp_var_list_parens): If parser->in_omp_decl_attribute
	don't expect any arguments, instead create clause or TREE_LIST for
	that decl.
	(c_maybe_parse_omp_decl): New function.
	(c_parser_omp_declare_target): If parser->in_omp_decl_attribute and
	first token isn't name or comma invoke c_parser_omp_var_list_parens.
	* c-decl.cc (c_decl_attributes): Uncomment omp::decl handling and
	use *node rather than non-existing *decl.

	* gcc.dg/gomp/attrs-19.c: New test.
	* gcc.dg/gomp/attrs-20.c: New test.
	* gcc.dg/gomp/attrs-21.c: New test.


	Jakub
  

Patch

--- gcc/c/c-parser.h.jj	2023-11-03 14:45:30.884543778 +0100
+++ gcc/c/c-parser.h	2023-11-03 15:24:44.859097248 +0100
@@ -204,5 +204,6 @@  extern void c_parser_declspecs (c_parser
 				bool, bool, bool, bool, bool,
 				enum c_lookahead_kind);
 extern struct c_type_name *c_parser_type_name (c_parser *, bool = false);
+extern bool c_maybe_parse_omp_decl (tree, tree);
 
 #endif
--- gcc/c/c-parser.cc.jj	2023-11-03 14:45:30.903543539 +0100
+++ gcc/c/c-parser.cc	2023-11-03 16:18:30.593079455 +0100
@@ -262,6 +262,10 @@  struct GTY(()) c_parser {
      attributes turned into pragma, vector of tokens created from that,
      otherwise NULL.  */
   vec<c_token, va_gc> *in_omp_attribute_pragma;
+
+  /* Set for omp::decl attribute parsing to the decl to which it
+     appertains.  */
+  tree in_omp_decl_attribute;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -5797,14 +5801,14 @@  c_parser_std_attribute (c_parser *parser
 		parens.skip_until_found_close (parser);
 		return attribute;
 	      }
-/*	    else if (is_attribute_p ("decl", name))
+	    else if (is_attribute_p ("decl", name))
 	      {
 		TREE_VALUE (TREE_PURPOSE (attribute))
 		  = get_identifier ("directive");
 		c_parser_omp_directive_args (parser, attribute, true);
 		parens.skip_until_found_close (parser);
 		return attribute;
-	      } */
+	      }
 	    else if (is_attribute_p ("sequence", name))
 	      {
 		TREE_VALUE (TREE_PURPOSE (attribute))
@@ -15147,6 +15151,19 @@  c_parser_omp_var_list_parens (c_parser *
   /* The clauses location.  */
   location_t loc = c_parser_peek_token (parser)->location;
 
+  if (parser->in_omp_decl_attribute)
+    {
+      if (kind)
+	{
+	  tree u = build_omp_clause (loc, kind);
+	  OMP_CLAUSE_DECL (u) = parser->in_omp_decl_attribute;
+	  OMP_CLAUSE_CHAIN (u) = list;
+	  return u;
+	}
+      else
+	return tree_cons (parser->in_omp_decl_attribute, NULL_TREE, list);
+    }
+
   matching_parens parens;
   if (parens.require_open (parser))
     {
@@ -24498,6 +24515,84 @@  c_finish_omp_declare_simd (c_parser *par
     clauses[0].type = CPP_PRAGMA;
 }
 
+/* D should be C_TOKEN_VEC from omp::decl attribute.  If it contains
+   a threadprivate, groupprivate, allocate or declare target directive,
+   return true and parse it for DECL.  */
+
+bool
+c_maybe_parse_omp_decl (tree decl, tree d)
+{
+  gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+  vec<c_token, va_gc> *toks = C_TOKEN_VEC_TOKENS (d);
+  c_token *first = toks->address ();
+  c_token *last = first + toks->length ();
+  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].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;
+
+  c_parser *parser = the_parser;
+  unsigned int tokens_avail = parser->tokens_avail;
+  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
+  toks = NULL;
+  vec_safe_reserve (toks, last - first + 2, true);
+  c_token tok = {};
+  tok.type = CPP_PRAGMA;
+  tok.keyword = RID_MAX;
+  tok.pragma_kind = pragma_kind (dir->id);
+  tok.location = first->location;
+  toks->quick_push (tok);
+  while (++first < last)
+    toks->quick_push (*first);
+  tok = {};
+  tok.type = CPP_PRAGMA_EOL;
+  tok.keyword = RID_MAX;
+  tok.location = last[-1].location;
+  toks->quick_push (tok);
+  tok = {};
+  tok.type = CPP_EOF;
+  tok.keyword = RID_MAX;
+  tok.location = last[-1].location;
+  tok.flags = tokens_avail;
+  toks->quick_push (tok);
+  parser->in_omp_decl_attribute = decl;
+  parser->tokens = toks->address ();
+  parser->tokens_avail = toks->length ();
+  parser->in_omp_attribute_pragma = toks;
+  c_parser_pragma (parser, pragma_external, NULL);
+  parser->in_omp_decl_attribute = NULL_TREE;
+  return true;
+}
 
 /* OpenMP 4.0:
    # pragma omp declare target new-line
@@ -24526,7 +24621,8 @@  c_parser_omp_declare_target (c_parser *p
 	  && c_parser_peek_2nd_token (parser)->type == CPP_NAME))
     clauses = c_parser_omp_all_clauses (parser, OMP_DECLARE_TARGET_CLAUSE_MASK,
 					"#pragma omp declare target");
-  else if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+  else if (parser->in_omp_decl_attribute
+	   || c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
       clauses = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_ENTER,
 					      clauses);
--- gcc/c/c-decl.cc.jj	2023-11-03 14:45:30.914543400 +0100
+++ gcc/c/c-decl.cc	2023-11-03 15:52:04.409276131 +0100
@@ -5385,11 +5385,11 @@  c_decl_attributes (tree *node, tree attr
 		    {
 		      tree d = TREE_VALUE (a);
 		      gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
-/*		      if (TREE_PUBLIC (d)
-			  && (VAR_P (*decl)
-			      || TREE_CODE (*decl) == FUNCTION_DECL)
-			  && c_maybe_parse_omp_decl (*decl, d))
-			continue; */
+		      if (TREE_PUBLIC (d)
+			  && (VAR_P (*node)
+			      || TREE_CODE (*node) == FUNCTION_DECL)
+			  && c_maybe_parse_omp_decl (*node, d))
+			continue;
 		      p = TREE_PUBLIC (d) ? "decl" : "directive";
 		    }
 		  if (p && !diagnosed)
--- gcc/testsuite/gcc.dg/gomp/attrs-19.c.jj	2023-11-03 15:55:55.936182600 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-19.c	2023-11-03 16:20:36.602388351 +0100
@@ -0,0 +1,69 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+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 ()
+{
+  [[omp :: decl (declare variant (bar1), match (construct={parallel,for}))]]
+  extern void bar2 ();
+  [[omp::sequence (directive (parallel), directive (for))]]
+  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);
+  [[omp::directive (declare simd,simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
+    omp::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 { 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;
+[[omp::decl (threadprivate)]] struct S t7, t8;
+[[omp::decl (declare target enter device_type (host))]] int d1, d2, d3 (int, int), d4;
+int x3, d5 [[omp::decl (declare target, enter, device_type (any))]], d6 [[omp::decl (declare target link)]], x4;
+int d7 [[omp::decl (declare target)]];
+[[omp::decl (declare target), omp::decl (declare target)]] int d8, d9;
+
+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;
+  [[omp::decl (declare target enter)]] extern int d1, d2, d3 (int, int), d4;
+  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/gcc.dg/gomp/attrs-20.c.jj	2023-11-03 15:57:40.055791401 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-20.c	2023-11-03 16:04:14.339523175 +0100
@@ -0,0 +1,192 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x -ffat-lto-objects -fdump-tree-gimple" } */
+
+extern 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_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f2 (int l)
+{
+  return l + 1;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f2:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f3 (int l)
+{
+  return l + 2;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f4 (int l)
+{
+  return l + 3;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f4:" { 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_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M8u_f5:" { 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 [[omp::decl (declare simd uniform (l) simdlen (8), notinbranch),
+     omp::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_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M4l_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]N4u_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M8u_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]N8l_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f7 (int l)
+{
+  return l + 6;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f7:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f8 (int l)
+{
+  return l + 7;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f8:" { 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_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f9:" 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" } } */
+
+int
+f10 (int x)
+{
+  return x;
+}
+
+[[omp::decl (declare simd, notinbranch)]] int f10 (int);
+
+/* { dg-final { scan-assembler-times "_ZGVbN4v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f11 (int x)
+{
+  return x + 1;
+}
+
+int f11 [[omp::decl (declare simd inbranch linear(x))]] (int x);
+
+/* { dg-final { scan-assembler-times "_ZGVbM4l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
--- gcc/testsuite/gcc.dg/gomp/attrs-21.c.jj	2023-11-03 16:04:38.486200542 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-21.c	2023-11-03 16:29:34.946165467 +0100
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" */
+
+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::directive' 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 "'omp::directive' scoped attribute directive 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" } */