diff mbox series

OpenMP: Add 'omp requires' to Fortran (mostly parsing)

Message ID 03601efc-b586-d58e-5950-3cfb40081d1a@codesourcery.com
State New
Headers show
Series OpenMP: Add 'omp requires' to Fortran (mostly parsing) | expand

Commit Message

Tobias Burnus July 24, 2020, 9 a.m. UTC
As with C/C++, the offloading requires are not implemented
an rejected early with a 'sorry'.
The mem-setting default for atomic is handled, but only 'seq_cst'
as currently gfortran's only supports this one. (C/C++ support more.)

In C/C++, the 'requires' has to be at file scope. For Fortran,
   "must appear in the specification part of a program unit,"
which is:
   "A program unit is a main program, an external subprogram,
    a module, a submodule, or a block data program unit."

For the atomic setting, one has:
   At most one requires directive with atomic_default_mem_order
   clause can appear in a single compilation unit.

For the offloading settings + dynamic_allocators, I do not
see this restriction, i.e. I guess one can specify it multiple
times.

However, there are the restrictions:
   A requires directive with a unified_address,
   unified_shared_memory, or reverse_offload clause must appear
   lexically before any device constructs or device routines.
(i.e. not: dynamic_allocators)

   The requires directive with atomic_default_mem_order clause
   may not appear lexically after any atomic construct
   on which memory-order-clause is not specified.

I do wonder whether the following should be permitted:
   subroutine one
     !$omp requires unified_shared_memory
     !$omp target
...
   subroutine two
     !$omp requires unified_shared_memory
     !$omp target

which conceptually in terms of Fortran makes sense, but is
currently not permitted (in the patch). On the other hand,
for larger modern code, code is usually in a module and there
is one module per file.

I am not sure whether it makes sense to store the offloading
requires also in a module – and what to do on 'use'; the patch
currently does not do this.

OK?

Tobias

-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter

Comments

Tobias Burnus July 28, 2020, 11:05 a.m. UTC | #1
Attached is an updated version – whether that will be fully
in line with OpenMP 5.1 remains to be seen. But in any case,
it now handles properly:
"If a directive appears in the declarative part of a module then the
behavior is as if that directive appears after any references to that
module."

Note: That with the current implementation, the restriction that
the 'requires' cannot come after an 'omp atomic' cannot occur as
'omp requires' belongs into the specification part of a 'program unit'
(Fortran; OpenMP: 'compilation unit') and 'omp atomic' belongs to
the execution part.
Thus, unless a later 'use m' implicitly provides the clause, it
cannot occur. (The current implementation does not permit that
use-stmt which is not at 'program unit' level can introduce new
clauses applicable at 'program unit' level.)

That's different to target as an 'omp declare target' also
belongs into the specification section. (Additionally, that
checking variable is also used for ensure that all program units
use the same offload clauses of omp requires.)

There is no 'use m' test for the three offload-related items
as the 'sorry, not implemented' prevents the creation of the
module file.

OK?

Tobias

-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
Tobias Burnus July 28, 2020, 3:12 p.m. UTC | #2
I just realized that supporting 'acq_rel' is simple; while
'omp atomic' parsing needs to be updated quite a bit for the
OpenMP 5 changes, just adding ACQ_REL support for 'requires'
is trivial.

Hence, I updated the requires-9.c testcase for 'acq_rel',
adjusted trans-openmp.c and did some openmp.c adjustments.

Thus: New version with this trivial change and being
able to tick-off the 'atomic_default_mem_order' clause :-)

Tobias

-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
Tobias Burnus July 29, 2020, 8:10 a.m. UTC | #3
And another update; I removed the global variable, which
was only used for checking at the end – and
reconstruct its value in a local variable just before calling
gfc_check_omp_requires.

(Following a suggestion by Jakub and removing a kind-of left
over from the first implementation.)

Tobias

On 7/28/20 5:12 PM, Tobias Burnus wrote:
> I just realized that supporting 'acq_rel' is simple; while
> 'omp atomic' parsing needs to be updated quite a bit for the
> OpenMP 5 changes, just adding ACQ_REL support for 'requires'
> is trivial.
>
> Hence, I updated the requires-9.c testcase for 'acq_rel',
> adjusted trans-openmp.c and did some openmp.c adjustments.
>
> Thus: New version with this trivial change and being
> able to tick-off the 'atomic_default_mem_order' clause :-)
>
> Tobias
>
-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
Jakub Jelinek July 29, 2020, 8:23 a.m. UTC | #4
On Wed, Jul 29, 2020 at 10:10:35AM +0200, Tobias Burnus wrote:
> +	    case AB_OMP_REQ_REVERSE_OFFLOAD:
> +	       gfc_omp_requires_add_clause (OMP_REQ_REVERSE_OFFLOAD,
> +					    "reverse_offload",
> +					    &gfc_current_locus,
> +					   module_name);

Even visually something is wrong with the indentation here and in a few
others, where I'd expect all the arguments to be in the same column (even
with the > + before it, but they are not.  E.g. above I think the
gfc_omp_... is indented by 3 instead of 2 columns from case, and "rev... and
&gfc... match that, but module_name is probably correct.

> +	      break;
> +	    case AB_OMP_REQ_UNIFIED_ADDRESS:
> +	      gfc_omp_requires_add_clause (OMP_REQ_UNIFIED_ADDRESS,
> +					   "unified_address",
> +					    &gfc_current_locus,

And e.g. here the &gfc... is off.
> --- a/gcc/fortran/openmp.c
> +++ b/gcc/fortran/openmp.c
> @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "diagnostic.h"
>  #include "gomp-constants.h"
>  
> +

Unnecessary.
>  /* Match an end of OpenMP directive.  End of OpenMP directive is optional
>     whitespace, followed by '\n' or comment '!'.  */
>  
> @@ -3745,6 +3970,26 @@ gfc_match_omp_oacc_atomic (bool omp_p)
>    new_st.op = (omp_p ? EXEC_OMP_ATOMIC : EXEC_OACC_ATOMIC);
>    if (seq_cst)
>      op = (gfc_omp_atomic_op) (op | GFC_OMP_ATOMIC_SEQ_CST);
> +  else

I wonder if this shouldn't be else if (omp_p), I'd think
OpenACC atomics shouldn't be affected by OpenMP requires directive.

>    for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
>         gfc_current_ns = gfc_current_ns->sibling)
> -    gfc_check_externals (gfc_current_ns);
> +     gfc_check_omp_requires (gfc_current_ns, omp_requires);

Again indentation looks weird.

Otherwise LGTM.

	Jakub
Tobias Burnus July 29, 2020, 10:32 a.m. UTC | #5
Committed as r11-2395, https://gcc.gnu.org/g:269322ece17202632bc354e9c510e4a5bd6ad84b
with review comments applied plus a follow-up commit for an indentation issue I missed:
r11-2399, https://gcc.gnu.org/g:6de5600a8bd1ef0ad3d57670efdcc68bb3484276

Tobias

On 7/29/20 10:23 AM, Jakub Jelinek wrote:

> On Wed, Jul 29, 2020 at 10:10:35AM +0200, Tobias Burnus wrote:
>> +        case AB_OMP_REQ_REVERSE_OFFLOAD:
>> +           gfc_omp_requires_add_clause (OMP_REQ_REVERSE_OFFLOAD,
>> +                                        "reverse_offload",
>> +                                        &gfc_current_locus,
>> +                                       module_name);
> Even visually something is wrong with the indentation here and in a few
> others, where I'd expect all the arguments to be in the same column (even
> with the > + before it, but they are not.  E.g. above I think the
> gfc_omp_... is indented by 3 instead of 2 columns from case, and "rev... and
> &gfc... match that, but module_name is probably correct.
>
>> +          break;
>> +        case AB_OMP_REQ_UNIFIED_ADDRESS:
>> +          gfc_omp_requires_add_clause (OMP_REQ_UNIFIED_ADDRESS,
>> +                                       "unified_address",
>> +                                        &gfc_current_locus,
> And e.g. here the &gfc... is off.
>> --- a/gcc/fortran/openmp.c
>> +++ b/gcc/fortran/openmp.c
>> @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
>>   #include "diagnostic.h"
>>   #include "gomp-constants.h"
>>
>> +
> Unnecessary.
>>   /* Match an end of OpenMP directive.  End of OpenMP directive is optional
>>      whitespace, followed by '\n' or comment '!'.  */
>>
>> @@ -3745,6 +3970,26 @@ gfc_match_omp_oacc_atomic (bool omp_p)
>>     new_st.op = (omp_p ? EXEC_OMP_ATOMIC : EXEC_OACC_ATOMIC);
>>     if (seq_cst)
>>       op = (gfc_omp_atomic_op) (op | GFC_OMP_ATOMIC_SEQ_CST);
>> +  else
> I wonder if this shouldn't be else if (omp_p), I'd think
> OpenACC atomics shouldn't be affected by OpenMP requires directive.
>
>>     for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
>>          gfc_current_ns = gfc_current_ns->sibling)
>> -    gfc_check_externals (gfc_current_ns);
>> +     gfc_check_omp_requires (gfc_current_ns, omp_requires);
> Again indentation looks weird.
>
> Otherwise LGTM.
>
>       Jakub
>
-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
diff mbox series

Patch

OpenMP: Add 'omp requires' to Fortran (mostly parsing)

gcc/fortran/ChangeLog:

	* gfortran.h (enum gfc_statement): Add ST_OMP_REQUIRES.
	(enum gfc_omp_requires_kind): New.
	(gfc_omp_requires, gfc_seen_omp_target,
	gfc_seen_omp_atomic_wo_memorder): New global vars.
	* match.h (gfc_match_omp_requires): New.
	* openmp.c (gfc_omp_requires, gfc_seen_omp_target,
	gfc_seen_omp_atomic_wo_memorder): New global vars.
	(gfc_match_omp_requires): New function.
	(gfc_match_omp_oacc_atomic): Set gfc_seen_omp_atomic_wo_memorder.
	* parse.c (decode_omp_directive): Parse 'omp requires', set
	gfc_seen_omp_target.
	(gfc_ascii_statement): Handle ST_OMP_REQUIRES.

gcc/testsuite/ChangeLog:

	* gfortran.dg/gomp/requires-1.f90: New test.
	* gfortran.dg/gomp/requires-2.f90: New test.
	* gfortran.dg/gomp/requires-3.f90: New test.
	* gfortran.dg/gomp/requires-4.f90: New test.
	* gfortran.dg/gomp/requires-5.f90: New test.
	* gfortran.dg/gomp/requires-6.f90: New test.
	* gfortran.dg/gomp/requires-7.f90: New test.

 gcc/fortran/gfortran.h                        |  20 +++-
 gcc/fortran/match.h                           |   1 +
 gcc/fortran/openmp.c                          | 157 ++++++++++++++++++++++++++
 gcc/fortran/parse.c                           |  31 ++++-
 gcc/testsuite/gfortran.dg/gomp/requires-1.f90 |  13 +++
 gcc/testsuite/gfortran.dg/gomp/requires-2.f90 |  12 ++
 gcc/testsuite/gfortran.dg/gomp/requires-3.f90 |   4 +
 gcc/testsuite/gfortran.dg/gomp/requires-4.f90 |  20 ++++
 gcc/testsuite/gfortran.dg/gomp/requires-5.f90 |  14 +++
 gcc/testsuite/gfortran.dg/gomp/requires-6.f90 |  10 ++
 gcc/testsuite/gfortran.dg/gomp/requires-7.f90 |  41 +++++++
 11 files changed, 321 insertions(+), 2 deletions(-)

diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 5fa86aa4e30..007cfe596d5 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -263,7 +263,7 @@  enum gfc_statement
   ST_OMP_TARGET_SIMD, ST_OMP_END_TARGET_SIMD,
   ST_OMP_TASKLOOP, ST_OMP_END_TASKLOOP,
   ST_OMP_TASKLOOP_SIMD, ST_OMP_END_TASKLOOP_SIMD, ST_OMP_ORDERED_DEPEND,
-  ST_PROCEDURE, ST_GENERIC, ST_CRITICAL, ST_END_CRITICAL,
+  ST_OMP_REQUIRES, ST_PROCEDURE, ST_GENERIC, ST_CRITICAL, ST_END_CRITICAL,
   ST_GET_FCN_CHARACTERISTICS, ST_LOCK, ST_UNLOCK, ST_EVENT_POST,
   ST_EVENT_WAIT, ST_FAIL_IMAGE, ST_FORM_TEAM, ST_CHANGE_TEAM,
   ST_END_TEAM, ST_SYNC_TEAM, ST_NONE
@@ -1334,6 +1334,20 @@  enum gfc_omp_if_kind
   OMP_IF_LAST
 };
 
+enum gfc_omp_requires_kind
+{
+  OMP_REQ_REVERSE_OFFLOAD = (1 << 0),
+  OMP_REQ_UNIFIED_ADDRESS = (1 << 1),
+  OMP_REQ_UNIFIED_SHARED_MEMORY = (1 << 2),
+  OMP_REQ_DYNAMIC_ALLOCATORS = (1 << 3),
+  OMP_REQ_ATOMIC_MEM_ORDER_SEQ_CST = (1 << 4),
+  OMP_REQ_ATOMIC_MEM_ORDER_ACQ_REL = (1 << 5),
+  OMP_REQ_ATOMIC_MEM_ORDER_RELAXED = (1 << 6),
+  OMP_REQ_ATOMIC_MEM_ORDER_MASK = (OMP_REQ_ATOMIC_MEM_ORDER_SEQ_CST
+				   | OMP_REQ_ATOMIC_MEM_ORDER_ACQ_REL
+				   | OMP_REQ_ATOMIC_MEM_ORDER_RELAXED)
+};
+
 typedef struct gfc_omp_clauses
 {
   struct gfc_expr *if_expr;
@@ -3269,6 +3283,10 @@  void gfc_free_case_list (gfc_case *);
 gfc_expr *gfc_get_parentheses (gfc_expr *);
 
 /* openmp.c */
+extern int gfc_omp_requires;
+extern bool gfc_seen_omp_target;
+extern bool gfc_seen_omp_atomic_wo_memorder;
+
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
 void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h
index b3fb7033891..7bf70d77016 100644
--- a/gcc/fortran/match.h
+++ b/gcc/fortran/match.h
@@ -177,6 +177,7 @@  match gfc_match_omp_parallel_do (void);
 match gfc_match_omp_parallel_do_simd (void);
 match gfc_match_omp_parallel_sections (void);
 match gfc_match_omp_parallel_workshare (void);
+match gfc_match_omp_requires (void);
 match gfc_match_omp_sections (void);
 match gfc_match_omp_simd (void);
 match gfc_match_omp_single (void);
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index f8f2439b6e4..3748734392a 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -28,6 +28,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "gomp-constants.h"
 
+int gfc_omp_requires = 0;
+bool gfc_seen_omp_target = false, gfc_seen_omp_atomic_wo_memorder = false;
+
 /* Match an end of OpenMP directive.  End of OpenMP directive is optional
    whitespace, followed by '\n' or comment '!'.  */
 
@@ -3424,6 +3427,158 @@  gfc_match_omp_parallel_workshare (void)
   return match_omp (EXEC_OMP_PARALLEL_WORKSHARE, OMP_PARALLEL_CLAUSES);
 }
 
+match
+gfc_match_omp_requires (void)
+{
+  static const char *clauses[] = {"reverse_offload",
+				  "unified_address",
+				  "unified_shared_memory",
+				  "dynamic_allocators",
+				  "atomic_default"};
+  const char *clause = NULL;
+  int requires_clauses = 0;
+  bool first = true;
+  locus old_loc;
+
+  if (gfc_current_ns->parent)
+    {
+      gfc_error ("!$OMP REQUIRES at %C must appear in the specification part "
+		 "of a program unit");
+      return MATCH_ERROR;
+    }
+
+  while (true)
+    {
+      old_loc = gfc_current_locus;
+      int requires_clause = 0;
+      if ((first || gfc_match_char (',') != MATCH_YES)
+	  && (first && gfc_match_space () != MATCH_YES))
+	goto error;
+      first = false;
+      gfc_gobble_whitespace ();
+      old_loc = gfc_current_locus;
+
+      if (gfc_match_omp_eos () != MATCH_NO)
+	break;
+      if (gfc_match (clauses[0]) == MATCH_YES)
+	{
+	  clause = clauses[0];
+	  requires_clause = (int) OMP_REQ_REVERSE_OFFLOAD;
+	  if (requires_clauses & (int) OMP_REQ_REVERSE_OFFLOAD)
+	    goto duplicate_clause;
+	  if (gfc_seen_omp_target)
+	    goto requires_after_target;
+	  if (gfc_omp_requires & (int) OMP_REQ_REVERSE_OFFLOAD)
+	    goto next;
+	}
+      else if (gfc_match (clauses[1]) == MATCH_YES)
+	{
+	  clause = clauses[1];
+	  requires_clause = (int) OMP_REQ_UNIFIED_ADDRESS;
+	  if (requires_clauses & (int) OMP_REQ_UNIFIED_ADDRESS)
+	    goto duplicate_clause;
+	  if (gfc_seen_omp_target)
+	    goto requires_after_target;
+	  if (gfc_omp_requires & (int) OMP_REQ_UNIFIED_ADDRESS)
+	    goto next;
+	}
+      else if (gfc_match (clauses[2]) == MATCH_YES)
+	{
+	  clause = clauses[2];
+	  requires_clause = (int) OMP_REQ_UNIFIED_SHARED_MEMORY;
+	  if (requires_clauses & (int) OMP_REQ_UNIFIED_SHARED_MEMORY)
+	    goto duplicate_clause;
+	  if (gfc_seen_omp_target)
+	    goto requires_after_target;
+	  if (gfc_omp_requires & (int) OMP_REQ_UNIFIED_SHARED_MEMORY)
+	    goto next;
+	}
+      else if (gfc_match (clauses[3]) == MATCH_YES)
+	{
+	  clause = clauses[3];
+	  requires_clause = (int)  OMP_REQ_DYNAMIC_ALLOCATORS;
+	  if (requires_clauses & (int) OMP_REQ_DYNAMIC_ALLOCATORS)
+	    goto duplicate_clause;
+	  if (gfc_omp_requires & (int) OMP_REQ_DYNAMIC_ALLOCATORS)
+	    goto next;
+	}
+      else if (gfc_match ("atomic_default_mem_order (") == MATCH_YES)
+	{
+	  clause = clauses[4];
+	  if (requires_clauses & OMP_REQ_ATOMIC_MEM_ORDER_MASK)
+	    goto duplicate_clause;
+	  if (gfc_match (" seq_cst )") == MATCH_YES)
+	    requires_clause = (int) OMP_REQ_ATOMIC_MEM_ORDER_SEQ_CST;
+	  else if (gfc_match (" acq_rel )") == MATCH_YES)
+	    requires_clause = (int) OMP_REQ_ATOMIC_MEM_ORDER_ACQ_REL;
+	  else if (gfc_match (" relaxed )") == MATCH_YES)
+	    requires_clause = (int) OMP_REQ_ATOMIC_MEM_ORDER_RELAXED;
+	  else
+	    {
+	      gfc_error ("Expected SEQ_CST, ACQ_REL or RELAXED for "
+			 "ATOMIC_DEFAULT_MEM_ORDER clause at %C");
+	      goto error;
+	    }
+	  if ((gfc_omp_requires & OMP_REQ_ATOMIC_MEM_ORDER_MASK)
+	      && ((gfc_omp_requires & OMP_REQ_ATOMIC_MEM_ORDER_MASK)
+		  != (requires_clause & OMP_REQ_ATOMIC_MEM_ORDER_MASK)))
+	    {
+	      gfc_error ("ATOMIC_DEFAULT_MEM_ORDER clause at %L "
+			 "specified with different value than previously "
+			 "in the same translation unit", &old_loc);
+	      goto error;
+	    }
+	  if (gfc_omp_requires & OMP_REQ_ATOMIC_MEM_ORDER_MASK)
+	    {
+	      gfc_error ("Only one ATOMIC_DEFAULT_MEM_ORDER clause such as at "
+			 "%L per translation unit permitted", &old_loc);
+	      goto error;
+	    }
+	  if (gfc_seen_omp_atomic_wo_memorder)
+	    {
+	      gfc_error ("OMP REQUIRES with ATOMIC_DEFAULT_MEM_ORDER clause at "
+			 "%L specified after first use of an OMP ATOMIC which "
+			 "uses the default-memory order", &old_loc);
+	      goto error;
+	    }
+	  /* TODO: Middle-end support exists, but not yet FE support.  */
+	  if (requires_clause != OMP_REQ_ATOMIC_MEM_ORDER_SEQ_CST)
+	    gfc_error_now ("Sorry, only SEQ_CST is currently supported for the "
+			   "ATOMIC_DEFAULT_MEM_ORDER clause at %L on the "
+			   "REQUIRES directive", &old_loc);
+	}
+      else
+	goto error;
+next:
+      requires_clauses |= requires_clause;
+      if (requires_clause & ~((int) OMP_REQ_ATOMIC_MEM_ORDER_MASK))
+	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
+		       "yet supported", clause, &old_loc);
+    }
+
+  gfc_omp_requires |= requires_clauses;
+  if (requires_clauses == 0)
+    {
+      if (!gfc_error_flag_test ())
+	gfc_error ("Clause expected at %C");
+      goto error;
+    }
+  return MATCH_YES;
+
+requires_after_target:
+  gfc_error ("Clause %qs at %L specified after a device construct/routine",
+	     clause, &old_loc);
+  goto error;
+duplicate_clause:
+  gfc_error ("%qs clause at %L specified more than once", clause, &old_loc);
+error:
+  if (!gfc_error_flag_test ())
+    gfc_error ("Expected UNIFIED_ADDRESS, UNIFIED_SHARED_MEMORY, "
+	       "DYNAMIC_ALLOCATORS, REVERSE_OFFLOAD, or "
+	       "ATOMIC_DEFAULT_MEM_ORDER clause at %L", &old_loc);
+  return MATCH_ERROR;
+}
+
 
 match
 gfc_match_omp_sections (void)
@@ -3745,6 +3900,8 @@  gfc_match_omp_oacc_atomic (bool omp_p)
   new_st.op = (omp_p ? EXEC_OMP_ATOMIC : EXEC_OACC_ATOMIC);
   if (seq_cst)
     op = (gfc_omp_atomic_op) (op | GFC_OMP_ATOMIC_SEQ_CST);
+  else
+    gfc_seen_omp_atomic_wo_memorder = true;
   new_st.ext.omp_atomic = op;
   return MATCH_YES;
 }
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 96fd4aaee5e..6d200da2f1c 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -995,6 +995,9 @@  decode_omp_directive (void)
 	      ST_OMP_PARALLEL_WORKSHARE);
       matcho ("parallel", gfc_match_omp_parallel, ST_OMP_PARALLEL);
       break;
+    case 'r':
+      matcho ("requires", gfc_match_omp_requires, ST_OMP_REQUIRES);
+      break;
     case 's':
       matcho ("sections", gfc_match_omp_sections, ST_OMP_SECTIONS);
       matcho ("section", gfc_match_omp_eos_error, ST_OMP_SECTION);
@@ -1086,6 +1089,28 @@  decode_omp_directive (void)
 	  return ST_NONE;
 	}
     }
+  switch (ret)
+    {
+    case ST_OMP_DECLARE_TARGET:
+    case ST_OMP_TARGET:
+    case ST_OMP_TARGET_DATA:
+    case ST_OMP_TARGET_ENTER_DATA:
+    case ST_OMP_TARGET_EXIT_DATA:
+    case ST_OMP_TARGET_TEAMS:
+    case ST_OMP_TARGET_TEAMS_DISTRIBUTE:
+    case ST_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD:
+    case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO:
+    case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
+    case ST_OMP_TARGET_PARALLEL:
+    case ST_OMP_TARGET_PARALLEL_DO:
+    case ST_OMP_TARGET_PARALLEL_DO_SIMD:
+    case ST_OMP_TARGET_SIMD:
+    case ST_OMP_TARGET_UPDATE:
+      gfc_seen_omp_target = true;
+      break;
+    default:
+      break;
+    }
   return ret;
 
  do_spec_only:
@@ -1604,7 +1629,8 @@  next_statement (void)
 /* OpenMP declaration statements.  */
 
 #define case_omp_decl case ST_OMP_THREADPRIVATE: case ST_OMP_DECLARE_SIMD: \
-  case ST_OMP_DECLARE_TARGET: case ST_OMP_DECLARE_REDUCTION
+  case ST_OMP_DECLARE_TARGET: case ST_OMP_DECLARE_REDUCTION: \
+  case ST_OMP_REQUIRES
 
 /* Block end statements.  Errors associated with interchanging these
    are detected in gfc_match_end().  */
@@ -2407,6 +2433,9 @@  gfc_ascii_statement (gfc_statement st)
     case ST_OMP_PARALLEL_WORKSHARE:
       p = "!$OMP PARALLEL WORKSHARE";
       break;
+    case ST_OMP_REQUIRES:
+      p = "!$OMP REQUIRES";
+      break;
     case ST_OMP_SECTIONS:
       p = "!$OMP SECTIONS";
       break;
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-1.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-1.f90
new file mode 100644
index 00000000000..b115a654e71
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-1.f90
@@ -0,0 +1,13 @@ 
+subroutine foo
+!$omp requires unified_address
+!$omp requires unified_shared_memory
+!$omp requires unified_shared_memory unified_address
+!$omp requires dynamic_allocators,reverse_offload
+end
+
+subroutine bar
+!$omp requires unified_shared_memory unified_address
+!$omp requires atomic_default_mem_order(seq_cst)
+end
+
+! { dg-prune-output "not yet supported" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-2.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-2.f90
new file mode 100644
index 00000000000..26ff019e986
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-2.f90
@@ -0,0 +1,12 @@ 
+!$omp requires	! { dg-error "Clause expected" }
+!$omp requires unified_shared_memory,unified_shared_memory	! { dg-error "specified more than once" }
+!$omp requires unified_address	unified_address	! { dg-error "specified more than once" }
+!$omp requires reverse_offload reverse_offload	! { dg-error "specified more than once" }
+!$omp requires foobarbaz	! { dg-error "Expected UNIFIED_ADDRESS, UNIFIED_SHARED_MEMORY, DYNAMIC_ALLOCATORS, REVERSE_OFFLOAD, or ATOMIC_DEFAULT_MEM_ORDER clause" }
+!$omp requires dynamic_allocators , dynamic_allocators	! { dg-error "specified more than once" }
+!$omp requires atomic_default_mem_order(seq_cst) atomic_default_mem_order(seq_cst)	! { dg-error "specified more than once" }
+!$omp requires atomic_default_mem_order (seq_cst)
+!$omp requires atomic_default_mem_order (seq_cst)	! { dg-error "Only one ATOMIC_DEFAULT_MEM_ORDER clause such as at .1. per translation unit permitted" }
+end
+
+! { dg-prune-output "not yet supported" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-3.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-3.f90
new file mode 100644
index 00000000000..4429aab2ee6
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-3.f90
@@ -0,0 +1,4 @@ 
+!$omp requires atomic_default_mem_order(acquire)	! { dg-error "Expected SEQ_CST, ACQ_REL or RELAXED for ATOMIC_DEFAULT_MEM_ORDER clause" }
+!$omp requires atomic_default_mem_order(release)	! { dg-error "Expected SEQ_CST, ACQ_REL or RELAXED for ATOMIC_DEFAULT_MEM_ORDER clause" }
+!$omp requires atomic_default_mem_order(foobar)	! { dg-error "Expected SEQ_CST, ACQ_REL or RELAXED for ATOMIC_DEFAULT_MEM_ORDER clause" }
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-4.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
new file mode 100644
index 00000000000..d7af86905d3
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
@@ -0,0 +1,20 @@ 
+subroutine bar
+!$omp requires unified_shared_memory,unified_address,reverse_offload
+end
+
+subroutine foo
+  !$omp target
+  !$omp end target
+end
+
+subroutine foobar
+i = 5  ! < execution statement
+!$omp requires atomic_default_mem_order(seq_cst) ! { dg-error "Unexpected ..OMP REQUIRES statement" }
+end
+
+!$omp requires dynamic_allocators ! OK
+!$omp requires unified_shared_memory	! { dg-error "specified after a device construct/routine" }
+!$omp requires unified_address	! { dg-error "specified after a device construct/routine" }
+!$omp requires reverse_offload	! { dg-error "specified after a device construct/routine" }
+end
+! { dg-prune-output "not yet supported" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-5.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-5.f90
new file mode 100644
index 00000000000..8b85e236f8a
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-5.f90
@@ -0,0 +1,14 @@ 
+subroutine bar
+!$omp requires atomic_default_mem_order(seq_cst)
+!$omp requires unified_shared_memory
+end
+
+subroutine foo
+!$omp requires unified_shared_memory
+!$omp requires atomic_default_mem_order(relaxed) ! { dg-error "specified with different value than previously in the same translation unit" }
+!$omp requires atomic_default_mem_order(seq_cst) ! { dg-error "Only one ATOMIC_DEFAULT_MEM_ORDER clause such as at .1. per translation unit permitted" }
+  !$omp target
+  !$omp end target
+end
+
+! { dg-prune-output "not yet supported" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-6.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-6.f90
new file mode 100644
index 00000000000..00326e0be4d
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-6.f90
@@ -0,0 +1,10 @@ 
+subroutine bar
+!$omp atomic
+ i = i + 5
+end
+
+subroutine foo
+!$omp requires atomic_default_mem_order(seq_cst) ! { dg-error "specified after first use of an OMP ATOMIC which uses the default-memory order" }
+end
+
+! { dg-prune-output "not yet supported" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-7.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-7.f90
new file mode 100644
index 00000000000..3d75b89e00b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-7.f90
@@ -0,0 +1,41 @@ 
+subroutine bar2
+  block
+    !$omp requires unified_shared_memory ! { dg-error "must appear in the specification part of a program unit" }
+  end block
+end
+
+subroutine bar
+contains
+  subroutine foo
+    !$omp requires unified_shared_memory ! { dg-error "must appear in the specification part of a program unit" }
+  end
+end
+
+module m
+contains
+  subroutine foo
+    !$omp requires unified_shared_memory ! { dg-error "must appear in the specification part of a program unit" }
+  end
+end
+
+module m2
+ interface
+  module subroutine foo()
+  end
+ end interface
+end
+
+submodule (m2) m2_sub
+    !$omp requires unified_shared_memory
+contains
+  module procedure foo
+  end
+end
+
+program main
+contains
+  subroutine foo
+    !$omp requires unified_shared_memory ! { dg-error "must appear in the specification part of a program unit" }
+  end
+end
+! { dg-prune-output "not yet supported" }