libgomp.texi: Update "Enabling OpenMP"

Message ID 507d6d48-1ca1-411a-a95d-45adb7a8f446@codesourcery.com
State Accepted
Headers
Series libgomp.texi: Update "Enabling OpenMP" |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Tobias Burnus Oct. 14, 2023, 7:43 p.m. UTC
  When browsing libgomp doc, I came across
https://gcc.gnu.org/onlinedocs/libgomp/Enabling-OpenMP.html

First, I found especially the Fortran part difficult to read. Secondly,
it missed the C++ attribute syntax. And I also missed a reference to
-fopenmp-simd.

The attached patch tries to improve this. Note that it talks about C and
C++ attributes, even though C23's [[omp::]] support has not yet landed.
(But is expected very soon.)

I also do not try to list what -fopenmp-simd supports as that's at
https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html#index-fopenmp
and I bet we won't keep both in sync and "man gcc" is more likely to be
up to date.

Comments?

Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
  

Comments

Sandra Loosemore Oct. 14, 2023, 9:46 p.m. UTC | #1
On 10/14/23 13:43, Tobias Burnus wrote:
> When browsing libgomp doc, I came across
> https://gcc.gnu.org/onlinedocs/libgomp/Enabling-OpenMP.html>> 
> First, I found especially the Fortran part difficult to read. Secondly,
> it missed the C++ attribute syntax. And I also missed a reference to
> -fopenmp-simd.
> 
> The attached patch tries to improve this. Note that it talks about C and
> C++ attributes, even though C23's [[omp::]] support has not yet landed.
> (But is expected very soon.)

Is somebody actively working on implementing that, and expecting to get it in 
before Stage 1 closes?  I don't think we should document features that don't 
exist yet.  This syntax for C also isn't even in the draft OpenMP 6.0 document 
so at this point it's just a hypothetical extension.  To me it seems a better 
use of resources to finish implementing things that are actually in earlier 
versions of the OpenMP standard, and to fill in documentation for features that 
are actually implemented.

Other than that...

> diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
> index 526d1be2955..d8126f96fe4 100644
> --- a/libgomp/libgomp.texi
> +++ b/libgomp/libgomp.texi
> @@ -136,15 +136,22 @@ changed to GNU Offloading and Multi Processing Runtime Library.
>  @node Enabling OpenMP
>  @chapter Enabling OpenMP
>  
> -To activate the OpenMP extensions for C/C++ and Fortran, the compile-time 
> -flag @command{-fopenmp} must be specified.  This enables the OpenMP directive
> -@code{#pragma omp} in C/C++ and @code{!$omp} directives in free form, 
> -@code{c$omp}, @code{*$omp} and @code{!$omp} directives in fixed form, 
> -@code{!$} conditional compilation sentinels in free form and @code{c$},
> -@code{*$} and @code{!$} sentinels in fixed form, for Fortran.  The flag also
> -arranges for automatic linking of the OpenMP runtime library 
> +To activate the OpenMP extensions for C/C++ and Fortran, the compile-time
> +flag @command{-fopenmp} must be specified.  For C and C++, this enables

Use @option markup on options, not @command.

> +the handling of the OpenMP directives using @code{#pragma omp} and the
> +@code{[[omp::directive(...)]]}, @code{[[omp::sequence(...)]]} and
> +@code{[[omp::decl(...)]]} attributes.  For Fortran, it enables for
> +free source form the @code{!$omp} sentinel for directives and the
> +@code{!$} conditional compilation sentinel and for fixed source form the
> +@code{c$omp}, @code{*$omp} and @code{!$omp} sentinels for directives and
> +the @code{c$}, @code{*$} and @code{!$} conditional compilation sentinels.
> +The flag also arranges for automatic linking of the OpenMP runtime library
>  (@ref{Runtime Library Routines}).

And I think all those @code markups should be @samp instead, or you could just 
replace this whole blurb with something like "This flag enables recognition of 
the directive syntax documented in the OpenMP specification, and also arranges 
for automatic linking..."

> +The @command{-fopenmp-simd} flag can be used to enable a subset of

This should be @option too.

> +OpenMP directives, which do not require the linking of neither the

s/, which/ that/
s/neither/either/

> +OpenMP runtime library nor the POSIX threads library.
> +
>  A complete description of all OpenMP directives may be found in the
>  @uref{https://www.openmp.org, OpenMP Application Program Interface} manuals.
>  See also @ref{OpenMP Implementation Status}.

-Sandra
  
Jakub Jelinek Oct. 14, 2023, 9:57 p.m. UTC | #2
On Sat, Oct 14, 2023 at 03:46:52PM -0600, Sandra Loosemore wrote:
> On 10/14/23 13:43, Tobias Burnus wrote:
> > When browsing libgomp doc, I came across
> > https://gcc.gnu.org/onlinedocs/libgomp/Enabling-OpenMP.html>> First, I
> > found especially the Fortran part difficult to read. Secondly,
> > it missed the C++ attribute syntax. And I also missed a reference to
> > -fopenmp-simd.
> > 
> > The attached patch tries to improve this. Note that it talks about C and
> > C++ attributes, even though C23's [[omp::]] support has not yet landed.
> > (But is expected very soon.)
> 
> Is somebody actively working on implementing that, and expecting to get it
> in before Stage 1 closes?  I don't think we should document features that

I am (attached is a WIP, which can now compile most of g++.dg/gomp/attrs-1.C
in -std=c2x -fopenmp, except for the scan/section directives).
That said, I agree it might be premature to document it before it is in.

> don't exist yet.  This syntax for C also isn't even in the draft OpenMP 6.0
> document so at this point it's just a hypothetical extension.

It is in OpenMP spec git and it is very unlikely it would be removed.

	Jakub
--- gcc/cp/parser.h.jj	2023-09-20 08:42:51.987008923 +0200
+++ gcc/cp/parser.h	2023-10-12 13:32:42.503496571 +0200
@@ -408,7 +408,8 @@ struct GTY(()) cp_parser {
      identifiers) rather than an explicit template parameter list.  */
   bool fully_implicit_function_template_p;
 
-  /* TRUE if omp::directive or omp::sequence attributes may not appear.  */
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
   bool omp_attrs_forbidden_p;
 
   /* Tracks the function's template parameter list when declaring a function
--- gcc/c/c-decl.cc.jj	2023-10-11 10:59:12.378170030 +0200
+++ gcc/c/c-decl.cc	2023-10-11 17:23:42.902257966 +0200
@@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.
 #include "context.h"  /* For 'g'.  */
 #include "omp-general.h"
 #include "omp-offload.h"  /* For offload_vars.  */
+#include "c-parser.h"
 
 #include "tree-pretty-print.h"
 
@@ -325,15 +326,34 @@ i_label_binding (tree node)
 
 /* The resulting tree type.  */
 
-union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE + 2 * (TREE_CODE (&%h.generic) == C_TOKEN_VEC)"),
        chain_next ("(union lang_tree_node *) c_tree_chain_next (&%h.generic)"))) lang_tree_node
  {
   union tree_node GTY ((tag ("0"),
 			desc ("tree_node_structure (&%h)")))
     generic;
   struct lang_identifier GTY ((tag ("1"))) identifier;
+  struct c_tree_token_vec GTY ((tag ("2"))) c_token_vec;
 };
 
+/* Langhook for tree_size.  */
+size_t
+c_tree_size (enum tree_code code)
+{
+  gcc_checking_assert (code >= NUM_TREE_CODES);
+  switch (code)
+    {
+    case C_TOKEN_VEC: return sizeof (c_tree_token_vec);
+    default:
+      switch (TREE_CODE_CLASS (code))
+	{
+	case tcc_declaration: return sizeof (tree_decl_non_common);
+	case tcc_type: return sizeof (tree_type_non_common);
+	default: gcc_unreachable ();
+	}
+    }
+}
+
 /* Track bindings and other things that matter for goto warnings.  For
    efficiency, we do not gather all the decls at the point of
    definition.  Instead, we point into the bindings structure.  As
--- gcc/c/c-parser.cc.jj	2023-10-11 10:59:12.426169364 +0200
+++ gcc/c/c-parser.cc	2023-10-13 17:47:27.329066662 +0200
@@ -247,12 +247,21 @@ struct GTY(()) c_parser {
      macro.  */
   BOOL_BITFIELD seen_string_literal : 1;
 
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
+  BOOL_BITFIELD omp_attrs_forbidden_p : 1;
+
   /* Location of the last consumed token.  */
   location_t last_token_location;
 
   /* Holds state for parsing collapsed OMP_FOR loops.  Managed by
      c_parser_omp_for_loop.  */
   struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
+
+  /* If we're in the context of OpenMP directives written as C23
+     attributes turned into pragma, vector of tokens created from that,
+     otherwise NULL.  */
+  vec<c_token, va_gc> *in_omp_attribute_pragma;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -1383,6 +1392,17 @@ c_parser_skip_to_pragma_eol (c_parser *p
     }
   while (token_type != CPP_PRAGMA_EOL);
 
+  if (parser->in_omp_attribute_pragma)
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_EOF)
+	{
+	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens_avail = token->flags;
+	  parser->in_omp_attribute_pragma = NULL;
+	}
+    }
+
   parser->error = false;
 }
 
@@ -5430,6 +5450,109 @@ c_parser_balanced_token_sequence (c_pars
     }
 }
 
+static bool c_parser_check_balanced_raw_token_sequence (c_parser *,
+							unsigned int *);
+
+/* Parse arguments of omp::directive or omp::decl attribute.
+
+   directive-name ,[opt] clause-list[opt]
+
+   For directive just remember the tokens in a vector for subsequent
+   parsing.  */
+
+static void
+c_parser_omp_directive_args (c_parser *parser, tree attribute, bool decl_p)
+{
+  unsigned int n = 1;
+  c_token *first = c_parser_peek_token (parser);
+  if (!c_parser_check_balanced_raw_token_sequence (parser, &n)
+      || (c_parser_peek_nth_token_raw (parser, n)->type
+	  != CPP_CLOSE_PAREN))
+    {
+      c_parser_balanced_token_sequence (parser);
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  if (n == 1)
+    {
+      error_at (first->location, "expected OpenMP directive name");
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  vec<c_token, va_gc> *v;
+  vec_alloc (v, n - 1);
+  for (--n; n; --n)
+    {
+      c_token *tok = c_parser_peek_token (parser);
+      v->quick_push (*tok);
+      c_parser_consume_token (parser);
+    }
+  tree arg = make_node (C_TOKEN_VEC);
+  ((struct c_tree_token_vec *) arg)->tokens = v;
+  if (decl_p)
+    TREE_PUBLIC (arg) = 1;
+  TREE_VALUE (attribute) = tree_cons (NULL_TREE, arg, TREE_VALUE (attribute));
+}
+
+/* Parse arguments of omp::sequence attribute.
+
+   omp::[opt] directive-attr [ , omp::[opt] directive-attr ]...  */
+
+static void
+c_parser_omp_sequence_args (c_parser *parser, tree attribute)
+{
+  do
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_NAME
+	  && strcmp (IDENTIFIER_POINTER (token->value), "omp") == 0
+	  && c_parser_peek_2nd_token (parser)->type == CPP_SCOPE)
+	{
+	  c_parser_consume_token (parser);
+	  c_parser_consume_token (parser);
+	  token = c_parser_peek_token (parser);
+	}
+      bool directive = false;
+      const char *p;
+      if (token->type != CPP_NAME)
+	p = "";
+      else
+	p = IDENTIFIER_POINTER (token->value);
+      if (strcmp (p, "directive") == 0)
+	directive = true;
+      else if (strcmp (p, "sequence") != 0)
+	{
+	  error_at (token->location, "expected %<directive%> or %<sequence%>");
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+	    {
+	      matching_parens parens;
+	      parens.consume_open (parser);
+	      c_parser_balanced_token_sequence (parser);
+	      parens.require_close (parser);
+	    }
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	  continue;
+	}
+      c_parser_consume_token (parser);
+      matching_parens parens;
+      if (parens.require_open (parser))
+	{
+	  if (directive)
+	    c_parser_omp_directive_args (parser, attribute, false);
+	  else
+	    c_parser_omp_sequence_args (parser, attribute);
+	  parens.skip_until_found_close (parser);
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	}
+    }
+  while (1);
+}
+
 /* Parse standard (C2X) attributes (including GNU attributes in the
    gnu:: namespace).
 
@@ -5512,7 +5635,19 @@ c_parser_std_attribute (c_parser *parser
   /* Parse the arguments, if any.  */
   const attribute_spec *as = lookup_attribute_spec (TREE_PURPOSE (attribute));
   if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-    goto out;
+    {
+      if ((flag_openmp || flag_openmp_simd)
+	  && ns
+	  && is_attribute_p ("omp", ns)
+	  && (is_attribute_p ("directive", name)
+	      || is_attribute_p ("sequence", name)
+	      || is_attribute_p ("decl", name)))
+	{
+	  error ("%<omp::%E%> attribute requires argument", name);
+	  return error_mark_node;
+	}
+      goto out;
+    }
   {
     location_t open_loc = c_parser_peek_token (parser)->location;
     matching_parens parens;
@@ -5549,7 +5684,37 @@ c_parser_std_attribute (c_parser *parser
 					  require_string, assume_attr, false);
       }
     else
-      c_parser_balanced_token_sequence (parser);
+      {
+	if ((flag_openmp || flag_openmp_simd)
+	    && ns
+	    && is_attribute_p ("omp", ns))
+	  {
+	    if (is_attribute_p ("directive", name))
+	      {
+		c_parser_omp_directive_args (parser, attribute, false);
+		parens.require_close (parser);
+		return attribute;
+	      }
+	    else if (is_attribute_p ("decl", name))
+	      {
+		TREE_VALUE (TREE_PURPOSE (attribute))
+		  = get_identifier ("directive");
+		c_parser_omp_directive_args (parser, attribute, true);
+		parens.require_close (parser);
+		return attribute;
+	      }
+	    else if (is_attribute_p ("sequence", name))
+	      {
+		TREE_VALUE (TREE_PURPOSE (attribute))
+		  = get_identifier ("directive");
+		c_parser_omp_sequence_args (parser, attribute);
+		parens.require_close (parser);
+		TREE_VALUE (attribute) = nreverse (TREE_VALUE (attribute));
+		return attribute;
+	      }
+	  }
+	c_parser_balanced_token_sequence (parser);
+      }
     parens.require_close (parser);
   }
  out:
@@ -6322,6 +6487,243 @@ add_structured_block_stmt (tree sl)
     add_stmt (build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl));
 }
 
+struct c_omp_attribute_data
+{
+  vec<c_token, va_gc> *tokens;
+  const c_omp_directive *dir;
+  c_omp_directive_kind kind;
+};
+
+/* Handle omp::directive and omp::sequence attributes in ATTRS
+   (if any) at the start of a statement or in attribute-declaration.  */
+
+static bool
+c_parser_handle_statement_omp_attributes (c_parser *parser, tree &attrs)
+{
+  if (!flag_openmp && !flag_openmp_simd)
+    return false;
+
+  auto_vec<c_omp_attribute_data, 16> vd;
+  int cnt = 0;
+  int tokens = 0;
+  bool bad = false;
+  for (tree *pa = &attrs; *pa; )
+    if (is_attribute_namespace_p ("omp", *pa)
+	&& is_attribute_p ("directive", get_attribute_name (*pa)))
+      {
+	cnt++;
+	for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a))
+	  {
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    vec<c_token, va_gc> *toks
+	      = ((struct c_tree_token_vec *) d)->tokens;
+	    c_token *first = toks->address ();
+	    c_token *last = first + toks->length ();
+	    if (parser->omp_attrs_forbidden_p)
+	      {
+		error_at (first->location,
+			  "mixing OpenMP directives with attribute and pragma "
+			  "syntax on the same statement");
+		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++)
+	      {
+		tree id = NULL_TREE;
+		if (first + i == last)
+		  break;
+		if (first[i].type == CPP_NAME)
+		  id = first[i].value;
+		else if (first[i].type == CPP_KEYWORD)
+		  id = ridpointers[(int) first[i].keyword];
+		else
+		  break;
+		directive[i] = 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",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
+		continue;
+	      }
+	    c_omp_directive_kind kind = dir->kind;
+	    if (dir->id == PRAGMA_OMP_ORDERED)
+	      {
+		/* ordered is C_OMP_DIR_CONSTRUCT only if it doesn't contain
+		   depend/doacross clause.  */
+		if (directive[1]
+		    && (strcmp (directive[1], "depend") == 0
+			|| strcmp (directive[1], "doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+		else if (first + 2 < last
+			 && first[1].type == CPP_COMMA
+			 && first[2].type == CPP_NAME
+			 && (strcmp (IDENTIFIER_POINTER (first[2].value),
+				     "depend") == 0
+			     || strcmp (IDENTIFIER_POINTER (first[2].value),
+					"doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+	      }
+	    else if (dir->id == PRAGMA_OMP_ERROR)
+	      {
+		/* error with at(execution) clause is C_OMP_DIR_STANDALONE.  */
+		int paren_depth = 0;
+		for (int i = 1; first + i < last; i++)
+		  if (first[i].type == CPP_OPEN_PAREN)
+		    paren_depth++;
+		  else if (first[i].type == CPP_CLOSE_PAREN)
+		    paren_depth--;
+		  else if (paren_depth == 0
+			   && first + i + 2 < last
+			   && first[i].type == CPP_NAME
+			   && first[i + 1].type == CPP_OPEN_PAREN
+			   && first[i + 2].type == CPP_NAME
+			   && !strcmp (IDENTIFIER_POINTER (first[i].value),
+				       "at")
+			   && !strcmp (IDENTIFIER_POINTER (first[i
+								 + 2].value),
+				       "execution"))
+		    {
+		      kind = C_OMP_DIR_STANDALONE;
+		      break;
+		    }
+	      }
+	    c_omp_attribute_data v = { toks, dir, kind };
+	    vd.safe_push (v);
+	    if (flag_openmp || dir->simd)
+	      tokens += (last - first) + 1;
+	  }
+	c_omp_attribute_data v = {};
+	vd.safe_push (v);
+	*pa = TREE_CHAIN (*pa);
+      }
+    else
+      pa = &TREE_CHAIN (*pa);
+
+  if (bad)
+    return false;
+
+  unsigned int i;
+  c_omp_attribute_data *v;
+  c_omp_attribute_data *construct_seen = nullptr;
+  c_omp_attribute_data *standalone_seen = nullptr;
+  c_omp_attribute_data *prev_standalone_seen = nullptr;
+  FOR_EACH_VEC_ELT (vd, i, v)
+    if (v->tokens)
+      {
+	if (v->kind == C_OMP_DIR_CONSTRUCT && !construct_seen)
+	  construct_seen = v;
+	else if (v->kind == C_OMP_DIR_STANDALONE && !standalone_seen)
+	  standalone_seen = v;
+      }
+    else
+      {
+	if (standalone_seen && !prev_standalone_seen)
+	  {
+	    prev_standalone_seen = standalone_seen;
+	    standalone_seen = nullptr;
+	  }
+      }
+
+  if (cnt > 1 && construct_seen)
+    {
+      error_at ((*construct_seen->tokens)[0].location,
+		"OpenMP construct among %<omp::directive%> attributes"
+		" requires all %<omp::directive%> attributes on the"
+		" same statement to be in the same %<omp::sequence%>");
+      return false;
+    }
+  if (cnt > 1 && standalone_seen && prev_standalone_seen)
+    {
+      error_at ((*standalone_seen->tokens)[0].location,
+		"multiple OpenMP standalone directives among"
+		" %<omp::directive%> attributes must be all within the"
+		" same %<omp::sequence%>");
+      return false;
+    }
+
+  if (prev_standalone_seen)
+    standalone_seen = prev_standalone_seen;
+  if (standalone_seen
+      && !c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      error_at (standalone_seen->tokens->address ()->location,
+		"standalone OpenMP directives in %<omp::directive%> attribute"
+		" can only appear on an empty statement");
+      return false;
+    }
+  if (cnt && c_parser_next_token_is (parser, CPP_PRAGMA))
+    {
+      c_token *token = c_parser_peek_token (parser);
+      enum pragma_kind kind = token->pragma_kind;
+      if (kind >= PRAGMA_OMP__START_ && kind <= PRAGMA_OMP__LAST_)
+	{
+	  error_at (token->location,
+		    "mixing OpenMP directives with attribute and pragma "
+		    "syntax on the same statement");
+	  return false;
+	}
+    }
+
+  if (!tokens)
+    return false;
+
+  unsigned int tokens_avail = parser->tokens_avail;
+  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
+
+  tokens++;
+  vec<c_token, va_gc> *toks = NULL;
+  vec_safe_reserve (toks, tokens, true);
+  FOR_EACH_VEC_ELT (vd, i, v)
+    {
+      if (!v->tokens)
+	continue;
+      if (!flag_openmp && !v->dir->simd)
+	continue;
+      c_token *first = v->tokens->address ();
+      c_token *last = first + v->tokens->length ();
+      c_token tok = {};
+      tok.type = CPP_PRAGMA;
+      tok.keyword = RID_MAX;
+      tok.pragma_kind = pragma_kind (v->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->location;
+      toks->quick_push (tok);
+    }
+
+  c_token tok = {};
+  tok.type = CPP_EOF;
+  tok.keyword = RID_MAX;
+  tok.location = toks->last ().location;
+  tok.flags = tokens_avail;
+  toks->quick_push (tok);
+
+  parser->tokens = toks->address ();
+  parser->tokens_avail = tokens;
+  parser->in_omp_attribute_pragma = toks;
+  return true;
+}
+
 /* Parse a compound statement except for the opening brace.  This is
    used for parsing both compound statements and statement expressions
    (which follow different paths to handling the opening).  */
@@ -6495,6 +6897,8 @@ c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_tokens_start_declaration (parser)
 	       || (have_std_attrs
+		   && !c_parser_handle_statement_omp_attributes (parser,
+								 std_attrs)
 		   && c_parser_next_token_is (parser, CPP_SEMICOLON)))
 	{
 	  if (last_label)
@@ -6569,8 +6973,10 @@ c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_token_is (parser, CPP_PRAGMA))
 	{
-	  if (have_std_attrs)
+	  if (have_std_attrs && !parser->in_omp_attribute_pragma)
 	    c_parser_error (parser, "expected declaration or statement");
+	  else if (std_attrs)
+	    c_warn_unused_attributes (std_attrs);
 	  /* External pragmas, and some omp pragmas, are not associated
 	     with regular c code, and so are not to be considered statements
 	     syntactically.  This ensures that the user doesn't put them
@@ -6588,7 +6994,7 @@ c_parser_compound_statement_nostart (c_p
 		check_omp_intervening_code (parser);
 	    }
 	  if (omp_for_parse_state)
-	      omp_for_parse_state->want_nested_loop = want_nested_loop;
+	    omp_for_parse_state->want_nested_loop = want_nested_loop;
 	}
       else if (c_parser_next_token_is (parser, CPP_EOF))
 	{
@@ -6687,8 +7093,10 @@ c_parser_all_labels (c_parser *parser)
 	    c_parser_error (parser, "expected statement");
 	}
     }
-   if (std_attrs)
-     c_warn_unused_attributes (std_attrs);
+  if (std_attrs
+      && (!c_parser_handle_statement_omp_attributes (parser, std_attrs)
+	  || std_attrs))
+    c_warn_unused_attributes (std_attrs);
 }
 
 /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1).
@@ -6935,6 +7343,7 @@ c_parser_statement (c_parser *parser, bo
   c_parser_all_labels (parser);
   if (loc_after_labels)
     *loc_after_labels = c_parser_peek_token (parser)->location;
+  parser->omp_attrs_forbidden_p = false;
   c_parser_statement_after_labels (parser, if_p, NULL);
 }
 
@@ -14206,6 +14615,7 @@ c_parser_omp_variable_list (c_parser *pa
   bool array_section_p;
   auto_vec<c_token> tokens;
   unsigned int tokens_avail = 0;
+  c_token *saved_tokens = NULL;
   bool first = true;
 
   while (1)
@@ -14285,8 +14695,8 @@ c_parser_omp_variable_list (c_parser *pa
 	  tokens.safe_push (eof_token);
 	  tokens.safe_push (eof_token);
 
+	  saved_tokens = parser->tokens;
 	  tokens_avail = parser->tokens_avail;
-	  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
 	  parser->tokens = tokens.address ();
 	  parser->tokens_avail = tokens.length ();
 	}
@@ -14503,7 +14913,7 @@ c_parser_omp_variable_list (c_parser *pa
 
       if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (c_parser_next_token_is_not (parser, CPP_COMMA))
@@ -18631,6 +19041,7 @@ static tree
 c_parser_omp_structured_block (c_parser *parser, bool *if_p)
 {
   tree stmt = push_stmt_list ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return pop_stmt_list (stmt);
 }
@@ -22022,6 +22433,7 @@ c_parser_omp_parallel (location_t loc, c
     }
 
   block = c_begin_omp_parallel ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   stmt = c_finish_omp_parallel (loc, clauses, block);
 
@@ -22117,6 +22529,7 @@ c_parser_omp_task (location_t loc, c_par
 				      "#pragma omp task");
 
   block = c_begin_omp_task ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return c_finish_omp_task (loc, clauses, block);
 }
@@ -23972,6 +24385,7 @@ static void
 c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
 {
   unsigned int tokens_avail = 0, i;
+  c_token *saved_tokens = NULL;
   vec<tree> types = vNULL;
   vec<c_token> clauses = vNULL;
   enum tree_code reduc_code = ERROR_MARK;
@@ -24142,8 +24556,8 @@ c_parser_omp_declare_reduction (c_parser
   int errs = errorcount;
   FOR_EACH_VEC_ELT (types, i, type)
     {
+      saved_tokens = parser->tokens;
       tokens_avail = parser->tokens_avail;
-      gcc_assert (parser->tokens == &parser->tokens_buf[0]);
       if (!clauses.is_empty ())
 	{
 	  parser->tokens = clauses.address ();
@@ -24304,7 +24718,7 @@ c_parser_omp_declare_reduction (c_parser
 
       if (!clauses.is_empty ())
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (bad)
--- gcc/c/c-objc-common.h.jj	2023-10-11 10:59:12.398169753 +0200
+++ gcc/c/c-objc-common.h	2023-10-11 17:23:42.906257914 +0200
@@ -26,6 +26,8 @@ along with GCC; see the file COPYING3.
 
 #undef LANG_HOOKS_IDENTIFIER_SIZE
 #define LANG_HOOKS_IDENTIFIER_SIZE C_SIZEOF_STRUCT_LANG_IDENTIFIER
+#undef LANG_HOOKS_TREE_SIZE
+#define LANG_HOOKS_TREE_SIZE c_tree_size
 #undef LANG_HOOKS_FINISH
 #define LANG_HOOKS_FINISH c_common_finish
 #undef LANG_HOOKS_OPTION_LANG_MASK
--- gcc/c/c-parser.h.jj	2023-10-11 10:59:12.446169086 +0200
+++ gcc/c/c-parser.h	2023-10-11 17:23:42.906257914 +0200
@@ -80,6 +80,12 @@ struct GTY (()) c_token {
   }
 };
 
+/* Used by C_TOKEN_VEC tree.  */
+struct GTY (()) c_tree_token_vec {
+  struct tree_base base;
+  vec<c_token, va_gc> *tokens;
+};
+
 /* The parser.  */
 struct c_parser;
 
--- gcc/c/c-tree.def.jj	2023-10-11 17:23:42.906257914 +0200
+++ gcc/c/c-tree.def	2023-10-11 17:23:42.906257914 +0200
@@ -0,0 +1,31 @@
+/* This file contains the definitions and documentation for the
+   additional tree codes used in the GNU C compiler (see tree.def
+   for the standard codes).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Tree nodes used in the C frontend only, not shared with C++ frontend.  */
+
+/* Used to represent a vector of tokens for deferred parsing.  */
+DEFTREECODE (C_TOKEN_VEC, "c_token_vec", tcc_exceptional, 0)
+
+/*
+Local variables:
+mode:c
+End:
+*/
--- gcc/c/c-lang.cc.jj	2023-10-11 10:59:12.398169753 +0200
+++ gcc/c/c-lang.cc	2023-10-11 17:23:42.906257914 +0200
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "langhooks-def.h"
 #include "c-objc-common.h"
+#include "c-family/c-pragma.h"
+#include "c-parser.h"
 
 enum c_language_kind c_language = clk_c;
 
--- gcc/c/c-tree.h.jj	2023-10-11 10:59:12.446169086 +0200
+++ gcc/c/c-tree.h	2023-10-11 17:23:42.906257914 +0200
@@ -596,6 +596,7 @@ enum c_inline_static_type {
 
 
 /* in c-parser.cc */
+struct c_tree_token_vec;
 extern void c_parse_init (void);
 extern bool c_keyword_starts_typename (enum rid keyword);
 
@@ -719,6 +720,7 @@ extern struct c_declspecs *declspecs_add
 extern struct c_declspecs *declspecs_add_alignas (location_t,
 						  struct c_declspecs *, tree);
 extern struct c_declspecs *finish_declspecs (struct c_declspecs *);
+extern size_t c_tree_size (enum tree_code);
 
 /* in c-objc-common.cc */
 extern bool c_objc_common_init (void);
--- gcc/objc/objc-act.h.jj	2023-10-11 10:59:12.496168392 +0200
+++ gcc/objc/objc-act.h	2023-10-11 17:23:42.906257914 +0200
@@ -738,8 +738,6 @@ struct objc_try_context
 
 extern tree objc_create_temporary_var (tree, const char *);
 
-size_t objc_common_tree_size (enum tree_code code);
-
 
 #define objc_is_object_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_object_id)
 #define objc_is_class_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_class_id)
--- gcc/objc/objc-lang.cc.jj	2023-10-11 10:59:12.496168392 +0200
+++ gcc/objc/objc-lang.cc	2023-10-11 17:23:42.927257640 +0200
@@ -44,8 +44,6 @@ enum c_language_kind c_language = clk_ob
 #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS objc_common_init_ts
-#undef LANG_HOOKS_TREE_SIZE
-#define LANG_HOOKS_TREE_SIZE objc_common_tree_size
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
 
--- gcc/objc/objc-act.cc.jj	2023-10-11 10:59:12.482168587 +0200
+++ gcc/objc/objc-act.cc	2023-10-11 17:23:42.928257627 +0200
@@ -10340,24 +10340,5 @@ objc_common_init_ts (void)
   MARK_TS_TYPED (PROPERTY_REF);
 }
 
-size_t
-objc_common_tree_size (enum tree_code code)
-{
-  switch (code)
-    {
-    case CLASS_METHOD_DECL:
-    case INSTANCE_METHOD_DECL:
-    case KEYWORD_DECL:
-    case PROPERTY_DECL:			return sizeof (tree_decl_non_common);
-    case CLASS_INTERFACE_TYPE:
-    case CLASS_IMPLEMENTATION_TYPE:
-    case CATEGORY_INTERFACE_TYPE:
-    case CATEGORY_IMPLEMENTATION_TYPE:
-    case PROTOCOL_INTERFACE_TYPE:	return sizeof (tree_type_non_common);
-    default:
-      gcc_unreachable ();
-    }
-}
-
 
 #include "gt-objc-objc-act.h"
  
Jakub Jelinek Oct. 20, 2023, 4:28 p.m. UTC | #3
On Sat, Oct 14, 2023 at 11:57:39PM +0200, Jakub Jelinek wrote:
> On Sat, Oct 14, 2023 at 03:46:52PM -0600, Sandra Loosemore wrote:
> > On 10/14/23 13:43, Tobias Burnus wrote:
> > > When browsing libgomp doc, I came across
> > > https://gcc.gnu.org/onlinedocs/libgomp/Enabling-OpenMP.html>> First, I
> > > found especially the Fortran part difficult to read. Secondly,
> > > it missed the C++ attribute syntax. And I also missed a reference to
> > > -fopenmp-simd.
> > > 
> > > The attached patch tries to improve this. Note that it talks about C and
> > > C++ attributes, even though C23's [[omp::]] support has not yet landed.
> > > (But is expected very soon.)
> > 
> > Is somebody actively working on implementing that, and expecting to get it
> > in before Stage 1 closes?  I don't think we should document features that
> 
> I am (attached is a WIP, which can now compile most of g++.dg/gomp/attrs-1.C
> in -std=c2x -fopenmp, except for the scan/section directives).
> That said, I agree it might be premature to document it before it is in.

Here is an updated WIP patch.
It handles everything except diagnostics of mixing attribute and pragma
syntax on begin assumes/end assumes (for now xfailed in attrs-15.c) and
omp::decl support is commented out.

--- gcc/cp/parser.h.jj	2023-10-13 18:34:03.335431229 +0200
+++ gcc/cp/parser.h	2023-10-16 17:48:49.804081420 +0200
@@ -408,7 +408,8 @@ struct GTY(()) cp_parser {
      identifiers) rather than an explicit template parameter list.  */
   bool fully_implicit_function_template_p;
 
-  /* TRUE if omp::directive or omp::sequence attributes may not appear.  */
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
   bool omp_attrs_forbidden_p;
 
   /* Tracks the function's template parameter list when declaring a function
--- gcc/cp/parser.cc.jj	2023-10-16 17:25:32.433781786 +0200
+++ gcc/cp/parser.cc	2023-10-16 17:48:49.783081715 +0200
@@ -43837,7 +43837,8 @@ cp_parser_omp_section_scan (cp_parser *p
       {
 	tree first = cp_lexer_peek_nth_token (parser->lexer, i)->u.value;
 	tree second = cp_lexer_peek_nth_token (parser->lexer, i + 2)->u.value;
-	if (strcmp (IDENTIFIER_POINTER (first), "directive"))
+	if (strcmp (IDENTIFIER_POINTER (first), "directive")
+	    && strcmp (IDENTIFIER_POINTER (first), "__directive__"))
 	  continue;
 	if (strcmp (IDENTIFIER_POINTER (second), directive) == 0)
 	  break;
--- gcc/testsuite/g++.dg/gomp/attrs-2.C.jj	2022-09-27 08:22:37.129634552 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-2.C	2023-10-17 18:02:13.512329537 +0200
@@ -1,4 +1,4 @@
-// { dg-do compile { target c++17 } }
+// { dg-do compile { target c++11 } }
 
 typedef enum omp_allocator_handle_t
 : __UINTPTR_TYPE__
@@ -162,7 +162,11 @@ bar (int d, int m, int i1, int i2, int i
     private (p),firstprivate (f),if (parallel: i2),default(shared),shared(s),copyin(t),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),allocate (f))]]
   {
+#if __cplusplus >= 201703L
     [[using omp:directive (section)]]
+#else
+    [[omp::directive (section)]]
+#endif
     {}
     [[omp::sequence (omp::directive (section))]]
     {}
@@ -176,11 +180,20 @@ bar (int d, int m, int i1, int i2, int i
     {}
   }
   [[omp::directive (barrier)]];
+#if __cplusplus >= 201703L
   [[using omp:sequence (omp::directive (single, private (p),firstprivate (f),allocate (f),nowait))]]
+#else
+  [[omp::sequence (omp::directive (single, private (p),firstprivate (f),allocate (f),nowait))]]
+#endif
     ;
   [[omp::sequence (directive (barrier))]];
+#if __cplusplus >= 201703L
   [[using omp:sequence (directive (parallel, private (p)),
     omp::directive (single, copyprivate (p),firstprivate (f),allocate (f)))]]
+#else
+  [[omp::sequence (directive (parallel, private (p)),
+    omp::directive (single, copyprivate (p),firstprivate (f),allocate (f)))]]
+#endif
     p = 6;
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
@@ -194,7 +207,13 @@ bar (int d, int m, int i1, int i2, int i
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:directive (target parallel for,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
@@ -209,12 +228,24 @@ bar (int d, int m, int i1, int i2, int i
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (target teams,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
-  [[using omp:sequence (directive (target,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
@@ -266,7 +297,13 @@ bar (int d, int m, int i1, int i2, int i
     order(concurrent),allocate (f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (omp::directive (taskgroup, task_reduction(+:r), allocate (r)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (omp::directive (taskgroup, task_reduction(+:r), allocate (r)),
     directive (taskloop simd,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied,if(i1),final(fi),mergeable,nogroup,priority (pp),
     safelen(8),simdlen(4),linear(ll: 1),aligned(q: 32),in_reduction(+:r),nontemporal(ntm),
@@ -321,7 +358,13 @@ bar (int d, int m, int i1, int i2, int i
     lastprivate (l),schedule(static, 4),order(concurrent),allocate (omp_default_mem_alloc: f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (target),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target),
     directive (teams distribute parallel for simd,
     private(p),firstprivate (f),shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),
@@ -389,21 +432,39 @@ bar (int d, int m, int i1, int i2, int i
     private (p),firstprivate (f),if (parallel: i2),default(shared),shared(s),reduction(+:r),
     num_threads (nth),proc_bind(spread),copyin(t),allocate (f))]]
     ;
-  [[using omp:sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
     omp::directive (master taskloop,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied, if(taskloop: i1),final(fi),mergeable, priority (pp),
     reduction(default, +:r),in_reduction(+:r2),allocate (f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
     omp::directive (masked taskloop,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied, if(taskloop: i1),final(fi),mergeable, priority (pp),
     reduction(default, +:r),in_reduction(+:r2),allocate (f),filter(d)))]]
   for (int i = 0; i < 64; i++)
     ll++;
+#if __cplusplus >= 201703L
   [[using omp:directive (master)]];
   [[using omp:directive (masked)]];
   [[using omp:directive (masked,filter(d))]];
+#else
+  [[omp::directive (master)]];
+  [[omp::directive (masked)]];
+  [[omp::directive (masked,filter(d))]];
+#endif
   [[omp::sequence (omp::directive (taskgroup task_reduction (+:r2),allocate (r2)),
     directive (master taskloop simd,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied,if(taskloop: i1),if(simd: i2),final(fi),mergeable,priority (pp),
@@ -544,7 +605,11 @@ bar (int d, int m, int i1, int i2, int i
   }
   [[omp::directive (critical (foobar),hint(omp_sync_hint_none))]]
   ;
+#if __cplusplus >= 201703L
   [[using omp:directive (taskwait, depend (inout: dd[0]))]]
+#else
+  [[omp::directive (taskwait, depend (inout: dd[0]))]]
+#endif
   ;
   [[omp::directive (taskgroup, task_reduction(+:r2),allocate (r2))]]
   ;
@@ -596,7 +661,13 @@ bar (int d, int m, int i1, int i2, int i
   [[omp::directive (scope, private (p), firstprivate (f), reduction(+:r), nowait,
     allocate(omp_default_mem_alloc: r))]]
     ;
-  [[using omp:directive (scope, private (p), firstprivate (f), reduction(task, +:r),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp ::
+#endif
+    directive (scope, private (p), firstprivate (f), reduction(task, +:r),
     allocate (omp_default_mem_alloc: f))]]
     ;
   extern int t2;
@@ -632,8 +703,13 @@ corge ()
   [[omp::directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     omp::directive (declare simd,simdlen(8),notinbranch)]]
   extern int corge3 (int l, int *p);
+#if __cplusplus >= 201703L
   [[using omp:directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     directive (declare simd, simdlen(8),notinbranch)]]
+#else
+  [[omp::directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
+    omp :: directive (declare simd, simdlen(8),notinbranch)]]
+#endif
   extern int corge4 (int l, int *p);
   [[omp::sequence (directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     omp::directive (declare simd, simdlen(8),notinbranch))]]
@@ -658,7 +734,11 @@ garply (int a, int *c, int *d, int *e, i
   for (i = 0; i < 64; i++)
     {
       a += c[i];
+#if __cplusplus >= 201703L
       [[using omp : sequence (sequence (directive (scan inclusive (a))))]]
+#else
+      [[omp:: sequence (sequence (directive (scan inclusive (a))))]]
+#endif
       d[i] = a;
     }
   return a;
--- gcc/testsuite/gcc.dg/gomp/attrs-13.c.jj	2023-10-20 18:04:32.670415014 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-13.c	2023-10-20 18:04:50.630164645 +0200
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+[[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error, at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error, message("my msg"))]];	/* { dg-error "'pragma omp error' encountered: my msg" } */
+[[omp::directive(error severity(warning)message("another message")at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: another message" } */
+
+int
+foo (int i, int x)
+{
+  [[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error, at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error, message("42 / 1"))]];	/* { dg-error "'pragma omp error' encountered: 42 / 1" } */
+  [[omp::directive(error severity(warning) message("bar") at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: bar" } */
+  if (x)
+    [[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+  i++;
+  if (x)
+    ;
+  else
+    [[omp::directive(error at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+  i++;
+  switch (0)
+    [[omp::directive(error, severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+  while (0)
+    [[omp::directive(error, message("42 - 1"))]];	/* { dg-error "'pragma omp error' encountered: 42 - 1" } */
+  i++;
+  lab:
+  [[omp::directive(error, severity(warning) message("bar"), at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: bar" } */
+  i++;
+  return i;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-12.c.jj	2023-10-20 18:03:52.766971289 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-12.c	2023-10-20 18:04:15.899648806 +0200
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp declare target
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int a;
+[[omp::directive (end declare target)]];
+#pragma omp end declare target
+#pragma omp end declare target
+[[omp::directive (declare target)]];
+int b;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int c;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int d;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int e;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp end declare target
+[[omp::directive (declare target)]];
+[[omp::directive (declare target)]];
+int f;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int g;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+[[omp::directive (end declare target)]];
+[[omp::directive (declare target)]];
+#pragma omp declare target
+int h;
+#pragma omp end declare target
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int i;
+[[omp::directive (end declare target)]];
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-6.c.jj	2023-10-19 14:25:11.035327850 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-6.c	2023-10-19 14:40:55.350046227 +0200
@@ -0,0 +1,104 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+void
+foo ()
+{
+  int a[10] = {};
+  #pragma omp parallel sections
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+  [[omp::directive (parallel sections)]]
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+  #pragma omp parallel sections
+  {
+    #pragma omp section
+    a[0]++;
+    a[4]++;
+    l1: a[5]++;
+    if (a[5] == 42) goto l1;
+    [[omp::directive (section)]] {
+    a[1]++;
+    a[6]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    a[7]++;
+    #pragma omp section
+    { a[3]++; }
+    a[8]++;
+  }
+  [[omp::directive (parallel sections)]]
+  {
+    #pragma omp section
+    a[0]++;
+    a[4]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    a[5]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    l2: a[6]++;
+    if (a[6] == 42)
+      goto l2;
+    a[7]++;
+    #pragma omp section
+    a[8]++;
+    { a[3]++; }
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  #pragma omp simd reduction (inscan, +: a)
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a))]]
+      a += c[i];
+    }
+  [[omp::directive (simd reduction (inscan, +: a))]]
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      #pragma omp scan inclusive (a)
+      d[i] = a;
+    }
+  #pragma omp simd reduction (inscan, +: a)
+  for (i = 0; i < 64; i++)
+    {
+      { int t = a;
+	d[i] = t; }
+      [[omp::directive (scan, exclusive (a))]]
+      { int u = c[i];
+	a += u; }
+    }
+  [[omp::directive (simd reduction (inscan, +: a))]]
+  for (i = 0; i < 64; i++)
+    {
+      { int t = c[i];
+	a += t; }
+      #pragma omp scan inclusive (a)
+      { int u = a;
+	d[i] = u; }
+    }
+  return a;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-15.c.jj	2023-10-20 18:20:14.935286457 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-15.c	2023-10-20 18:24:43.788545387 +0200
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp begin assumes absent (target)
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int a;
+[[omp::directive (end assumes)]];
+#pragma omp end assumes
+#pragma omp end assumes
+[[omp::directive (begin assumes absent (target))]];
+int b;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" "" { xfail *-*-* } } */
+#pragma omp begin assumes absent (target)
+int c;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" "" { xfail *-*-* } } */
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int d;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" "" { xfail *-*-* } } */
+#pragma omp begin assumes absent (target)
+int e;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" "" { xfail *-*-* } } */
+#pragma omp end assumes
+[[omp::directive (begin assumes absent (target))]];
+[[omp::directive (begin assumes absent (target))]];
+int f;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" "" { xfail *-*-* } } */
+#pragma omp begin assumes absent (target)
+int g;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" "" { xfail *-*-* } } */
+[[omp::directive (end assumes)]];
+[[omp::directive (begin assumes absent (target))]];
+#pragma omp begin assumes absent (target)
+int h;
+#pragma omp end assumes
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" "" { xfail *-*-* } } */
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int i;
+[[omp::directive (end assumes)]];
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" "" { xfail *-*-* } } */
--- gcc/testsuite/gcc.dg/gomp/attrs-18.c.jj	2023-10-20 18:22:00.619815864 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-18.c	2023-10-20 18:22:19.023559777 +0200
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp begin declare target
+#pragma omp begin declare target device_type (any)
+[[omp::directive (begin declare target, device_type (host))]];
+int a;
+[[omp::directive (end declare target)]];
+#pragma omp end declare target
+#pragma omp end declare target
+[[omp::directive (begin declare target device_type (nohost))]];
+int b;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int c;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp begin declare target device_type (host)
+[[omp::directive (begin declare target)]];
+int d;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int e;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp end declare target
+[[omp::directive (begin declare target device_type (any))]];
+[[omp::directive (begin declare target)]];
+int f;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int g;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+[[omp::directive (end declare target)]];
+[[omp::directive (begin declare target)]];
+#pragma omp begin declare target
+int h;
+#pragma omp end declare target
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+[[omp::directive (begin declare target)]];
+int i;
+[[omp::directive (end declare target)]];
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-9.c.jj	2023-10-19 18:42:51.674746067 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-9.c	2023-10-19 18:42:55.802687938 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-9.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-2.c.jj	2023-10-16 17:48:49.804081420 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-2.c	2023-10-17 18:03:03.197622459 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-2.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-10.c.jj	2023-10-20 15:30:18.480073323 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-10.c	2023-10-20 15:31:17.416238619 +0200
@@ -0,0 +1,192 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x -ffat-lto-objects -fdump-tree-gimple" } */
+
+extern void abort ();
+
+[[omp::directive (declare simd, linear (l))]] extern int f1 (int l);
+extern int f2 (int), f3 [[omp::directive (declare simd, uniform (m))]] (int m), f4 (int), z;
+[[omp::directive (declare simd, linear (l), simdlen(4))]] extern int f5 [[omp::directive (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::directive (declare simd, linear (l), simdlen(4), notinbranch),
+  omp::directive (declare simd, uniform (l), simdlen(4), inbranch)]]
+int
+f6 [[omp::sequence (directive (declare simd uniform (l) simdlen (8), notinbranch),
+		    omp::directive (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::sequence (omp::directive (declare variant (f7), match (construct={parallel})),
+		 directive (declare simd uniform (l), simdlen(4)))]]
+int
+f9 [[omp::directive (declare simd uniform (l) simdlen (8)),
+     omp::directive (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::directive (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::directive (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-14.c.jj	2023-10-20 18:05:05.751953834 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-14.c	2023-10-20 18:05:29.865617682 +0200
@@ -0,0 +1,5 @@
+/* PR c++/102413 */
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+[[omp::directive(error]];	/* { dg-error "expected|declare" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-16.c.jj	2023-10-20 18:21:09.158531947 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-16.c	2023-10-20 18:21:13.707468646 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-16.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-11.c.jj	2023-10-20 17:00:45.363850566 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-11.c	2023-10-20 18:03:12.980525934 +0200
@@ -0,0 +1,88 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x -Wno-attributes" } */
+
+void
+foo ()
+{
+  [[omp::directive (parallel)]] __asm ("");
+  __extension__ [[omp::directive (parallel)]] asm ("");		/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] __label__ foo;			/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] static_assert (true, "");	/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] int a = 5;			/* { dg-error "not allowed to be specified in this context" } */
+  int b = 0;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wattributes"
+  [[omp::directive (parallel)]] l: b++;				/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  switch (0)
+    {
+      [[omp::directive (parallel)]] case 6: break;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+      [[omp::directive (parallel)]] default: break;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+    }
+#pragma GCC diagnostic pop
+}
+
+void
+bar ()
+{
+  [[omp::directive (declare simd)]] int a;		/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+  [[omp::directive (declare simd)]] int b, f1 (int);	/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+  [[omp::directive (declare simd)]] int f2 (int), c;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+  int d [[omp::directive (declare simd)]];		/* { dg-error "not immediately followed by a function declaration or definition" } */
+  int f3 [[omp::directive (declare simd)]] (int), f4 [[omp::directive (declare simd)]] (int);
+  __extension__ [[omp::directive (declare simd)]] int f5 (int);
+  __extension__ int f6 [[omp::directive (declare simd, notinbranch)]] (int);
+  #pragma omp declare simd notinbranch
+  [[omp::directive (declare simd inbranch)]] int f7 (int);	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } */
+  [[omp::directive (declare simd notinbranch)]]
+  #pragma omp declare simd inbranch	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  int f8 (int);
+  static int t1, t2, t3, t4;
+  [[omp::directive (declare simd), omp::directive (foobar)]] int f9 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  [[omp::directive (foobar), omp::directive (declare simd)]] int f10 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  [[omp::directive (threadprivate (t1)), omp::directive (declare simd)]] int f10 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  [[omp::directive (declare simd), omp::directive (threadprivate (t2))]] int f11 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int f12 [[omp::directive (declare simd), omp::directive (foobar)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  int f13 [[omp::directive (foobar), omp::directive (declare simd)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  int f14 [[omp::directive (threadprivate (t3)), omp::directive (declare simd)]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int f15 [[omp::directive (declare simd), omp::directive (threadprivate (t4))]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+}
+
+[[omp::directive (declare simd)]] int a;		/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+[[omp::directive (declare simd)]] int b, f16 (int);	/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+[[omp::directive (declare simd)]] int f17 (int), c;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+int d [[omp::directive (declare simd)]];		/* { dg-error "not immediately followed by a function declaration or definition" } */
+int f18 [[omp::directive (declare simd)]] (int), f19 [[omp::directive (declare simd)]] (int);
+__extension__ [[omp::directive (declare simd)]] int f20 (int);
+__extension__ int f21 [[omp::directive (declare simd, notinbranch)]] (int);
+#pragma omp declare simd notinbranch
+[[omp::directive (declare simd inbranch)]] int f22 (int);	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } */
+[[omp::directive (declare simd notinbranch)]]
+#pragma omp declare simd inbranch	/* { dg-error "before '#pragma'" } */
+int f23 (int);				/* { dg-error "not immediately followed by a function declaration or definition" "" { target *-*-* } .-1 } */
+int t5, t6, t7, t8;
+[[omp::directive (declare simd), omp::directive (foobar)]] int f24 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+[[omp::directive (foobar), omp::directive (declare simd)]] int f25 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+[[omp::directive (threadprivate (t5)), omp::directive (declare simd)]] int f26 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+[[omp::directive (declare simd), omp::directive (threadprivate (t6))]] int f27 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+int f28 [[omp::directive (declare simd), omp::directive (foobar)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+int f29 [[omp::directive (foobar), omp::directive (declare simd)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+int f30 [[omp::directive (threadprivate (t7)), omp::directive (declare simd)]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+int f31 [[omp::directive (declare simd), omp::directive (threadprivate (t8))]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+
+void
+baz ()
+{
+  #pragma omp parallel
+  [[omp::directive (declare simd)]];				/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  [[omp::directive (parallel)]]
+  #pragma omp declare simd	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  extern int f34 (int);
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-1.c.jj	2023-10-16 17:48:49.804081420 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-1.c	2023-10-16 17:48:49.804081420 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-1.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-8.c.jj	2023-10-19 17:24:23.644128194 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-8.c	2023-10-19 17:24:28.359061858 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-8.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-4.c.jj	2023-10-19 12:45:01.061926324 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-4.c	2023-10-19 12:45:50.563263207 +0200
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+void
+foo (int x)
+{
+  [[omp::directive (parallel)]]
+  #pragma omp for						/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  for (int i = 0; i < 16; i++)
+    ;
+  [[omp::directive (barrier)]]					/* { dg-error "standalone OpenMP directives in 'omp::directive' attribute can only appear on an empty statement" } */
+  #pragma omp flush
+  ;
+  #pragma omp parallel
+  [[omp::directive (master)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp teams
+  [[omp::sequence (directive (parallel), directive (master))]]	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp task
+  [[omp::directive (flush)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp master
+  [[omp::directive (flush)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp for ordered
+  for (int i = 0; i < 16; i++)
+    #pragma omp ordered
+    [[omp::directive (flush)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+    ;
+  #pragma omp single
+  [[omp::directive (flush)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp taskgroup
+  [[omp::directive (taskyield)]]				/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp target data map (tofrom: x)
+  [[omp::directive (flush)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp target
+  [[omp::directive (teams)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  [[omp::directive (parallel)]]
+  #pragma omp master						/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  [[omp::sequence (omp::directive (taskloop))]]			/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  for (int i = 0; i < 16; i++)
+    ;
+  #pragma omp parallel
+  [[omp::directive (for)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  for (int i = 0; i < 16; i++)
+    ;
+  #pragma omp for
+  [[omp::directive (master)]]					/* { dg-error "for statement expected before '\\\[' token" } */
+  ;
+  #pragma omp target teams
+  [[omp::directive (parallel)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  ;
+  #pragma omp parallel master
+  [[omp::directive (taskloop)]]					/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  for (int i = 0; i < 16; i++)
+    ;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-7.c.jj	2023-10-19 14:59:28.800372401 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-7.c	2023-10-19 17:14:15.896678429 +0200
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+void
+foo ()
+{
+
+  [[omp::directive (parallel sections)]]
+  {
+    [[omp::directive (parallel)]];
+    [[omp::sequence (directive (section), directive (flush))]];		/* { dg-error "must be the only specified attribute on a statement" } */
+									/* { dg-error "expected '#pragma omp section' or '\\\}'" "" { target *-*-* } .-2 } */
+    [[omp::sequence (directive (flush), omp::directive (section))]];	/* { dg-error "must be the only specified attribute on a statement" } */
+    [[gnu::cold, omp::directive (section)]];				/* { dg-error "must be the only specified attribute on a statement" } */
+    [[omp::directive (section)]] [[gnu::cold]];				/* { dg-error "must be the only specified attribute on a statement" } */
+    [[omp::directive (section foo)]];					/* { dg-error "expected end of line before 'foo'" } */
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::sequence (omp::directive (parallel), omp::directive (scan, exclusive (a)))]]	/* { dg-error "must be the only specified attribute on a statement" } */
+      a += c[i];
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      [[omp::sequence (directive (scan inclusive (a)), directive (critical))]]		/* { dg-error "must be the only specified attribute on a statement" } */
+
+      d[i] = a;
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[gnu::cold]] [[omp::directive (scan, exclusive (a))]]
+											/* { dg-error "#pragma omp scan" "" { target *-*-* } .-1 } */
+      a += c[i];									/* { dg-warning "'cold' attribute ignored" } */
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a)), gnu::cold]]				/* { dg-error "must be the only specified attribute on a statement" } */
+      a += c[i];
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan)]]								/* { dg-error "expected 'inclusive' or 'exclusive' clause before end of line" } */
+      a += c[i];
+    }
+  return a;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-17.c.jj	2023-10-20 18:21:33.078199108 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-17.c	2023-10-20 18:21:36.883146159 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-17.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-3.c.jj	2023-10-17 19:01:09.333156141 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-3.c	2023-10-20 17:12:06.569305359 +0200
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+int i;
+int t1, t2, t3, t4, t5, t6, t7;
+
+void
+foo ()
+{
+  [[omp::directive]];		/* { dg-error "'omp::directive' attribute requires argument" } */
+  [[omp::directive ()]];	/* { dg-error "expected OpenMP directive name" } */
+  [[omp::directive (nonexistent foobar)]];	/* { dg-error "unknown OpenMP directive name in 'omp::directive' attribute argument" } */
+  [[omp::sequence]];		/* { dg-error "'omp::sequence' attribute requires argument" } */
+  [[omp::sequence()]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(omp::foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(directive(taskwait), foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(omp::directive(taskwait), omp::foobar())]];	/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(directive(taskwait) foobar())]];		/* { dg-error "expected '\\\)' before 'foobar'" } */
+  [[omp::sequence(directive)]];		/* { dg-error "expected '\\\(' before '\\\)' token" } */
+  [[omp::sequence(omp::sequence)]];	/* { dg-error "expected '\\\(' before '\\\)' token" } */
+  [[omp::directive (parallel), omp::directive (single)]]	/* { dg-error "OpenMP construct among 'omp::directive' attributes requires all 'omp::directive' attributes on the same statement to be in the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (parallel)]]	/* { dg-error "OpenMP construct among 'omp::directive' attributes requires all 'omp::directive' attributes on the same statement to be in the same 'omp::sequence'" } */
+  [[omp::directive (single)]]
+    ;
+  [[omp::directive (taskwait), omp::directive (taskyield)]]	/* { dg-error "multiple OpenMP standalone directives among 'omp::directive' attributes must be all within the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (taskwait)]]
+  [[omp::directive (taskyield)]]	/* { dg-error "multiple OpenMP standalone directives among 'omp::directive' attributes must be all within the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (flush)]]	/* { dg-error "standalone OpenMP directives in 'omp::directive' attribute can only appear on an empty statement" } */
+    i++;
+  int [[omp::directive (threadprivate (t2))]] b;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  int *[[omp::directive (threadprivate (t3))]] c;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  typedef int T [[omp::directive (threadprivate (t5))]];	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int e[10] [[omp::directive (threadprivate (t6))]];		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  struct [[omp::directive (threadprivate (t7))]] S {};		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-5.c.jj	2023-10-19 14:24:45.199691224 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-5.c	2023-10-19 14:24:49.439631592 +0200
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-5.C"
--- gcc/c/c-decl.cc.jj	2023-10-13 18:34:03.025435402 +0200
+++ gcc/c/c-decl.cc	2023-10-20 17:10:38.567534432 +0200
@@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.
 #include "context.h"  /* For 'g'.  */
 #include "omp-general.h"
 #include "omp-offload.h"  /* For offload_vars.  */
+#include "c-parser.h"
 
 #include "tree-pretty-print.h"
 
@@ -325,15 +326,34 @@ i_label_binding (tree node)
 
 /* The resulting tree type.  */
 
-union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE + 2 * (TREE_CODE (&%h.generic) == C_TOKEN_VEC)"),
        chain_next ("(union lang_tree_node *) c_tree_chain_next (&%h.generic)"))) lang_tree_node
  {
   union tree_node GTY ((tag ("0"),
 			desc ("tree_node_structure (&%h)")))
     generic;
   struct lang_identifier GTY ((tag ("1"))) identifier;
+  struct c_tree_token_vec GTY ((tag ("2"))) c_token_vec;
 };
 
+/* Langhook for tree_size.  */
+size_t
+c_tree_size (enum tree_code code)
+{
+  gcc_checking_assert (code >= NUM_TREE_CODES);
+  switch (code)
+    {
+    case C_TOKEN_VEC: return sizeof (c_tree_token_vec);
+    default:
+      switch (TREE_CODE_CLASS (code))
+	{
+	case tcc_declaration: return sizeof (tree_decl_non_common);
+	case tcc_type: return sizeof (tree_type_non_common);
+	default: gcc_unreachable ();
+	}
+    }
+}
+
 /* Track bindings and other things that matter for goto warnings.  For
    efficiency, we do not gather all the decls at the point of
    definition.  Instead, we point into the bindings structure.  As
@@ -5334,6 +5354,49 @@ c_decl_attributes (tree *node, tree attr
 	}
     }
 
+  if (flag_openmp || flag_openmp_simd)
+    {
+      bool diagnosed = false;
+      for (tree *pa = &attributes; *pa; )
+	{
+	  if (is_attribute_namespace_p ("omp", *pa))
+	    {
+	      tree name = get_attribute_name (*pa);
+	      if (is_attribute_p ("directive", name)
+		  || is_attribute_p ("sequence", name)
+		  || is_attribute_p ("decl", name))
+		{
+		  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))
+		    {
+		      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; */
+		      p = TREE_PUBLIC (d) ? "decl" : "directive";
+		    }
+		  if (p && !diagnosed)
+		    {
+		      error ("%<omp::%s%> not allowed to be specified in "
+			     "this context", p);
+		      diagnosed = true;
+		    }
+		  if (p)
+		    {
+		      *pa = TREE_CHAIN (*pa);
+		      continue;
+		    }
+		}
+	    }
+	  pa = &TREE_CHAIN (*pa);
+	}
+    }
+
   /* Look up the current declaration with all the attributes merged
      so far so that attributes on the current declaration that's
      about to be pushed that conflict with the former can be detected,
--- gcc/c/c-parser.cc.jj	2023-10-13 18:34:03.206432965 +0200
+++ gcc/c/c-parser.cc	2023-10-20 18:19:10.478183371 +0200
@@ -247,12 +247,21 @@ struct GTY(()) c_parser {
      macro.  */
   BOOL_BITFIELD seen_string_literal : 1;
 
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
+  BOOL_BITFIELD omp_attrs_forbidden_p : 1;
+
   /* Location of the last consumed token.  */
   location_t last_token_location;
 
   /* Holds state for parsing collapsed OMP_FOR loops.  Managed by
      c_parser_omp_for_loop.  */
   struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
+
+  /* If we're in the context of OpenMP directives written as C23
+     attributes turned into pragma, vector of tokens created from that,
+     otherwise NULL.  */
+  vec<c_token, va_gc> *in_omp_attribute_pragma;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -1288,7 +1297,19 @@ c_parser_skip_until_found (c_parser *par
   unsigned nesting_depth = 0;
 
   if (c_parser_require (parser, type, msgid, matching_location))
-    return;
+    {
+      if (UNLIKELY (type == CPP_PRAGMA_EOL) && parser->in_omp_attribute_pragma)
+	{
+	  c_token *token = c_parser_peek_token (parser);
+	  if (token->type == CPP_EOF)
+	    {
+	      parser->tokens = &parser->tokens_buf[0];
+	      parser->tokens_avail = token->flags;
+	      parser->in_omp_attribute_pragma = NULL;
+	    }
+	}
+      return;
+    }
 
   /* Skip tokens until the desired token is found.  */
   while (true)
@@ -1299,6 +1320,17 @@ c_parser_skip_until_found (c_parser *par
       if (token->type == type && !nesting_depth)
 	{
 	  c_parser_consume_token (parser);
+	  if (UNLIKELY (type == CPP_PRAGMA_EOL)
+	      && parser->in_omp_attribute_pragma)
+	    {
+	      c_token *token = c_parser_peek_token (parser);
+	      if (token->type == CPP_EOF)
+		{
+		  parser->tokens = &parser->tokens_buf[0];
+		  parser->tokens_avail = token->flags;
+		  parser->in_omp_attribute_pragma = NULL;
+		}
+	    }
 	  break;
 	}
 
@@ -1383,6 +1415,17 @@ c_parser_skip_to_pragma_eol (c_parser *p
     }
   while (token_type != CPP_PRAGMA_EOL);
 
+  if (parser->in_omp_attribute_pragma)
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_EOF)
+	{
+	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens_avail = token->flags;
+	  parser->in_omp_attribute_pragma = NULL;
+	}
+    }
+
   parser->error = false;
 }
 
@@ -1581,6 +1624,8 @@ static void c_parser_declaration_or_fnde
 					   tree attrs = NULL,
 					   struct oacc_routine_data * = NULL,
 					   bool * = NULL);
+static bool c_parser_handle_statement_omp_attributes (c_parser *, tree &,
+						      bool *);
 static void c_parser_static_assert_declaration_no_semi (c_parser *);
 static void c_parser_static_assert_declaration (c_parser *);
 static struct c_typespec c_parser_enum_specifier (c_parser *);
@@ -1999,6 +2044,8 @@ c_parser_external_declaration (c_parser
     }
 }
 
+static void c_parser_handle_directive_omp_attributes (tree &, vec<c_token> *&,
+						      vec<c_token> *);
 static void c_finish_omp_declare_simd (c_parser *, tree, tree, vec<c_token> *);
 static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
 
@@ -2301,6 +2348,22 @@ c_parser_declaration_or_fndef (c_parser
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
     {
       bool handled_assume = false;
+      if (specs->attrs
+	  && !nested
+	  && specs->typespec_kind == ctsk_none
+	  && c_parser_handle_statement_omp_attributes (parser, specs->attrs,
+						       NULL))
+	{
+	  if (specs->attrs)
+	    c_warn_unused_attributes (specs->attrs);
+	  while (parser->in_omp_attribute_pragma)
+            {
+	      gcc_assert (c_parser_next_token_is (parser, CPP_PRAGMA));
+	      c_parser_pragma (parser, pragma_external, NULL);
+	    }
+	  c_parser_consume_token (parser);
+	  return;
+	}
       if (specs->typespec_kind == ctsk_none
 	  && lookup_attribute ("gnu", "assume", specs->attrs))
 	{
@@ -2440,6 +2503,10 @@ c_parser_declaration_or_fndef (c_parser
     warning_at (here, OPT_Wattributes,
 		"%<assume%> attribute not followed by %<;%>");
 
+  auto_vec<c_token> omp_declare_simd_attr_clauses;
+  c_parser_handle_directive_omp_attributes (specs->attrs,
+					    omp_declare_simd_clauses,
+					    &omp_declare_simd_attr_clauses);
   pending_xref_error ();
   prefix_attrs = specs->attrs;
   all_prefix_attrs = prefix_attrs;
@@ -2451,6 +2518,7 @@ c_parser_declaration_or_fndef (c_parser
       timevar_id_t tv;
       tree fnbody = NULL_TREE;
       tree underspec_name = NULL_TREE;
+      auto_vec<c_token> omp_dsimd_idattr_clauses;
       /* Declaring either one or more declarators (in which case we
 	 should diagnose if there were no declaration specifiers) or a
 	 function definition (in which case the diagnostic for
@@ -2468,6 +2536,15 @@ c_parser_declaration_or_fndef (c_parser
 	  c_parser_skip_to_end_of_block_or_statement (parser);
 	  return;
 	}
+      if (flag_openmp || flag_openmp_simd)
+	{
+	  struct c_declarator *d = declarator;
+	  while (d->kind != cdk_id)
+	    d = d->declarator;
+	  vec<c_token> *dummy = NULL;
+	  c_parser_handle_directive_omp_attributes (d->u.id.attrs, dummy,
+						    &omp_dsimd_idattr_clauses);
+	}
       if (gnu_auto_type_p && declarator->kind != cdk_id)
 	{
 	  error_at (here,
@@ -2607,6 +2684,9 @@ c_parser_declaration_or_fndef (c_parser
 		  if (omp_declare_simd_clauses)
 		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
 					       omp_declare_simd_clauses);
+		  if (!omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		}
 	      else
 		{
@@ -2628,6 +2708,10 @@ c_parser_declaration_or_fndef (c_parser
 		  if (!specs->constexpr_p && omp_declare_simd_clauses)
 		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
 					       omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		  start_init (d, asm_name,
 			      TREE_STATIC (d) || specs->constexpr_p,
 			      specs->constexpr_p, &richloc);
@@ -2646,6 +2730,10 @@ c_parser_declaration_or_fndef (c_parser
 		      if (omp_declare_simd_clauses)
 			c_finish_omp_declare_simd (parser, d, NULL_TREE,
 						   omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		    }
 		  finish_init ();
 		}
@@ -2715,7 +2803,8 @@ c_parser_declaration_or_fndef (c_parser
 		      warn_parm_array_mismatch (lastloc, d, parms);
 		    }
 		}
-	      if (omp_declare_simd_clauses)
+	      if (omp_declare_simd_clauses
+		  || !omp_dsimd_idattr_clauses.is_empty ())
 		{
 		  tree parms = NULL_TREE;
 		  if (d && TREE_CODE (d) == FUNCTION_DECL)
@@ -2732,8 +2821,13 @@ c_parser_declaration_or_fndef (c_parser
 		    }
 		  if (parms)
 		    temp_store_parm_decls (d, parms);
-		  c_finish_omp_declare_simd (parser, d, parms,
-					     omp_declare_simd_clauses);
+		  if (omp_declare_simd_clauses)
+		    c_finish_omp_declare_simd (parser, d, parms,
+					       omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, parms,
+					       &omp_dsimd_idattr_clauses);
 		  if (parms)
 		    temp_pop_parm_decls ();
 		}
@@ -2878,6 +2972,9 @@ c_parser_declaration_or_fndef (c_parser
       if (omp_declare_simd_clauses)
 	c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
 				   omp_declare_simd_clauses);
+      if (!omp_dsimd_idattr_clauses.is_empty ())
+	c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
+				   &omp_dsimd_idattr_clauses);
       if (oacc_routine_data)
 	c_finish_oacc_routine (oacc_routine_data, current_function_decl, true);
       location_t startloc = c_parser_peek_token (parser)->location;
@@ -5430,6 +5527,134 @@ c_parser_balanced_token_sequence (c_pars
     }
 }
 
+static bool c_parser_check_balanced_raw_token_sequence (c_parser *,
+							unsigned int *);
+
+/* Parse arguments of omp::directive or omp::decl attribute.
+
+   directive-name ,[opt] clause-list[opt]
+
+   For directive just remember the tokens in a vector for subsequent
+   parsing.  */
+
+static void
+c_parser_omp_directive_args (c_parser *parser, tree attribute, bool decl_p)
+{
+  unsigned int n = 1;
+  c_token *first = c_parser_peek_token (parser);
+  if (!c_parser_check_balanced_raw_token_sequence (parser, &n)
+      || (c_parser_peek_nth_token_raw (parser, n)->type
+	  != CPP_CLOSE_PAREN))
+    {
+      c_parser_balanced_token_sequence (parser);
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  if (n == 1)
+    {
+      error_at (first->location, "expected OpenMP directive name");
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  vec<c_token, va_gc> *v;
+  vec_alloc (v, n - 1);
+  for (--n; n; --n)
+    {
+      c_token *tok = c_parser_peek_token (parser);
+      v->quick_push (*tok);
+      c_parser_consume_token (parser);
+    }
+  tree arg = make_node (C_TOKEN_VEC);
+  ((struct c_tree_token_vec *) arg)->tokens = v;
+  if (decl_p)
+    TREE_PUBLIC (arg) = 1;
+  TREE_VALUE (attribute) = tree_cons (NULL_TREE, arg, TREE_VALUE (attribute));
+}
+
+/* Parse arguments of omp::sequence attribute.
+
+   omp::[opt] directive-attr [ , omp::[opt] directive-attr ]...  */
+
+static void
+c_parser_omp_sequence_args (c_parser *parser, tree attribute)
+{
+  do
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_NAME
+	  && strcmp (IDENTIFIER_POINTER (token->value), "omp") == 0
+	  && c_parser_peek_2nd_token (parser)->type == CPP_SCOPE)
+	{
+	  c_parser_consume_token (parser);
+	  c_parser_consume_token (parser);
+	  token = c_parser_peek_token (parser);
+	}
+      bool directive = false;
+      const char *p;
+      if (token->type != CPP_NAME)
+	p = "";
+      else
+	p = IDENTIFIER_POINTER (token->value);
+      if (strcmp (p, "directive") == 0)
+	directive = true;
+      else if (strcmp (p, "sequence") != 0)
+	{
+	  error_at (token->location, "expected %<directive%> or %<sequence%>");
+	  unsigned nesting_depth = 0;
+   
+	  while (true)
+	    {         
+	      /* Peek at the next token.  */
+	      token = c_parser_peek_token (parser);
+	      /* If we've reached the token we want, consume it and stop.  */
+	      if ((token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+		  && !nesting_depth)
+		break;
+	      /* If we've run out of tokens, stop.  */
+	      if (token->type == CPP_EOF)
+		break;
+	      if (token->type == CPP_PRAGMA_EOL && parser->in_pragma)
+		break;
+	      if (token->type == CPP_OPEN_BRACE
+		  || token->type == CPP_OPEN_PAREN
+		  || token->type == CPP_OPEN_SQUARE)
+		++nesting_depth;
+	      else if (token->type == CPP_CLOSE_BRACE
+		       || token->type == CPP_CLOSE_PAREN
+		       || token->type == CPP_CLOSE_SQUARE)
+		{
+		  if (nesting_depth-- == 0)
+		    break;
+		}
+	      /* Consume this token.  */
+	      c_parser_consume_token (parser);
+	    }
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	  continue;
+	}
+      c_parser_consume_token (parser);
+      matching_parens parens;
+      if (parens.require_open (parser))
+	{
+	  if (directive)
+	    c_parser_omp_directive_args (parser, attribute, false);
+	  else
+	    c_parser_omp_sequence_args (parser, attribute);
+	  parens.skip_until_found_close (parser);
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	}
+      else if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	break;
+      else
+	c_parser_consume_token (parser);
+    }
+  while (1);
+}
+
 /* Parse standard (C2X) attributes (including GNU attributes in the
    gnu:: namespace).
 
@@ -5512,7 +5737,19 @@ c_parser_std_attribute (c_parser *parser
   /* Parse the arguments, if any.  */
   const attribute_spec *as = lookup_attribute_spec (TREE_PURPOSE (attribute));
   if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-    goto out;
+    {
+      if ((flag_openmp || flag_openmp_simd)
+	  && ns
+	  && is_attribute_p ("omp", ns)
+	  && (is_attribute_p ("directive", name)
+	      || is_attribute_p ("sequence", name)
+	      || is_attribute_p ("decl", name)))
+	{
+	  error ("%<omp::%E%> attribute requires argument", name);
+	  return error_mark_node;
+	}
+      goto out;
+    }
   {
     location_t open_loc = c_parser_peek_token (parser)->location;
     matching_parens parens;
@@ -5549,7 +5786,37 @@ c_parser_std_attribute (c_parser *parser
 					  require_string, assume_attr, false);
       }
     else
-      c_parser_balanced_token_sequence (parser);
+      {
+	if ((flag_openmp || flag_openmp_simd)
+	    && ns
+	    && is_attribute_p ("omp", ns))
+	  {
+	    if (is_attribute_p ("directive", name))
+	      {
+		c_parser_omp_directive_args (parser, attribute, false);
+		parens.skip_until_found_close (parser);
+		return attribute;
+	      }
+/*	    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))
+		  = get_identifier ("directive");
+		c_parser_omp_sequence_args (parser, attribute);
+		parens.skip_until_found_close (parser);
+		TREE_VALUE (attribute) = nreverse (TREE_VALUE (attribute));
+		return attribute;
+	      }
+	  }
+	c_parser_balanced_token_sequence (parser);
+      }
     parens.require_close (parser);
   }
  out:
@@ -6322,6 +6589,352 @@ add_structured_block_stmt (tree sl)
     add_stmt (build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl));
 }
 
+struct c_omp_attribute_data
+{
+  vec<c_token, va_gc> *tokens;
+  const c_omp_directive *dir;
+  c_omp_directive_kind kind;
+};
+
+/* Handle omp::directive and omp::sequence attributes in ATTRS
+   (if any) at the start of a statement or in attribute-declaration.  */
+
+static bool
+c_parser_handle_statement_omp_attributes (c_parser *parser, tree &attrs,
+					  bool *have_std_attrs)
+{
+  if (!flag_openmp && !flag_openmp_simd)
+    return false;
+
+  auto_vec<c_omp_attribute_data, 16> vd;
+  int cnt = 0;
+  int tokens = 0;
+  bool bad = false;
+  for (tree *pa = &attrs; *pa; )
+    if (is_attribute_namespace_p ("omp", *pa)
+	&& is_attribute_p ("directive", get_attribute_name (*pa)))
+      {
+	cnt++;
+	for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a))
+	  {
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    vec<c_token, va_gc> *toks
+	      = ((struct c_tree_token_vec *) d)->tokens;
+	    c_token *first = toks->address ();
+	    c_token *last = first + toks->length ();
+	    if (parser->omp_attrs_forbidden_p)
+	      {
+		error_at (first->location,
+			  "mixing OpenMP directives with attribute and pragma "
+			  "syntax on the same statement");
+		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++)
+	      {
+		tree id = NULL_TREE;
+		if (first + i == last)
+		  break;
+		if (first[i].type == CPP_NAME)
+		  id = first[i].value;
+		else if (first[i].type == CPP_KEYWORD)
+		  id = ridpointers[(int) first[i].keyword];
+		else
+		  break;
+		directive[i] = 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",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
+		continue;
+	      }
+	    c_omp_directive_kind kind = dir->kind;
+	    if (dir->id == PRAGMA_OMP_ORDERED)
+	      {
+		/* ordered is C_OMP_DIR_CONSTRUCT only if it doesn't contain
+		   depend/doacross clause.  */
+		if (directive[1]
+		    && (strcmp (directive[1], "depend") == 0
+			|| strcmp (directive[1], "doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+		else if (first + 2 < last
+			 && first[1].type == CPP_COMMA
+			 && first[2].type == CPP_NAME
+			 && (strcmp (IDENTIFIER_POINTER (first[2].value),
+				     "depend") == 0
+			     || strcmp (IDENTIFIER_POINTER (first[2].value),
+					"doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+	      }
+	    else if (dir->id == PRAGMA_OMP_ERROR)
+	      {
+		/* error with at(execution) clause is C_OMP_DIR_STANDALONE.  */
+		int paren_depth = 0;
+		for (int i = 1; first + i < last; i++)
+		  if (first[i].type == CPP_OPEN_PAREN)
+		    paren_depth++;
+		  else if (first[i].type == CPP_CLOSE_PAREN)
+		    paren_depth--;
+		  else if (paren_depth == 0
+			   && first + i + 2 < last
+			   && first[i].type == CPP_NAME
+			   && first[i + 1].type == CPP_OPEN_PAREN
+			   && first[i + 2].type == CPP_NAME
+			   && !strcmp (IDENTIFIER_POINTER (first[i].value),
+				       "at")
+			   && !strcmp (IDENTIFIER_POINTER (first[i
+								 + 2].value),
+				       "execution"))
+		    {
+		      kind = C_OMP_DIR_STANDALONE;
+		      break;
+		    }
+	      }
+	    c_omp_attribute_data v = { toks, dir, kind };
+	    vd.safe_push (v);
+	    if (flag_openmp || dir->simd)
+	      tokens += (last - first) + 1;
+	  }
+	c_omp_attribute_data v = {};
+	vd.safe_push (v);
+	*pa = TREE_CHAIN (*pa);
+      }
+    else
+      pa = &TREE_CHAIN (*pa);
+
+  if (bad)
+    {
+    fail:
+      if (have_std_attrs && attrs == NULL)
+	*have_std_attrs = false;
+      return false;
+    }
+
+  unsigned int i;
+  c_omp_attribute_data *v;
+  c_omp_attribute_data *construct_seen = nullptr;
+  c_omp_attribute_data *standalone_seen = nullptr;
+  c_omp_attribute_data *prev_standalone_seen = nullptr;
+  FOR_EACH_VEC_ELT (vd, i, v)
+    if (v->tokens)
+      {
+	if (v->kind == C_OMP_DIR_CONSTRUCT && !construct_seen)
+	  construct_seen = v;
+	else if (v->kind == C_OMP_DIR_STANDALONE && !standalone_seen)
+	  standalone_seen = v;
+      }
+    else
+      {
+	if (standalone_seen && !prev_standalone_seen)
+	  {
+	    prev_standalone_seen = standalone_seen;
+	    standalone_seen = nullptr;
+	  }
+      }
+
+  if (cnt > 1 && construct_seen)
+    {
+      error_at ((*construct_seen->tokens)[0].location,
+		"OpenMP construct among %<omp::directive%> attributes"
+		" requires all %<omp::directive%> attributes on the"
+		" same statement to be in the same %<omp::sequence%>");
+      goto fail;
+    }
+  if (cnt > 1 && standalone_seen && prev_standalone_seen)
+    {
+      error_at ((*standalone_seen->tokens)[0].location,
+		"multiple OpenMP standalone directives among"
+		" %<omp::directive%> attributes must be all within the"
+		" same %<omp::sequence%>");
+      goto fail;
+    }
+
+  if (prev_standalone_seen)
+    standalone_seen = prev_standalone_seen;
+  if (standalone_seen
+      && !c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      error_at (standalone_seen->tokens->address ()->location,
+		"standalone OpenMP directives in %<omp::directive%> attribute"
+		" can only appear on an empty statement");
+      goto fail;
+    }
+  if (cnt && c_parser_next_token_is (parser, CPP_PRAGMA))
+    {
+      c_token *token = c_parser_peek_token (parser);
+      enum pragma_kind kind = token->pragma_kind;
+      if (kind >= PRAGMA_OMP__START_ && kind <= PRAGMA_OMP__LAST_)
+	{
+	  error_at (token->location,
+		    "mixing OpenMP directives with attribute and pragma "
+		    "syntax on the same statement");
+	  goto fail;
+	}
+    }
+
+  if (!tokens)
+    return false;
+
+  unsigned int tokens_avail = parser->tokens_avail;
+  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
+
+  tokens++;
+  vec<c_token, va_gc> *toks = NULL;
+  vec_safe_reserve (toks, tokens, true);
+  FOR_EACH_VEC_ELT (vd, i, v)
+    {
+      if (!v->tokens)
+	continue;
+      if (!flag_openmp && !v->dir->simd)
+	continue;
+      c_token *first = v->tokens->address ();
+      c_token *last = first + v->tokens->length ();
+      c_token tok = {};
+      tok.type = CPP_PRAGMA;
+      tok.keyword = RID_MAX;
+      tok.pragma_kind = pragma_kind (v->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);
+    }
+
+  c_token tok = {};
+  tok.type = CPP_EOF;
+  tok.keyword = RID_MAX;
+  tok.location = toks->last ().location;
+  tok.flags = tokens_avail;
+  toks->quick_push (tok);
+
+  parser->tokens = toks->address ();
+  parser->tokens_avail = tokens;
+  parser->in_omp_attribute_pragma = toks;
+  return true;
+}
+
+/* Handle omp::directive and omp::sequence attributes in ATTRS
+   (if any) at the start or after declaration-id of a declaration.  */
+
+static void
+c_parser_handle_directive_omp_attributes (tree &attrs,
+					  vec<c_token> *&pragma_clauses,
+					  vec<c_token> *attr_clauses)
+{
+  if (!flag_openmp && !flag_openmp_simd)
+    return;
+
+  for (tree *pa = &attrs; *pa; )
+    if (is_attribute_namespace_p ("omp", *pa)
+	&& is_attribute_p ("directive", get_attribute_name (*pa)))
+      {
+	int cnt = 0;
+	for (tree *pa2 = &TREE_VALUE (*pa); *pa2; )
+	  {
+	    tree a = *pa2;
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    vec<c_token, va_gc> *toks
+	      = ((struct c_tree_token_vec *) d)->tokens;
+	    c_token *first = toks->address ();
+	    c_token *last = first + toks->length ();
+	    const char *directive[3] = {};
+	    for (int i = 0; i < 3; i++)
+	      {
+		tree id = NULL_TREE;
+		if (first + i == last)
+		  break;
+		if (first[i].type == CPP_NAME)
+		  id = first[i].value;
+		else if (first[i].type == CPP_KEYWORD)
+		  id = ridpointers[(int) first[i].keyword];
+		else
+		  break;
+		directive[i] = 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",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
+		*pa2 = TREE_CHAIN (a);
+	      }
+            else if (dir->id == PRAGMA_OMP_DECLARE
+		     && (strcmp (directive[1], "simd") == 0
+			 || strcmp (directive[1], "variant") == 0))
+	      {
+		if (pragma_clauses)
+		  {
+		    error_at (first->location,
+			      "mixing OpenMP directives with attribute and "
+			      "pragma syntax on the same declaration");
+		    for (pa = &attrs; *pa; )
+		      if (is_attribute_namespace_p ("omp", *pa)
+			  && is_attribute_p ("directive",
+					     get_attribute_name (*pa)))
+			*pa = TREE_CHAIN (*pa);
+		      else
+			pa = &TREE_CHAIN (*pa);
+		    return;
+		  }
+		++cnt;
+		attr_clauses->reserve (attr_clauses->length ()
+				       + toks->length () + 2);
+		for (++first; first < last; ++first)
+		  attr_clauses->quick_push (*first);
+		c_token tok = {};
+		tok.type = CPP_PRAGMA_EOL;
+		tok.keyword = RID_MAX;
+		tok.location = last[-1].location;
+		attr_clauses->quick_push (tok);
+		*pa2 = TREE_CHAIN (a);
+	      }
+	    else
+	      pa2 = &TREE_CHAIN (a);
+	  }
+	if (cnt && TREE_VALUE (*pa) == NULL_TREE)
+	  *pa = TREE_CHAIN (*pa);
+	else
+	  pa = &TREE_CHAIN (*pa);
+      }
+    else
+      pa = &TREE_CHAIN (*pa);
+  if (attr_clauses->length ())
+    {
+      c_token tok = {};
+      tok.type = CPP_EOF;
+      tok.keyword = RID_MAX;
+      tok.location = attr_clauses->last ().location;
+      attr_clauses->quick_push (tok);
+      attr_clauses->quick_push (tok);
+      pragma_clauses = attr_clauses;
+    }
+}
+
 /* Parse a compound statement except for the opening brace.  This is
    used for parsing both compound statements and statement expressions
    (which follow different paths to handling the opening).  */
@@ -6495,7 +7108,10 @@ c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_tokens_start_declaration (parser)
 	       || (have_std_attrs
-		   && c_parser_next_token_is (parser, CPP_SEMICOLON)))
+		   && !c_parser_handle_statement_omp_attributes
+				(parser, std_attrs, &have_std_attrs)
+		   && c_parser_next_token_is (parser, CPP_SEMICOLON)
+		   && (have_std_attrs = true)))
 	{
 	  if (last_label)
 	    pedwarn_c11 (c_parser_peek_token (parser)->location, OPT_Wpedantic,
@@ -6569,8 +7185,10 @@ c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_token_is (parser, CPP_PRAGMA))
 	{
-	  if (have_std_attrs)
+	  if (have_std_attrs && !parser->in_omp_attribute_pragma)
 	    c_parser_error (parser, "expected declaration or statement");
+	  else if (std_attrs)
+	    c_warn_unused_attributes (std_attrs);
 	  /* External pragmas, and some omp pragmas, are not associated
 	     with regular c code, and so are not to be considered statements
 	     syntactically.  This ensures that the user doesn't put them
@@ -6588,7 +7206,7 @@ c_parser_compound_statement_nostart (c_p
 		check_omp_intervening_code (parser);
 	    }
 	  if (omp_for_parse_state)
-	      omp_for_parse_state->want_nested_loop = want_nested_loop;
+	    omp_for_parse_state->want_nested_loop = want_nested_loop;
 	}
       else if (c_parser_next_token_is (parser, CPP_EOF))
 	{
@@ -6666,13 +7284,10 @@ c_parser_compound_statement_nostart (c_p
 static void
 c_parser_all_labels (c_parser *parser)
 {
+  bool have_std_attrs;
   tree std_attrs = NULL;
-  if (c_parser_nth_token_starts_std_attributes (parser, 1))
-    {
-      std_attrs = c_parser_std_attribute_specifier_sequence (parser);
-      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
-	c_parser_error (parser, "expected statement");
-    }
+  if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1)))
+    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
   while (c_parser_next_token_is_keyword (parser, RID_CASE)
 	 || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
 	 || (c_parser_next_token_is (parser, CPP_NAME)
@@ -6680,15 +7295,20 @@ c_parser_all_labels (c_parser *parser)
     {
       c_parser_label (parser, std_attrs);
       std_attrs = NULL;
-      if (c_parser_nth_token_starts_std_attributes (parser, 1))
-	{
-	  std_attrs = c_parser_std_attribute_specifier_sequence (parser);
-	  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
-	    c_parser_error (parser, "expected statement");
-	}
+      if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser,
+								      1)))
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+    }
+  if (std_attrs
+      && (!c_parser_handle_statement_omp_attributes (parser, std_attrs, &have_std_attrs)
+	  || std_attrs))
+    {
+      if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON))
+	c_parser_error (parser, "expected statement");
+      c_warn_unused_attributes (std_attrs);
     }
-   if (std_attrs)
-     c_warn_unused_attributes (std_attrs);
+  else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON))
+    c_parser_error (parser, "expected statement");
 }
 
 /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1).
@@ -6935,6 +7555,7 @@ c_parser_statement (c_parser *parser, bo
   c_parser_all_labels (parser);
   if (loc_after_labels)
     *loc_after_labels = c_parser_peek_token (parser)->location;
+  parser->omp_attrs_forbidden_p = false;
   c_parser_statement_after_labels (parser, if_p, NULL);
 }
 
@@ -14206,6 +14827,7 @@ c_parser_omp_variable_list (c_parser *pa
   bool array_section_p;
   auto_vec<c_token> tokens;
   unsigned int tokens_avail = 0;
+  c_token *saved_tokens = NULL;
   bool first = true;
 
   while (1)
@@ -14285,8 +14907,8 @@ c_parser_omp_variable_list (c_parser *pa
 	  tokens.safe_push (eof_token);
 	  tokens.safe_push (eof_token);
 
+	  saved_tokens = parser->tokens;
 	  tokens_avail = parser->tokens_avail;
-	  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
 	  parser->tokens = tokens.address ();
 	  parser->tokens_avail = tokens.length ();
 	}
@@ -14503,7 +15125,7 @@ c_parser_omp_variable_list (c_parser *pa
 
       if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (c_parser_next_token_is_not (parser, CPP_COMMA))
@@ -18631,6 +19253,7 @@ static tree
 c_parser_omp_structured_block (c_parser *parser, bool *if_p)
 {
   tree stmt = push_stmt_list ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return pop_stmt_list (stmt);
 }
@@ -20736,6 +21359,80 @@ c_parser_omp_flush (c_parser *parser)
   c_finish_omp_flush (loc, mo);
 }
 
+/* Return true if next tokens contain a standard attribute that contains
+   omp::directive (DIRECTIVE).  */
+
+static bool
+c_parser_omp_section_scan (c_parser *parser, const char *directive,
+			   bool tentative)
+{
+  if (!c_parser_nth_token_starts_std_attributes (parser, 1))
+    return false;
+  unsigned int n = 3;
+  if (!c_parser_check_balanced_raw_token_sequence (parser, &n))
+    return false;
+  c_token *token = c_parser_peek_nth_token_raw (parser, n);
+  if (token->type != CPP_CLOSE_SQUARE)
+    return false;
+  token = c_parser_peek_nth_token_raw (parser, n + 1);
+  if (token->type != CPP_CLOSE_SQUARE)
+    return false;
+  if (n < 9)
+    return false;
+  if (c_parser_peek_nth_token_raw (parser, 3)->type == CPP_NAME
+      && c_parser_peek_nth_token_raw (parser, 4)->type == CPP_OPEN_PAREN
+      && c_parser_peek_nth_token_raw (parser, 5)->type == CPP_NAME)
+    {
+      tree first = c_parser_peek_nth_token_raw (parser, 3)->value;
+      tree second = c_parser_peek_nth_token_raw (parser, 5)->value;
+      if (strcmp (IDENTIFIER_POINTER (first), "directive")
+	  && strcmp (IDENTIFIER_POINTER (first), "__directive__"))
+	return false;
+      if (strcmp (IDENTIFIER_POINTER (second), directive))
+	return false;
+    }
+  if (tentative)
+    return true;
+  location_t first_loc = c_parser_peek_token (parser)->location;
+  location_t last_loc = c_parser_peek_nth_token_raw (parser, n + 1)->location;
+  location_t middle_loc = UNKNOWN_LOCATION;
+  tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+  bool seen = false;
+  int cnt = 0;
+  for (tree attr = std_attrs; attr; attr = TREE_CHAIN (attr))
+    if (is_attribute_namespace_p ("omp", attr)
+	&& is_attribute_p ("directive", get_attribute_name (attr)))
+      {
+	for (tree a = TREE_VALUE (attr); a; a = TREE_CHAIN (a))
+	  {
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    c_token *first
+	      = ((struct c_tree_token_vec *) d)->tokens->address ();
+	    cnt++;
+	    if (first->type == CPP_NAME
+		&& strcmp (IDENTIFIER_POINTER (first->value),
+			   directive) == 0)
+	      {
+		seen = true;
+		if (middle_loc == UNKNOWN_LOCATION)
+		  middle_loc = first->location;
+	      }
+	  }
+      }
+  if (!seen)
+    return false;
+  if (cnt != 1 || TREE_CHAIN (std_attrs))
+    {
+      error_at (make_location (first_loc, last_loc, middle_loc),
+		"%<[[omp::directive(%s)]]%> must be the only specified "
+		"attribute on a statement", directive);
+      return false;
+    }
+  c_parser_handle_statement_omp_attributes (parser, std_attrs, NULL);
+  return true;
+}
+
 /* Parse an OpenMP structured block sequence.  KIND is the corresponding
    separating directive.  */
 
@@ -20755,6 +21452,13 @@ c_parser_omp_structured_block_sequence (
       if (kind != PRAGMA_NONE
 	  && c_parser_peek_token (parser)->pragma_kind == kind)
 	break;
+
+      if (kind != PRAGMA_NONE
+	  && c_parser_omp_section_scan (parser,
+					kind == PRAGMA_OMP_SCAN
+					? "scan" : "section", false))
+	break;
+
       c_parser_statement (parser, NULL);
     }
   while (1);
@@ -21744,7 +22448,8 @@ c_parser_omp_sections_scope (location_t
 
   stmt = push_stmt_list ();
 
-  if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION)
+  if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION
+      && !c_parser_omp_section_scan (parser, "section", true))
     {
       substmt = c_parser_omp_structured_block_sequence (parser,
 							PRAGMA_OMP_SECTION);
@@ -21761,6 +22466,7 @@ c_parser_omp_sections_scope (location_t
 	break;
 
       loc = c_parser_peek_token (parser)->location;
+      c_parser_omp_section_scan (parser, "section", false);
       if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION)
 	{
 	  c_parser_consume_pragma (parser);
@@ -22022,6 +22728,7 @@ c_parser_omp_parallel (location_t loc, c
     }
 
   block = c_begin_omp_parallel ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   stmt = c_finish_omp_parallel (loc, clauses, block);
 
@@ -22117,6 +22824,7 @@ c_parser_omp_task (location_t loc, c_par
 				      "#pragma omp task");
 
   block = c_begin_omp_task ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return c_finish_omp_task (loc, clauses, block);
 }
@@ -23155,6 +23863,13 @@ c_parser_omp_declare_simd (c_parser *par
 	     IDENTIFIER_POINTER (kind));
       break;
     case pragma_compound:
+      bool have_std_attrs;
+      tree std_attrs;
+      have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1);
+      if (have_std_attrs)
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+      else
+	std_attrs = NULL_TREE;
       if (c_parser_next_token_is (parser, CPP_KEYWORD)
 	  && c_parser_peek_token (parser)->keyword == RID_EXTENSION)
 	{
@@ -23163,10 +23878,12 @@ c_parser_omp_declare_simd (c_parser *par
 	    c_parser_consume_token (parser);
 	  while (c_parser_next_token_is (parser, CPP_KEYWORD)
 		 && c_parser_peek_token (parser)->keyword == RID_EXTENSION);
-	  if (c_parser_next_tokens_start_declaration (parser))
+	  if (c_parser_next_tokens_start_declaration (parser)
+	      || c_parser_nth_token_starts_std_attributes (parser, 1))
 	    {
 	      c_parser_declaration_or_fndef (parser, true, true, true, true,
-					     true, NULL, &clauses);
+					     true, NULL, &clauses,
+					     have_std_attrs, std_attrs);
 	      restore_extension_diagnostics (ext);
 	      break;
 	    }
@@ -23175,7 +23892,8 @@ c_parser_omp_declare_simd (c_parser *par
       else if (c_parser_next_tokens_start_declaration (parser))
 	{
 	  c_parser_declaration_or_fndef (parser, true, true, true, true, true,
-					 NULL, &clauses);
+					 NULL, &clauses, have_std_attrs,
+					 std_attrs);
 	  break;
 	}
       error ("%<#pragma omp declare %s%> must be followed by "
@@ -23703,8 +24421,8 @@ c_finish_omp_declare_simd (c_parser *par
   while (parser->tokens_avail > 3)
     {
       c_token *token = c_parser_peek_token (parser);
-      gcc_assert (token->type == CPP_NAME
-		  && strcmp (IDENTIFIER_POINTER (token->value), kind) == 0);
+      gcc_assert (token->type == CPP_NAME);
+      kind = IDENTIFIER_POINTER (token->value);
       c_parser_consume_token (parser);
       parser->in_pragma = true;
 
@@ -23770,8 +24488,9 @@ c_parser_omp_declare_target (c_parser *p
     }
   else
     {
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_skip_to_pragma_eol (parser);
-      c_omp_declare_target_attr attr = { -1 };
+      c_omp_declare_target_attr attr = { attr_syntax, -1 };
       vec_safe_push (current_omp_declare_target_attribute, attr);
       return;
     }
@@ -23878,6 +24597,7 @@ c_parser_omp_begin (c_parser *parser)
       if (strcmp (p, "target") == 0)
 	{
 	  c_parser_consume_token (parser);
+	  bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
 	  tree clauses
 	    = c_parser_omp_all_clauses (parser,
 					OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK,
@@ -23886,7 +24606,7 @@ c_parser_omp_begin (c_parser *parser)
 	  for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE_TYPE)
 	      device_type |= OMP_CLAUSE_DEVICE_TYPE_KIND (c);
-	  c_omp_declare_target_attr attr = { device_type };
+	  c_omp_declare_target_attr attr = { attr_syntax, device_type };
 	  vec_safe_push (current_omp_declare_target_attribute, attr);
 	}
       else
@@ -23935,13 +24655,34 @@ c_parser_omp_end (c_parser *parser)
 	  c_parser_skip_to_pragma_eol (parser);
 	  return;
 	}
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_skip_to_pragma_eol (parser);
       if (!vec_safe_length (current_omp_declare_target_attribute))
 	error_at (loc, "%<#pragma omp end declare target%> without "
 		       "corresponding %<#pragma omp declare target%> or "
 		       "%<#pragma omp begin declare target%>");
       else
-	current_omp_declare_target_attribute->pop ();
+	{
+	  c_omp_declare_target_attr
+	    a = current_omp_declare_target_attribute->pop ();
+	  if (a.attr_syntax != attr_syntax)
+	    {
+	      if (a.attr_syntax)
+		error_at (loc,
+			  "%qs in attribute syntax terminated "
+			  "with %qs in pragma syntax",
+			  a.device_type >= 0 ? "begin declare target"
+					     : "declare target",
+			  "end declare target");
+	      else
+		error_at (loc,
+			  "%qs in pragma syntax terminated "
+			  "with %qs in attribute syntax",
+			  a.device_type >= 0 ? "begin declare target"
+					     : "declare target",
+			  "end declare target");
+	    }
+	}
     }
   else if (strcmp (p, "assumes") == 0)
     {
@@ -23972,6 +24713,7 @@ static void
 c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
 {
   unsigned int tokens_avail = 0, i;
+  c_token *saved_tokens = NULL;
   vec<tree> types = vNULL;
   vec<c_token> clauses = vNULL;
   enum tree_code reduc_code = ERROR_MARK;
@@ -24142,8 +24884,8 @@ c_parser_omp_declare_reduction (c_parser
   int errs = errorcount;
   FOR_EACH_VEC_ELT (types, i, type)
     {
+      saved_tokens = parser->tokens;
       tokens_avail = parser->tokens_avail;
-      gcc_assert (parser->tokens == &parser->tokens_buf[0]);
       if (!clauses.is_empty ())
 	{
 	  parser->tokens = clauses.address ();
@@ -24304,7 +25046,7 @@ c_parser_omp_declare_reduction (c_parser
 
       if (!clauses.is_empty ())
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (bad)
--- gcc/c/c-lang.h.jj	2023-01-16 11:52:15.902736732 +0100
+++ gcc/c/c-lang.h	2023-10-20 18:08:49.067840689 +0200
@@ -61,6 +61,7 @@ struct GTY(()) language_function {
 };
 
 struct GTY(()) c_omp_declare_target_attr {
+  bool attr_syntax;
   int device_type;
 };
 
--- gcc/c/c-objc-common.h.jj	2023-10-13 18:34:03.140433854 +0200
+++ gcc/c/c-objc-common.h	2023-10-16 17:48:49.895080138 +0200
@@ -26,6 +26,8 @@ along with GCC; see the file COPYING3.
 
 #undef LANG_HOOKS_IDENTIFIER_SIZE
 #define LANG_HOOKS_IDENTIFIER_SIZE C_SIZEOF_STRUCT_LANG_IDENTIFIER
+#undef LANG_HOOKS_TREE_SIZE
+#define LANG_HOOKS_TREE_SIZE c_tree_size
 #undef LANG_HOOKS_FINISH
 #define LANG_HOOKS_FINISH c_common_finish
 #undef LANG_HOOKS_OPTION_LANG_MASK
--- gcc/c/c-parser.h.jj	2023-10-13 18:34:03.259432252 +0200
+++ gcc/c/c-parser.h	2023-10-16 17:48:49.863080589 +0200
@@ -80,6 +80,12 @@ struct GTY (()) c_token {
   }
 };
 
+/* Used by C_TOKEN_VEC tree.  */
+struct GTY (()) c_tree_token_vec {
+  struct tree_base base;
+  vec<c_token, va_gc> *tokens;
+};
+
 /* The parser.  */
 struct c_parser;
 
--- gcc/c/c-tree.def.jj	2023-10-16 17:48:49.896080124 +0200
+++ gcc/c/c-tree.def	2023-10-16 17:48:49.896080124 +0200
@@ -0,0 +1,31 @@
+/* This file contains the definitions and documentation for the
+   additional tree codes used in the GNU C compiler (see tree.def
+   for the standard codes).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Tree nodes used in the C frontend only, not shared with C++ frontend.  */
+
+/* Used to represent a vector of tokens for deferred parsing.  */
+DEFTREECODE (C_TOKEN_VEC, "c_token_vec", tcc_exceptional, 0)
+
+/*
+Local variables:
+mode:c
+End:
+*/
--- gcc/c/c-lang.cc.jj	2023-10-13 18:34:03.089434540 +0200
+++ gcc/c/c-lang.cc	2023-10-16 17:48:49.906079983 +0200
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "langhooks-def.h"
 #include "c-objc-common.h"
+#include "c-family/c-pragma.h"
+#include "c-parser.h"
 
 enum c_language_kind c_language = clk_c;
 
--- gcc/c/c-tree.h.jj	2023-10-13 18:34:03.289431848 +0200
+++ gcc/c/c-tree.h	2023-10-16 17:48:49.856080687 +0200
@@ -596,6 +596,7 @@ enum c_inline_static_type {
 
 
 /* in c-parser.cc */
+struct c_tree_token_vec;
 extern void c_parse_init (void);
 extern bool c_keyword_starts_typename (enum rid keyword);
 
@@ -719,6 +720,7 @@ extern struct c_declspecs *declspecs_add
 extern struct c_declspecs *declspecs_add_alignas (location_t,
 						  struct c_declspecs *, tree);
 extern struct c_declspecs *finish_declspecs (struct c_declspecs *);
+extern size_t c_tree_size (enum tree_code);
 
 /* in c-objc-common.cc */
 extern bool c_objc_common_init (void);
--- gcc/objc/objc-act.h.jj	2023-10-13 18:34:03.598427689 +0200
+++ gcc/objc/objc-act.h	2023-10-16 17:48:49.950079363 +0200
@@ -738,8 +738,6 @@ struct objc_try_context
 
 extern tree objc_create_temporary_var (tree, const char *);
 
-size_t objc_common_tree_size (enum tree_code code);
-
 
 #define objc_is_object_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_object_id)
 #define objc_is_class_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_class_id)
--- gcc/objc/objc-lang.cc.jj	2023-10-13 18:34:03.638427150 +0200
+++ gcc/objc/objc-lang.cc	2023-10-16 17:48:49.950079363 +0200
@@ -44,8 +44,6 @@ enum c_language_kind c_language = clk_ob
 #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS objc_common_init_ts
-#undef LANG_HOOKS_TREE_SIZE
-#define LANG_HOOKS_TREE_SIZE objc_common_tree_size
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
 
--- gcc/objc/objc-act.cc.jj	2023-10-13 18:34:03.529428618 +0200
+++ gcc/objc/objc-act.cc	2023-10-16 17:48:49.943079462 +0200
@@ -10340,24 +10340,5 @@ objc_common_init_ts (void)
   MARK_TS_TYPED (PROPERTY_REF);
 }
 
-size_t
-objc_common_tree_size (enum tree_code code)
-{
-  switch (code)
-    {
-    case CLASS_METHOD_DECL:
-    case INSTANCE_METHOD_DECL:
-    case KEYWORD_DECL:
-    case PROPERTY_DECL:			return sizeof (tree_decl_non_common);
-    case CLASS_INTERFACE_TYPE:
-    case CLASS_IMPLEMENTATION_TYPE:
-    case CATEGORY_INTERFACE_TYPE:
-    case CATEGORY_IMPLEMENTATION_TYPE:
-    case PROTOCOL_INTERFACE_TYPE:	return sizeof (tree_type_non_common);
-    default:
-      gcc_unreachable ();
-    }
-}
-
 
 #include "gt-objc-objc-act.h"


	Jakub
  

Patch

libgomp.texi: Update "Enabling OpenMP"

libgomp/
	* libgomp.texi (Enabling OpenMP): Update for C/C++ attributes;
	improve wording especially for Fortran; mention -fopenmp-simd.

diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 526d1be2955..d8126f96fe4 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -136,15 +136,22 @@  changed to GNU Offloading and Multi Processing Runtime Library.
 @node Enabling OpenMP
 @chapter Enabling OpenMP
 
-To activate the OpenMP extensions for C/C++ and Fortran, the compile-time 
-flag @command{-fopenmp} must be specified.  This enables the OpenMP directive
-@code{#pragma omp} in C/C++ and @code{!$omp} directives in free form, 
-@code{c$omp}, @code{*$omp} and @code{!$omp} directives in fixed form, 
-@code{!$} conditional compilation sentinels in free form and @code{c$},
-@code{*$} and @code{!$} sentinels in fixed form, for Fortran.  The flag also
-arranges for automatic linking of the OpenMP runtime library 
+To activate the OpenMP extensions for C/C++ and Fortran, the compile-time
+flag @command{-fopenmp} must be specified.  For C and C++, this enables
+the handling of the OpenMP directives using @code{#pragma omp} and the
+@code{[[omp::directive(...)]]}, @code{[[omp::sequence(...)]]} and
+@code{[[omp::decl(...)]]} attributes.  For Fortran, it enables for
+free source form the @code{!$omp} sentinel for directives and the
+@code{!$} conditional compilation sentinel and for fixed source form the
+@code{c$omp}, @code{*$omp} and @code{!$omp} sentinels for directives and
+the @code{c$}, @code{*$} and @code{!$} conditional compilation sentinels.
+The flag also arranges for automatic linking of the OpenMP runtime library
 (@ref{Runtime Library Routines}).
 
+The @command{-fopenmp-simd} flag can be used to enable a subset of
+OpenMP directives, which do not require the linking of neither the
+OpenMP runtime library nor the POSIX threads library.
+
 A complete description of all OpenMP directives may be found in the
 @uref{https://www.openmp.org, OpenMP Application Program Interface} manuals.
 See also @ref{OpenMP Implementation Status}.