diff mbox series

[6/7] OpenMP: Fortran front-end support for dispatch + adjust_args

Message ID 20240527115439.3967217-7-parras@baylibre.com
State New
Headers show
Series OpenMP: dispatch + adjust_args support | expand

Commit Message

Paul-Antoine Arras May 27, 2024, 11:54 a.m. UTC
This patch adds support for the `dispatch` construct and the `adjust_args`
clause to the Fortran front-end.

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.
	(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:.
	* types.def (BT_FN_PTR_CONST_PTR_INT): Declare.

gcc/testsuite/ChangeLog:

	* 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-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/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.
---
 gcc/fortran/dump-parse-tree.cc                |  17 ++
 gcc/fortran/frontend-passes.cc                |   2 +
 gcc/fortran/gfortran.h                        |  11 +-
 gcc/fortran/match.h                           |   1 +
 gcc/fortran/openmp.cc                         | 193 ++++++++++++++++--
 gcc/fortran/parse.cc                          |  38 ++++
 gcc/fortran/resolve.cc                        |   2 +
 gcc/fortran/st.cc                             |   1 +
 gcc/fortran/trans-decl.cc                     |   9 +-
 gcc/fortran/trans-openmp.cc                   | 161 +++++++++++++++
 gcc/fortran/trans.cc                          |   1 +
 gcc/fortran/types.def                         |   1 +
 .../gfortran.dg/gomp/adjust-args-1.f90        |  54 +++++
 .../gfortran.dg/gomp/adjust-args-2.f90        |  18 ++
 .../gfortran.dg/gomp/adjust-args-3.f90        |  26 +++
 .../gfortran.dg/gomp/adjust-args-4.f90        |  58 ++++++
 .../gfortran.dg/gomp/adjust-args-5.f90        |  58 ++++++
 .../gfortran.dg/gomp/declare-variant-2.f90    |   6 +-
 gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90 |  77 +++++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90 |  75 +++++++
 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 |  24 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90 |  38 ++++
 gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90 |  27 +++
 gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90 |  39 ++++
 26 files changed, 976 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/adjust-args-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/adjust-args-4.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/adjust-args-5.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-3.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-4.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90

Comments

Tobias Burnus May 28, 2024, 7:14 a.m. UTC | #1
Hi PA, hi all,

two remarks while quickly browsing the code:

Paul-Antoine Arras:
> +		  if (n->sym->ts.type != BT_DERIVED
> +		      || !n->sym->ts.u.derived->ts.is_iso_c)
> +		    {
> +		      gfc_error ("argument list item %qs in "
> +				 "%<need_device_ptr%> at %L must be of "
> +				 "TYPE(C_PTR)",
> +				 n->sym->name, &n->where);

I think you need to rule out 'c_funptr' as well, e.g. via:

                         || (n->sym->ts.u.derived->intmod_sym_id
                             != ISOCBINDING_PTR)))

I do note that in openmp.cc, we have one check which checks explicitly 
for c_ptr and one existing one which only checks for (c_ptr or 
c_funptr); can you fix that one as well?

* * *

But I mainly miss an update to 'module.cc' for the 'declare variant' 
change; the 'adjust_args' (for 'need_device_ptr', only) list items have
to be saved in the .mod file - otherwise the following will not work:

<some-file>-aux.f90
! { dg-do compile { target skip-all-targets } }
module my_mod
   ...
   !$omp declare variant ... adjust_args(need_device_ptr: ...)
   ...
end module

<some-file>.f90
{ dg-do ...
! { dg-additional-sources <some-file>-aux.f90 }
   ...
   call <base-function>
   ...
   !$omp displatch
        call <base-function>
end


For C++ modules, it should be fine as those for those, the tree is dumped.

Tobias
Paul-Antoine Arras May 31, 2024, 10:07 a.m. UTC | #2
Hi Tobias,

Thanks for your comments. Here is an updated patch.

On 28/05/2024 09:14, Tobias Burnus wrote:
> Paul-Antoine Arras:
>> +          if (n->sym->ts.type != BT_DERIVED
>> +              || !n->sym->ts.u.derived->ts.is_iso_c)
>> +            {
>> +              gfc_error ("argument list item %qs in "
>> +                 "%<need_device_ptr%> at %L must be of "
>> +                 "TYPE(C_PTR)",
>> +                 n->sym->name, &n->where);
> 
> I think you need to rule out 'c_funptr' as well, e.g. via:
> 
>                          || (n->sym->ts.u.derived->intmod_sym_id
>                              != ISOCBINDING_PTR)))
> 
> I do note that in openmp.cc, we have one check which checks explicitly 
> for c_ptr and one existing one which only checks for (c_ptr or 
> c_funptr); can you fix that one as well?

This is now handled in the new patch.

> But I mainly miss an update to 'module.cc' for the 'declare variant' 
> change; the 'adjust_args' (for 'need_device_ptr', only) list items have
> to be saved in the .mod file - otherwise the following will not work:
> 
> <some-file>-aux.f90
> ! { dg-do compile { target skip-all-targets } }
> module my_mod
>    ...
>    !$omp declare variant ... adjust_args(need_device_ptr: ...)
>    ...
> end module
> 
> <some-file>.f90
> { dg-do ...
> ! { dg-additional-sources <some-file>-aux.f90 }
>    ...
>    call <base-function>
>    ...
>    !$omp displatch
>         call <base-function>
> end

I added a new testcase along those lines. However, I had to xfail it due 
to completely missing support for declare variant (even without 
adjust_args) in module.cc. For reference, Tobias created this PR: 
https://gcc.gnu.org/PR115271.
diff mbox series

Patch

diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index 87a65036a3d..f64dec63655 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -2119,6 +2119,18 @@  show_omp_clauses (gfc_omp_clauses *omp_clauses)
     }
   if (omp_clauses->assume)
     show_omp_assumes (omp_clauses->assume);
+  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
@@ -2156,6 +2168,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;
@@ -2257,6 +2272,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:
@@ -3498,6 +3514,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 3c06018fdbb..1a0ef50b91d 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 de1a7cd0935..361b4bece15 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -321,7 +321,7 @@  enum gfc_statement
   ST_OMP_ALLOCATE, ST_OMP_ALLOCATE_EXEC,
   ST_OMP_ALLOCATORS, ST_OMP_END_ALLOCATORS,
   /* Note: gfc_match_omp_nothing returns ST_NONE. */
-  ST_OMP_NOTHING, ST_NONE
+  ST_OMP_NOTHING, ST_NONE, ST_OMP_DISPATCH
 };
 
 /* Types of interfaces that we can have.  Assignment interfaces are
@@ -1004,6 +1004,9 @@  typedef struct
   ENUM_BITFIELD (gfc_omp_device_type) omp_device_type:2;
   unsigned omp_allocate:1;
 
+  /* Mentioned in OMP DECLARE VARIANT.  */
+  unsigned omp_declare_variant_need_device_ptr : 1;
+
   /* Mentioned in OACC DECLARE.  */
   unsigned oacc_declare_create:1;
   unsigned oacc_declare_copyin:1;
@@ -1431,6 +1434,7 @@  enum
   OMP_LIST_HAS_DEVICE_ADDR,
   OMP_LIST_ENTER,
   OMP_LIST_USES_ALLOCATORS,
+  OMP_LIST_ADJUST_ARGS,
   OMP_LIST_NUM /* Must be the last.  */
 };
 
@@ -1576,6 +1580,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;
   const char *critical_name;
   enum gfc_omp_default_sharing default_sharing;
@@ -1702,6 +1708,7 @@  typedef struct gfc_omp_declare_variant
   struct gfc_symtree *variant_proc_symtree;
 
   gfc_omp_set_selector *set_selectors;
+  gfc_omp_namelist *need_device_ptr_arg_list;
 
   bool checked_p : 1; /* Set if previously checked for errors.  */
   bool error_p : 1; /* Set if error found in directive.  */
@@ -3033,7 +3040,7 @@  enum gfc_exec_op
   EXEC_OMP_TARGET_TEAMS_LOOP, EXEC_OMP_MASKED, EXEC_OMP_PARALLEL_MASKED,
   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_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS
+  EXEC_OMP_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS, EXEC_OMP_DISPATCH
 };
 
 typedef struct gfc_code
diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h
index b09921357fd..448f631275e 100644
--- a/gcc/fortran/match.h
+++ b/gcc/fortran/match.h
@@ -163,6 +163,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 5246647e6f8..b29f39febe6 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -71,7 +71,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) */
@@ -180,6 +180,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);
@@ -321,6 +323,8 @@  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->need_device_ptr_arg_list, false, false,
+			     false);
       free (current);
     }
 }
@@ -1098,6 +1102,8 @@  enum omp_mask2
   OMP_CLAUSE_ASSUMPTIONS, /* OpenMP 5.1. */
   OMP_CLAUSE_USES_ALLOCATORS, /* OpenMP 5.0  */
   OMP_CLAUSE_INDIRECT, /* OpenMP 5.1  */
+  OMP_CLAUSE_NOVARIANTS, /* OpenMP 5.1  */
+  OMP_CLAUSE_NOCONTEXT, /* OpenMP 5.1  */
   /* This must come last.  */
   OMP_MASK2_LAST
 };
@@ -3215,6 +3221,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)
@@ -4530,6 +4555,9 @@  cleanup:
   omp_mask (OMP_CLAUSE_NOWAIT)
 #define OMP_ALLOCATORS_CLAUSES \
   omp_mask (OMP_CLAUSE_ALLOCATE)
+#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
@@ -4843,6 +4871,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)
 {
@@ -6069,6 +6103,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->need_device_ptr_arg_list = NULL;
   odv->base_proc_symtree = base_proc_st;
   odv->next = NULL;
   odv->error_p = false;
@@ -6085,13 +6120,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
@@ -6104,18 +6155,86 @@  gfc_match_omp_declare_variant (void)
 	  return MATCH_ERROR;
 	}
 
-      if (gfc_match_omp_context_selector_specification (odv) != MATCH_YES)
-	return MATCH_ERROR;
-
-      if (gfc_match (" )") != MATCH_YES)
+      if (ccode == match)
 	{
-	  gfc_error ("expected %<)%> at %C");
-	  return MATCH_ERROR;
+	  has_match = true;
+	  if (gfc_match_omp_context_selector_specification (odv)
+	      != MATCH_YES)
+	    return MATCH_ERROR;
+	  if (gfc_match (" )") != MATCH_YES)
+	    {
+	      gfc_error ("expected %<)%> at %C");
+	      return MATCH_ERROR;
+	    }
+	}
+      else if (ccode == adjust_args)
+	{
+	  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;
+	    }
+	  if (need_device_ptr_p)
+	    {
+	      if (gfc_match_omp_variable_list (" :",
+					       &odv->need_device_ptr_arg_list,
+					       false)
+		  != MATCH_YES)
+		{
+		  gfc_error ("expected argument list at %C");
+		  return MATCH_ERROR;
+		}
+	      for (gfc_omp_namelist *n = odv->need_device_ptr_arg_list;
+		   n != NULL; n = n->next)
+		{
+		  if (!n->sym->attr.dummy)
+		    {
+		      gfc_error ("list item %qs at %L is not a dummy argument",
+				 n->sym->name, &n->where);
+		      return MATCH_ERROR;
+		    }
+		  if (n->sym->ts.type != BT_DERIVED
+		      || !n->sym->ts.u.derived->ts.is_iso_c)
+		    {
+		      gfc_error ("argument list item %qs in "
+				 "%<need_device_ptr%> at %L must be of "
+				 "TYPE(C_PTR)",
+				 n->sym->name, &n->where);
+		      return MATCH_ERROR;
+		    }
+		}
+	    }
+	  else
+	    {
+	      gfc_omp_namelist *nothing_arg_list = NULL;
+	      if (gfc_match_omp_variable_list (" :", &nothing_arg_list, false)
+		  != MATCH_YES)
+		{
+		  gfc_error ("expected argument list at %C");
+		  return MATCH_ERROR;
+		}
+	      gfc_free_omp_namelist (nothing_arg_list, false, false, false);
+	    }
 	}
 
       first_p = false;
     }
 
+  if (has_adjust_args && !has_match)
+    {
+      gfc_error ("an %<adjust_args%> clause at %C 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;
 }
 
@@ -7544,7 +7663,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" };
+	"USES_ALLOCATORS", "ADJUST_ARGS" };
   STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
 
   if (omp_clauses == NULL)
@@ -7726,6 +7845,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)
@@ -8675,9 +8814,9 @@  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)
+		    && 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);
@@ -10290,6 +10429,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);
@@ -11168,6 +11308,8 @@  omp_code_to_statement (gfc_code *code)
       return ST_OMP_PARALLEL_LOOP;
     case EXEC_OMP_DEPOBJ:
       return ST_OMP_DEPOBJ;
+    case EXEC_OMP_DISPATCH:
+      return ST_OMP_DISPATCH;
     default:
       gcc_unreachable ();
     }
@@ -11583,6 +11725,26 @@  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;
+  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->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);
+}
 
 /* Resolve OpenMP directive clauses and check various requirements
    of each directive.  */
@@ -11696,6 +11858,11 @@  gfc_resolve_omp_directive (gfc_code *code, gfc_namespace *ns)
       code->ext.omp_clauses->if_present = false;
       resolve_omp_clauses (code, code->ext.omp_clauses, 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;
     }
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index 79c810c86ba..74fc249269d 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -1050,6 +1050,7 @@  decode_omp_directive (void)
       break;
     case 'd':
       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);
@@ -1916,6 +1917,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_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: \
@@ -2597,6 +2599,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;
@@ -6183,6 +6188,35 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
 }
 
 
+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_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 ();
+  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
@@ -6383,6 +6417,10 @@  parse_executable (gfc_statement st)
 	  st = parse_omp_oacc_atomic (true);
 	  continue;
 
+	case ST_OMP_DISPATCH:
+	  st = parse_omp_dispatch ();
+	  continue;
+
 	default:
 	  return st;
 	}
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index d7a0856fcca..755d1302ce9 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -11378,6 +11378,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:
@@ -13054,6 +13055,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:
diff --git a/gcc/fortran/st.cc b/gcc/fortran/st.cc
index 6a605ad91d4..90ee1352ba4 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 dca7779528b..4390769146a 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -2124,6 +2124,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.  */
 
@@ -2377,7 +2379,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 f867e2240bf..5e4450184d1 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -4233,6 +4233,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;
@@ -6311,6 +6341,30 @@  gfc_trans_omp_depobj (gfc_code *code)
   return gfc_finish_block (&block);
 }
 
+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);
+
+  tree body = gfc_trans_code (next);
+  gfc_start_block (&block);
+  tree omp_clauses
+    = gfc_trans_omp_clauses (&block, code->ext.omp_clauses, code->loc);
+
+  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)
 {
@@ -8221,6 +8275,8 @@  gfc_trans_omp_directive (gfc_code *code)
     case EXEC_OMP_TASKLOOP:
       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:
@@ -8337,6 +8393,7 @@  gfc_trans_omp_declare_variant (gfc_namespace *ns)
   tree base_fn_decl = ns->proc_name->backend_decl;
   gfc_namespace *search_ns = ns;
   gfc_omp_declare_variant *next;
+  vec<tree> adjust_args_list = vNULL;
 
   for (gfc_omp_declare_variant *odv = search_ns->omp_declare_variant;
        search_ns; odv = next)
@@ -8532,6 +8589,19 @@  gfc_trans_omp_declare_variant (gfc_namespace *ns)
 		  variant_proc_sym = NULL;
 		}
 	    }
+	  if (odv->need_device_ptr_arg_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);
@@ -8548,6 +8618,97 @@  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
+		  for (gfc_omp_namelist *arg_list
+		       = odv->need_device_ptr_arg_list;
+		       arg_list != NULL; arg_list = arg_list->next)
+		    {
+		      if (arg_list->sym->backend_decl == NULL_TREE)
+			{
+			   gfc_error (
+			     "%s at %L is not a base function argument",
+			     arg_list->sym->name, &arg_list->where);
+			   continue;
+			}
+
+		      tree base_fn_arg_decl = arg_list->sym->backend_decl;
+		      if (base_fn_arg_decl != error_mark_node)
+			{
+			   // Is t specified more than once?
+			   if (adjust_args_list.contains (base_fn_arg_decl))
+			     {
+			       gfc_error (
+				 "%qD at %L is specified more than once",
+				 base_fn_arg_decl, &arg_list->where);
+			       continue;
+			     }
+			   adjust_args_list.safe_push (base_fn_arg_decl);
+
+			   // Handle variant argument
+			   tree variant
+			     = gfc_get_symbol_decl (variant_proc_sym);
+			   tree variant_parm = DECL_ARGUMENTS (variant);
+			   int idx;
+			   tree arg;
+			   for (arg = DECL_ARGUMENTS (base_fn_decl), idx = 0;
+				arg != NULL; arg = TREE_CHAIN (arg), idx++)
+			     if (arg == base_fn_arg_decl)
+			       break;
+			   gcc_assert (arg != NULL_TREE);
+			   if (variant_parm == NULL_TREE)
+			     {
+			       gfc_formal_arglist *arg
+				 = variant_proc_sym->formal;
+			       for (int i = 0; i < idx; i++)
+				 {
+				  arg = arg->next;
+				  gcc_assert (arg != NULL);
+				 }
+
+			       // Check we got the right parameter name
+			       if (strcmp (arg_list->sym->name, arg->sym->name)
+				   != 0)
+				 {
+				  gfc_error ("%s at %L is not a variant "
+					     "function argument",
+					     arg_list->sym->name,
+					     &arg_list->where);
+				  continue;
+				 }
+			       arg->sym->attr
+				 .omp_declare_variant_need_device_ptr
+				 = 1;
+			     }
+			   else
+			     {
+			       for (int i = 0; i < idx; i++)
+				 {
+				  variant_parm = TREE_CHAIN (variant_parm);
+				  gcc_assert (variant_parm != NULL_TREE);
+				 }
+			       // Check we got the right parameter name
+			       if (strcmp (arg_list->sym->name,
+					   IDENTIFIER_POINTER (
+					     DECL_NAME (variant_parm)))
+				   != 0)
+				 {
+				  gfc_error ("%s at %L is not a variant "
+					     "function argument",
+					     arg_list->sym->name,
+					     &arg_list->where);
+				  continue;
+				 }
+
+			       tree attr = tree_cons (
+				 get_identifier (
+				   "omp declare variant adjust_args "
+				   "need_device_ptr"),
+				 NULL_TREE, DECL_ATTRIBUTES (variant_parm));
+			       DECL_ATTRIBUTES (variant_parm) = attr;
+			     }
+			}
+		    }
 		}
 	    }
 	}
diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc
index badad6ae892..2795cdf7464 100644
--- a/gcc/fortran/trans.cc
+++ b/gcc/fortran/trans.cc
@@ -2596,6 +2596,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/fortran/types.def b/gcc/fortran/types.def
index 390cc9542f7..5047c8f816a 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -120,6 +120,7 @@  DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_INT_BOOL, BT_BOOL, BT_INT, BT_BOOL)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTRMODE,
 		     BT_VOID, BT_PTR, BT_PTRMODE)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_CONST_PTR_SIZE, BT_VOID, BT_CONST_PTR, BT_SIZE)
+DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_INT, BT_PTR, BT_CONST_PTR, BT_INT)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
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 00000000000..982c2c2cb9d
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-1.f90
@@ -0,0 +1,54 @@ 
+! Test parsing of OMP clause adjust_args
+! { dg-do compile } 
+
+module main
+  use iso_c_binding, only: c_ptr
+  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
+  integer function 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 function
+  integer function f12 (a)
+  import c_ptr
+  type(c_ptr), intent(inout) :: a
+  !$omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) ! { dg-error "list item 'b' at .1. is not a dummy argument" }
+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 00000000000..c65a4839ca5
--- /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-3.f90 b/gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90
new file mode 100644
index 00000000000..b731cb340c1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/adjust-args-3.f90
@@ -0,0 +1,26 @@ 
+! Test translation of OMP clause adjust_args
+! { dg-do compile }
+
+module main
+  use iso_c_binding, only: c_ptr
+  implicit none
+  !type(c_ptr) :: a
+  
+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 variant2 (a)
+    type(c_ptr), intent(inout) :: a
+  end subroutine
+  subroutine variant3 (i)
+    integer :: i
+  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 00000000000..75e884044b2
--- /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, 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 00000000000..75e884044b2
--- /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, D\.\[0-9]+\\);" 2 "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-2.f90
index 7fc5071feff..62d2cb96fac 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 00000000000..12c30904131
--- /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-2.f90 b/gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90
new file mode 100644
index 00000000000..f52df4446c4
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-2.f90
@@ -0,0 +1,75 @@ 
+module main
+  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
+    
+    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 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 00000000000..84590fd883a
--- /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 00000000000..149d0613b97
--- /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-times "#pragma omp task if\\(0\\)" 1 "gimple" } }
+    call f2 ()
+  !$omp dispatch nowait ! { dg-final { scan-tree-dump-times "#pragma omp task if\\(1\\)" 1 "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 00000000000..e45397f3f96
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-5.f90
@@ -0,0 +1,24 @@ 
+! { 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_1 \\* 2;.*#pragma omp dispatch device\\(D\.\[0-9]+\\) shared\\(D\.\[0-9]+\\).*#pragma omp task shared\\(D\.\[0-9]+\\).*__builtin_omp_set_default_device \\(D\.\[0-9]+\\);" 1 "gimple" } }
+    call f2 (a)
+  end subroutine
+end module
+
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 00000000000..9f4fa2970ca
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-6.f90
@@ -0,0 +1,38 @@ 
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+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
+
+  !$omp dispatch
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p)
+    ! { dg-final { scan-tree-dump-times "#pragma omp task shared\\(p\\) shared\\(p2\\)\[^\n\r]*\[ \t\n\r]*\{\[ \t\n\r]*integer\\(kind=4\\) D\.\[0-9]+;\[ \t\n\r]*void \\* D\.\[0-9]+;\[ \t\n\r]*p = {CLOBBER};\[ \t\n\r]*D\.\[0-9]+ = __builtin_omp_get_default_device \\(\\);\[ \t\n\r]*D\.\[0-9]+ = __builtin_omp_get_mapped_ptr \\(&p2, D\.\[0-9]+\\);\[ \t\n\r]*f1 \\(&p, D\.\[0-9]+\\);" 1 "gimple" } }
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p2)
+    ! { dg-final { scan-tree-dump-times "#pragma omp task shared\\(p2\\) shared\\(p\\)\[^\n\r]*\[ \t\n\r]*\{\[ \t\n\r]*integer\\(kind=4\\) D\.\[0-9]+;\[ \t\n\r]*void \\* D\.\[0-9]+;\[ \t\n\r]*p = {CLOBBER};\[ \t\n\r]*D\.\[0-9]+ = __builtin_omp_get_default_device \\(\\);\[ \t\n\r]*D\.\[0-9]+ = __builtin_omp_get_mapped_ptr \\(&p, D\.\[0-9]+\\);\[ \t\n\r]*f1 \\(D\.\[0-9]+, &p2\\);" 1 "gimple" } }
+    call f2 (p, p2)
+  !$omp dispatch is_device_ptr(p, p2)
+    ! { dg-final { scan-tree-dump-times "#pragma omp task shared\\(p\\) shared\\(p2\\)\[^\n\r]*\[ \t\n\r]*\{\[ \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/dispatch-7.f90 b/gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90
new file mode 100644
index 00000000000..32b6347be67
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-7.f90
@@ -0,0 +1,27 @@ 
+! { 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-times "__builtin_GOMP_task \\(.*, .*, .*, .*, .*, .*, 0B, .*, .*\\);" 1 "ompexp" } }
+    call f2 (p)
+  !$omp dispatch depend(inout: p)
+    ! { dg-final { scan-tree-dump-times "D\.\[0-9]+\\\[2] = &p;" 1 "ompexp" } }
+    ! { dg-final { scan-tree-dump-times "__builtin_GOMP_task \\(.*, .*, .*, .*, .*, .*, &D\.\[0-9]+, .*, .*\\);" 1 "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 00000000000..6771336aa33
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/dispatch-8.f90
@@ -0,0 +1,39 @@ 
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple -fdump-tree-omplower" }
+
+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\\) shared\\(D\.\[0-9]+\\) shared\\(D\.\[0-9]+\\)" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp task shared\\(D\.\[0-9]+\\) shared\\(D\.\[0-9]+\\)" 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" } }
+
+! { dg-final { scan-tree-dump-times ".omp_data_o.1.D\.\[0-9]+ = D\.\[0-9]+;" 2 "omplower" } }
+! { dg-final { scan-tree-dump-times ".omp_data_o.1.a = &a;" 1 "omplower" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = .omp_data_i->D\.\[0-9]+;" 2 "omplower" } }
+! { dg-final { scan-tree-dump-times "D\.\[0-9]+ = .omp_data_i->a;" 3 "omplower" } }
+! { dg-final { scan-tree-dump-times "\\*D\.\[0-9]+ = D\.\[0-9]+;" 3 "omplower" } }