On 24/10/2024 13:42, Tobias Burnus wrote:
Hi,

some more comments:

Paul-Antoine Arras wrote:
Here is an updated patch following these comments.
     gcc/testsuite/ChangeLog:
* gcc.dg/gomp/adjust-args-1.c: New test.
             * gcc.dg/gomp/dispatch-1.c: New test.

The ChangeLog misses to include libgomp/testsuite/libgomp.c/dispatch-1.c and libgomp/testsuite/libgomp.c/dispatch-2.c, which are part of this patch.

But there is reason to move them to 5/7: I think we also need a run test for C++ to make sure that it works, i.e. moving them to libgomp.c-c++- common/ makes sense, which in turn requires the 4/7 C++ FE patch.

Agreed. That's actually what I did in my local tree but forgot to move it to the right commit.

* * *

+/* Parse a function dispatch structured block:
+
+    lvalue-expression = target-call ( [expression-list] );
+    or
+    target-call ( [expression-list] );
+
+   Adapted from c_parser_expr_no_commas.
+*/

Can you expand the description, e.g. like:

    Adapted from c_parser_expr_no_commas and
    c_parser_postfix_expression (CPP_NAME/C_ID_ID) for the
    function name).

Done.

And:

+static tree
+c_parser_omp_dispatch_body (c_parser *parser)
+{
...
+  /* Parse function name.  */
+  if (!c_parser_next_token_is (parser, CPP_NAME))
+    {
+      c_parser_error (parser, "expected a function name");
+      rhs.set_error ();
+      return rhs.value;
+    }
+  expr_loc = c_parser_peek_token (parser)->location;
+  tree id = c_parser_peek_token (parser)->value;
+  c_parser_consume_token (parser);
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+    return error_mark_node;
+
+  rhs.value = build_external_ref (expr_loc, id, true, &rhs.original_type);
+  set_c_expr_source_range (&rhs, tok_range);
+  /* Parse argument list.  */

Wouldn't it be way easier and future proof (less code duplication)
to just do the following:

Note:
* rhs.set_error() / return rhs.value; → error_mark_node;
* 'c_parser_require (parser, CPP_OPEN_PAREN' → c_parser_next_token_is
* And then checking that the returned code is the expected CALL.

   /* Parse function name.  */
   if (!c_parser_next_token_is (parser, CPP_NAME))
     {
       c_parser_error (parser, "expected a function name");
       return error_mark_node;
     }
   expr_loc = c_parser_peek_token (parser)->location;
   tree id = c_parser_peek_token (parser)->value;
   c_parser_consume_token (parser);
   if (!c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
       c_parser_error (parser, "expected a function name");
       return error_mark_node;
     }

   rhs.value = build_external_ref (expr_loc, id, true, &rhs.original_type);
   set_c_expr_source_range (&rhs, tok_range);
/* Parse argument list. */
   rhs = c_parser_postfix_expression_after_primary
     (parser, EXPR_LOC_OR_LOC (rhs.value, expr_loc), rhs);
   if (TREE_CODE (lhs.value) != CALL_EXPR)
     {
       error_at (EXPR_LOC_OR_LOC (rhs.value, expr_loc),
                 "expected target-function call");
       return error_mark_node;
     }

Yes, much better indeed!

* * *

Testing it shows then the following error:

dispatch3.c:11:13: Fehler: expected target-function call
    11 |    x = bar()[0];
       |    ~~~~~~~~~^~~

which seems to be reasonable.

* * *

  #define OMP_DECLARE_SIMD_CLAUSE_MASK                          \
@@ -25242,77 +25512,223 @@ c_finish_omp_declare_variant (c_parser *parser, tree 
fndecl, tree parms)

[...]

The old code did:

tree ctx = c_parser_omp_context_selector_specification (parser, parms); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx); if (ctx != error_mark_node && variant != error_mark_node) { if (TREE_CODE (variant) != FUNCTION_DECL)

i.e. it effectively finished parsing (except for the ')', then checks the context selector and, finally, the variant declaration. The new code is similar, except that the variant declaration check is inside the 'match(…)' check – and missing this when processing 'adjust_args'. Either all parsing needs to happen before this - or the handling needs to be be but next to the relative item. NOTE: You need to ensure that the location is still okay if you move the checking after parsing. * * *

Added a check for error_mark after parsing, just before the list loop.

As mentioned in the older 2/7 review, this will fail (ICE) if the variant has issues (e.g. variant function not declared) as the adjust- args code assumes that it is valid. → testcase in that email (+ some off list communication) * * * As mentioned off list, the following fails as well:

void variant_fn();  // Assume C < C23; in C++/C23 it becomes the same as 
'…(void)'.

#pragma omp declare variant(variant_fn) match(construct={dispatch}) 
adjust_args(need_device_ptr: x,y)
void bar(int *x, int *y);

void sub(int *x, *y)
{
   #pragma omp dispatch is_device_ptr(x)
      bar(x, y);
}


Issue: the host-to-device pointer conversion is lacking for 'variant_fn'.

I implemented the approach suggested off list -- copy TYPE_ARG_TYPES from base to variant -- and added the test case. This also required a minor change in the middle end (see attached diff).

* * *

Side note: I somehow had expected that there is a warning when
mixing incompatible calls in the same translation unit, but all
tried compiler accept it silently even with some warnings turned
on. (Ignoring the warnings that C23 will disallow it.)

void f();  // Assume C < C23.

void g()
{
   f (5); // Call with one integer argument
}

void sub(int *x, int *y)
{
   f (x,y);  // Call with two pointer arguments
}

Thus, bluntly applying the pointer conversion for the case above
seems to be sufficient without doing anything else.

* * *

+  do
      {
-      c_parser_error (parser, "expected %<match%>");
[cf. above]
+                     if (strcmp (p, "need_device_ptr") == 0
+                         && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+                       {
+                         error_at (loc, "%qD is not a C pointer", decl);
+                         goto fail;
+                       }

The wording is odd (or sounds like the wording used for Fortran, where a 'type(C_ptr)' is required). How about 'not a pointer' or 'not of pointer type'?

Current OpenMP 6.0 draft uses for C: "If the need_device_ptr adjust-op modifier is present, each list item that appears in the clause that refers to a specific named argument in the declaration of the function variant must be of pointer type."

I agree; probably an unfortunate copy-paste from Fortran. Rephrased it as "not of pointer type" to stick to the OpenMP spec wording.

(For C++: + reference type and for Fortran: C_PTR type.)

* * *

+      parens.require_close (parser);
+  } while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL));
+  c_parser_skip_to_pragma_eol (parser);
+
+  if (has_adjust_args)
+    {
+      if (!has_match)
+       {
+         error_at (
+           adjust_args_loc,
+           "an %<adjust_args%> clause can only be specified if the "
+           "%<dispatch%> selector of the construct selector set appears "
+           "in the %<match%> clause");

I wonder whether it should also be %<construct%> as it is also a keyword.

Right, fixed.

* * *

The following testcase gives an ICE due to the type conversion:

long long *f();
int variant_fn();

#pragma omp declare variant(variant_fn) match(construct={dispatch})
int bar();

void sub()
{
   #pragma omp dispatch
    *f() = bar();
}

Added a check for NOP_EXPR in c_parser_omp_dispatch + suggested test case.

* * *

Tobias



--
PA
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index c86928bb1fc..24f521ef55c 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -4097,13 +4097,13 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 			  TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0))))
 		      != NULL_TREE)
 		{
-		  tree param
-		    = DECL_ARGUMENTS (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0));
+		  tree arg_types = TYPE_ARG_TYPES (
+		    TREE_TYPE (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0)));
 
-		  if (param != NULL_TREE)
+		  if (arg_types != NULL_TREE)
 		    {
 		      for (int param_idx = 0; param_idx < i; param_idx++)
-			param = TREE_CHAIN (param);
+			arg_types = TREE_CHAIN (arg_types);
 
 		      bool need_device_ptr = false;
 		      for (tree arg
commit 97ffe70930c226e4a12f809b7a47eb168afd620d
Author: Paul-Antoine Arras <par...@baylibre.com>
Date:   Fri May 24 17:28:55 2024 +0200

    OpenMP: C front-end support for dispatch + adjust_args
    
    This patch adds support to the C front-end to parse the `dispatch` construct and
    the `adjust_args` clause. It also includes some common C/C++ bits for pragmas
    and attributes.
    
    Additional common C/C++ testcases are in a later patch in the series.
    
    gcc/c-family/ChangeLog:
    
            * c-attribs.cc (c_common_gnu_attributes): Add attribute for adjust_args
            need_device_ptr.
            * c-omp.cc (c_omp_directives): Uncomment dispatch.
            * c-pragma.cc (omp_pragmas): Add dispatch.
            * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_DISPATCH.
            (enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_NOCONTEXT and
            PRAGMA_OMP_CLAUSE_NOVARIANTS.
    
    gcc/c/ChangeLog:
    
            * c-parser.cc (c_parser_omp_dispatch): New function.
            (c_parser_omp_clause_name): Handle nocontext and novariants clauses.
            (c_parser_omp_clause_novariants): New function.
            (c_parser_omp_clause_nocontext): Likewise.
            (c_parser_omp_all_clauses): Handle nocontext and novariants clauses.
            (c_parser_omp_dispatch_body): New function adapted from
            c_parser_expr_no_commas.
            (OMP_DISPATCH_CLAUSE_MASK): Define.
            (c_parser_omp_dispatch): New function.
            (c_finish_omp_declare_variant): Parse adjust_args.
            (c_parser_omp_construct): Handle PRAGMA_OMP_DISPATCH.
            * c-typeck.cc (c_finish_omp_clauses): Handle OMP_CLAUSE_NOVARIANTS and
            OMP_CLAUSE_NOCONTEXT.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/gomp/adjust-args-1.c: New test.
            * gcc.dg/gomp/dispatch-1.c: New test.

diff --git gcc/c-family/c-attribs.cc gcc/c-family/c-attribs.cc
index 4dd2eecbea5..c4c71ea7514 100644
--- gcc/c-family/c-attribs.cc
+++ gcc/c-family/c-attribs.cc
@@ -571,6 +571,9 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_omp_declare_variant_attribute, NULL },
   { "omp declare variant variant", 0, -1, true,  false, false, false,
 			      handle_omp_declare_variant_attribute, NULL },
+  { "omp declare variant adjust_args need_device_ptr", 0, -1, true,  false,
+			      false, false,
+			      handle_omp_declare_variant_attribute, NULL },
   { "simd",		      0, 1, true,  false, false, false,
 			      handle_simd_attribute, NULL },
   { "omp declare target",     0, -1, true, false, false, false,
diff --git gcc/c-family/c-omp.cc gcc/c-family/c-omp.cc
index 620a3c1353a..5a0ed636677 100644
--- gcc/c-family/c-omp.cc
+++ gcc/c-family/c-omp.cc
@@ -4300,8 +4300,8 @@ const struct c_omp_directive c_omp_directives[] = {
     C_OMP_DIR_DECLARATIVE, false },
   { "depobj", nullptr, nullptr, PRAGMA_OMP_DEPOBJ,
     C_OMP_DIR_STANDALONE, false },
-  /* { "dispatch", nullptr, nullptr, PRAGMA_OMP_DISPATCH,
-    C_OMP_DIR_CONSTRUCT, false },  */
+  { "dispatch", nullptr, nullptr, PRAGMA_OMP_DISPATCH,
+    C_OMP_DIR_DECLARATIVE, false },
   { "distribute", nullptr, nullptr, PRAGMA_OMP_DISTRIBUTE,
     C_OMP_DIR_CONSTRUCT, true },
   { "end", "assumes", nullptr, PRAGMA_OMP_END,
diff --git gcc/c-family/c-pragma.cc gcc/c-family/c-pragma.cc
index ed2a7a00e9e..040370cbb6f 100644
--- gcc/c-family/c-pragma.cc
+++ gcc/c-family/c-pragma.cc
@@ -1526,6 +1526,7 @@ static const struct omp_pragma_def omp_pragmas[] = {
   { "cancellation", PRAGMA_OMP_CANCELLATION_POINT },
   { "critical", PRAGMA_OMP_CRITICAL },
   { "depobj", PRAGMA_OMP_DEPOBJ },
+  { "dispatch", PRAGMA_OMP_DISPATCH },
   { "error", PRAGMA_OMP_ERROR },
   { "end", PRAGMA_OMP_END },
   { "flush", PRAGMA_OMP_FLUSH },
diff --git gcc/c-family/c-pragma.h gcc/c-family/c-pragma.h
index 2ebde06c471..6b6826b2426 100644
--- gcc/c-family/c-pragma.h
+++ gcc/c-family/c-pragma.h
@@ -55,6 +55,7 @@ enum pragma_kind {
   PRAGMA_OMP_CRITICAL,
   PRAGMA_OMP_DECLARE,
   PRAGMA_OMP_DEPOBJ,
+  PRAGMA_OMP_DISPATCH,
   PRAGMA_OMP_DISTRIBUTE,
   PRAGMA_OMP_ERROR,
   PRAGMA_OMP_END,
@@ -135,9 +136,11 @@ enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_LINK,
   PRAGMA_OMP_CLAUSE_MAP,
   PRAGMA_OMP_CLAUSE_MERGEABLE,
+  PRAGMA_OMP_CLAUSE_NOCONTEXT,
   PRAGMA_OMP_CLAUSE_NOGROUP,
   PRAGMA_OMP_CLAUSE_NONTEMPORAL,
   PRAGMA_OMP_CLAUSE_NOTINBRANCH,
+  PRAGMA_OMP_CLAUSE_NOVARIANTS,
   PRAGMA_OMP_CLAUSE_NOWAIT,
   PRAGMA_OMP_CLAUSE_NUM_TASKS,
   PRAGMA_OMP_CLAUSE_NUM_TEAMS,
diff --git gcc/c/c-parser.cc gcc/c/c-parser.cc
index fe01f955e21..395939a1a40 100644
--- gcc/c/c-parser.cc
+++ gcc/c/c-parser.cc
@@ -1747,6 +1747,7 @@ static void c_parser_omp_assumption_clauses (c_parser *, bool);
 static void c_parser_omp_allocate (c_parser *);
 static void c_parser_omp_assumes (c_parser *);
 static bool c_parser_omp_ordered (c_parser *, enum pragma_context, bool *);
+static tree c_parser_omp_dispatch (location_t, c_parser *);
 static void c_parser_oacc_routine (c_parser *, enum pragma_context);
 
 /* These Objective-C parser functions are only ever called when
@@ -15090,6 +15091,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	case 'n':
 	  if (!strcmp ("no_create", p))
 	    result = PRAGMA_OACC_CLAUSE_NO_CREATE;
+	  else if (!strcmp ("nocontext", p))
+	    result = PRAGMA_OMP_CLAUSE_NOCONTEXT;
 	  else if (!strcmp ("nogroup", p))
 	    result = PRAGMA_OMP_CLAUSE_NOGROUP;
 	  else if (!strcmp ("nohost", p))
@@ -15098,6 +15101,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_NONTEMPORAL;
 	  else if (!strcmp ("notinbranch", p))
 	    result = PRAGMA_OMP_CLAUSE_NOTINBRANCH;
+	  else if (!strcmp ("novariants", p))
+	    result = PRAGMA_OMP_CLAUSE_NOVARIANTS;
 	  else if (!strcmp ("nowait", p))
 	    result = PRAGMA_OMP_CLAUSE_NOWAIT;
 	  else if (!strcmp ("num_gangs", p))
@@ -19365,6 +19370,60 @@ c_parser_omp_clause_partial (c_parser *parser, tree list)
   return c;
 }
 
+/* OpenMP 5.1
+   novariants ( scalar-expression ) */
+
+static tree
+c_parser_omp_clause_novariants (c_parser *parser, tree list)
+{
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return list;
+
+  location_t loc = c_parser_peek_token (parser)->location;
+  c_expr expr = c_parser_expr_no_commas (parser, NULL);
+  tree t = convert_lvalue_to_rvalue (loc, expr, true, true).value;
+  t = c_objc_common_truthvalue_conversion (loc, t);
+  t = c_fully_fold (t, false, NULL);
+  parens.skip_until_found_close (parser);
+
+  check_no_duplicate_clause (list, OMP_CLAUSE_NOVARIANTS, "novariants");
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_NOVARIANTS);
+  OMP_CLAUSE_NOVARIANTS_EXPR (c) = t;
+  OMP_CLAUSE_CHAIN (c) = list;
+  list = c;
+
+  return list;
+}
+
+/* OpenMP 5.1
+   nocontext ( scalar-expression ) */
+
+static tree
+c_parser_omp_clause_nocontext (c_parser *parser, tree list)
+{
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return list;
+
+  location_t loc = c_parser_peek_token (parser)->location;
+  c_expr expr = c_parser_expr_no_commas (parser, NULL);
+  tree t = convert_lvalue_to_rvalue (loc, expr, true, true).value;
+  t = c_objc_common_truthvalue_conversion (loc, t);
+  t = c_fully_fold (t, false, NULL);
+  parens.skip_until_found_close (parser);
+
+  check_no_duplicate_clause (list, OMP_CLAUSE_NOCONTEXT, "nocontext");
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_NOCONTEXT);
+  OMP_CLAUSE_NOCONTEXT_EXPR (c) = t;
+  OMP_CLAUSE_CHAIN (c) = list;
+  list = c;
+
+  return list;
+}
+
 /* OpenMP 5.0:
    detach ( event-handle ) */
 
@@ -19984,6 +20043,14 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  c_name = "partial";
 	  clauses = c_parser_omp_clause_partial (parser, clauses);
 	  break;
+	case PRAGMA_OMP_CLAUSE_NOVARIANTS:
+	  c_name = "novariants";
+	  clauses = c_parser_omp_clause_novariants (parser, clauses);
+	  break;
+	case PRAGMA_OMP_CLAUSE_NOCONTEXT:
+	  c_name = "nocontext";
+	  clauses = c_parser_omp_clause_nocontext (parser, clauses);
+	  break;
 	default:
 	  c_parser_error (parser, "expected an OpenMP clause");
 	  goto saw_error;
@@ -23799,6 +23866,148 @@ c_parser_omp_scope (location_t loc, c_parser *parser, bool *if_p)
   return add_stmt (stmt);
 }
 
+/* Parse a function dispatch structured block:
+
+    lvalue-expression = target-call ( [expression-list] );
+    or
+    target-call ( [expression-list] );
+
+   Adapted from c_parser_expr_no_commas and c_parser_postfix_expression
+   (CPP_NAME/C_ID_ID) for the function name.
+*/
+static tree
+c_parser_omp_dispatch_body (c_parser *parser)
+{
+  struct c_expr lhs, rhs, ret;
+  location_t expr_loc = c_parser_peek_token (parser)->location;
+  source_range tok_range = c_parser_peek_token (parser)->get_range ();
+
+  lhs = c_parser_conditional_expression (parser, NULL, NULL);
+  if (TREE_CODE (lhs.value) == CALL_EXPR)
+    return lhs.value;
+
+  location_t op_location = c_parser_peek_token (parser)->location;
+  if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
+    return error_mark_node;
+
+  /* Parse function name.  */
+  if (!c_parser_next_token_is (parser, CPP_NAME))
+    {
+      c_parser_error (parser, "expected a function name");
+      return error_mark_node;
+    }
+  expr_loc = c_parser_peek_token (parser)->location;
+  tree id = c_parser_peek_token (parser)->value;
+  c_parser_consume_token (parser);
+  if (!c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+    {
+      c_parser_error (parser, "expected a function name");
+      return error_mark_node;
+    }
+
+  rhs.value = build_external_ref (expr_loc, id, true, &rhs.original_type);
+  set_c_expr_source_range (&rhs, tok_range);
+
+  /* Parse argument list.  */
+  rhs = c_parser_postfix_expression_after_primary (
+    parser, EXPR_LOC_OR_LOC (rhs.value, expr_loc), rhs);
+  if (TREE_CODE (rhs.value) != CALL_EXPR)
+    {
+      error_at (EXPR_LOC_OR_LOC (rhs.value, expr_loc),
+		"expected target-function call");
+      return error_mark_node;
+    }
+
+  /* Build assignment. */
+  rhs = convert_lvalue_to_rvalue (expr_loc, rhs, true, true);
+  ret.value
+    = build_modify_expr (op_location, lhs.value, lhs.original_type, NOP_EXPR,
+			 expr_loc, rhs.value, rhs.original_type);
+  ret.m_decimal = 0;
+  set_c_expr_source_range (&ret, lhs.get_start (), rhs.get_finish ());
+  ret.original_code = MODIFY_EXPR;
+  ret.original_type = NULL;
+  return ret.value;
+}
+
+/* OpenMP 5.1:
+   # pragma omp dispatch dispatch-clause[optseq] new-line
+     expression-stmt
+
+   LOC is the location of the #pragma.
+*/
+
+#define OMP_DISPATCH_CLAUSE_MASK                                               \
+  ((OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE)                             \
+   | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND)                           \
+   | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOVARIANTS)                       \
+   | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOCONTEXT)                        \
+   | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)                    \
+   | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT))
+
+static tree
+c_parser_omp_dispatch (location_t loc, c_parser *parser)
+{
+  tree stmt = make_node (OMP_DISPATCH);
+  SET_EXPR_LOCATION (stmt, loc);
+  TREE_TYPE (stmt) = void_type_node;
+
+  OMP_DISPATCH_CLAUSES (stmt)
+    = c_parser_omp_all_clauses (parser, OMP_DISPATCH_CLAUSE_MASK,
+				"#pragma omp dispatch");
+
+  // Extract depend clauses and create taskwait
+  tree depend_clauses = NULL_TREE;
+  tree *depend_clauses_ptr = &depend_clauses;
+  for (tree c = OMP_DISPATCH_CLAUSES (stmt); c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND)
+	{
+	  *depend_clauses_ptr = c;
+	  depend_clauses_ptr = &OMP_CLAUSE_CHAIN (c);
+	}
+    }
+  if (depend_clauses != NULL_TREE)
+    {
+      tree stmt = make_node (OMP_TASK);
+      TREE_TYPE (stmt) = void_node;
+      OMP_TASK_CLAUSES (stmt) = depend_clauses;
+      OMP_TASK_BODY (stmt) = NULL_TREE;
+      SET_EXPR_LOCATION (stmt, loc);
+      add_stmt (stmt);
+    }
+
+  // Parse body as expression statement
+  loc = c_parser_peek_token (parser)->location;
+  tree dispatch_body = c_parser_omp_dispatch_body (parser);
+  if (dispatch_body == error_mark_node)
+    {
+      inform (loc, "%<#pragma omp dispatch%> must be followed by a function "
+		   "call with optional assignment");
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return NULL_TREE;
+    }
+
+  // Walk the tree to find the dispatch function call and wrap it into an IFN
+  gcc_assert (TREE_CODE (dispatch_body) == CALL_EXPR
+	      || TREE_CODE (dispatch_body) == MODIFY_EXPR);
+  tree *dispatch_call = TREE_CODE (dispatch_body) == MODIFY_EXPR
+			  ? &TREE_OPERAND (dispatch_body, 1)
+			  : &dispatch_body;
+  while (TREE_CODE (*dispatch_call) == FLOAT_EXPR
+	 || TREE_CODE (*dispatch_call) == CONVERT_EXPR
+	 || TREE_CODE (*dispatch_call) == NOP_EXPR)
+    dispatch_call = &TREE_OPERAND (*dispatch_call, 0);
+  *dispatch_call = build_call_expr_internal_loc (
+    loc, IFN_GOMP_DISPATCH,
+    TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (*dispatch_call))), 1, *dispatch_call);
+
+  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+  OMP_DISPATCH_BODY (stmt) = dispatch_body;
+
+  return add_stmt (stmt);
+}
+
 /* OpenMP 3.0:
    # pragma omp task task-clause[optseq] new-line
 
@@ -24779,6 +24988,10 @@ check_clauses:
 
    OpenMP 5.0:
    # pragma omp declare variant (identifier) match(context-selector) new-line
+
+   OpenMP 5.1
+   # pragma omp declare variant (identifier) match(context-selector) \
+      adjust_args(adjust-op:argument-list) new-line
    */
 
 #define OMP_DECLARE_SIMD_CLAUSE_MASK				\
@@ -25242,77 +25455,235 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 
   parens.require_close (parser);
 
-  if (c_parser_next_token_is (parser, CPP_COMMA)
-      && c_parser_peek_2nd_token (parser)->type == CPP_NAME)
-    c_parser_consume_token (parser);
+  vec<tree> adjust_args_list = vNULL;
+  bool has_match = false, has_adjust_args = false;
+  location_t adjust_args_loc = UNKNOWN_LOCATION;
+  tree need_device_ptr_list = make_node (TREE_LIST);
 
-  const char *clause = "";
-  location_t match_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_next_token_is (parser, CPP_NAME))
-    clause = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
-  if (strcmp (clause, "match"))
+  do
     {
-      c_parser_error (parser, "expected %<match%>");
-      goto fail;
-    }
+      if (c_parser_next_token_is (parser, CPP_COMMA)
+	  && c_parser_peek_2nd_token (parser)->type == CPP_NAME)
+	c_parser_consume_token (parser);
 
-  c_parser_consume_token (parser);
+      const char *clause = "";
+      location_t match_loc = c_parser_peek_token (parser)->location;
+      if (c_parser_next_token_is (parser, CPP_NAME))
+	clause = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
 
-  if (!parens.require_open (parser))
-    goto fail;
+      enum clause
+      {
+	match,
+	adjust_args
+      } ccode;
 
-  if (parms == NULL_TREE)
-    parms = error_mark_node;
-
-  tree ctx = c_parser_omp_context_selector_specification (parser, parms);
-  if (ctx == error_mark_node)
-    goto fail;
-  ctx = omp_check_context_selector (match_loc, ctx);
-  if (ctx != error_mark_node && variant != error_mark_node)
-    {
-      if (TREE_CODE (variant) != FUNCTION_DECL)
+      if (strcmp (clause, "match") == 0)
+	ccode = match;
+      else if (strcmp (clause, "adjust_args") == 0)
 	{
-	  error_at (token->location, "variant %qD is not a function", variant);
-	  variant = error_mark_node;
+	  ccode = adjust_args;
+	  adjust_args_loc = match_loc;
 	}
-      else if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
-					  OMP_TRAIT_CONSTRUCT_SIMD)
-	       && !comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+      else
 	{
-	  error_at (token->location, "variant %qD and base %qD have "
-				     "incompatible types", variant, fndecl);
-	  variant = error_mark_node;
+	  c_parser_error (parser, "expected %<match%> or %<adjust_args%>");
+	  goto fail;
 	}
-      else if (fndecl_built_in_p (variant)
-	       && (strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
-			    "__builtin_", strlen ("__builtin_")) == 0
-		   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
-			       "__sync_", strlen ("__sync_")) == 0
-		   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
-			       "__atomic_", strlen ("__atomic_")) == 0))
+
+      c_parser_consume_token (parser);
+
+      if (!parens.require_open (parser))
+	goto fail;
+
+      if (parms == NULL_TREE)
+	parms = error_mark_node;
+
+      if (ccode == match)
 	{
-	  error_at (token->location, "variant %qD is a built-in", variant);
-	  variant = error_mark_node;
-	}
-      if (variant != error_mark_node)
-	{
-	  C_DECL_USED (variant) = 1;
-	  tree construct
-	    = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
-	  omp_mark_declare_variant (match_loc, variant, construct);
-	  if (omp_context_selector_matches (ctx))
+	  has_match = true;
+	  tree ctx
+	    = c_parser_omp_context_selector_specification (parser, parms);
+	  if (ctx == error_mark_node)
+	    goto fail;
+	  ctx = omp_check_context_selector (match_loc, ctx);
+	  if (ctx != error_mark_node && variant != error_mark_node)
 	    {
-	      tree attr
-		= tree_cons (get_identifier ("omp declare variant base"),
-			     build_tree_list (variant, ctx),
-			     DECL_ATTRIBUTES (fndecl));
-	      DECL_ATTRIBUTES (fndecl) = attr;
+	      if (TREE_CODE (variant) != FUNCTION_DECL)
+		{
+		  error_at (token->location, "variant %qD is not a function",
+			    variant);
+		  variant = error_mark_node;
+		}
+	      else if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
+						  OMP_TRAIT_CONSTRUCT_SIMD))
+		{
+		  if (comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+		    {
+		      if (TYPE_ARG_TYPES (TREE_TYPE (variant)) == NULL_TREE)
+			TYPE_ARG_TYPES (TREE_TYPE (variant))
+			  = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+		    }
+		  else
+		    {
+		      error_at (token->location,
+				"variant %qD and base %qD have "
+				"incompatible types",
+				variant, fndecl);
+		      variant = error_mark_node;
+		    }
+		}
+	      else if (fndecl_built_in_p (variant)
+		       && (strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+				    "__builtin_", strlen ("__builtin_"))
+			     == 0
+			   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+				       "__sync_", strlen ("__sync_"))
+				== 0
+			   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+				       "__atomic_", strlen ("__atomic_"))
+				== 0))
+		{
+		  error_at (token->location, "variant %qD is a built-in",
+			    variant);
+		  variant = error_mark_node;
+		}
+	      if (variant != error_mark_node)
+		{
+		  C_DECL_USED (variant) = 1;
+		  tree construct
+		    = omp_get_context_selector_list (ctx,
+						     OMP_TRAIT_SET_CONSTRUCT);
+		  omp_mark_declare_variant (match_loc, variant, construct);
+		  if (omp_context_selector_matches (ctx))
+		    {
+		      tree attr = tree_cons (get_identifier (
+					       "omp declare variant base"),
+					     build_tree_list (variant, ctx),
+					     DECL_ATTRIBUTES (fndecl));
+		      DECL_ATTRIBUTES (fndecl) = attr;
+		    }
+		}
+	    }
+	}
+      else if (ccode == adjust_args)
+	{
+	  has_adjust_args = true;
+	  if (c_parser_next_token_is (parser, CPP_NAME)
+	      && c_parser_peek_2nd_token (parser)->type == CPP_COLON)
+	    {
+	      const char *p
+		= IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+	      if (strcmp (p, "need_device_ptr") == 0
+		  || strcmp (p, "nothing") == 0)
+		{
+		  c_parser_consume_token (parser); // need_device_ptr
+		  c_parser_consume_token (parser); // :
+
+		  location_t loc = c_parser_peek_token (parser)->location;
+		  tree list
+		    = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_ERROR,
+						  NULL_TREE);
+
+		  tree arg;
+		  if (variant != error_mark_node)
+		    for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c))
+		      {
+			tree decl = TREE_PURPOSE (c);
+			int idx;
+			for (arg = parms, idx = 0; arg != NULL;
+			     arg = TREE_CHAIN (arg), idx++)
+			  if (arg == decl)
+			    break;
+			if (arg == NULL_TREE)
+			  {
+			    error_at (loc, "%qD is not a function argument",
+				      decl);
+			    goto fail;
+			  }
+			if (adjust_args_list.contains (arg))
+			  {
+			    error_at (loc, "%qD is specified more than once",
+				      decl);
+			    goto fail;
+			  }
+			if (strcmp (p, "need_device_ptr") == 0
+			    && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+			  {
+			    error_at (loc, "%qD is not of pointer type", decl);
+			    goto fail;
+			  }
+			adjust_args_list.safe_push (arg);
+			if (strcmp (p, "need_device_ptr") == 0)
+			  {
+			    need_device_ptr_list = chainon (
+			      need_device_ptr_list,
+			      build_tree_list (
+				NULL_TREE,
+				build_int_cst (
+				  integer_type_node,
+				  idx))); // Store 0-based argument index,
+					  // as in gimplify_call_expr
+			  }
+		      }
+		}
+	      else
+		{
+		  error_at (c_parser_peek_token (parser)->location,
+			    "expected %<nothing%> or %<need_device_ptr%>");
+		  goto fail;
+		}
+	    }
+	  else
+	    {
+	      error_at (c_parser_peek_token (parser)->location,
+			"expected %<nothing%> or %<need_device_ptr%> "
+			"followed by %<:%>");
+	      goto fail;
+	    }
+	}
+
+      parens.require_close (parser);
+  } while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL));
+  c_parser_skip_to_pragma_eol (parser);
+
+  if (has_adjust_args)
+    {
+      if (!has_match)
+	{
+	  error_at (
+	    adjust_args_loc,
+	    "an %<adjust_args%> clause can only be specified if the "
+	    "%<dispatch%> selector of the %<construct%> selector set appears "
+	    "in the %<match%> clause");
+	}
+      else
+	{
+	  tree attr = lookup_attribute ("omp declare variant base",
+					DECL_ATTRIBUTES (fndecl));
+	  if (attr != NULL_TREE)
+	    {
+	      tree ctx = TREE_VALUE (TREE_VALUE (attr));
+	      if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
+					     OMP_TRAIT_CONSTRUCT_DISPATCH))
+		error_at (
+		  adjust_args_loc,
+		  "an %<adjust_args%> clause can only be specified if the "
+		  "%<dispatch%> selector of the %<construct%> selector set "
+		  "appears in the %<match%> clause");
 	    }
 	}
     }
 
-  parens.require_close (parser);
-  c_parser_skip_to_pragma_eol (parser);
+  if (TREE_CHAIN (need_device_ptr_list) != NULL_TREE
+      && variant != error_mark_node)
+    {
+      tree variant_decl = tree_strip_nop_conversions (variant);
+      DECL_ATTRIBUTES (variant_decl)
+	= tree_cons (get_identifier ("omp declare variant variant adjust_args"),
+		     build_tree_list (need_device_ptr_list,
+				      NULL_TREE /*need_device_addr */),
+		     DECL_ATTRIBUTES (variant_decl));
+    }
 }
 
 /* Finalize #pragma omp declare simd or #pragma omp declare variant
@@ -26132,7 +26503,6 @@ c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
   types.release ();
 }
 
-
 /* OpenMP 4.0
    #pragma omp declare simd declare-simd-clauses[optseq] new-line
    #pragma omp declare reduction (reduction-id : typename-list : expression) \
@@ -26140,7 +26510,11 @@ c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
    #pragma omp declare target new-line
 
    OpenMP 5.0
-   #pragma omp declare variant (identifier) match (context-selector)  */
+   #pragma omp declare variant (identifier) match (context-selector)
+
+   OpenMP 5.1
+   #pragma omp declare variant (identifier) match (context-selector) \
+      adjust_args(adjust-op:argument-list)  */
 
 static bool
 c_parser_omp_declare (c_parser *parser, enum pragma_context context)
@@ -27058,6 +27432,9 @@ c_parser_omp_construct (c_parser *parser, bool *if_p)
     case PRAGMA_OMP_UNROLL:
       stmt = c_parser_omp_unroll (loc, parser, if_p);
       break;
+    case PRAGMA_OMP_DISPATCH:
+      stmt = c_parser_omp_dispatch (loc, parser);
+      break;
     default:
       gcc_unreachable ();
     }
diff --git gcc/c/c-typeck.cc gcc/c/c-typeck.cc
index ba6d96d26b2..fa4777dd35a 100644
--- gcc/c/c-typeck.cc
+++ gcc/c/c-typeck.cc
@@ -16333,6 +16333,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_FINALIZE:
 	case OMP_CLAUSE_NOHOST:
 	case OMP_CLAUSE_INDIRECT:
+	case OMP_CLAUSE_NOVARIANTS:
+	case OMP_CLAUSE_NOCONTEXT:
 	  pc = &OMP_CLAUSE_CHAIN (c);
 	  continue;
 
diff --git gcc/testsuite/gcc.dg/gomp/adjust-args-1.c gcc/testsuite/gcc.dg/gomp/adjust-args-1.c
new file mode 100644
index 00000000000..b0f1bbaf31d
--- /dev/null
+++ gcc/testsuite/gcc.dg/gomp/adjust-args-1.c
@@ -0,0 +1,32 @@
+/* Test parsing of OMP clause adjust_args */
+/* { dg-do compile } */
+
+int b;
+
+int f0 (void *a);
+int g (void *a);
+int f1 (int);
+
+#pragma omp declare variant (f0) match (construct={target}) adjust_args (nothing: a) /* { dg-error "an 'adjust_args' clause can only be specified if the 'dispatch' selector of the 'construct' selector set appears in the 'match' clause" } */
+int f2 (void *a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (other: a) /* { dg-error "expected 'nothing' or 'need_device_ptr'" } */
+int f3 (int a);
+#pragma omp declare variant (f0) adjust_args (nothing: a) /* { dg-error "an 'adjust_args' clause can only be specified if the 'dispatch' selector of the 'construct' selector set appears in the 'match' clause" } */
+int f4 (void *a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args () /* { dg-error "expected 'nothing' or 'need_device_ptr' followed by ':'" } */
+int f5 (int a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing) /* { dg-error "expected 'nothing' or 'need_device_ptr' followed by ':'" } */
+int f6 (int a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing:) /* { dg-error "expected identifier before '\\)' token" } */
+int f7 (int a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: z) /* { dg-error "'z' undeclared here \\(not in a function\\)" } */
+int f8 (int a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (need_device_ptr: a) /* { dg-error "'a' is not of pointer type" } */
+int f9 (int a);
+#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: a) adjust_args (nothing: a) /* { dg-error "'a' is specified more than once" } */
+int f10 (int a);
+#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (nothing: a) adjust_args (need_device_ptr: a) /* { dg-error "'a' is specified more than once" } */
+int f11 (void *a);
+#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) /* { dg-error "'b' is not a function argument" } */
+int f12 (void *a);
+
diff --git gcc/testsuite/gcc.dg/gomp/dispatch-1.c gcc/testsuite/gcc.dg/gomp/dispatch-1.c
new file mode 100644
index 00000000000..a15cf87978f
--- /dev/null
+++ gcc/testsuite/gcc.dg/gomp/dispatch-1.c
@@ -0,0 +1,53 @@
+/* Test parsing of #pragma omp dispatch */
+/* { dg-do compile } */
+
+int f0 (int);
+
+void f1 (void)
+{
+  int a, b;
+  double x;
+  struct {int a; int b;} s;
+  int arr[1];
+
+#pragma omp dispatch
+  int c = f0 (a);	/* { dg-error "expected expression before 'int'" } */
+#pragma omp dispatch
+  int f2 (int d);	/* { dg-error "expected expression before 'int'" } */
+#pragma omp dispatch
+  a = b;	/* { dg-error "expected a function name before ';' token" } */
+#pragma omp dispatch
+  s.a = f0(a) + b;	/* { dg-error "expected ';' before '\\+' token" } */
+#pragma omp dispatch
+  b = !f0(a);	/* { dg-error "expected a function name before '!' token" } */
+#pragma omp dispatch
+  s.b += f0(s.a);	/* { dg-error "expected '=' before '\\+=' token" } */
+#pragma omp dispatch
+#pragma omp threadprivate(a)	/* { dg-error "expected expression before '#pragma'" } */
+  a = f0(b);
+  
+#pragma omp dispatch nocontext(s) /* { dg-error "used struct type value where scalar is required" } */
+  f0(a);
+#pragma omp dispatch nocontext(a, b) /* { dg-error "expected '\\)' before ','" } */
+  f0(a);
+#pragma omp dispatch nocontext(a) nocontext(b) /* { dg-error "too many 'nocontext' clauses" } */
+  f0(a);
+#pragma omp dispatch novariants(s) /* { dg-error "used struct type value where scalar is required" } */
+  f0(a);
+#pragma omp dispatch novariants(a, b) /* { dg-error "expected '\\)' before ','" } */
+  f0(a);
+#pragma omp dispatch novariants(a) novariants(b) /* { dg-error "too many 'novariants' clauses" } */
+  f0(a);
+#pragma omp dispatch nowait nowait /* { dg-error "too many 'nowait' clauses" } */
+  f0(a);
+#pragma omp dispatch device(x) /* { dg-error "expected integer expression before end of line" } */
+  f0(a);
+#pragma omp dispatch device(arr) /* { dg-error "expected integer expression before end of line" } */
+  f0(a);
+#pragma omp dispatch is_device_ptr(x) /* { dg-error "'is_device_ptr' variable is neither a pointer nor an array" } */
+  f0(a);
+#pragma omp dispatch is_device_ptr(&x) /* { dg-error "expected identifier before '&' token" } */
+  f0(a);
+#pragma omp dispatch depend(inout: f0) /* { dg-error "'f0' is not lvalue expression nor array section in 'depend' clause" } */
+  f0(a);
+}
diff --git gcc/testsuite/gcc.dg/gomp/dispatch-2.c gcc/testsuite/gcc.dg/gomp/dispatch-2.c
new file mode 100644
index 00000000000..bebf2e12d6c
--- /dev/null
+++ gcc/testsuite/gcc.dg/gomp/dispatch-2.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+
+/* Check that the missing declaration for construct does not trigger an ICE but 
+   is rejected as invalid code.  */
+
+int *f();
+
+struct t {
+  int *a, *b;
+};
+
+#pragma omp declare variant(construct) match(construct={dispatch}) adjust_args(need_device_ptr: x,y) /* { dg-error "'construct' undeclared here \\(not in a function\\)" } */
+#pragma omp declare variant(noconstruct) match(implementation={vendor(gnu)}) /* { dg-error "'noconstruct' undeclared here \\(not in a function\\)" } */
+void bar(int *x, int *y);
+
+int nocontext, novariant;
+
+void sub(struct t *s, void *y)
+{
+    bar( f(), s->b);
+ #pragma omp dispatch device(0) is_device_ptr(s)
+    bar( f(), s->b);
+    
+    bar( (int *) y, s->b);
+ #pragma omp dispatch device(0) is_device_ptr(y)
+    bar( (int *) y, s->b);
+}
+
+
+
diff --git gcc/testsuite/gcc.dg/gomp/dispatch-3.c gcc/testsuite/gcc.dg/gomp/dispatch-3.c
new file mode 100644
index 00000000000..e15d1dcc5d9
--- /dev/null
+++ gcc/testsuite/gcc.dg/gomp/dispatch-3.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+/* Check that the assignment from pointer to integer does not trigger an ICE but 
+   is rejected as invalid code.  */
+
+long long *f();
+int *variant_fn();
+
+#pragma omp declare variant(variant_fn) match(construct={dispatch})
+int *bar();
+
+void sub()
+{
+  #pragma omp dispatch
+   *f() = bar(); /* { dg-error "assignment to 'long long int' from 'int \\*' makes integer from pointer without a cast" } */
+}
diff --git gcc/testsuite/gcc.dg/gomp/dispatch-4.c gcc/testsuite/gcc.dg/gomp/dispatch-4.c
new file mode 100644
index 00000000000..feeaeef7e6f
--- /dev/null
+++ gcc/testsuite/gcc.dg/gomp/dispatch-4.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Check that when the variant has an empty parameter list, the host-to-device 
+   pointer conversion still happens.  */
+
+void variant_fn();  // Assume C < C23; in C++/C23 it becomes the same as '…(void)'.
+
+#pragma omp declare variant(variant_fn) match(construct={dispatch}) adjust_args(need_device_ptr: x,y)
+void bar(int *x, int *y);
+
+void sub(int *x, int *y)
+{
+  #pragma omp dispatch is_device_ptr(y)
+     bar(x, y);
+}
+
+/* { dg-final { scan-tree-dump-times "D\\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(x, D\\.\[0-9\]+\\);" 1 "gimple" } } */

Reply via email to