https://gcc.gnu.org/g:1985a02ef74582d7cdcb2c139aa7d22a9c619dc3

commit 1985a02ef74582d7cdcb2c139aa7d22a9c619dc3
Author: Paul-Antoine Arras <par...@baylibre.com>
Date:   Fri May 24 19:13:50 2024 +0200

    OpenMP: Fortran front-end support for dispatch + adjust_args
    
    This patch adds support for the `dispatch` construct and the `adjust_args`
    clause to the Fortran front-end.
    
    Handling of `adjust_args` across translation units is missing due to 
PR115271.
    
    Minor modifications to the C++ FE and the ME are also folded into this 
patch as
    a side effect of the Fortran work.
    
    gcc/c-family/ChangeLog:
    
            * c-attribs.cc: (c_common_gnu_attributes): Rename "omp declare 
variant
            variant adjust_args" into "omp declare variant variant args" to also
            accommodate append_args.
    
    gcc/cp/ChangeLog:
    
            * parser.cc (cp_parser_omp_dispatch): Handle INDIRECT_REF.
    
    gcc/fortran/ChangeLog:
    
            * dump-parse-tree.cc (show_omp_clauses): Handle novariants and 
nocontext
            clauses.
            (show_omp_node): Handle EXEC_OMP_DISPATCH.
            (show_code_node): Likewise.
            * frontend-passes.cc (gfc_code_walker): Handle novariants and 
nocontext.
            * gfortran.h (enum gfc_statement): Add ST_OMP_DISPATCH.
            (symbol_attribute): Add omp_declare_variant_need_device_ptr.
            (gfc_omp_clauses): Add novariants and nocontext.
            (gfc_omp_declare_variant): Add need_device_ptr_arg_list.
            (enum gfc_exec_op): Add EXEC_OMP_DISPATCH.
            * match.h (gfc_match_omp_dispatch): Declare.
            * openmp.cc (gfc_free_omp_clauses): Free novariants and nocontext
            clauses.
            (gfc_free_omp_declare_variant_list): Free need_device_ptr_arg_list
            namelist.
            (enum omp_mask2): Add OMP_CLAUSE_NOVARIANTS and 
OMP_CLAUSE_NOCONTEXT.
            (gfc_match_omp_clauses): Handle OMP_CLAUSE_NOVARIANTS and
            OMP_CLAUSE_NOCONTEXT.
            (OMP_DISPATCH_CLAUSES): Define.
            (gfc_match_omp_dispatch): New function.
            (gfc_match_omp_declare_variant): Parse adjust_args.
            (resolve_omp_clauses): Handle adjust_args, novariants and nocontext.
            Adjust handling of OMP_LIST_IS_DEVICE_PTR.
            (icode_code_error_callback): Handle EXEC_OMP_DISPATCH.
            (omp_code_to_statement): Likewise.
            (resolve_omp_dispatch): New function.
            (gfc_resolve_omp_directive): Handle EXEC_OMP_DISPATCH.
            * parse.cc (decode_omp_directive): Match dispatch.
            (next_statement): Handle ST_OMP_DISPATCH.
            (gfc_ascii_statement): Likewise.
            (parse_omp_dispatch): New function.
            (parse_executable): Handle ST_OMP_DISPATCH.
            * resolve.cc (gfc_resolve_blocks): Handle EXEC_OMP_DISPATCH.
            * st.cc (gfc_free_statement): Likewise.
            * trans-decl.cc (create_function_arglist): Declare.
            (gfc_get_extern_function_decl): Call it.
            * trans-openmp.cc (gfc_trans_omp_clauses): Handle novariants and
            nocontext.
            (replace_omp_dispatch_call): New function.
            (gfc_trans_omp_dispatch): New function.
            (gfc_trans_omp_directive): Handle EXEC_OMP_DISPATCH.
            (gfc_trans_omp_declare_variant): Handle adjust_args.
            * trans.cc (trans_code): Handle EXEC_OMP_DISPATCH:.
    
    gcc/ChangeLog:
    
            * gimplify.cc (gimplify_call_expr): Fix handling of need_device_ptr 
for
            type(c_ptr). Fix handling of nested function calls in a dispatch 
region.
            (find_ifn_gomp_dispatch): Return the IFN without stripping it.
            (gimplify_omp_dispatch): Keep IFN_GOMP_DISPATCH until
            gimplify_call_expr.
    
    libgomp/ChangeLog:
            * testsuite/libgomp.fortran/declare-variant-2-aux.f90: New test.
            * testsuite/libgomp.fortran/declare-variant-2.f90: New test (xfail).
            * testsuite/libgomp.fortran/dispatch-1.f90: New test.
            * testsuite/libgomp.fortran/dispatch-2.f90: New test.
            * testsuite/libgomp.fortran/dispatch-3.f90: New test.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/gomp/dispatch-3.C: Update scan dumps.
            * gfortran.dg/gomp/declare-variant-2.f90: Update dg-error.
            * gfortran.dg/gomp/adjust-args-1.f90: New test.
            * gfortran.dg/gomp/adjust-args-2.f90: New test.
            * gfortran.dg/gomp/adjust-args-2a.f90: New test.
            * gfortran.dg/gomp/adjust-args-3.f90: New test.
            * gfortran.dg/gomp/adjust-args-4.f90: New test.
            * gfortran.dg/gomp/adjust-args-5.f90: New test.
            * gfortran.dg/gomp/adjust-args-6.f90: New test.
            * gfortran.dg/gomp/adjust-args-7.f90: New test.
            * gfortran.dg/gomp/adjust-args-8.f90: New test.
            * gfortran.dg/gomp/adjust-args-9.f90: New test.
            * gfortran.dg/gomp/dispatch-1.f90: New test.
            * gfortran.dg/gomp/dispatch-2.f90: New test.
            * gfortran.dg/gomp/dispatch-3.f90: New test.
            * gfortran.dg/gomp/dispatch-4.f90: New test.
            * gfortran.dg/gomp/dispatch-5.f90: New test.
            * gfortran.dg/gomp/dispatch-6.f90: New test.
            * gfortran.dg/gomp/dispatch-7.f90: New test.
            * gfortran.dg/gomp/dispatch-8.f90: New test.
            * gfortran.dg/gomp/dispatch-9.f90: New test.
            * gfortran.dg/gomp/dispatch-9a.f90: New test.
            * gfortran.dg/gomp/dispatch-10.f90: New test.
    
    (cherry picked from commit bca8b13bd7bc3dbe07004664ba3411a2f2991f5c)

Diff:
---
 gcc/ChangeLog.omp                                  |  11 ++
 gcc/c-family/ChangeLog.omp                         |   9 +
 gcc/c-family/c-attribs.cc                          |   2 +-
 gcc/cp/ChangeLog.omp                               |   7 +
 gcc/cp/parser.cc                                   |   5 +-
 gcc/fortran/ChangeLog.omp                          |  49 +++++
 gcc/fortran/dump-parse-tree.cc                     |  17 ++
 gcc/fortran/frontend-passes.cc                     |   2 +
 gcc/fortran/gfortran.h                             |  12 +-
 gcc/fortran/match.h                                |   1 +
 gcc/fortran/openmp.cc                              | 204 +++++++++++++++++++--
 gcc/fortran/parse.cc                               |  54 +++++-
 gcc/fortran/resolve.cc                             |   4 +-
 gcc/fortran/st.cc                                  |   1 +
 gcc/fortran/trans-decl.cc                          |   9 +-
 gcc/fortran/trans-openmp.cc                        | 197 ++++++++++++++++++++
 gcc/fortran/trans.cc                               |   1 +
 gcc/gimplify.cc                                    | 192 +++++++++++--------
 gcc/testsuite/ChangeLog.omp                        |  29 +++
 gcc/testsuite/g++.dg/gomp/dispatch-3.C             |   2 +-
 gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90   |  45 +++++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-2.f90   |  18 ++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-2a.f90  |  36 ++++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90   |  27 +++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-4.f90   |  58 ++++++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-5.f90   |  58 ++++++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-6.f90   |  16 ++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-7.f90   |  17 ++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-8.f90   |  51 ++++++
 gcc/testsuite/gfortran.dg/gomp/adjust-args-9.f90   |  25 +++
 .../gfortran.dg/gomp/declare-variant-2.f90         |   6 +-
 gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90      |  77 ++++++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-10.f90     |  21 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90      |  79 ++++++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-3.f90      |  39 ++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-4.f90      |  19 ++
 gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90      |  25 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90      |  22 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90      |  26 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90      |  36 ++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-9.f90      |  24 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-9a.f90     |  27 +++
 libgomp/ChangeLog.omp                              |  11 ++
 .../libgomp.fortran/declare-variant-2-aux.f90      |  25 +++
 .../libgomp.fortran/declare-variant-2.f90          |  22 +++
 libgomp/testsuite/libgomp.fortran/dispatch-1.f90   | 120 ++++++++++++
 libgomp/testsuite/libgomp.fortran/dispatch-2.f90   |  69 +++++++
 libgomp/testsuite/libgomp.fortran/dispatch-3.f90   |  80 ++++++++
 48 files changed, 1782 insertions(+), 105 deletions(-)

diff --git a/gcc/ChangeLog.omp b/gcc/ChangeLog.omp
index f8a43011f549..bda3e3de82fd 100644
--- a/gcc/ChangeLog.omp
+++ b/gcc/ChangeLog.omp
@@ -1,3 +1,14 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * gimplify.cc (gimplify_call_expr): Fix handling of need_device_ptr for
+       type(c_ptr). Fix handling of nested function calls in a dispatch region.
+       (find_ifn_gomp_dispatch): Return the IFN without stripping it.
+       (gimplify_omp_dispatch): Keep IFN_GOMP_DISPATCH until
+       gimplify_call_expr.
+
 2025-01-24  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/c-family/ChangeLog.omp b/gcc/c-family/ChangeLog.omp
index 7f249b657250..75a1c1795410 100644
--- a/gcc/c-family/ChangeLog.omp
+++ b/gcc/c-family/ChangeLog.omp
@@ -1,3 +1,12 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * c-attribs.cc: (c_common_gnu_attributes): Rename "omp declare variant
+       variant adjust_args" into "omp declare variant variant args" to also
+       accommodate append_args.
+
 2025-01-27  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 4e9a9007fcaa..af31487742bf 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -556,7 +556,7 @@ 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,
+  { "omp declare variant variant args", 0, -1, true,  false,
                              false, false,
                              handle_omp_declare_variant_attribute, NULL },
   { "simd",                  0, 1, true,  false, false, false,
diff --git a/gcc/cp/ChangeLog.omp b/gcc/cp/ChangeLog.omp
index 01483816e018..95aa7a78283e 100644
--- a/gcc/cp/ChangeLog.omp
+++ b/gcc/cp/ChangeLog.omp
@@ -1,3 +1,10 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * parser.cc (cp_parser_omp_dispatch): Handle INDIRECT_REF.
+
 2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index b1c17a451d79..9c40ceacce35 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -49805,8 +49805,9 @@ cp_parser_omp_dispatch (cp_parser *parser, cp_token 
*pragma_tok)
     default:
       gcc_unreachable ();
     }
-  if (TREE_CODE (*dispatch_call) == FLOAT_EXPR
-      || TREE_CODE (*dispatch_call) == CONVERT_EXPR)
+  while (TREE_CODE (*dispatch_call) == FLOAT_EXPR
+        || TREE_CODE (*dispatch_call) == CONVERT_EXPR
+        || TREE_CODE (*dispatch_call) == INDIRECT_REF)
     dispatch_call = &TREE_OPERAND (*dispatch_call, 0);
   *dispatch_call = build_call_expr_internal_loc (loc, IFN_GOMP_DISPATCH,
                                                 TREE_TYPE (*dispatch_call), 1,
diff --git a/gcc/fortran/ChangeLog.omp b/gcc/fortran/ChangeLog.omp
index 5a99b7d37f07..6b92d87bd68f 100644
--- a/gcc/fortran/ChangeLog.omp
+++ b/gcc/fortran/ChangeLog.omp
@@ -1,3 +1,52 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * dump-parse-tree.cc (show_omp_clauses): Handle novariants and nocontext
+       clauses.
+       (show_omp_node): Handle EXEC_OMP_DISPATCH.
+       (show_code_node): Likewise.
+       * frontend-passes.cc (gfc_code_walker): Handle novariants and nocontext.
+       * gfortran.h (enum gfc_statement): Add ST_OMP_DISPATCH.
+       (symbol_attribute): Add omp_declare_variant_need_device_ptr.
+       (gfc_omp_clauses): Add novariants and nocontext.
+       (gfc_omp_declare_variant): Add need_device_ptr_arg_list.
+       (enum gfc_exec_op): Add EXEC_OMP_DISPATCH.
+       * match.h (gfc_match_omp_dispatch): Declare.
+       * openmp.cc (gfc_free_omp_clauses): Free novariants and nocontext
+       clauses.
+       (gfc_free_omp_declare_variant_list): Free need_device_ptr_arg_list
+       namelist.
+       (enum omp_mask2): Add OMP_CLAUSE_NOVARIANTS and OMP_CLAUSE_NOCONTEXT.
+       (gfc_match_omp_clauses): Handle OMP_CLAUSE_NOVARIANTS and
+       OMP_CLAUSE_NOCONTEXT.
+       (OMP_DISPATCH_CLAUSES): Define.
+       (gfc_match_omp_dispatch): New function.
+       (gfc_match_omp_declare_variant): Parse adjust_args.
+       (resolve_omp_clauses): Handle adjust_args, novariants and nocontext.
+       Adjust handling of OMP_LIST_IS_DEVICE_PTR.
+       (icode_code_error_callback): Handle EXEC_OMP_DISPATCH.
+       (omp_code_to_statement): Likewise.
+       (resolve_omp_dispatch): New function.
+       (gfc_resolve_omp_directive): Handle EXEC_OMP_DISPATCH.
+       * parse.cc (decode_omp_directive): Match dispatch.
+       (next_statement): Handle ST_OMP_DISPATCH.
+       (gfc_ascii_statement): Likewise.
+       (parse_omp_dispatch): New function.
+       (parse_executable): Handle ST_OMP_DISPATCH.
+       * resolve.cc (gfc_resolve_blocks): Handle EXEC_OMP_DISPATCH.
+       * st.cc (gfc_free_statement): Likewise.
+       * trans-decl.cc (create_function_arglist): Declare.
+       (gfc_get_extern_function_decl): Call it.
+       * trans-openmp.cc (gfc_trans_omp_clauses): Handle novariants and
+       nocontext.
+       (replace_omp_dispatch_call): New function.
+       (gfc_trans_omp_dispatch): New function.
+       (gfc_trans_omp_directive): Handle EXEC_OMP_DISPATCH.
+       (gfc_trans_omp_declare_variant): Handle adjust_args.
+       * trans.cc (trans_code): Handle EXEC_OMP_DISPATCH:.
+
 2025-01-23  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index f09fb4eb6abc..60c03cf0b733 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -2218,6 +2218,18 @@ show_omp_clauses (gfc_omp_clauses *omp_clauses)
        }
       fputc (')', dumpfile);
     }
+  if (omp_clauses->novariants)
+    {
+      fputs (" NOVARIANTS(", dumpfile);
+      show_expr (omp_clauses->novariants);
+      fputc (')', dumpfile);
+    }
+  if (omp_clauses->nocontext)
+    {
+      fputs (" NOCONTEXT(", dumpfile);
+      show_expr (omp_clauses->nocontext);
+      fputc (')', dumpfile);
+    }
 }
 
 /* Show a single OpenMP or OpenACC directive node and everything underneath it
@@ -2255,6 +2267,9 @@ show_omp_node (int level, gfc_code *c)
     case EXEC_OMP_CANCEL: name = "CANCEL"; break;
     case EXEC_OMP_CANCELLATION_POINT: name = "CANCELLATION POINT"; break;
     case EXEC_OMP_CRITICAL: name = "CRITICAL"; break;
+    case EXEC_OMP_DISPATCH:
+      name = "DISPATCH";
+      break;
     case EXEC_OMP_DISTRIBUTE: name = "DISTRIBUTE"; break;
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
       name = "DISTRIBUTE PARALLEL DO"; break;
@@ -2360,6 +2375,7 @@ show_omp_node (int level, gfc_code *c)
     case EXEC_OMP_ASSUME:
     case EXEC_OMP_CANCEL:
     case EXEC_OMP_CANCELLATION_POINT:
+    case EXEC_OMP_DISPATCH:
     case EXEC_OMP_DISTRIBUTE:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
@@ -3612,6 +3628,7 @@ show_code_node (int level, gfc_code *c)
     case EXEC_OMP_BARRIER:
     case EXEC_OMP_CRITICAL:
     case EXEC_OMP_DEPOBJ:
+    case EXEC_OMP_DISPATCH:
     case EXEC_OMP_DISTRIBUTE:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
diff --git a/gcc/fortran/frontend-passes.cc b/gcc/fortran/frontend-passes.cc
index 3c06018fdbbf..1a0ef50b91dd 100644
--- a/gcc/fortran/frontend-passes.cc
+++ b/gcc/fortran/frontend-passes.cc
@@ -5669,6 +5669,8 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, 
walk_expr_fn_t exprfn,
                  WALK_SUBEXPR (co->ext.omp_clauses->num_tasks);
                  WALK_SUBEXPR (co->ext.omp_clauses->priority);
                  WALK_SUBEXPR (co->ext.omp_clauses->detach);
+                 WALK_SUBEXPR (co->ext.omp_clauses->novariants);
+                 WALK_SUBEXPR (co->ext.omp_clauses->nocontext);
                  for (idx = 0; idx < ARRAY_SIZE (list_types); idx++)
                    for (n = co->ext.omp_clauses->lists[list_types[idx]];
                         n; n = n->next)
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index ed1acf667b4f..97ed99c8b195 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -325,7 +325,8 @@ enum gfc_statement
   /* Note: gfc_match_omp_nothing returns ST_NONE. */
   ST_OMP_NOTHING, ST_NONE,
   ST_OMP_UNROLL, ST_OMP_END_UNROLL,
-  ST_OMP_TILE, ST_OMP_END_TILE, ST_OMP_INTEROP
+  ST_OMP_TILE, ST_OMP_END_TILE, ST_OMP_INTEROP, ST_OMP_DISPATCH,
+  ST_OMP_END_DISPATCH
 };
 
 /* Types of interfaces that we can have.  Assignment interfaces are
@@ -1397,6 +1398,7 @@ typedef struct gfc_omp_namelist
          bool target;
          bool targetsync;
        } init;
+      bool need_device_ptr;
     } u;
   union
     {
@@ -1454,6 +1456,7 @@ enum
   OMP_LIST_INIT,
   OMP_LIST_USE,
   OMP_LIST_DESTROY,
+  OMP_LIST_ADJUST_ARGS,
   OMP_LIST_NUM /* Must be the last.  */
 };
 
@@ -1599,6 +1602,8 @@ typedef struct gfc_omp_clauses
   struct gfc_expr *depobj;
   struct gfc_expr *dist_chunk_size;
   struct gfc_expr *message;
+  struct gfc_expr *novariants;
+  struct gfc_expr *nocontext;
   struct gfc_omp_assumptions *assume;
   struct gfc_expr_list *sizes_list;
   const char *critical_name;
@@ -1729,6 +1734,7 @@ typedef struct gfc_omp_declare_variant
   struct gfc_symtree *variant_proc_symtree;
 
   gfc_omp_set_selector *set_selectors;
+  gfc_omp_namelist *adjust_args_list;
 
   bool checked_p : 1; /* Set if previously checked for errors.  */
   bool error_p : 1; /* Set if error found in directive.  */
@@ -3115,7 +3121,7 @@ enum gfc_exec_op
   EXEC_OMP_PARALLEL_MASKED_TASKLOOP, EXEC_OMP_PARALLEL_MASKED_TASKLOOP_SIMD,
   EXEC_OMP_MASKED_TASKLOOP, EXEC_OMP_MASKED_TASKLOOP_SIMD, EXEC_OMP_SCOPE,
   EXEC_OMP_UNROLL, EXEC_OMP_TILE, EXEC_OMP_METADIRECTIVE, EXEC_OMP_INTEROP,
-  EXEC_OMP_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS
+  EXEC_OMP_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS, EXEC_OMP_DISPATCH
 };
 
 typedef struct gfc_code
@@ -3807,7 +3813,7 @@ void gfc_resolve_do_iterator (gfc_code *, gfc_symbol *, 
bool);
 void gfc_resolve_omp_local_vars (gfc_namespace *);
 void gfc_resolve_omp_parallel_blocks (gfc_code *, gfc_namespace *);
 void gfc_resolve_omp_do_blocks (gfc_code *, gfc_namespace *);
-void gfc_resolve_omp_declare_simd (gfc_namespace *);
+void gfc_resolve_omp_declare (gfc_namespace *);
 void gfc_resolve_omp_udrs (gfc_symtree *);
 void gfc_resolve_omp_udms (gfc_symtree *);
 void gfc_omp_instantiate_mappers (gfc_code *, gfc_omp_clauses *,
diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h
index 929fe73e7c98..e9137b4eed74 100644
--- a/gcc/fortran/match.h
+++ b/gcc/fortran/match.h
@@ -165,6 +165,7 @@ match gfc_match_omp_declare_simd (void);
 match gfc_match_omp_declare_target (void);
 match gfc_match_omp_declare_variant (void);
 match gfc_match_omp_depobj (void);
+match gfc_match_omp_dispatch (void);
 match gfc_match_omp_distribute (void);
 match gfc_match_omp_distribute_parallel_do (void);
 match gfc_match_omp_distribute_parallel_do_simd (void);
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 6567e0269a2e..1411521ed9c2 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -74,7 +74,7 @@ static const struct gfc_omp_directive gfc_omp_directives[] = {
   {"declare target", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_TARGET},
   {"declare variant", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_VARIANT},
   {"depobj", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DEPOBJ},
-  /* {"dispatch", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DISPATCH}, */
+  {"dispatch", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DISPATCH},
   {"distribute", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DISTRIBUTE},
   {"do", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DO},
   /* "error" becomes GFC_OMP_DIR_EXECUTABLE with at(execution) */
@@ -192,6 +192,8 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   gfc_free_expr (c->num_tasks);
   gfc_free_expr (c->priority);
   gfc_free_expr (c->detach);
+  gfc_free_expr (c->novariants);
+  gfc_free_expr (c->nocontext);
   gfc_free_expr (c->async_expr);
   gfc_free_expr (c->gang_num_expr);
   gfc_free_expr (c->gang_static_expr);
@@ -331,6 +333,7 @@ gfc_free_omp_declare_variant_list (gfc_omp_declare_variant 
*list)
       gfc_omp_declare_variant *current = list;
       list = list->next;
       gfc_free_omp_set_selector_list (current->set_selectors);
+      gfc_free_omp_namelist (current->adjust_args_list);
       free (current);
     }
 }
@@ -1143,6 +1146,8 @@ enum omp_mask2
   OMP_CLAUSE_INIT,  /* OpenMP 5.1.  */
   OMP_CLAUSE_DESTROY,  /* OpenMP 5.1.  */
   OMP_CLAUSE_USE,  /* OpenMP 5.1.  */
+  OMP_CLAUSE_NOVARIANTS, /* OpenMP 5.1  */
+  OMP_CLAUSE_NOCONTEXT, /* OpenMP 5.1  */
   /* This must come last.  */
   OMP_MASK2_LAST
 };
@@ -3783,6 +3788,25 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const 
omp_mask mask,
              c->assume->no_parallelism = needs_space = true;
              continue;
            }
+
+         if ((mask & OMP_CLAUSE_NOVARIANTS)
+             && (m = gfc_match_dupl_check (!c->novariants, "novariants", true,
+                                           &c->novariants))
+                  != MATCH_NO)
+           {
+             if (m == MATCH_ERROR)
+               goto error;
+             continue;
+           }
+         if ((mask & OMP_CLAUSE_NOCONTEXT)
+             && (m = gfc_match_dupl_check (!c->nocontext, "nocontext", true,
+                                           &c->nocontext))
+                  != MATCH_NO)
+           {
+             if (m == MATCH_ERROR)
+               goto error;
+             continue;
+           }
          if ((mask & OMP_CLAUSE_NOGROUP)
              && (m = gfc_match_dupl_check (!c->nogroup, "nogroup"))
                 != MATCH_NO)
@@ -5153,6 +5177,9 @@ cleanup:
 #define OMP_INTEROP_CLAUSES \
   (omp_mask (OMP_CLAUSE_DEPEND) | OMP_CLAUSE_NOWAIT | OMP_CLAUSE_DEVICE \
    | OMP_CLAUSE_INIT | OMP_CLAUSE_DESTROY | OMP_CLAUSE_USE)
+#define OMP_DISPATCH_CLAUSES                                                   
\
+  (omp_mask (OMP_CLAUSE_DEVICE) | OMP_CLAUSE_DEPEND | OMP_CLAUSE_NOVARIANTS    
\
+   | OMP_CLAUSE_NOCONTEXT | OMP_CLAUSE_IS_DEVICE_PTR | OMP_CLAUSE_NOWAIT)
 
 
 static match
@@ -5466,6 +5493,12 @@ error:
   return MATCH_ERROR;
 }
 
+match
+gfc_match_omp_dispatch (void)
+{
+  return match_omp (EXEC_OMP_DISPATCH, OMP_DISPATCH_CLAUSES);
+}
+
 match
 gfc_match_omp_distribute (void)
 {
@@ -6865,6 +6898,7 @@ gfc_match_omp_declare_variant (void)
   odv = gfc_get_omp_declare_variant ();
   odv->where = gfc_current_locus;
   odv->variant_proc_symtree = variant_proc_st;
+  odv->adjust_args_list = NULL;
   odv->base_proc_symtree = base_proc_st;
   odv->next = NULL;
   odv->error_p = false;
@@ -6881,13 +6915,29 @@ gfc_match_omp_declare_variant (void)
       return MATCH_ERROR;
     }
 
+  bool has_match = false, has_adjust_args = false;
+  locus adjust_args_loc;
+
   for (;;)
     {
-      if (gfc_match (" match") != MATCH_YES)
+      enum clause
+      {
+       match,
+       adjust_args
+      } ccode;
+
+      if (gfc_match (" match") == MATCH_YES)
+       ccode = match;
+      else if (gfc_match (" adjust_args") == MATCH_YES)
+       {
+         ccode = adjust_args;
+         adjust_args_loc = gfc_current_locus;
+       }
+      else
        {
          if (first_p)
            {
-             gfc_error ("expected %<match%> at %C");
+             gfc_error ("expected %<match%> or %<adjust_args%> at %C");
              return MATCH_ERROR;
            }
          else
@@ -6900,20 +6950,57 @@ gfc_match_omp_declare_variant (void)
          return MATCH_ERROR;
        }
 
-      if (gfc_match_omp_context_selector_specification (&odv->set_selectors,
+      if (ccode == match)
+       {
+         has_match = true;
+         if (gfc_match_omp_context_selector_specification (&odv->set_selectors,
                                                        false)
-         != MATCH_YES)
-       return MATCH_ERROR;
-
-      if (gfc_match (" )") != MATCH_YES)
+             != MATCH_YES)
+           return MATCH_ERROR;
+         if (gfc_match (" )") != MATCH_YES)
+           {
+             gfc_error ("expected %<)%> at %C");
+             return MATCH_ERROR;
+           }
+       }
+      else if (ccode == adjust_args)
        {
-         gfc_error ("expected %<)%> at %C");
-         return MATCH_ERROR;
+         has_adjust_args = true;
+         bool need_device_ptr_p;
+         if (gfc_match (" nothing") == MATCH_YES)
+           need_device_ptr_p = false;
+         else if (gfc_match (" need_device_ptr") == MATCH_YES)
+           need_device_ptr_p = true;
+         else
+           {
+             gfc_error ("expected %<nothing%> or %<need_device_ptr%> at %C");
+             return MATCH_ERROR;
+           }
+         gfc_omp_namelist **head = NULL;
+         if (gfc_match_omp_variable_list (" :", &odv->adjust_args_list, false,
+                                          NULL, &head)
+             != MATCH_YES)
+           {
+             gfc_error ("expected argument list at %C");
+             return MATCH_ERROR;
+           }
+         if (need_device_ptr_p)
+           for (gfc_omp_namelist *n = *head; n != NULL; n = n->next)
+             n->u.need_device_ptr = true;
        }
 
       first_p = false;
     }
 
+  if (has_adjust_args && !has_match)
+    {
+      gfc_error ("an %<adjust_args%> clause at %L can only be specified if the 
"
+                "%<dispatch%> selector of the construct selector set appears "
+                "in the %<match%> clause",
+                &adjust_args_loc);
+      return MATCH_ERROR;
+    }
+
   return MATCH_YES;
 }
 
@@ -9234,7 +9321,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses 
*omp_clauses,
        "DEVICE_RESIDENT", "LINK", "USE_DEVICE",
        "CACHE", "IS_DEVICE_PTR", "USE_DEVICE_PTR", "USE_DEVICE_ADDR",
        "NONTEMPORAL", "ALLOCATE", "HAS_DEVICE_ADDR", "ENTER",
-       "USES_ALLOCATORS", "INIT", "USE", "DESTROY" };
+       "USES_ALLOCATORS", "INIT", "USE", "DESTROY", "ADJUST_ARGS" };
   STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
 
   if (omp_clauses == NULL)
@@ -9418,6 +9505,26 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses 
*omp_clauses,
        gfc_error ("FINAL clause at %L requires a scalar LOGICAL expression",
                   &expr->where);
     }
+  if (omp_clauses->novariants)
+    {
+      gfc_expr *expr = omp_clauses->novariants;
+      if (!gfc_resolve_expr (expr) || expr->ts.type != BT_LOGICAL
+         || expr->rank != 0)
+       gfc_error (
+         "NOVARIANTS clause at %L requires a scalar LOGICAL expression",
+         &expr->where);
+      if_without_mod = true;
+    }
+  if (omp_clauses->nocontext)
+    {
+      gfc_expr *expr = omp_clauses->nocontext;
+      if (!gfc_resolve_expr (expr) || expr->ts.type != BT_LOGICAL
+         || expr->rank != 0)
+       gfc_error (
+         "NOCONTEXT clause at %L requires a scalar LOGICAL expression",
+         &expr->where);
+      if_without_mod = true;
+    }
   if (omp_clauses->num_threads)
     resolve_positive_int_expr (omp_clauses->num_threads, "NUM_THREADS");
   if (omp_clauses->chunk_size)
@@ -9717,14 +9824,18 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses 
*omp_clauses,
            last = NULL;
            for (n = omp_clauses->lists[list]; n != NULL; )
              {
-               if (n->sym->ts.type == BT_DERIVED
-                   && n->sym->ts.u.derived->ts.is_iso_c
-                   && code->op != EXEC_OMP_TARGET)
+               if ((n->sym->ts.type != BT_DERIVED
+                    || !n->sym->ts.u.derived->ts.is_iso_c
+                    || (n->sym->ts.u.derived->intmod_sym_id
+                        != ISOCBINDING_PTR))
+                   && code->op == EXEC_OMP_DISPATCH)
                  /* Non-TARGET (i.e. DISPATCH) requires a C_PTR.  */
                  gfc_error ("List item %qs in %s clause at %L must be of "
                             "TYPE(C_PTR)", n->sym->name, name, &n->where);
                else if (n->sym->ts.type != BT_DERIVED
-                        || !n->sym->ts.u.derived->ts.is_iso_c)
+                        || !n->sym->ts.u.derived->ts.is_iso_c
+                        || (n->sym->ts.u.derived->intmod_sym_id
+                            != ISOCBINDING_PTR))
                  {
                    /* For TARGET, non-C_PTR are deprecated and handled as
                       has_device_addr.  */
@@ -11399,6 +11510,7 @@ icode_code_error_callback (gfc_code **codep,
     case EXEC_OMP_MASKED_TASKLOOP_SIMD:
     case EXEC_OMP_SCOPE:
     case EXEC_OMP_ERROR:
+    case EXEC_OMP_DISPATCH:
       gfc_error ("%s cannot contain OpenMP directive in intervening code "
                 "at %L",
                 state->name, &code->loc);
@@ -12388,6 +12500,8 @@ omp_code_to_statement (gfc_code *code)
       return ST_OMP_TILE;
     case EXEC_OMP_UNROLL:
       return ST_OMP_UNROLL;
+    case EXEC_OMP_DISPATCH:
+      return ST_OMP_DISPATCH;
     default:
       gcc_unreachable ();
     }
@@ -12822,6 +12936,41 @@ resolve_omp_target (gfc_code *code)
 #undef GFC_IS_TEAMS_CONSTRUCT
 }
 
+static void
+resolve_omp_dispatch (gfc_code *code)
+{
+  gfc_code *next = code->block->next;
+  if (next == NULL)
+    return;
+
+  gfc_exec_op op = next->op;
+  gcc_assert (op == EXEC_CALL || op == EXEC_ASSIGN);
+  if (op != EXEC_CALL
+      && (op != EXEC_ASSIGN || next->expr2->expr_type != EXPR_FUNCTION))
+    gfc_error (
+      "%<OMP DISPATCH%> directive at %L must be followed by a procedure "
+      "call with optional assignment",
+      &code->loc);
+
+  if ((op == EXEC_CALL && next->resolved_sym != NULL
+       && next->resolved_sym->attr.proc_pointer)
+      || (op == EXEC_ASSIGN && gfc_expr_attr (next->expr2).proc_pointer))
+    gfc_error ("%<OMP DISPATCH%> directive at %L cannot be followed by a "
+              "procedure pointer",
+              &code->loc);
+
+  gfc_omp_declare_variant *odv = gfc_current_ns->omp_declare_variant;
+  if (odv != NULL)
+    for (gfc_omp_namelist *n = odv->adjust_args_list; n != NULL; n = n->next)
+      if (n->sym->ts.type != BT_DERIVED || !n->sym->ts.u.derived->ts.is_iso_c
+         || (n->sym->ts.u.derived->intmod_sym_id != ISOCBINDING_PTR))
+       {
+         gfc_error (
+           "argument list item %qs in %<need_device_ptr%> at %L must be of "
+           "TYPE(C_PTR)",
+           n->sym->name, &n->where);
+       }
+}
 
 /* Resolve OpenMP directive clauses and check various requirements
    of each directive.  */
@@ -12941,18 +13090,23 @@ gfc_resolve_omp_directive (gfc_code *code, 
gfc_namespace *ns)
     case EXEC_OMP_METADIRECTIVE:
       resolve_omp_metadirective (code, ns);
       break;
+    case EXEC_OMP_DISPATCH:
+      if (code->ext.omp_clauses)
+       resolve_omp_clauses (code, code->ext.omp_clauses, ns);
+      resolve_omp_dispatch (code);
+      break;
     default:
       break;
     }
 }
 
-/* Resolve !$omp declare simd constructs in NS.  */
+/* Resolve !$omp declare {variant|simd} constructs in NS.
+   Note that !$omp declare target is resolved in resolve_symbol.  */
 
 void
-gfc_resolve_omp_declare_simd (gfc_namespace *ns)
+gfc_resolve_omp_declare (gfc_namespace *ns)
 {
   gfc_omp_declare_simd *ods;
-
   for (ods = ns->omp_declare_simd; ods; ods = ods->next)
     {
       if (ods->proc_name != NULL
@@ -12962,6 +13116,20 @@ gfc_resolve_omp_declare_simd (gfc_namespace *ns)
       if (ods->clauses)
        resolve_omp_clauses (NULL, ods->clauses, ns);
     }
+
+  gfc_omp_declare_variant *odv;
+  for (odv = ns->omp_declare_variant; odv; odv = odv->next)
+    for (gfc_omp_namelist *n = odv->adjust_args_list; n != NULL; n = n->next)
+      if (n->u.need_device_ptr
+         && (!gfc_resolve_expr (n->expr) || n->sym->ts.type != BT_DERIVED
+             || !n->sym->ts.u.derived->ts.is_iso_c
+             || (n->sym->ts.u.derived->intmod_sym_id != ISOCBINDING_PTR)))
+       {
+         gfc_error (
+           "argument list item %qs in %<need_device_ptr%> at %L must be of "
+           "TYPE(C_PTR)",
+           n->sym->name, &n->where);
+       }
 }
 
 struct omp_udr_callback_data
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index fa731cec0ca7..2af580784c9d 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -1071,6 +1071,7 @@ decode_omp_directive (void)
       matchds ("declare reduction", gfc_match_omp_declare_reduction,
               ST_OMP_DECLARE_REDUCTION);
       matcho ("depobj", gfc_match_omp_depobj, ST_OMP_DEPOBJ);
+      matcho ("dispatch", gfc_match_omp_dispatch, ST_OMP_DISPATCH);
       matchs ("distribute parallel do simd",
              gfc_match_omp_distribute_parallel_do_simd,
              ST_OMP_DISTRIBUTE_PARALLEL_DO_SIMD);
@@ -1086,6 +1087,7 @@ decode_omp_directive (void)
       matcho ("end allocators", gfc_match_omp_eos_error, 
ST_OMP_END_ALLOCATORS);
       matcho ("end atomic", gfc_match_omp_eos_error, ST_OMP_END_ATOMIC);
       matcho ("end critical", gfc_match_omp_end_critical, ST_OMP_END_CRITICAL);
+      matcho ("end dispatch", gfc_match_omp_end_nowait, ST_OMP_END_DISPATCH);
       matchs ("end distribute parallel do simd", gfc_match_omp_eos_error,
              ST_OMP_END_DISTRIBUTE_PARALLEL_DO_SIMD);
       matcho ("end distribute parallel do", gfc_match_omp_eos_error,
@@ -1959,7 +1961,7 @@ next_statement (void)
   case ST_OMP_LOOP: case ST_OMP_PARALLEL_LOOP: case ST_OMP_TEAMS_LOOP: \
   case ST_OMP_TARGET_PARALLEL_LOOP: case ST_OMP_TARGET_TEAMS_LOOP: \
   case ST_OMP_ALLOCATE_EXEC: case ST_OMP_ALLOCATORS: case ST_OMP_ASSUME: \
-  case ST_OMP_TILE: case ST_OMP_UNROLL: \
+  case ST_OMP_TILE: case ST_OMP_UNROLL: case ST_OMP_DISPATCH: \
   case ST_CRITICAL: \
   case ST_OACC_PARALLEL_LOOP: case ST_OACC_PARALLEL: case ST_OACC_KERNELS: \
   case ST_OACC_DATA: case ST_OACC_HOST_DATA: case ST_OACC_LOOP: \
@@ -2685,6 +2687,9 @@ gfc_ascii_statement (gfc_statement st, bool 
strip_sentinel)
     case ST_OMP_DEPOBJ:
       p = "!$OMP DEPOBJ";
       break;
+    case ST_OMP_DISPATCH:
+      p = "!$OMP DISPATCH";
+      break;
     case ST_OMP_DISTRIBUTE:
       p = "!$OMP DISTRIBUTE";
       break;
@@ -2715,6 +2720,9 @@ gfc_ascii_statement (gfc_statement st, bool 
strip_sentinel)
     case ST_OMP_END_CRITICAL:
       p = "!$OMP END CRITICAL";
       break;
+    case ST_OMP_END_DISPATCH:
+      p = "!$OMP END DISPATCH";
+      break;
     case ST_OMP_END_DISTRIBUTE:
       p = "!$OMP END DISTRIBUTE";
       break;
@@ -6383,6 +6391,46 @@ parse_omp_metadirective_body (gfc_statement omp_st)
   return next_st;
 }
 
+static gfc_statement
+parse_omp_dispatch (void)
+{
+  gfc_statement st;
+  gfc_code *cp, *np;
+  gfc_state_data s;
+
+  accept_statement (ST_OMP_DISPATCH);
+
+  cp = gfc_state_stack->tail;
+  push_state (&s, COMP_OMP_STRUCTURED_BLOCK, NULL);
+  np = new_level (cp);
+  np->op = cp->op;
+  np->block = NULL;
+
+  st = next_statement ();
+  if (st == ST_NONE)
+    return st;
+  if (st == ST_CALL || st == ST_ASSIGNMENT)
+    accept_statement (st);
+  else
+    {
+      gfc_error ("%<OMP DISPATCH%> directive must be followed by a procedure "
+                "call with optional assignment at %C");
+      reject_statement ();
+    }
+  pop_state ();
+  st = next_statement ();
+  if (st == ST_OMP_END_DISPATCH)
+    {
+      if (cp->ext.omp_clauses->nowait && new_st.ext.omp_bool)
+       gfc_error_now ("Duplicated NOWAIT clause on !$OMP DISPATCH and !$OMP "
+                      "END DISPATCH at %C");
+      cp->ext.omp_clauses->nowait |= new_st.ext.omp_bool;
+      accept_statement (st);
+      st = next_statement ();
+    }
+  return st;
+}
+
 /* Accept a series of executable statements.  We return the first
    statement that doesn't fit to the caller.  Any block statements are
    passed on to the correct handler, which usually passes the buck
@@ -6537,6 +6585,10 @@ parse_executable (gfc_statement st)
          st = parse_omp_oacc_atomic (true);
          continue;
 
+       case ST_OMP_DISPATCH:
+         st = parse_omp_dispatch ();
+         continue;
+
        case ST_OMP_METADIRECTIVE:
        case ST_OMP_BEGIN_METADIRECTIVE:
          st = parse_omp_metadirective_body (st);
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index ac81f10f1516..6db384d26d32 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -11403,6 +11403,7 @@ gfc_resolve_blocks (gfc_code *b, gfc_namespace *ns)
        case EXEC_OMP_ALLOCATORS:
        case EXEC_OMP_ASSUME:
        case EXEC_OMP_CRITICAL:
+       case EXEC_OMP_DISPATCH:
        case EXEC_OMP_DISTRIBUTE:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
@@ -13099,6 +13100,7 @@ start:
        case EXEC_OMP_CRITICAL:
        case EXEC_OMP_FLUSH:
        case EXEC_OMP_DEPOBJ:
+       case EXEC_OMP_DISPATCH:
        case EXEC_OMP_DISTRIBUTE:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
@@ -18443,7 +18445,7 @@ resolve_types (gfc_namespace *ns)
 
   gfc_traverse_ns (ns, gfc_verify_DTIO_procedures);
 
-  gfc_resolve_omp_declare_simd (ns);
+  gfc_resolve_omp_declare (ns);
 
   gfc_resolve_omp_udrs (ns->omp_udr_root);
 
diff --git a/gcc/fortran/st.cc b/gcc/fortran/st.cc
index 6db632b00c04..0aebafb6c6cd 100644
--- a/gcc/fortran/st.cc
+++ b/gcc/fortran/st.cc
@@ -222,6 +222,7 @@ gfc_free_statement (gfc_code *p)
     case EXEC_OMP_CANCELLATION_POINT:
     case EXEC_OMP_CRITICAL:
     case EXEC_OMP_DEPOBJ:
+    case EXEC_OMP_DISPATCH:
     case EXEC_OMP_DISTRIBUTE:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index 3d0431cac10b..2b68789bf085 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -2166,6 +2166,8 @@ get_proc_pointer_decl (gfc_symbol *sym)
   return decl;
 }
 
+static void
+create_function_arglist (gfc_symbol *sym);
 
 /* Get a basic decl for an external function.  */
 
@@ -2417,7 +2419,12 @@ module_sym:
       if (sym->formal_ns->omp_declare_simd)
        gfc_trans_omp_declare_simd (sym->formal_ns);
       if (flag_openmp)
-       gfc_trans_omp_declare_variant (sym->formal_ns);
+       {
+         // We need DECL_ARGUMENTS to put attributes on, in case some arguments
+         // need adjustment
+         create_function_arglist (sym->formal_ns->proc_name);
+         gfc_trans_omp_declare_variant (sym->formal_ns);
+       }
     }
 
   return fndecl;
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index d96dc17a05f4..d4465e40bbe7 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -6574,6 +6574,36 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
       omp_clauses = gfc_trans_add_clause (c, omp_clauses);
     }
 
+  if (clauses->novariants)
+    {
+      tree novariants_var;
+
+      gfc_init_se (&se, NULL);
+      gfc_conv_expr (&se, clauses->novariants);
+      gfc_add_block_to_block (block, &se.pre);
+      novariants_var = gfc_evaluate_now (se.expr, block);
+      gfc_add_block_to_block (block, &se.post);
+
+      c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_NOVARIANTS);
+      OMP_CLAUSE_NOVARIANTS_EXPR (c) = novariants_var;
+      omp_clauses = gfc_trans_add_clause (c, omp_clauses);
+    }
+
+  if (clauses->nocontext)
+    {
+      tree nocontext_var;
+
+      gfc_init_se (&se, NULL);
+      gfc_conv_expr (&se, clauses->nocontext);
+      gfc_add_block_to_block (block, &se.pre);
+      nocontext_var = gfc_evaluate_now (se.expr, block);
+      gfc_add_block_to_block (block, &se.post);
+
+      c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_NOCONTEXT);
+      OMP_CLAUSE_NOCONTEXT_EXPR (c) = nocontext_var;
+      omp_clauses = gfc_trans_add_clause (c, omp_clauses);
+    }
+
   if (clauses->num_threads)
     {
       tree num_threads;
@@ -8706,6 +8736,113 @@ gfc_trans_omp_depobj (gfc_code *code)
   return gfc_finish_block (&block);
 }
 
+/* Callback for walk_tree to find an OMP dispatch call and wrap it into an
+ * IFN_GOMP_DISPATCH.  */
+
+static tree
+replace_omp_dispatch_call (tree *tp, int *, void *decls_p)
+{
+  tree t = *tp;
+  tree decls = (tree) decls_p;
+  tree orig_fn_decl = TREE_PURPOSE (decls);
+  tree dup_fn_decl = TREE_VALUE (decls);
+  if (TREE_CODE (t) == CALL_EXPR)
+    {
+      if (CALL_EXPR_FN (t) == dup_fn_decl)
+       CALL_EXPR_FN (t) = orig_fn_decl;
+      else if (TREE_CODE (CALL_EXPR_FN (t)) == ADDR_EXPR
+              && TREE_OPERAND (CALL_EXPR_FN (t), 0) == dup_fn_decl)
+       TREE_OPERAND (CALL_EXPR_FN (t), 0) = dup_fn_decl;
+      else
+       return NULL_TREE;
+      *tp = build_call_expr_internal_loc (input_location, IFN_GOMP_DISPATCH,
+                                         TREE_TYPE (t), 1, t);
+      return *tp;
+    }
+
+  return NULL_TREE;
+}
+
+static tree
+gfc_trans_omp_dispatch (gfc_code *code)
+{
+  stmtblock_t block;
+  gfc_code *next = code->block->next;
+  // assume ill-formed "function dispatch structured
+  // block" have already been rejected by resolve_omp_dispatch
+  gcc_assert (next->op == EXEC_CALL || next->op == EXEC_ASSIGN);
+
+  // Make duplicate decl for dispatch function call to make it easy to spot
+  // after translation
+  gfc_symbol *orig_fn_sym;
+  gfc_expr *call_expr = next->op == EXEC_CALL ? next->expr1 : next->expr2;
+  if (call_expr != NULL) // function
+    {
+      if (call_expr->value.function.isym != NULL) // dig into convert 
intrinsics
+       call_expr = call_expr->value.function.actual->expr;
+      gcc_assert (call_expr->expr_type == EXPR_FUNCTION);
+      orig_fn_sym = call_expr->value.function.esym
+                     ? call_expr->value.function.esym
+                     : call_expr->symtree->n.sym;
+    }
+  else // subroutine
+    {
+      orig_fn_sym = next->resolved_sym;
+    }
+  if (!orig_fn_sym->backend_decl)
+    gfc_get_symbol_decl (orig_fn_sym);
+  gfc_symbol dup_fn_sym = *orig_fn_sym;
+  dup_fn_sym.backend_decl = copy_node (orig_fn_sym->backend_decl);
+  if (call_expr != NULL)
+    call_expr->value.function.esym = &dup_fn_sym;
+  else
+    next->resolved_sym = &dup_fn_sym;
+
+  tree body = gfc_trans_code (next);
+
+  // Walk the tree to find the duplicate decl, wrap IFN call and replace
+  // dup decl with original
+  tree fn_decls
+    = build_tree_list (orig_fn_sym->backend_decl, dup_fn_sym.backend_decl);
+  tree dispatch_call
+    = walk_tree (&body, replace_omp_dispatch_call, fn_decls, NULL);
+  gcc_assert (dispatch_call != NULL_TREE);
+
+  gfc_start_block (&block);
+  tree omp_clauses
+    = gfc_trans_omp_clauses (&block, code->ext.omp_clauses, code->loc);
+
+  // Extract depend clauses and create taskwait
+  tree depend_clauses = NULL_TREE;
+  tree *depend_clauses_ptr = &depend_clauses;
+  for (tree c = omp_clauses; 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, gfc_get_location (&code->loc));
+      gfc_add_expr_to_block (&block, stmt);
+    }
+
+  tree stmt = make_node (OMP_DISPATCH);
+  SET_EXPR_LOCATION (stmt, gfc_get_location (&code->loc));
+  TREE_TYPE (stmt) = void_type_node;
+  OMP_DISPATCH_BODY (stmt) = body;
+  OMP_DISPATCH_CLAUSES (stmt) = omp_clauses;
+
+  gfc_add_expr_to_block (&block, stmt);
+  return gfc_finish_block (&block);
+}
+
 static tree
 gfc_trans_omp_error (gfc_code *code)
 {
@@ -10822,6 +10959,8 @@ gfc_trans_omp_directive (gfc_code *code)
     case EXEC_OMP_UNROLL:
       return gfc_trans_omp_do (code, code->op, NULL, code->ext.omp_clauses,
                               NULL);
+    case EXEC_OMP_DISPATCH:
+      return gfc_trans_omp_dispatch (code);
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
     case EXEC_OMP_DISTRIBUTE_SIMD:
@@ -11151,6 +11290,18 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns)
                  variant_proc_sym = NULL;
                }
            }
+         if (odv->adjust_args_list != NULL
+             && omp_get_context_selector (set_selectors,
+                                          OMP_TRAIT_SET_CONSTRUCT,
+                                          OMP_TRAIT_CONSTRUCT_DISPATCH)
+                  == NULL_TREE)
+           {
+             gfc_error ("an %<adjust_args%> clause can only be specified if "
+                        "the %<dispatch%> selector of the construct "
+                        "selector set appears in the %<match%> clause at %L",
+                        &odv->where);
+             variant_proc_sym = NULL;
+           }
          if (variant_proc_sym != NULL)
            {
              gfc_set_sym_referenced (variant_proc_sym);
@@ -11168,6 +11319,52 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns)
                  DECL_ATTRIBUTES (base_fn_decl)
                    = tree_cons (id, build_tree_list (variant, set_selectors),
                                 DECL_ATTRIBUTES (base_fn_decl));
+
+                 // Handle adjust_args
+                 tree need_device_ptr_list = make_node (TREE_LIST);
+                 vec<gfc_symbol *> adjust_args_list = vNULL;
+                 for (gfc_omp_namelist *arg_list = odv->adjust_args_list;
+                      arg_list != NULL; arg_list = arg_list->next)
+                   {
+                     if (!arg_list->sym->attr.dummy)
+                       {
+                         gfc_error (
+                           "list item %qs at %L is not a dummy argument",
+                           arg_list->sym->name, &arg_list->where);
+                         continue;
+                       }
+                     if (adjust_args_list.contains (arg_list->sym))
+                       {
+                         gfc_error ("%qs at %L is specified more than once",
+                                    arg_list->sym->name, &arg_list->where);
+                         continue;
+                       }
+                     adjust_args_list.safe_push (arg_list->sym);
+                     if (arg_list->u.need_device_ptr)
+                       {
+                         int idx;
+                         gfc_formal_arglist *arg;
+                         for (arg = ns->proc_name->formal, idx = 0;
+                              arg != NULL; arg = arg->next, idx++)
+                           if (arg->sym == arg_list->sym)
+                             break;
+                         gcc_assert (arg != NULL);
+                         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
+                       }
+                   }
+
+                 DECL_ATTRIBUTES (variant) = 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));
                }
            }
        }
diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc
index 4d8e3ad05c99..cb5baec9cea0 100644
--- a/gcc/fortran/trans.cc
+++ b/gcc/fortran/trans.cc
@@ -2584,6 +2584,7 @@ trans_code (gfc_code * code, tree cond)
        case EXEC_OMP_CANCELLATION_POINT:
        case EXEC_OMP_CRITICAL:
        case EXEC_OMP_DEPOBJ:
+       case EXEC_OMP_DISPATCH:
        case EXEC_OMP_DISTRIBUTE:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
        case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index b5f54ad7cf1a..06995d9172d2 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -4075,7 +4075,7 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, 
fallback_t fallback)
   enum gimplify_status ret;
   int i, nargs;
   gcall *call;
-  bool builtin_va_start_p = false;
+  bool builtin_va_start_p = false, omp_dispatch_p = false;
   location_t loc = EXPR_LOCATION (*expr_p);
 
   gcc_assert (TREE_CODE (*expr_p) == CALL_EXPR);
@@ -4088,69 +4088,79 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, 
fallback_t fallback)
   /* Gimplify internal functions created in the FEs.  */
   if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
     {
-      if (want_value)
-       return GS_ALL_DONE;
-
-      nargs = call_expr_nargs (*expr_p);
       enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
-      auto_vec<tree> vargs (nargs);
-
-      if (ifn == IFN_ASSUME)
+      if (ifn == IFN_GOMP_DISPATCH)
        {
-         if (simple_condition_p (CALL_EXPR_ARG (*expr_p, 0)))
-           {
-             /* If the [[assume (cond)]]; condition is simple
-                enough and can be evaluated unconditionally
-                without side-effects, expand it as
-                if (!cond) __builtin_unreachable ();  */
-             tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
-             *expr_p = build3 (COND_EXPR, void_type_node,
-                               CALL_EXPR_ARG (*expr_p, 0), void_node,
-                               build_call_expr_loc (EXPR_LOCATION (*expr_p),
-                                                    fndecl, 0));
-             return GS_OK;
-           }
-         /* If not optimizing, ignore the assumptions.  */
-         if (!optimize || seen_error ())
+         gcc_assert (gimplify_omp_ctxp->code == OMP_DISPATCH);
+         *expr_p = CALL_EXPR_ARG (*expr_p, 0);
+         omp_dispatch_p = true;
+       }
+      else
+       {
+         if (want_value)
+           return GS_ALL_DONE;
+
+         nargs = call_expr_nargs (*expr_p);
+         auto_vec<tree> vargs (nargs);
+
+         if (ifn == IFN_ASSUME)
            {
+             if (simple_condition_p (CALL_EXPR_ARG (*expr_p, 0)))
+               {
+                 /* If the [[assume (cond)]]; condition is simple
+                    enough and can be evaluated unconditionally
+                    without side-effects, expand it as
+                    if (!cond) __builtin_unreachable ();  */
+                 tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
+                 *expr_p
+                   = build3 (COND_EXPR, void_type_node,
+                             CALL_EXPR_ARG (*expr_p, 0), void_node,
+                             build_call_expr_loc (EXPR_LOCATION (*expr_p),
+                                                  fndecl, 0));
+                 return GS_OK;
+               }
+             /* If not optimizing, ignore the assumptions.  */
+             if (!optimize || seen_error ())
+               {
+                 *expr_p = NULL_TREE;
+                 return GS_ALL_DONE;
+               }
+             /* Temporarily, until gimple lowering, transform
+                .ASSUME (cond);
+                into:
+                [[assume (guard)]]
+                {
+                  guard = cond;
+                }
+                such that gimple lowering can outline the condition into
+                a separate function easily.  */
+             tree guard = create_tmp_var (boolean_type_node);
+             *expr_p = build2 (MODIFY_EXPR, void_type_node, guard,
+                               gimple_boolify (CALL_EXPR_ARG (*expr_p, 0)));
+             *expr_p = build3 (BIND_EXPR, void_type_node, NULL, *expr_p, NULL);
+             push_gimplify_context ();
+             gimple_seq body = NULL;
+             gimple *g = gimplify_and_return_first (*expr_p, &body);
+             pop_gimplify_context (g);
+             g = gimple_build_assume (guard, body);
+             gimple_set_location (g, loc);
+             gimplify_seq_add_stmt (pre_p, g);
              *expr_p = NULL_TREE;
              return GS_ALL_DONE;
            }
-         /* Temporarily, until gimple lowering, transform
-            .ASSUME (cond);
-            into:
-            [[assume (guard)]]
-            {
-              guard = cond;
-            }
-            such that gimple lowering can outline the condition into
-            a separate function easily.  */
-         tree guard = create_tmp_var (boolean_type_node);
-         *expr_p = build2 (MODIFY_EXPR, void_type_node, guard,
-                           gimple_boolify (CALL_EXPR_ARG (*expr_p, 0)));
-         *expr_p = build3 (BIND_EXPR, void_type_node, NULL, *expr_p, NULL);
-         push_gimplify_context ();
-         gimple_seq body = NULL;
-         gimple *g = gimplify_and_return_first (*expr_p, &body);
-         pop_gimplify_context (g);
-         g = gimple_build_assume (guard, body);
-         gimple_set_location (g, loc);
-         gimplify_seq_add_stmt (pre_p, g);
-         *expr_p = NULL_TREE;
-         return GS_ALL_DONE;
-       }
 
-      for (i = 0; i < nargs; i++)
-       {
-         gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
-                       EXPR_LOCATION (*expr_p));
-         vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
-       }
+         for (i = 0; i < nargs; i++)
+           {
+             gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+                           EXPR_LOCATION (*expr_p));
+             vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+           }
 
-      gcall *call = gimple_build_call_internal_vec (ifn, vargs);
-      gimple_call_set_nothrow (call, TREE_NOTHROW (*expr_p));
-      gimplify_seq_add_stmt (pre_p, call);
-      return GS_ALL_DONE;
+         gcall *call = gimple_build_call_internal_vec (ifn, vargs);
+         gimple_call_set_nothrow (call, TREE_NOTHROW (*expr_p));
+         gimplify_seq_add_stmt (pre_p, call);
+         return GS_ALL_DONE;
+       }
     }
 
   /* This may be a call to a builtin function.
@@ -4415,21 +4425,36 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, 
fallback_t fallback)
                                  gimplify_seq_add_stmt (pre_p, call);
                                }
 
-                             // mapped_arg = omp_get_mapped_ptr (arg,
+                             // We want to emit the following statement:
+                             //   mapped_arg = omp_get_mapped_ptr (arg,
                              // device_num)
+                             // but arg has to be the actual pointer, not a
+                             // reference or a conversion expression.
+                             tree actual_ptr
+                               = (TREE_CODE (*arg_p) == ADDR_EXPR)
+                                   ? TREE_OPERAND (*arg_p, 0)
+                                   : *arg_p;
+                             if (TREE_CODE (actual_ptr) == NOP_EXPR
+                                 && TREE_CODE (
+                                      TREE_TYPE (TREE_OPERAND (actual_ptr, 0)))
+                                      == REFERENCE_TYPE)
+                               {
+                                 actual_ptr = TREE_OPERAND (actual_ptr, 0);
+                                 actual_ptr = build1 (INDIRECT_REF,
+                                                      TREE_TYPE (actual_ptr),
+                                                      actual_ptr);
+                               }
+                             gimplify_arg (&actual_ptr, pre_p, loc);
+                             gimplify_arg (&device_num, pre_p, loc);
                              tree fn = builtin_decl_explicit (
                                BUILT_IN_OMP_GET_MAPPED_PTR);
-                             gimplify_arg (arg_p, pre_p, loc);
-                             gimplify_arg (&device_num, pre_p, loc);
-                             call
-                               = gimple_build_call (fn, 2, *arg_p, device_num);
+                             call = gimple_build_call (fn, 2, actual_ptr,
+                                                       device_num);
                              tree mapped_arg = create_tmp_var (
                                gimple_call_return_type (call));
                              gimple_call_set_lhs (call, mapped_arg);
                              gimplify_seq_add_stmt (pre_p, call);
 
-                             *arg_p = mapped_arg;
-
                              // gimplify_call_expr might be called several
                              // times on the same call, which would result in
                              // duplicated calls to omp_get_default_device and
@@ -4440,9 +4465,19 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, 
fallback_t fallback)
                              tree c
                                = build_omp_clause (input_location,
                                                    OMP_CLAUSE_IS_DEVICE_PTR);
-                             OMP_CLAUSE_DECL (c) = *arg_p;
+                             OMP_CLAUSE_DECL (c) = mapped_arg;
                              OMP_CLAUSE_CHAIN (c) = gimplify_omp_ctxp->clauses;
                              gimplify_omp_ctxp->clauses = c;
+
+                             if (TREE_CODE (*arg_p) == ADDR_EXPR
+                                 || TREE_CODE (TREE_TYPE (actual_ptr))
+                                      == REFERENCE_TYPE)
+                               mapped_arg = build_fold_addr_expr (mapped_arg);
+                             else if (TREE_CODE (*arg_p) == NOP_EXPR)
+                               mapped_arg
+                                 = build1 (NOP_EXPR, TREE_TYPE (*arg_p),
+                                           mapped_arg);
+                             *arg_p = mapped_arg;
                            }
                        }
                    }
@@ -19363,10 +19398,7 @@ find_ifn_gomp_dispatch (tree *tp, int *, void *modify)
   tree t = *tp;
 
   if (TREE_CODE (t) == CALL_EXPR && CALL_EXPR_IFN (t) == IFN_GOMP_DISPATCH)
-    {
-      *tp = CALL_EXPR_ARG (t, 0);
-      return *(tree *) modify ? *(tree *) modify : *tp;
-    }
+    return *(tree *) modify ? *(tree *) modify : *tp;
 
   if (TREE_CODE (t) == MODIFY_EXPR)
     *(tree *) modify = *tp;
@@ -19432,12 +19464,7 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
        base_call_expr
          = walk_tree (&stmt, find_ifn_gomp_dispatch, &modify, NULL);
        if (base_call_expr != NULL_TREE)
-         {
-           tsi_link_before (&tsi, base_call_expr, TSI_CONTINUE_LINKING);
-           tsi_next (&tsi);
-           tsi_delink (&tsi);
-           break;
-         }
+         break;
       }
   else
     {
@@ -19453,6 +19480,7 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
       dst = TREE_OPERAND (base_call_expr, 0);
       base_call_expr = TREE_OPERAND (base_call_expr, 1);
     }
+
   while (TREE_CODE (base_call_expr) == FLOAT_EXPR
         || TREE_CODE (base_call_expr) == CONVERT_EXPR
         || TREE_CODE (base_call_expr) == COMPLEX_EXPR
@@ -19460,6 +19488,9 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
         || TREE_CODE (base_call_expr) == NOP_EXPR)
     base_call_expr = TREE_OPERAND (base_call_expr, 0);
 
+  gcc_assert (CALL_EXPR_IFN (base_call_expr) == IFN_GOMP_DISPATCH);
+  base_call_expr = CALL_EXPR_ARG (base_call_expr, 0);
+
   tree base_fndecl = get_callee_fndecl (base_call_expr);
   if (base_fndecl != NULL_TREE)
     {
@@ -19530,6 +19561,11 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
 
              gimplify_seq_add_stmt (&body, gimple_build_label (base_label));
              tree base_call_expr2 = copy_node (base_call_expr);
+             base_call_expr2
+               = build_call_expr_internal_loc (EXPR_LOCATION (base_call_expr2),
+                                               IFN_GOMP_DISPATCH,
+                                               TREE_TYPE (base_call_expr2), 1,
+                                               base_call_expr2);
              if (TREE_CODE (dispatch_body) == MODIFY_EXPR)
                {
                  base_call_expr2 = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst,
@@ -19557,6 +19593,9 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
              gimplify_seq_add_stmt (&body,
                                     gimple_build_label (variant1_label));
              tree variant_call_expr = copy_node (base_call_expr);
+             variant_call_expr = build_call_expr_internal_loc (
+               EXPR_LOCATION (variant_call_expr), IFN_GOMP_DISPATCH,
+               TREE_TYPE (variant_call_expr), 1, variant_call_expr);
              if (TREE_CODE (dispatch_body) == MODIFY_EXPR)
                {
                  variant_call_expr = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst,
@@ -19571,6 +19610,11 @@ gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p)
            }
 
          tree variant_call_expr = base_call_expr;
+         variant_call_expr
+           = build_call_expr_internal_loc (EXPR_LOCATION (variant_call_expr),
+                                           IFN_GOMP_DISPATCH,
+                                           TREE_TYPE (variant_call_expr), 1,
+                                           variant_call_expr);
          if (TREE_CODE (dispatch_body) == MODIFY_EXPR)
            {
              variant_call_expr
diff --git a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp
index ccec4308bf22..70a249e0ea00 100644
--- a/gcc/testsuite/ChangeLog.omp
+++ b/gcc/testsuite/ChangeLog.omp
@@ -1,3 +1,32 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * g++.dg/gomp/dispatch-3.C: Update scan dumps.
+       * gfortran.dg/gomp/declare-variant-2.f90: Update dg-error.
+       * gfortran.dg/gomp/adjust-args-1.f90: New test.
+       * gfortran.dg/gomp/adjust-args-2.f90: New test.
+       * gfortran.dg/gomp/adjust-args-2a.f90: New test.
+       * gfortran.dg/gomp/adjust-args-3.f90: New test.
+       * gfortran.dg/gomp/adjust-args-4.f90: New test.
+       * gfortran.dg/gomp/adjust-args-5.f90: New test.
+       * gfortran.dg/gomp/adjust-args-6.f90: New test.
+       * gfortran.dg/gomp/adjust-args-7.f90: New test.
+       * gfortran.dg/gomp/adjust-args-8.f90: New test.
+       * gfortran.dg/gomp/adjust-args-9.f90: New test.
+       * gfortran.dg/gomp/dispatch-1.f90: New test.
+       * gfortran.dg/gomp/dispatch-2.f90: New test.
+       * gfortran.dg/gomp/dispatch-3.f90: New test.
+       * gfortran.dg/gomp/dispatch-4.f90: New test.
+       * gfortran.dg/gomp/dispatch-5.f90: New test.
+       * gfortran.dg/gomp/dispatch-6.f90: New test.
+       * gfortran.dg/gomp/dispatch-7.f90: New test.
+       * gfortran.dg/gomp/dispatch-8.f90: New test.
+       * gfortran.dg/gomp/dispatch-9.f90: New test.
+       * gfortran.dg/gomp/dispatch-9a.f90: New test.
+       * gfortran.dg/gomp/dispatch-10.f90: New test.
+
 2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-3.C 
b/gcc/testsuite/g++.dg/gomp/dispatch-3.C
index 03fd7dc6f6ce..90e5c04c3f0c 100644
--- a/gcc/testsuite/g++.dg/gomp/dispatch-3.C
+++ b/gcc/testsuite/g++.dg/gomp/dispatch-3.C
@@ -12,6 +12,6 @@ void g(int *x)
   //        ^ only this call to f is a dispatch call
 }
 
-/* { dg-final { scan-tree-dump "\.GOMP_DISPATCH \\(\\*f \\(\\*f \\(2\\)\\)\\)" 
"original" } } */
+/* { dg-final { scan-tree-dump "\\*\.GOMP_DISPATCH \\(f \\(\\*f \\(2\\)\\)\\)" 
"original" } } */
 /* { dg-final { scan-tree-dump-times "\.GOMP_DISPATCH" 1 "original" } } */
 /* { dg-final { scan-tree-dump-not "\.GOMP_DISPATCH" "gimple" } } */
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90
new file mode 100644
index 000000000000..d9f2a670a683
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90
@@ -0,0 +1,45 @@
+! Test parsing of OMP clause adjust_args
+! { dg-do compile }
+
+module main
+   use iso_c_binding, only: c_ptr, c_funptr
+   implicit none
+   integer :: b
+   interface
+      integer function f0 (a)
+         import c_ptr
+         type(c_ptr), intent(inout) :: a
+      end function
+      integer function g (a)
+         import c_ptr
+         type(c_ptr), intent(inout) :: a
+      end function
+      integer function f1 (i)
+         integer, intent(in) :: i
+      end function
+
+      integer function f3 (a)
+         import c_ptr
+         type(c_ptr), intent(inout) :: a
+         !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(other: a) ! { dg-error "expected 'nothing' or 'need_device_ptr' at .1." }
+      end function
+      integer function f4 (a)
+         import c_ptr
+         type(c_ptr), intent(inout) :: a
+         !$omp declare variant (f0) adjust_args (nothing: a) ! { dg-error "an 
'adjust_args' clause at .1. can only be specified if the 'dispatch' selector of 
the construct selector set appears in the 'match' clause" }
+      end function
+      integer function f5 (i)
+         integer, intent(inout) :: i
+         !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
() ! { dg-error "expected 'nothing' or 'need_device_ptr' at .1." }
+      end function
+      integer function f6 (i)
+         integer, intent(inout) :: i
+         !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(nothing) ! { dg-error "expected argument list at .1." }
+      end function
+      integer function f7 (i)
+         integer, intent(inout) :: i
+         !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(nothing:) ! { dg-error "expected argument list at .1." }
+      end function
+
+   end interface
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-2.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-2.f90
new file mode 100644
index 000000000000..c65a4839ca5d
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-2.f90
@@ -0,0 +1,18 @@
+! Test resolution of OMP clause adjust_args
+! { dg-do compile } 
+
+module main
+  implicit none
+interface
+subroutine f1 (i)
+  integer, intent(inout) :: i
+end subroutine
+end interface
+contains
+
+  subroutine f3 (i)
+    integer, intent(inout) :: i
+    !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(nothing: z) ! { dg-error "Symbol 'z' at .1. has no IMPLICIT type" }
+  end subroutine
+  
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-2a.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-2a.f90
new file mode 100644
index 000000000000..9a32d2b7d92f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-2a.f90
@@ -0,0 +1,36 @@
+! Test resolution of OMP clause adjust_args
+! { dg-do compile } 
+
+module main
+  use iso_c_binding, only: c_ptr, c_funptr
+  implicit none
+  interface
+    subroutine f1 (i)
+      integer, intent(inout) :: i
+    end subroutine
+    subroutine h (a)
+      import c_funptr
+      type(c_funptr), intent(inout) :: a
+    end subroutine
+  end interface
+contains
+
+  subroutine f9 (i)
+    integer, intent(inout) :: i
+    !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(need_device_ptr: i) ! { dg-error "argument list item 'i' in 'need_device_ptr' 
at .1. must be of TYPE.C_PTR." }
+  end subroutine
+  subroutine f13 (a)
+    type(c_funptr), intent(inout) :: a
+    !$omp declare variant (h) match (construct={dispatch}) adjust_args 
(need_device_ptr: a) ! { dg-error "argument list item 'a' in 'need_device_ptr' 
at .1. must be of TYPE.C_PTR." }
+  end subroutine
+
+  subroutine test
+    integer :: i
+    type(c_funptr) :: a
+    !$omp dispatch
+    call f9(i)
+    !$omp dispatch
+    call f13(a)
+  end subroutine
+  
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90
new file mode 100644
index 000000000000..291bb47aaa25
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90
@@ -0,0 +1,27 @@
+! Test translation of OMP clause adjust_args
+! { dg-do compile }
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  type(c_ptr) :: b
+  
+contains
+  subroutine base2 (a)
+    type(c_ptr), intent(inout) :: a
+    !$omp declare variant (variant2) match (construct={parallel}) adjust_args 
(need_device_ptr: 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 at .1." }
+  end subroutine
+  subroutine base3 (a)
+    type(c_ptr), intent(inout) :: a
+    !$omp declare variant (variant2) match (construct={dispatch}) adjust_args 
(need_device_ptr: a) adjust_args (need_device_ptr: a) ! { dg-error "'a' at .1. 
is specified more than once" }
+  end subroutine
+  subroutine base4 (a)
+    type(c_ptr), intent(inout) :: a
+    !$omp declare variant (variant2) match (construct={dispatch}) adjust_args 
(need_device_ptr: b) ! { dg-error "list item 'b' at .1. is not a dummy 
argument" }
+  end subroutine
+
+  subroutine variant2 (a)
+    type(c_ptr), intent(inout) :: a
+  end subroutine
+
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-4.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-4.f90
new file mode 100644
index 000000000000..2f44c0026dbf
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-4.f90
@@ -0,0 +1,58 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+
+  type :: struct
+    integer :: a
+    real :: b
+  end type
+
+  interface
+    integer function f(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+    end function
+    integer function f0(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+      !$omp  declare variant (f) match (construct={dispatch}) &
+      !$omp&         adjust_args (nothing: a) adjust_args (need_device_ptr: b, 
c)
+    end function
+    integer function f1(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+      !$omp declare variant (f) match (construct={dispatch}) &
+      !$omp&        adjust_args (nothing: a) adjust_args (need_device_ptr: b) 
adjust_args (need_device_ptr: c)
+    end function
+  end interface
+
+contains
+subroutine test
+  integer :: a
+  type(c_ptr) :: b
+  type(c_ptr) :: c(2)
+  type(struct) :: s
+
+  s%a = f0 (a, b, c)
+  !$omp dispatch
+  s%a = f0 (a, b, c)
+
+  s%b = f1 (a, b, c)
+  !$omp dispatch
+  s%b = f1 (a, b, c)
+
+end subroutine
+end module
+
+! { dg-final { scan-tree-dump-times "__builtin_omp_get_default_device \\(\\);" 
2 "gimple" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = __builtin_omp_get_mapped_ptr 
\\(parm\.\[0-9]+, D\.\[0-9]+\\);" 2 "gimple" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = __builtin_omp_get_mapped_ptr 
\\(b\.\[0-9]+, D\.\[0-9]+\\);" 2 "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-5.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-5.f90
new file mode 100644
index 000000000000..2f44c0026dbf
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-5.f90
@@ -0,0 +1,58 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+
+  type :: struct
+    integer :: a
+    real :: b
+  end type
+
+  interface
+    integer function f(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+    end function
+    integer function f0(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+      !$omp  declare variant (f) match (construct={dispatch}) &
+      !$omp&         adjust_args (nothing: a) adjust_args (need_device_ptr: b, 
c)
+    end function
+    integer function f1(a, b, c)
+      import c_ptr
+      integer, intent(in) :: a
+      type(c_ptr), intent(inout) :: b
+      type(c_ptr), intent(out) :: c(:)
+      !$omp declare variant (f) match (construct={dispatch}) &
+      !$omp&        adjust_args (nothing: a) adjust_args (need_device_ptr: b) 
adjust_args (need_device_ptr: c)
+    end function
+  end interface
+
+contains
+subroutine test
+  integer :: a
+  type(c_ptr) :: b
+  type(c_ptr) :: c(2)
+  type(struct) :: s
+
+  s%a = f0 (a, b, c)
+  !$omp dispatch
+  s%a = f0 (a, b, c)
+
+  s%b = f1 (a, b, c)
+  !$omp dispatch
+  s%b = f1 (a, b, c)
+
+end subroutine
+end module
+
+! { dg-final { scan-tree-dump-times "__builtin_omp_get_default_device \\(\\);" 
2 "gimple" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = __builtin_omp_get_mapped_ptr 
\\(parm\.\[0-9]+, D\.\[0-9]+\\);" 2 "gimple" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = __builtin_omp_get_mapped_ptr 
\\(b\.\[0-9]+, D\.\[0-9]+\\);" 2 "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-6.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-6.f90
new file mode 100644
index 000000000000..1410fb8bae0f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-6.f90
@@ -0,0 +1,16 @@
+! { dg-do compile }
+
+! Check that duplicate adjust_args list items are reported
+
+module m
+  use iso_c_binding
+  implicit none (type, external)
+contains
+  subroutine foo(x,y)
+    type(C_ptr), value :: x, y
+    !$omp declare variant(bar) match ( construct = { dispatch } ) 
adjust_args(nothing : x ,y ) adjust_args(need_device_ptr : y )   !{ dg-error 
"'y' at .1. is specified more than once" }
+  end
+  subroutine bar(a,b)
+    type(C_ptr), value :: a, b  ! OK
+  end
+end 
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-7.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-7.f90
new file mode 100644
index 000000000000..92e8a1f8fd4e
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-7.f90
@@ -0,0 +1,17 @@
+! { dg-do compile }
+
+! Ensure that type(C_ptr) check is done at resolve rather than parse time
+
+
+module m
+  use iso_c_binding
+  implicit none (type, external)
+contains
+  subroutine foo(x,y)
+    !$omp declare variant(bar) match ( construct = { dispatch } ) 
adjust_args(nothing : x ) adjust_args(need_device_ptr : y )
+    type(C_ptr), value :: x, y
+  end
+  subroutine bar(a,b)
+    type(C_ptr), value :: a, b
+  end
+end 
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-8.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-8.f90
new file mode 100644
index 000000000000..af47e2175eb5
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-8.f90
@@ -0,0 +1,51 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+! Check that __builtin_omp_get_default_device and __builtin_omp_get_mapped_ptr
+! are called with the right arguments depending on is_device_ptr. By default,
+! Fortran passes arguments by reference, so it is important to check that:
+! (1) __builtin_omp_get_mapped_ptr arguments are the actual pointers; and
+! (2) f1 arguments are references to pointers.
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  interface
+  subroutine f1 (p, p2)
+    import :: c_ptr
+    type(c_ptr), intent(out) :: p
+    type(c_ptr), intent(in) :: p2
+  end subroutine
+  subroutine f2 (p, p2)
+    import :: c_ptr
+    type(c_ptr), intent(out) :: p
+    type(c_ptr), intent(in) :: p2
+  !$omp declare variant (f1) match (construct={dispatch}) adjust_args 
(need_device_ptr: p, p2)
+  end subroutine
+  end interface
+  contains
+  
+  subroutine test ()
+    type(c_ptr) :: p, p2
+
+  ! Note there are multiple matches because every variable capturing matches 
in addition,
+  ! i.e. scan-tree-dump-times = 1 plus number of captures used for backward 
references.
+  !
+  ! For the first scan-tree-dump, on some targets the 
__builtin_omp_get_mapped_ptr get
+  ! swapped.
+
+  !$omp dispatch
+    ! { dg-final { scan-tree-dump-times "#pragma omp dispatch.*(D\.\[0-9]+) = 
__builtin_omp_get_default_device \\(\\);\[ \t\n\r]*(p2?\.\[0-9]) = p2?;\[ 
\t\n\r]*(D\.\[0-9]+) = __builtin_omp_get_mapped_ptr \\(\\2, \\1\\);\[ 
\t\n\r]*(D\.\[0-9]+) = \\3;\[ \t\n\r]*(p2?\.\[0-9]) = p2?;\[ 
\t\n\r]*(D\.\[0-9]+) = __builtin_omp_get_mapped_ptr \\(\\5, \\1\\);\[ 
\t\n\r]*(D\.\[0-9]+) = \\6;\[ \t\n\r]*f1 \\((?:&\\7, &\\4|&\\4, &\\7)\\);" 8 
"gimple" } }
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p)
+    ! { dg-final { scan-tree-dump-times "#pragma omp dispatch 
is_device_ptr\\(p\\).*(D\.\[0-9]+) = __builtin_omp_get_default_device \\(\\);\[ 
\t\n\r]*(p2\.\[0-9]) = p2;\[ \t\n\r]*(D\.\[0-9]+) = 
__builtin_omp_get_mapped_ptr \\(\\2, \\1\\);\[ \t\n\r]*(D\.\[0-9]+) = \\3;\[ 
\t\n\r]*f1 \\(&p, &\\4\\);" 5 "gimple" } }
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p2)
+    ! { dg-final { scan-tree-dump-times "#pragma omp dispatch 
is_device_ptr\\(p2\\).*(D\.\[0-9]+) = __builtin_omp_get_default_device 
\\(\\);\[ \t\n\r]*(p\.\[0-9]) = p;\[ \t\n\r]*(D\.\[0-9]+) = 
__builtin_omp_get_mapped_ptr \\(\\2, \\1\\);\[ \t\n\r]*(D\.\[0-9]+) = \\3;\[ 
\t\n\r]*f1 \\(&\\4, &p2\\);" 5 "gimple" } }
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p, p2)
+    ! { dg-final { scan-tree-dump-times "#pragma omp dispatch 
is_device_ptr\\(p\\) is_device_ptr\\(p2\\)\[ \t\n\r\{]*p = {CLOBBER};\[ 
\t\n\r]*f1 \\(&p, &p2\\);" 1 "gimple" } }
+    call f2 (p, p2)
+  end subroutine
+end module
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/adjust-args-9.f90 
b/gcc/testsuite/gfortran.dg/gomp/adjust-args-9.f90
new file mode 100644
index 000000000000..02fe54ece700
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-9.f90
@@ -0,0 +1,25 @@
+! { dg-do compile }
+
+! Check that a missing call does not cause a segfault
+
+module m
+use iso_c_binding
+implicit none(type,external)
+contains
+subroutine f(x,y,z)
+  type(c_ptr) :: x,y,z
+end
+subroutine g(x,y,z)
+  type(c_ptr) :: x,y,z
+  !$omp declare variant(f) adjust_args(need_device_ptr: x,y) 
adjust_args(nothing : z,x) match(construct={dispatch})
+end
+end 
+
+use m
+implicit none(type,external)
+  type(c_ptr) :: a,b,c
+  !$omp dispatch
+     g(a,b,c) ! { dg-error "'g' at .1. is not a variable" }
+    ! Should be: call g(a,b,c)
+end ! { dg-error "Unexpected END statement at .1." }
+! { dg-error "Unexpected end of file in .*" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90 
b/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90
index 7fc5071feff6..62d2cb96fac3 100644
--- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90
@@ -18,10 +18,10 @@ contains
     !$omp declare variant match(user={condition(.false.)})     ! { dg-error 
"expected '\\(' at .1." }
   end subroutine
   subroutine f6 ()
-    !$omp declare variant (f1) ! { dg-error "expected 'match' at .1." }
+    !$omp declare variant (f1) ! { dg-error "expected 'match' or 'adjust_args' 
at .1." }
   end subroutine
   subroutine f7 ()
-    !$omp declare variant (f1) simd    ! { dg-error "expected 'match' at .1." }
+    !$omp declare variant (f1) simd    ! { dg-error "expected 'match' or 
'adjust_args' at .1." }
   end subroutine
   subroutine f8 ()
     !$omp declare variant (f1) match   ! { dg-error "expected '\\(' at .1." }
@@ -183,7 +183,7 @@ contains
     !$omp declare variant (f1) match(construct={requires})     ! { dg-warning 
"unknown selector 'requires' for context selector set 'construct' at .1." }
   end subroutine
   subroutine f75 ()
-    !$omp declare variant (f1),match(construct={parallel})     ! { dg-error 
"expected 'match' at .1." }
+    !$omp declare variant (f1),match(construct={parallel})     ! { dg-error 
"expected 'match' or 'adjust_args' at .1." }
   end subroutine
   subroutine f76 ()
     !$omp declare variant (f1) 
match(implementation={atomic_default_mem_order("relaxed")})     ! { dg-error 
"expected identifier at .1." }
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90
new file mode 100644
index 000000000000..12c309041317
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90
@@ -0,0 +1,77 @@
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  contains
+  
+  subroutine f1 ()
+    integer :: a, b, arr(10)
+    real :: x
+    complex :: c
+    character :: ch
+    logical :: bool
+    type :: struct
+      integer :: a
+      real :: b
+    end type
+    type(struct) :: s
+    type(c_ptr) :: p
+    
+    interface
+    subroutine f0 (a, c, bool, s)
+      import :: struct
+      integer, intent(in) :: a
+      complex, intent(out) :: c
+      logical, intent(inout) :: bool
+      type(struct) :: s
+    end subroutine
+    integer function f2 (arr, x, ch, b)
+      integer, intent(inout) :: arr(:)
+      real, intent(in) :: x
+      character, intent(out) :: ch
+      real :: b
+    end function
+    subroutine f3 (p)
+      import :: c_ptr
+      type(c_ptr) :: p
+    end subroutine
+    integer function f4 ()
+    end function
+    end interface
+
+    !$omp dispatch
+      b = f2(arr, x, ch, s%b)
+    !$omp dispatch
+      c = f2(arr(:5), x * 2.4, ch, s%b)
+    !$omp dispatch
+      arr(1) = f2(arr, x, ch, s%b)
+    !$omp dispatch
+      s%a = f2(arr, x, ch, s%b)
+    !$omp dispatch
+      x = f2(arr, x, ch, s%b)
+    !$omp dispatch
+      call f0(a, c, bool, s)
+    !$omp dispatch
+      call f0(f4(), c, bool, s)
+      
+    !$omp dispatch nocontext(.TRUE.)
+      call f0(a, c, bool, s)
+    !$omp dispatch nocontext(arr(2) < 10)
+      call f0(a, c, bool, s)
+    !$omp dispatch novariants(.FALSE.)
+      call f0(a, c, bool, s)
+    !$omp dispatch novariants(bool)
+      call f0(a, c, bool, s)
+    !$omp dispatch nowait
+      call f0(a, c, bool, s)
+    !$omp dispatch device(arr(9))
+      call f0(a, c, bool, s)
+    !$omp dispatch device(a + a)
+      call f0(a, c, bool, s)
+    !$omp dispatch device(-25373654)
+      call f0(a, c, bool, s)
+    !$omp dispatch is_device_ptr(p)
+      call f3(p)
+    !$omp dispatch depend(in: a, c, bool) depend(inout: s, arr(:3))
+      call f0(a, c, bool, s)
+  end subroutine
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-10.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-10.f90
new file mode 100644
index 000000000000..391879c7c243
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-10.f90
@@ -0,0 +1,21 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
+
+! Check that the right call to f is wrapped in a GOMP_DISPATCH internal 
function
+! before translation and that it is stripped during gimplification.
+
+subroutine g(x,f)
+  interface
+    integer function f(y)
+       allocatable :: f
+       integer :: y
+    end
+  end interface
+  integer, allocatable :: X(:)
+  
+  !$omp dispatch
+    x(f(3)) = f(f(2))
+end
+
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = \.GOMP_DISPATCH \\(f 
\\(&D\.\[0-9]+\\)\\);" 1 "original" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = f \\(&D\.\[0-9]+\\);" 1 
"gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90
new file mode 100644
index 000000000000..d2d555b59325
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90
@@ -0,0 +1,79 @@
+module main
+  use iso_c_binding, only: c_funptr
+  implicit none
+  contains
+  
+  subroutine f1 ()
+    integer :: a, b, arr(10)
+    real :: x
+    complex :: c
+    character :: ch
+    logical :: bool
+    type :: struct
+      integer :: a
+      real :: b
+    end type
+    type(struct) :: s
+    type(c_funptr) :: p
+    
+    interface
+    subroutine f0 (a, c, bool, s)
+      import :: struct
+      integer, intent(in) :: a
+      complex, intent(out) :: c
+      logical, intent(inout) :: bool
+      type(struct) :: s
+    end subroutine
+    integer function f2 (arr, x, ch, b)
+      integer, intent(inout) :: arr(:)
+      real, intent(in) :: x
+      character, intent(out) :: ch
+      real :: b
+    end function
+    end interface
+    procedure(f0), pointer:: fp => NULL()
+
+    !$omp dispatch              !{ dg-error "'OMP DISPATCH' directive at .1. 
must be followed by a procedure call with optional assignment" }
+50    b = f2(arr, x, ch, s%b) + a
+    !$omp dispatch              !{ dg-error "'OMP DISPATCH' directive at .1. 
must be followed by a procedure call with optional assignment" }
+      a = b
+    !$omp dispatch              !{ dg-error "'OMP DISPATCH' directive at .1. 
must be followed by a procedure call with optional assignment" }
+      b = Not (2)
+    !$omp dispatch
+    !$omp threadprivate(a)     !{ dg-error "'OMP DISPATCH' directive must be 
followed by a procedure call with optional assignment at .1." } 
+      a = f2(arr, x, ch, s%b)
+    !$omp dispatch
+      print *, 'This is not allowed here.'  !{ dg-error "'OMP DISPATCH' 
directive must be followed by a procedure call with optional assignment at .1." 
} 
+    !$omp dispatch
+      goto 50                   !{ dg-error "'OMP DISPATCH' directive must be 
followed by a procedure call with optional assignment at .1." } 
+    !$omp dispatch              !{ dg-error "'OMP DISPATCH' directive at .1. 
cannot be followed by a procedure pointer" }
+      call fp(a, c, bool, s)
+      
+    !$omp dispatch nocontext(s) !{ dg-error "NOCONTEXT clause at .1. requires 
a scalar LOGICAL expression" } 
+      call f0(a, c, bool, s)
+    !$omp dispatch nocontext(a, b) !{ dg-error "Invalid expression after 
'nocontext.' at .1." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch nocontext(a) nocontext(b) !{ dg-error "Duplicated 
'nocontext' clause at .1." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch novariants(s) !{ dg-error "NOVARIANTS clause at .1. 
requires a scalar LOGICAL expression" } 
+      call f0(a, c, bool, s)
+    !$omp dispatch novariants(a, b) !{ dg-error "Invalid expression after 
'novariants.' at .1." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch novariants(a) novariants(b) !{ dg-error "Duplicated 
'novariants' clause at .1." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch nowait nowait !{ dg-error "Duplicated 'nowait' clause at 
.1." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch device(x) !{ dg-error "DEVICE clause at .1. requires a 
scalar INTEGER expression" } 
+      call f0(a, c, bool, s)
+    !$omp dispatch device(arr) !{ dg-error "DEVICE clause at .1. requires a 
scalar INTEGER expression" } 
+      call f0(a, c, bool, s)
+    !$omp dispatch is_device_ptr(x) !{ dg-error "List item 'x' in 
IS_DEVICE_PTR clause at .1. must be of TYPE.C_PTR." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch is_device_ptr(arr) !{ dg-error "List item 'arr' in 
IS_DEVICE_PTR clause at .1. must be of TYPE.C_PTR." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch is_device_ptr(p) !{ dg-error "List item 'p' in 
IS_DEVICE_PTR clause at .1. must be of TYPE.C_PTR." } 
+      call f0(a, c, bool, s)
+    !$omp dispatch depend(inout: f0) !{ dg-error "Object 'f0' is not a 
variable at .1." } 
+      call f0(a, c, bool, s)
+  end subroutine
+end module
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-3.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-3.f90
new file mode 100644
index 000000000000..84590fd883ab
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-3.f90
@@ -0,0 +1,39 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module main
+  implicit none
+    interface
+      integer function f0 ()
+      end function
+
+      integer function f1 ()
+      end function
+
+      integer function f2 ()
+        !$omp declare variant (f0) match (construct={dispatch})
+        !$omp declare variant (f1) match (implementation={vendor(gnu)})
+      end function
+    end interface
+  contains
+  
+  integer function test ()
+    integer :: a
+
+    !$omp dispatch
+      a = f2 ()
+    !$omp dispatch novariants(.TRUE.)
+      a = f2 ()
+    !$omp dispatch novariants(.FALSE.)
+      a = f2 ()
+    !$omp dispatch nocontext(.TRUE.)
+      a = f2 ()
+    !$omp dispatch nocontext(.FALSE.)
+      a = f2 ()
+  end function
+end module
+
+
+! { dg-final { scan-tree-dump-times "a = f0 \\\(\\\);" 3 "gimple" } }
+! { dg-final { scan-tree-dump-times "a = f1 \\\(\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "a = f2 \\\(\\\);" 1 "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-4.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-4.f90
new file mode 100644
index 000000000000..edcd799a718a
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-4.f90
@@ -0,0 +1,19 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module main
+  implicit none
+    interface
+      subroutine f2 ()
+      end subroutine
+    end interface
+  contains
+  
+  subroutine test ()
+  !$omp dispatch  ! { dg-final { scan-tree-dump-not "#pragma omp task" 
"gimple" } }
+    call f2 ()
+  !$omp dispatch nowait ! { dg-final { scan-tree-dump-not "nowait" "gimple" } }
+    call f2 ()
+  end subroutine
+end module
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90
new file mode 100644
index 000000000000..f6fead0dae2e
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90
@@ -0,0 +1,25 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module main
+  implicit none
+    interface
+      subroutine f2 (a)
+        integer, intent(in) :: a
+      end subroutine
+    end interface
+  contains
+  
+  subroutine test ()
+    integer :: a
+
+  !$omp dispatch device(-25373654)
+    ! { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device 
\\(-25373654\\);" 1 "gimple" } } 
+    call f2 (a)
+  !$omp dispatch device(a + a)
+    ! { dg-final { scan-tree-dump-times "(D\.\[0-9]+) = a.\[0-9_]+ \\* 
2;.*#pragma omp dispatch.*__builtin_omp_set_default_device \\(\\1\\);.*f2 
\\(&a\\)" 2 "gimple" } }
+    call f2 (a)
+  end subroutine
+end module
+
+! { dg-final { scan-tree-dump-times "(D\.\[0-9]+) = 
__builtin_omp_get_default_device \\(\\);.*__builtin_omp_set_default_device 
\\(\\1\\);" 4 "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90
new file mode 100644
index 000000000000..98e200f37571
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90
@@ -0,0 +1,22 @@
+! { dg-do compile }
+
+! Check for proper error recovery in resolve_omp_dispatch
+
+module m
+  use iso_c_binding
+  implicit none (type, external)
+contains
+  subroutine foo(x,y)
+    !$omp declare variant(bar) match ( construct = { dispatch } )
+    type(C_ptr), value :: x, y
+  end
+  subroutine bar(a,b)
+    type(C_ptr), value :: a, b
+  end
+end
+
+use m
+  integer :: y, z
+  !$omp dispatch device(5)
+    call foo(c_loc(y),c_loc(z)) !{ dg-error "Argument X at .1. to C_LOC shall 
have either the POINTER or the TARGET attribute" }
+end 
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90
new file mode 100644
index 000000000000..6b40af6f315b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90
@@ -0,0 +1,26 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-ompexp" }
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  interface
+  subroutine f2 (p)
+    import :: c_ptr
+    type(c_ptr), intent(out) :: p
+  end subroutine
+  end interface
+  contains
+  
+  subroutine test ()
+    type(c_ptr) :: p
+
+  !$omp dispatch
+    ! { dg-final { scan-tree-dump-not "__builtin_GOMP_task " "ompexp" } }
+    call f2 (p)
+  !$omp dispatch depend(inout: p)
+    ! { dg-final { scan-tree-dump-times "(D\.\[0-9]+)\\\[2] = &p;\[ 
\n]*__builtin_GOMP_taskwait_depend \\(&\\1\\);" 2 "ompexp" } }
+    call f2 (p)
+  end subroutine
+end module
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90
new file mode 100644
index 000000000000..e8c3a3513656
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90
@@ -0,0 +1,36 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+! Check that, when the novariants or nocontext clauses cannot be evaluated at
+! compile time, both variants are emitted.
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  interface
+  integer function f0 ()
+  end function
+  integer function f1 ()
+  end function
+  integer function f2 ()
+    !$omp declare variant (f0) match (construct={dispatch})
+    !$omp declare variant (f1) match (implementation={vendor(gnu)})
+  end function
+  end interface
+  contains
+  
+  subroutine test ()
+    integer :: a, n
+
+  !$omp dispatch novariants(n < 1024) nocontext(n > 1024)
+    a = f2 ()
+  end subroutine
+end module
+
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = n <= 1023;" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = n > 1024;" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp dispatch novariants\\(0\\) 
nocontext\\(0\\)" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "a = f2 \\\(\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "a = f1 \\\(\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "a = f0 \\\(\\\);" 1 "gimple" } }
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-9.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-9.f90
new file mode 100644
index 000000000000..bd421e7239aa
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-9.f90
@@ -0,0 +1,24 @@
+module m
+contains
+subroutine f1 (ar)
+  integer :: arr(10)
+end
+subroutine f0 (ar)
+  integer :: arr(10)
+   !$omp declare variant (f1) match (construct={dispatch})
+end
+end module
+
+subroutine call_it(ctx, arr)
+  logical :: ctx
+  integer :: arr(:)
+  !$omp dispatch nocontext(ctx)
+    call f0(arr)
+  !$omp end dispatch        ! valid since 5.2
+  !$omp dispatch nocontext(ctx)
+    call f0(arr)
+  !$omp end dispatch nowait ! likewise valid (unless there is a 'nowait' at 
'!$omp dispatch')
+  !$omp dispatch nowait
+    call f0(arr)
+  !$omp end dispatch nowait !{ dg-error "Duplicated NOWAIT clause on !.OMP 
DISPATCH and !.OMP END DISPATCH at .1." }
+end 
diff --git a/gcc/testsuite/gfortran.dg/gomp/dispatch-9a.f90 
b/gcc/testsuite/gfortran.dg/gomp/dispatch-9a.f90
new file mode 100644
index 000000000000..cc051a8851a0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-9a.f90
@@ -0,0 +1,27 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module m
+contains
+subroutine f1 (ar)
+  integer :: arr(10)
+end
+subroutine f0 (ar)
+  integer :: arr(10)
+   !$omp declare variant (f1) match (construct={dispatch})
+end
+end module
+
+subroutine call_it(x, arr)
+  logical :: x
+  integer :: arr(:)
+  !$omp dispatch depend(inout:x) nowait
+    call f0(arr)
+  !$omp end dispatch        ! valid since 5.2
+  !$omp dispatch depend(inout:x)
+    call f0(arr)
+  !$omp end dispatch nowait ! likewise valid (unless there is a 'nowait' at 
'!$omp dispatch')
+end
+
+! { dg-final { scan-tree-dump-times "#pragma omp taskwait depend\\(inout:x\\) 
nowait" 2 "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp dispatch nowait" 2 "gimple" } 
}
diff --git a/libgomp/ChangeLog.omp b/libgomp/ChangeLog.omp
index 65232a1b8612..d3c73a8e7ee1 100644
--- a/libgomp/ChangeLog.omp
+++ b/libgomp/ChangeLog.omp
@@ -1,3 +1,14 @@
+2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
+
+       Backported from master:
+       2025-01-02  Paul-Antoine Arras  <par...@baylibre.com>
+
+       * testsuite/libgomp.fortran/declare-variant-2-aux.f90: New test.
+       * testsuite/libgomp.fortran/declare-variant-2.f90: New test (xfail).
+       * testsuite/libgomp.fortran/dispatch-1.f90: New test.
+       * testsuite/libgomp.fortran/dispatch-2.f90: New test.
+       * testsuite/libgomp.fortran/dispatch-3.f90: New test.
+
 2025-01-27  Paul-Antoine Arras  <par...@baylibre.com>
 
        Backported from master:
diff --git a/libgomp/testsuite/libgomp.fortran/declare-variant-2-aux.f90 
b/libgomp/testsuite/libgomp.fortran/declare-variant-2-aux.f90
new file mode 100644
index 000000000000..59b55e0bb853
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/declare-variant-2-aux.f90
@@ -0,0 +1,25 @@
+! { dg-do compile { target skip-all-targets } } 
+
+! Test XFAILed due to https://gcc.gnu.org/PR115271
+
+
+subroutine base_proc (a)
+   use iso_c_binding, only: c_ptr
+   type(c_ptr), intent(inout) :: a
+end subroutine
+
+program main
+   use iso_c_binding, only: c_ptr
+   use my_mod
+   implicit none
+
+   type(c_ptr) :: a
+
+
+   call base_proc(a)
+   !call variant_proc(a)
+
+   !$omp dispatch
+   call base_proc(a)
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/declare-variant-2.f90 
b/libgomp/testsuite/libgomp.fortran/declare-variant-2.f90
new file mode 100644
index 000000000000..b49833ddf724
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/declare-variant-2.f90
@@ -0,0 +1,22 @@
+! { dg-do run }
+! { dg-additional-sources declare-variant-2-aux.f90 }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+module my_mod
+   use iso_c_binding, only: c_ptr
+   implicit none
+   interface
+      subroutine base_proc (a)
+         use iso_c_binding, only: c_ptr
+         type(c_ptr), intent(inout) :: a
+      end subroutine
+   end interface
+
+contains
+   subroutine variant_proc (a)
+      type(c_ptr), intent(inout) :: a
+      !$omp declare variant (base_proc) match (construct={dispatch}) 
adjust_args(need_device_ptr: a)
+   end subroutine
+end module
+
+! { dg-final { scan-tree-dump "variant_proc \\(&a\\)" "gimple" { xfail *-*-* } 
} }
diff --git a/libgomp/testsuite/libgomp.fortran/dispatch-1.f90 
b/libgomp/testsuite/libgomp.fortran/dispatch-1.f90
new file mode 100644
index 000000000000..7b2f03f9d687
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/dispatch-1.f90
@@ -0,0 +1,120 @@
+module procedures
+  use iso_c_binding, only: c_ptr, c_f_pointer
+  use omp_lib
+  implicit none
+
+  contains
+
+  function foo(bv, av, n) result(res)
+    implicit none
+    integer :: res, n, i
+    type(c_ptr) :: bv
+    type(c_ptr) :: av
+    real(8), pointer :: fp_bv(:), fp_av(:)  ! Fortran pointers for array access
+    !$omp declare variant(bar) match(construct={dispatch}) 
adjust_args(need_device_ptr: bv, av)
+    !$omp declare variant(baz) match(implementation={vendor(gnu)})
+
+    ! Associate C pointers with Fortran pointers
+    call c_f_pointer(bv, fp_bv, [n])
+    call c_f_pointer(av, fp_av, [n])
+
+    ! Perform operations using Fortran pointers
+    do i = 1, n
+      fp_bv(i) = fp_av(i) * i
+    end do
+    res = -1
+  end function foo
+
+  function baz(d_bv, d_av, n) result(res)
+    implicit none
+    integer :: res, n, i
+    type(c_ptr) :: d_bv
+    type(c_ptr) :: d_av
+    real(8), pointer :: fp_bv(:), fp_av(:)  ! Fortran pointers for array access
+  
+    ! Associate C pointers with Fortran pointers
+    call c_f_pointer(d_bv, fp_bv, [n])
+    call c_f_pointer(d_av, fp_av, [n])
+
+    !$omp distribute parallel do
+    do i = 1, n
+      fp_bv(i) = fp_av(i) * i
+    end do
+    res = -3
+  end function baz
+
+  function bar(d_bv, d_av, n) result(res)
+    implicit none
+    integer :: res, n, i
+    type(c_ptr) :: d_bv
+    type(c_ptr) :: d_av
+    real(8), pointer :: fp_bv(:), fp_av(:)  ! Fortran pointers for array access
+
+    ! Associate C pointers with Fortran pointers
+    call c_f_pointer(d_bv, fp_bv, [n])
+    call c_f_pointer(d_av, fp_av, [n])
+
+    ! Perform operations on target
+    do i = 1, n
+      fp_bv(i) = fp_av(i) * i
+    end do
+    res = -2
+  end function bar
+
+  function test(n) result(res)
+    use iso_c_binding, only: c_ptr, c_loc
+    implicit none
+    integer :: n, res, i, f, ff, last_dev
+    real(8), allocatable, target :: av(:), bv(:), d_bv(:)
+    real(8), parameter :: e = 2.71828d0
+    type(c_ptr) :: c_av, c_bv, c_d_bv
+    
+    allocate(av(n), bv(n), d_bv(n))
+    
+    ! Initialize arrays
+    do i = 1, n
+      av(i) = e * i
+      bv(i) = 0.0d0
+      d_bv(i) = 0.0d0
+    end do
+
+    last_dev = omp_get_num_devices() - 1
+    
+    c_av = c_loc(av)
+    c_d_bv = c_loc(d_bv)
+    !$omp target data map(to: av(:n)) map(from: d_bv(:n)) device(last_dev) 
if(n == 1024)
+      !$omp dispatch nocontext(n > 1024) novariants(n < 1024) device(last_dev)
+      f = foo(c_d_bv, c_av, n)
+    !$omp end target data
+    
+    c_bv = c_loc(bv)
+    ff = foo(c_bv, c_loc(av), n)
+    
+    ! Verify results
+    do i = 1, n
+      if (d_bv(i) /= bv(i)) then
+        write(0,*) 'ERROR at ', i, ': ', d_bv(i), ' (act) != ', bv(i), ' (exp)'
+        res = 1
+        return
+      end if
+    end do
+    
+    res = f
+    deallocate(av, bv, d_bv)
+  end function test
+end module procedures
+
+program main
+  use procedures
+  implicit none
+  integer :: ret
+  
+  ret = test(1023)
+  if (ret /= -1) stop 1
+  
+  ret = test(1024)
+  if (ret /= -2) stop 1
+  
+  ret = test(1025)
+  if (ret /= -3) stop 1
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/dispatch-2.f90 
b/libgomp/testsuite/libgomp.fortran/dispatch-2.f90
new file mode 100644
index 000000000000..042b4d9f06d6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/dispatch-2.f90
@@ -0,0 +1,69 @@
+module m
+  use iso_c_binding
+  implicit none (type, external)
+  type(c_ptr) :: ref1, ref2, ref3, ref4
+contains
+  subroutine foo(v, w, x, y)
+    type(C_ptr) :: v, w, x, y
+    value :: w, y
+    optional :: x, y
+    !$omp declare variant(bar) match ( construct = { dispatch } )   &
+    !$omp&                     adjust_args(need_device_ptr : v, w, x, y )
+    stop 1  ! should not get called
+  end
+  subroutine bar(a, b, c, d)
+    type(C_ptr) :: a, b, c, d
+    value :: b, d
+    optional :: c, d
+    if (.not. c_associated (a, ref1)) stop 2
+    if (.not. c_associated (b, ref2)) stop 3
+    if (.not. c_associated (c, ref3)) stop 3
+    if (.not. c_associated (d, ref4)) stop 3
+  end
+end
+
+program main
+  use omp_lib
+  use m
+  implicit none (type, external)
+  integer, target :: a, b, c, d
+  type(c_ptr) :: v, w, y, z
+  integer :: dev
+
+  do dev = -1, omp_get_num_devices ()
+    print *, 'dev ', dev
+
+    ! Cross check (1)
+    ref1 = omp_target_alloc (32_c_size_t, dev)
+    ref2 = omp_target_alloc (32_c_size_t, dev)
+    ref3 = omp_target_alloc (32_c_size_t, dev)
+    ref4 = omp_target_alloc (32_c_size_t, dev)
+    call bar (ref1, ref2, ref3, ref4)
+    call omp_target_free (ref1, dev)
+    call omp_target_free (ref2, dev)
+    call omp_target_free (ref3, dev)
+    call omp_target_free (ref4, dev)
+
+    v = c_loc(a)
+    w = c_loc(b)
+    y = c_loc(b)
+    z = c_loc(b)
+
+    !$omp target enter data device(dev) map(a, b, c, d)
+
+    ! Cross check (2)
+    ! This should be effectively identical to 'dispatch'
+    !$omp target data device(dev) use_device_ptr(v, w, y, z)
+      ref1 = v
+      ref2 = w
+      ref3 = y
+      ref4 = z
+      call bar (v, w, y, z)
+    !$omp end target data
+
+    !$omp dispatch device(dev)
+      call foo (v, w, y, z)
+
+    !$omp target exit data device(dev) map(a, b, c, d)
+  end do
+end
diff --git a/libgomp/testsuite/libgomp.fortran/dispatch-3.f90 
b/libgomp/testsuite/libgomp.fortran/dispatch-3.f90
new file mode 100644
index 000000000000..4a914da1ccc3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/dispatch-3.f90
@@ -0,0 +1,80 @@
+! { dg-do run }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+! Check that nested function calls in a dispatch region are handled correctly,
+! i.e. that the adjust_args clause is applied only to the outer call.
+
+module m
+  use iso_c_binding
+  use omp_lib
+  implicit none(type,external)
+contains
+  integer function f(x, y1, y2, z1, z2)
+    allocatable :: f
+    integer, value :: x
+    type(c_ptr), value :: y1, y2
+    type(c_ptr) :: z1, z2
+
+    if (x == 1) then  ! HOST
+      block
+        integer, pointer :: iy1, iy2, iz1, iz2
+        call c_f_pointer (y1, iy1)
+        call c_f_pointer (y2, iy2)
+        call c_f_pointer (z1, iz1)
+        call c_f_pointer (z2, iz2)
+        f = (iy1 + iy2) + 10 * (iz1+iz2)
+      end block
+    else
+      allocate(f)
+      !$omp target is_device_ptr(y1, y2, z1, z2) map(tofrom: f)
+      block
+        integer, pointer :: iy1, iy2, iz1, iz2
+        call c_f_pointer (y1, iy1)
+        call c_f_pointer (y2, iy2)
+        call c_f_pointer (z1, iz1)
+        call c_f_pointer (z2, iz2)
+        f = -(iy1+iy2)*23  -127 * (iz1+iz2) - x * 3
+      end block
+    end if
+  end
+
+  integer function g(x, y1, y2, z1, z2)
+    !$omp declare variant(f) match(construct={dispatch}) 
adjust_args(need_device_ptr : y1, y2, z1, z2)
+    allocatable :: g
+    integer, value :: x
+    type(c_ptr), value :: y1, y2
+    type(c_ptr) :: z1, z2
+    g = x
+    stop 2  ! should not get called
+  end
+end 
+
+program main
+  use m
+  implicit none (type, external)
+  integer, target :: v1, v2
+  integer :: res, ref
+  v1 = 5
+  v2 = 11
+
+  ref = 5*2 + 10 * 11*2
+  ref = -(5*2)*23 -127 * (11*2) - ref * 3
+
+  !$omp target data map(v1,v2)
+    res = func (c_loc(v1), c_loc(v1), c_loc(v2), c_loc(v2))
+  !$omp end target data
+
+  if (res /= ref) stop 1
+contains
+integer function func(x1, x2, x3, x4)
+  use m
+  implicit none(type,external)
+  type(c_ptr) :: x1, x2, x3, x4
+  value :: x1, x3
+
+  !$omp dispatch
+    func = g(g(1,x1,x2,x3,x4), x1,x2,x3,x4)
+end
+end
+
+! { dg-final { scan-tree-dump-times "__builtin_omp_get_mapped_ptr" 4 "gimple" 
} }

Reply via email to