https://gcc.gnu.org/g:c54051c282dbba6297a23173feb82fa066149973

commit c54051c282dbba6297a23173feb82fa066149973
Author: Paul-Antoine Arras <par...@baylibre.com>
Date:   Wed Nov 20 15:28:57 2024 +0100

    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.
            * gcc.dg/gomp/dispatch-2.c: New test.
            * gcc.dg/gomp/dispatch-3.c: New test.
            * gcc.dg/gomp/dispatch-4.c: New test.
            * gcc.dg/gomp/dispatch-5.c: New test.
    
    (cherry picked from commit d7d8d9dae9f86df6ca5d03f0eb5d78898e6aa804)

Diff:
---
 gcc/c-family/ChangeLog.omp                |  13 +
 gcc/c-family/c-attribs.cc                 |   3 +
 gcc/c-family/c-omp.cc                     |   4 +-
 gcc/c-family/c-pragma.cc                  |   1 +
 gcc/c-family/c-pragma.h                   |   3 +
 gcc/c/ChangeLog.omp                       |  19 ++
 gcc/c/c-parser.cc                         | 487 ++++++++++++++++++++++++++----
 gcc/c/c-typeck.cc                         |   2 +
 gcc/testsuite/ChangeLog.omp               |  12 +
 gcc/testsuite/gcc.dg/gomp/adjust-args-1.c |  32 ++
 gcc/testsuite/gcc.dg/gomp/dispatch-1.c    |  53 ++++
 gcc/testsuite/gcc.dg/gomp/dispatch-2.c    |  30 ++
 gcc/testsuite/gcc.dg/gomp/dispatch-3.c    |  16 +
 gcc/testsuite/gcc.dg/gomp/dispatch-4.c    |  19 ++
 gcc/testsuite/gcc.dg/gomp/dispatch-5.c    |  21 ++
 15 files changed, 658 insertions(+), 57 deletions(-)

diff --git a/gcc/c-family/ChangeLog.omp b/gcc/c-family/ChangeLog.omp
index 9048fec81289..7f249b657250 100644
--- a/gcc/c-family/ChangeLog.omp
+++ b/gcc/c-family/ChangeLog.omp
@@ -1,3 +1,16 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2024-11-20  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * 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.
+
 2025-01-23  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 582d99ada1bb..4e9a9007fcaa 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -556,6 +556,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 a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 479b69741a54..20c2609836fb 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -5040,8 +5040,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 a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index 6548ff45d087..1f4c8aa091bd 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/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 a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 8444e3f2b207..8b14fcd8be29 100644
--- a/gcc/c-family/c-pragma.h
+++ b/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,
@@ -136,9 +137,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 a/gcc/c/ChangeLog.omp b/gcc/c/ChangeLog.omp
index 2381d31ef0e4..56656442267f 100644
--- a/gcc/c/ChangeLog.omp
+++ b/gcc/c/ChangeLog.omp
@@ -1,3 +1,22 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2024-11-20  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * 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.
+
 2025-01-23  Tobias Burnus  <tbur...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index a284a916ac62..7c657997799a 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1759,6 +1759,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
@@ -15160,6 +15161,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))
@@ -15168,6 +15171,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))
@@ -20021,6 +20026,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 ) */
 
@@ -20644,6 +20703,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;
@@ -24454,6 +24521,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
 
@@ -25476,6 +25685,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                           \
@@ -25936,77 +26149,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;
+      if (strcmp (clause, "match") == 0)
+       ccode = match;
+      else if (strcmp (clause, "adjust_args") == 0)
+       {
+         ccode = adjust_args;
+         adjust_args_loc = match_loc;
+       }
+      else
+       {
+         c_parser_error (parser, "expected %<match%> clause");
+         goto fail;
+       }
 
-  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, false);
-  if (ctx != error_mark_node && variant != error_mark_node)
-    {
-      if (TREE_CODE (variant) != FUNCTION_DECL)
+      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 not a function", variant);
-         variant = error_mark_node;
+         if (has_match)
+           error_at (match_loc, "too many %<match%> clauses");
+         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, false);
+         if (ctx != error_mark_node && variant != error_mark_node)
+           {
+             if (TREE_CODE (variant) != FUNCTION_DECL)
+               {
+                 error_at (token->location, "variant %qD is not a function",
+                           variant);
+                 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;
+               }
+             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;
+                   }
+               }
+             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, NULL_TREE, false))
+                   {
+                     tree attr = tree_cons (get_identifier (
+                                              "omp declare variant base"),
+                                            build_tree_list (variant, ctx),
+                                            DECL_ATTRIBUTES (fndecl));
+                     DECL_ATTRIBUTES (fndecl) = attr;
+                   }
+               }
+           }
        }
-      else if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
-                                         OMP_TRAIT_CONSTRUCT_SIMD)
-              && !comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+      else if (ccode == adjust_args)
        {
-         error_at (token->location, "variant %qD and base %qD have "
-                                    "incompatible types", variant, fndecl);
-         variant = error_mark_node;
+         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))
+                         {
+                           // TODO fix location
+                           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;
+           }
        }
-      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))
+
+      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 (token->location, "variant %qD is a built-in", variant);
-         variant = error_mark_node;
+         error_at (adjust_args_loc,
+                   "an %<adjust_args%> clause requires a %<match%> clause");
        }
-      if (variant != error_mark_node)
+      else
        {
-         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, NULL_TREE, false))
+         tree attr = lookup_attribute ("omp declare variant base",
+                                       DECL_ATTRIBUTES (fndecl));
+         if (attr != NULL_TREE)
            {
-             tree attr
-               = tree_cons (get_identifier ("omp declare variant base"),
-                            build_tree_list (variant, ctx),
-                            DECL_ATTRIBUTES (fndecl));
-             DECL_ATTRIBUTES (fndecl) = attr;
+             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
@@ -26826,7 +27197,6 @@ c_parser_omp_declare_reduction (c_parser *parser, enum 
pragma_context context)
   types.release ();
 }
 
-
 /* OpenMP 5.0
    #pragma omp declare mapper ([mapper-identifier :] type var) \
                              [clause [ [,] clause ] ... ] new-line  */
@@ -26979,7 +27349,11 @@ c_parser_omp_declare_mapper (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)
@@ -28311,6 +28685,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 a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index fa773824c1b3..91d016aaed5e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -16403,6 +16403,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 a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp
index b6551c7f8879..6959c9b8854d 100644
--- a/gcc/testsuite/ChangeLog.omp
+++ b/gcc/testsuite/ChangeLog.omp
@@ -1,3 +1,15 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2024-11-20  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * gcc.dg/gomp/adjust-args-1.c: New test.
+       * gcc.dg/gomp/dispatch-1.c: New test.
+       * gcc.dg/gomp/dispatch-2.c: New test.
+       * gcc.dg/gomp/dispatch-3.c: New test.
+       * gcc.dg/gomp/dispatch-4.c: New test.
+       * gcc.dg/gomp/dispatch-5.c: New test.
+
 2025-01-23  Tobias Burnus  <tbur...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c 
b/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c
new file mode 100644
index 000000000000..90787ef2f9d9
--- /dev/null
+++ b/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 requires a '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 a/gcc/testsuite/gcc.dg/gomp/dispatch-1.c 
b/gcc/testsuite/gcc.dg/gomp/dispatch-1.c
new file mode 100644
index 000000000000..a15cf87978f2
--- /dev/null
+++ b/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 a/gcc/testsuite/gcc.dg/gomp/dispatch-2.c 
b/gcc/testsuite/gcc.dg/gomp/dispatch-2.c
new file mode 100644
index 000000000000..bebf2e12d6c0
--- /dev/null
+++ b/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 a/gcc/testsuite/gcc.dg/gomp/dispatch-3.c 
b/gcc/testsuite/gcc.dg/gomp/dispatch-3.c
new file mode 100644
index 000000000000..e15d1dcc5d93
--- /dev/null
+++ b/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 a/gcc/testsuite/gcc.dg/gomp/dispatch-4.c 
b/gcc/testsuite/gcc.dg/gomp/dispatch-4.c
new file mode 100644
index 000000000000..d905cbc83f04
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/dispatch-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=gnu17" } */
+/* { 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" } } */
diff --git a/gcc/testsuite/gcc.dg/gomp/dispatch-5.c 
b/gcc/testsuite/gcc.dg/gomp/dispatch-5.c
new file mode 100644
index 000000000000..282c018cc7d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/dispatch-5.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c23" } */
+
+
+void variant_fn(int *x, int *y, int *z);
+
+[[omp::directive (declare variant(variant_fn) match(construct={dispatch}) 
adjust_args(need_device_ptr: x,y) adjust_args(nothing: z))]]
+void bar(int *x, int *y, int *z);
+
+void sub(int *x, int *y, int *z)
+{
+  [[omp::directive (dispatch is_device_ptr(y))]]
+     bar(x, y, z);
+  [[omp::directive (dispatch device(0))]]
+     bar(x, y, z);
+  [[omp::directive (dispatch nocontext(1) novariants(1))]]
+     bar(x, y, z);
+  [[omp::directive (dispatch depend(inout: x) nowait)]]
+     bar(x, y, z);
+}
+

Reply via email to