public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH, OpenMP 5.0] More implementation of the requires directive
@ 2021-01-13 15:07 Chung-Lin Tang
  2021-01-13 15:27 ` Jakub Jelinek
                   ` (3 more replies)
  0 siblings, 4 replies; 42+ messages in thread
From: Chung-Lin Tang @ 2021-01-13 15:07 UTC (permalink / raw)
  To: gcc-patches, Jakub Jelinek, Catherine Moore, Andrew Stubbs

[-- Attachment #1: Type: text/plain, Size: 3971 bytes --]

Hi Jakub,
this patch provides more implementation of the requires directive, basically:

(1) The collection of the reverse_offload, unified_address, and unified_shared_memory
clauses into a .gnu.gomp_requires section

(2) libgomp checking of consistency across the entire .gnu.gomp_requires section,
and querying into the offload plugin to see if the offload target supports the required
features (as of now, the setting is that none of those features are supported by any
of the plugins).

We currently emit errors, but do not fatally cause exit of the program if those
are not met. We're still unsure if complete block-out of program execution is the right
thing for the user. This can be discussed later.

Is this okay for trunk after stage1 re-opens?

Thanks,
Chung-Lin

2021-01-13  Chung-Lin Tang  <cltang@codesourcery.com>

	gcc/c/
	* c-parser.c (c_parser_declaration_or_fndef): Set
	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
	"omp declare target" attribute.
	(c_parser_omp_target_data): Set	OMP_REQUIRES_TARGET_USED in
	omp_requires_mask.
	(c_parser_omp_target_enter_data): Likewise.
	(c_parser_omp_target_exit_data): Likewise.
	(c_parser_omp_requires): Adjust to only mention "not implemented yet"
	for OMP_REQUIRES_DYNAMIC_ALLOCATORS.

	gcc/cp/
	* parser.c (cp_parser_simple_declaration): Set
	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
	"omp declare target" attribute.
	(cp_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
	omp_requires_mask.
	(cp_parser_omp_target_enter_data): Likewise.
	(cp_parser_omp_target_exit_data): Likewise.
	(cp_parser_omp_requires): Adjust to only mention "not implemented yet"
	for OMP_REQUIRES_DYNAMIC_ALLOCATORS.

	gcc/fortran/
	* openmp.c (gfc_check_omp_requires): Fix REVERSE_OFFLOAD typo.
	(gfc_match_omp_requires): Adjust to only mention "not implemented yet"
	for OMP_REQUIRES_DYNAMIC_ALLOCATORS.
	* parse.c ("tree.h"): Add include.
	("omp-general.h"): Likewise.
	(gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.

	gcc/
	* omp-offload.c (omp_finish_file): Add code to reate OpenMP requires
	mask variable in .gnu.gomp_requires section if needed.

	gcc/testsuite/
	* c-c++-common/gomp/requires-4.c: Remove prune of "not supported yet".
	* gcc/testsuite/gfortran.dg/gomp/requires-4.f90: Fix REVERSE_OFFLOAD typo.
	* gcc/testsuite/gfortran.dg/gomp/requires-8.f90: Likewise.

	include/
	* gomp-constants.h (GOMP_REQUIRES_UNIFIED_ADDRESS): New symbol.
	(GOMP_REQUIRES_UNIFIED_SHARED_MEMORY): Likewise.
	(GOMP_REQUIRES_REVERSE_OFFLOAD): Likewise.

	libgcc/
	* offloadstuff.c (__requires_mask_table): New symbol to mark start of
	.gnu.gomp_requires section.
	(__requires_mask_table_end): New symbol to mark end of
	.gnu.gomp_requires section.

	libgomp/
	* libgomp-plugin.h (GOMP_OFFLOAD_supported_features): New declaration.
	* libgomp.h (struct gomp_device_descr): New 'supported_features_func'
	plugin hook field.
	* oacc-host.c (host_supported_features): New host hook function.
	(host_dispatch): Initialize 'supported_features_func' host hook.
	* plugin/plugin-gcn.c (GOMP_OFFLOAD_supported_features): New function.
	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_supported_features): Likewise.
	* target.c (<stdio.h>): Add include of standard header.
	(gomp_requires_mask): New static variable.
	(__requires_mask_table): New declaration.
	(__requires_mask_table_end): Likewise.
	(gomp_load_plugin_for_device): Add loading of 'supported_features' hook.
	(gomp_target_init): Add code to summarize .gnu._gomp_requires section
	mask values, emit error if inconsistency found.

	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New file linked with
	above test.
	* testsuite/libgomp.c-c++-common/requires-2.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New file linked with
	above test.

	liboffloadmic/
	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_supported_features):
	New function.

[-- Attachment #2: requires.patch --]
[-- Type: text/plain, Size: 20745 bytes --]

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index c77d9fccdc2..e685b26746e 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -2475,6 +2475,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	  break;
 	}
 
+      if (flag_openmp
+	  && lookup_attribute ("omp declare target",
+			       DECL_ATTRIBUTES (current_function_decl)))
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
       if (DECL_DECLARED_INLINE_P (current_function_decl))
         tv = TV_PARSE_INLINE;
       else
@@ -19556,6 +19562,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
 static tree
 c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				"#pragma omp target data");
@@ -19698,6 +19708,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
       return NULL_TREE;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				"#pragma omp target enter data");
@@ -19784,6 +19798,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
       return NULL_TREE;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				"#pragma omp target exit data");
@@ -21371,7 +21389,7 @@ c_parser_omp_requires (c_parser *parser)
 	      c_parser_skip_to_pragma_eol (parser, false);
 	      return;
 	    }
-	  if (p)
+	  if (this_req == OMP_REQUIRES_DYNAMIC_ALLOCATORS)
 	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
 			    "supported yet", p);
 	  if (p)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c713852fe93..afbc4e551d4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14455,6 +14455,11 @@ cp_parser_simple_declaration (cp_parser* parser,
 	  /* Otherwise, we're done with the list of declarators.  */
 	  else
 	    {
+	      if (flag_openmp && lookup_attribute ("omp declare target",
+						   DECL_ATTRIBUTES (decl)))
+		omp_requires_mask
+		  = (enum omp_requires) (omp_requires_mask
+					 | OMP_REQUIRES_TARGET_USED);
 	      pop_deferring_access_checks ();
 	      return;
 	    }
@@ -41432,6 +41437,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
 static tree
 cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				 "#pragma omp target data", pragma_tok);
@@ -41535,6 +41544,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
       return NULL_TREE;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				 "#pragma omp target enter data", pragma_tok);
@@ -41625,6 +41638,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
       return NULL_TREE;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				 "#pragma omp target exit data", pragma_tok);
@@ -43819,7 +43836,7 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
 	      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	      return false;
 	    }
-	  if (p)
+	  if (this_req == OMP_REQUIRES_DYNAMIC_ALLOCATORS)
 	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
 			    "supported yet", p);
 	  if (p)
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index cb166f956b7..c25531a4989 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -3668,7 +3668,7 @@ gfc_check_omp_requires (gfc_namespace *ns, int ref_omp_requires)
       if ((ref_omp_requires & OMP_REQ_REVERSE_OFFLOAD)
 	  && !(ns->omp_requires & OMP_REQ_REVERSE_OFFLOAD))
 	gfc_error ("Program unit at %L has OpenMP device constructs/routines "
-		   "but does not set !$OMP REQUIRES REVERSE_OFFSET but other "
+		   "but does not set !$OMP REQUIRES REVERSE_OFFLOAD but other "
 		   "program units do", &ns->proc_name->declared_at);
       if ((ref_omp_requires & OMP_REQ_UNIFIED_ADDRESS)
 	  && !(ns->omp_requires & OMP_REQ_UNIFIED_ADDRESS))
@@ -3855,7 +3855,8 @@ gfc_match_omp_requires (void)
       else
 	goto error;
 
-      if (requires_clause & ~OMP_REQ_ATOMIC_MEM_ORDER_MASK)
+      /* Currently, everything except 'dynamic_allocators' is allowed.  */
+      if (requires_clause == OMP_REQ_DYNAMIC_ALLOCATORS)
 	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
 		       "yet supported", clause, &old_loc);
       if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 1549f8e1635..4731bca2cf7 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -22,10 +22,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "options.h"
+#include "tree.h"
 #include "gfortran.h"
 #include <setjmp.h>
 #include "match.h"
 #include "parse.h"
+#include "omp-general.h"
 
 /* Current statement label.  Zero means no statement label.  Because new_st
    can get wiped during statement matching, we have to keep it separate.  */
@@ -6572,6 +6574,23 @@ done:
        gfc_current_ns = gfc_current_ns->sibling)
     gfc_check_omp_requires (gfc_current_ns, omp_requires);
 
+  if (omp_requires)
+    {
+      omp_requires_mask = (enum omp_requires) OMP_REQUIRES_TARGET_USED;
+      if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_REVERSE_OFFLOAD);
+      if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_ADDRESS);
+      if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+    }
+
   /* Do the parse tree dump.  */
   gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
 
diff --git a/gcc/omp-offload.c b/gcc/omp-offload.c
index ba0937fba94..9cc7d2945fc 100644
--- a/gcc/omp-offload.c
+++ b/gcc/omp-offload.c
@@ -437,6 +437,24 @@ omp_finish_file (void)
 
       varpool_node::finalize_decl (vars_decl);
       varpool_node::finalize_decl (funcs_decl);
+
+      if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
+	{
+	  const char *requires_section = ".gnu.gomp_requires";
+	  tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+				     get_identifier (".gomp_requires_mask"),
+				     unsigned_type_node);
+	  SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));
+	  TREE_STATIC (maskvar) = 1;
+	  DECL_INITIAL (maskvar)
+	    = build_int_cst (unsigned_type_node,
+			     ((unsigned int) omp_requires_mask
+			      & (OMP_REQUIRES_UNIFIED_ADDRESS
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+				 | OMP_REQUIRES_REVERSE_OFFLOAD)));
+	  set_decl_section_name (maskvar, requires_section);
+	  varpool_node::finalize_decl (maskvar);
+	}
     }
   else
     {
diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
index 88ba7746cf8..8f45d83ea6e 100644
--- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
@@ -9,5 +9,3 @@ foo (void)
 #pragma omp requires unified_shared_memory	/* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires unified_address	/* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires reverse_offload	/* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
-
-/* { dg-prune-output "not supported yet" } */
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-4.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
index b17aceb898b..c870a2840d3 100644
--- a/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
@@ -9,7 +9,7 @@ end module m
 subroutine foo
   !$omp target
   !$omp end target
-! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFSET but other program units do" "" { target *-*-* } 9 }
+! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" "" { target *-*-* } 9 }
 ! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_ADDRESS but other program units do" "" { target *-*-* } 9 }
 ! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do" "" { target *-*-* } 9 }
 end
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
index 3c32ae9860e..3819b0c28cc 100644
--- a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
@@ -13,7 +13,7 @@ contains
  end subroutine foo
 end module m
 
-subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFSET but other program units do" }
+subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
   !use m
   !$omp requires unified_shared_memory
   !$omp declare target
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 11a9308e3d2..d5a0b2c5ea7 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -301,6 +301,12 @@ enum gomp_map_kind
 #define GOMP_DEPEND_INOUT		3
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 
+/* Flag values for requires-directive features, must match corresponding
+   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
+#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
+#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgcc/offloadstuff.c b/libgcc/offloadstuff.c
index b19428af6d8..78210a88f15 100644
--- a/libgcc/offloadstuff.c
+++ b/libgcc/offloadstuff.c
@@ -54,6 +54,9 @@ const void *const __offload_var_table[0]
   __attribute__ ((__used__, visibility ("hidden"),
 		  section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
 
+const unsigned int const __requires_mask_table[0]
+  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
+
 #elif defined CRT_END
 
 const void *const __offload_funcs_end[0]
@@ -63,6 +66,9 @@ const void *const __offload_vars_end[0]
   __attribute__ ((__used__, visibility ("hidden"),
 		  section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
 
+const unsigned int const __requires_mask_table_end[0]
+  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
+
 #elif defined CRT_TABLE
 
 extern const void *const __offload_func_table[];
@@ -77,6 +83,9 @@ const void *const __OFFLOAD_TABLE__[]
   &__offload_var_table, &__offload_vars_end
 };
 
+extern const unsigned int const __requires_mask_table[];
+extern const unsigned int const __requires_mask_table_end[];
+
 #else /* ! CRT_BEGIN && ! CRT_END && ! CRT_TABLE  */
 #error "One of CRT_BEGIN, CRT_END or CRT_TABLE must be defined."
 #endif
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 62645ce9954..f54469fdd6b 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -122,6 +122,7 @@ extern int GOMP_OFFLOAD_get_type (void);
 extern int GOMP_OFFLOAD_get_num_devices (void);
 extern bool GOMP_OFFLOAD_init_device (int);
 extern bool GOMP_OFFLOAD_fini_device (int);
+extern bool GOMP_OFFLOAD_supported_features (unsigned *);
 extern unsigned GOMP_OFFLOAD_version (void);
 extern int GOMP_OFFLOAD_load_image (int, unsigned, const void *,
 				    struct addr_pair **);
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 305cba3aa02..09f2ac67943 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1130,6 +1130,7 @@ struct gomp_device_descr
   __typeof (GOMP_OFFLOAD_get_num_devices) *get_num_devices_func;
   __typeof (GOMP_OFFLOAD_init_device) *init_device_func;
   __typeof (GOMP_OFFLOAD_fini_device) *fini_device_func;
+  __typeof (GOMP_OFFLOAD_supported_features) *supported_features_func;
   __typeof (GOMP_OFFLOAD_version) *version_func;
   __typeof (GOMP_OFFLOAD_load_image) *load_image_func;
   __typeof (GOMP_OFFLOAD_unload_image) *unload_image_func;
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index f3bbd2b9c61..94a7fac2a39 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -71,6 +71,12 @@ host_fini_device (int n __attribute__ ((unused)))
   return true;
 }
 
+static bool
+host_supported_features (unsigned int *n)
+{
+  return (*n == 0);
+}
+
 static unsigned
 host_version (void)
 {
@@ -273,6 +279,7 @@ static struct gomp_device_descr host_dispatch =
     .get_num_devices_func = host_get_num_devices,
     .init_device_func = host_init_device,
     .fini_device_func = host_fini_device,
+    .supported_features_func = host_supported_features,
     .version_func = host_version,
     .load_image_func = host_load_image,
     .unload_image_func = host_unload_image,
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 47f0b6e25f8..718d78173fe 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -3991,4 +3991,12 @@ GOMP_OFFLOAD_openacc_destroy_thread_data (void *data)
   free (data);
 }
 
+/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
+
+bool
+GOMP_OFFLOAD_supported_features (unsigned int *mask)
+{
+  return (*mask == 0);
+}
+
 /* }}} */
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 681c344b9c2..4cc25fbe232 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1236,6 +1236,14 @@ GOMP_OFFLOAD_fini_device (int n)
   return true;
 }
 
+/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
+
+bool
+GOMP_OFFLOAD_supported_features (unsigned int *mask)
+{
+  return (*mask == 0);
+}
+
 /* Return the libgomp version number we're compatible with.  There is
    no requirement for cross-version compatibility.  */
 
diff --git a/libgomp/target.c b/libgomp/target.c
index 4a4e1f80745..f06df7ba28d 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -31,6 +31,7 @@
 #include "gomp-constants.h"
 #include <limits.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #ifdef HAVE_INTTYPES_H
 # include <inttypes.h>  /* For PRIu64.  */
@@ -79,6 +80,16 @@ static int num_devices;
 /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
 static int num_devices_openmp;
 
+/* Mask of requires directive clause values, summarized from .gnu.gomp.requires
+   section. Offload plugins are queried with this mask to see if all required
+   features are supported.  */
+static unsigned int gomp_requires_mask;
+
+/* Start/end of .gnu.gomp.requires section of program, defined in
+   crtoffloadbegin/end.o.  */
+extern const unsigned int __requires_mask_table[];
+extern const unsigned int __requires_mask_table_end[];
+
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
 
 static void *
@@ -1961,6 +1972,20 @@ gomp_init_device (struct gomp_device_descr *devicep)
       gomp_fatal ("device initialization failed");
     }
 
+  unsigned int features = gomp_requires_mask;
+  if (!devicep->supported_features_func (&features))
+    {
+      char buf[64], *end = buf + sizeof (buf), *p = buf;
+      if (features & GOMP_REQUIRES_UNIFIED_ADDRESS)
+	p += snprintf (p, end - p, "unified_address");
+      if (features & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+	p += snprintf (p, end - p, "%sunified_shared_memory",
+		       (p == buf ? "" : ", "));
+      if (features & GOMP_REQUIRES_REVERSE_OFFLOAD)
+	p += snprintf (p, end - p, "%sreverse_offload", (p == buf ? "" : ", "));
+      gomp_error ("device does not support required features: %s", buf);
+    }
+
   /* Load to device all images registered by the moment.  */
   for (i = 0; i < num_offload_images; i++)
     {
@@ -3200,6 +3225,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
   DLSYM (get_num_devices);
   DLSYM (init_device);
   DLSYM (fini_device);
+  DLSYM (supported_features);
   DLSYM (load_image);
   DLSYM (unload_image);
   DLSYM (alloc);
@@ -3310,6 +3336,28 @@ gomp_target_init (void)
   if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED)
     return;
 
+  gomp_requires_mask = 0;
+  const unsigned int *mask_ptr = __requires_mask_table;
+  bool error_emitted = false;
+  while (mask_ptr != __requires_mask_table_end)
+    {
+      if (gomp_requires_mask == 0)
+	gomp_requires_mask = *mask_ptr;
+      else if (gomp_requires_mask != *mask_ptr)
+	{
+	  if (!error_emitted)
+	    {
+	      gomp_error ("requires-directive clause inconsistency between "
+			  "compilation units detected");
+	      error_emitted = true;
+	    }
+	  /* This is inconsistent, but still merge to query for all features
+	     later.  */
+	  gomp_requires_mask |= *mask_ptr;
+	}
+      mask_ptr++;
+    }
+
   cur = OFFLOAD_PLUGINS;
   if (*cur)
     do
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
new file mode 100644
index 00000000000..8b9341523c6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
new file mode 100644
index 00000000000..b5a3c512d28
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -0,0 +1,21 @@
+/* { dg-additional-sources requires-1-aux.c } */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "libgomp: requires-directive clause inconsistency between compilation units detected" } */
+/* { dg-prune-output "device does not support required features" } */
+/* { dg-shouldfail "" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
new file mode 100644
index 00000000000..8b9341523c6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
new file mode 100644
index 00000000000..6fb280baabd
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -0,0 +1,20 @@
+/* { dg-additional-sources requires-2-aux.c } */
+
+#pragma omp requires reverse_offload
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "libgomp: device does not support required features: reverse_offload" } */
+/* { dg-shouldfail "" } */
diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
index d1678d0514e..f92418fa416 100644
--- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
+++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
@@ -233,6 +233,14 @@ GOMP_OFFLOAD_fini_device (int device)
   return true;
 }
 
+/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
+
+extern "C" bool
+GOMP_OFFLOAD_supported_features (unsigned int *mask)
+{
+  return (*mask == 0);
+}
+
 static bool
 get_target_table (int device, int &num_funcs, int &num_vars, void **&table)
 {

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH, OpenMP 5.0] More implementation of the requires directive
  2021-01-13 15:07 [PATCH, OpenMP 5.0] More implementation of the requires directive Chung-Lin Tang
@ 2021-01-13 15:27 ` Jakub Jelinek
  2021-03-25 11:18 ` Thomas Schwinge
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 42+ messages in thread
From: Jakub Jelinek @ 2021-01-13 15:27 UTC (permalink / raw)
  To: Chung-Lin Tang; +Cc: gcc-patches, Catherine Moore, Andrew Stubbs

On Wed, Jan 13, 2021 at 11:07:44PM +0800, Chung-Lin Tang wrote:
> 2021-01-13  Chung-Lin Tang  <cltang@codesourcery.com>

...
Looks mostly ok, with some nits.

> 	* parse.c ("tree.h"): Add include.
> 	("omp-general.h"): Likewise.

I think the usual way is to write:
	* parse.c: Include "tree.h" and "omp-general.h".
 	(gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.

Something I miss in the patch is that for the device API calls (I'd bother
only with direct calls) we should probably set OMP_REQUIRES_TARGET_USED
too, so perhaps do that during gimplification if flag_openmp and
in gimplify_call_expr there is fndecl and DECL_NAME of it is non-NULL and
starts with "omp_" it looks at DECL_ASSEMBLER_NAME and compares that to a
selected list of device APIs.

Also, would it be possible to diagnose .gnu.gomp_requires mismatches also
at link time through the linker plugin/mkoffload?
At least if we have LTO offloading bytecode in and the plugin is doing
something...

> +      if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
> +	{
> +	  const char *requires_section = ".gnu.gomp_requires";
> +	  tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> +				     get_identifier (".gomp_requires_mask"),
> +				     unsigned_type_node);
> +	  SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));
> +	  TREE_STATIC (maskvar) = 1;
> +	  DECL_INITIAL (maskvar)
> +	    = build_int_cst (unsigned_type_node,
> +			     ((unsigned int) omp_requires_mask
> +			      & (OMP_REQUIRES_UNIFIED_ADDRESS
> +				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +				 | OMP_REQUIRES_REVERSE_OFFLOAD)));
> +	  set_decl_section_name (maskvar, requires_section);

This probably needs to sorry if the target doesn't support named sections.
We probably don't support LTO in that case either though.

Also, the diagnostic of the mismatches on the library side should print
details, say that libfoobar is #pragma omp requires unified_shared_memory
while libbar is not.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH, OpenMP 5.0] More implementation of the requires directive
  2021-01-13 15:07 [PATCH, OpenMP 5.0] More implementation of the requires directive Chung-Lin Tang
  2021-01-13 15:27 ` Jakub Jelinek
@ 2021-03-25 11:18 ` Thomas Schwinge
  2022-03-29 13:42 ` Andrew Stubbs
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
  3 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2021-03-25 11:18 UTC (permalink / raw)
  To: Chung-Lin Tang, gcc-patches
  Cc: Jakub Jelinek, Catherine Moore, Andrew Stubbs, Tobias Burnus

[-- Attachment #1: Type: text/plain, Size: 30337 bytes --]

Hi!

On 2021-01-13T23:07:44+0800, Chung-Lin Tang <cltang@codesourcery.com> wrote:
> this patch provides more implementation of the requires directive, basically:
>
> (1) The collection of the reverse_offload, unified_address, and unified_shared_memory
> clauses into a .gnu.gomp_requires section
>
> (2) libgomp checking of consistency across the entire .gnu.gomp_requires section,
> and querying into the offload plugin to see if the offload target supports the required
> features (as of now, the setting is that none of those features are supported by any
> of the plugins).
>
> We currently emit errors, but do not fatally cause exit of the program if those
> are not met. We're still unsure if complete block-out of program execution is the right
> thing for the user. This can be discussed later.
>
> Is this okay for trunk after stage1 re-opens?

(As posted, per a quick check) this got pushed to devel/omp/gcc-10 branch
in commit c2e4a17adc0989f216c7fc3f93f150c66adba23a "OpenMP 5.0: requires
directive".


Building the libgomp Intel MIC plugin fails:

    make[3]: Entering directory '[...]/build-gcc-offload-x86_64-intelmicemul-linux-gnu/x86_64-intelmicemul-linux-gnu/liboffloadmic/plugin'
    [...]/build-gcc-offload-x86_64-intelmicemul-linux-gnu/./gcc/xg++ [...] -loffloadmic_target -lcoi_device -lgomp -rdynamic ../ofldbegin.o offload_target_main.o ../ofldend.o -o offload_target_main
    ./../../libgomp/.libs/libgomp.so: undefined reference to `__requires_mask_table_end'
    ./../../libgomp/.libs/libgomp.so: undefined reference to `__requires_mask_table'
    collect2: error: ld returned 1 exit status
    Makefile:806: recipe for target 'offload_target_main' failed
    make[3]: *** [offload_target_main] Error 1

I've pushed "[WIP] OpenMP 5.0: requires directive: workaround to fix
libgomp IntelMIC plugin build" to devel/omp/gcc-10 branch in commit
ff77b4a0db75bc82a5519e31a882f9a25a02cd56, see attached.  This seemed like
a safe default, to get this un-stuck, but I suppose this will need
further work.

I haven't read up what this OpenMP functionality exactly is, and haven't
thought about how it ought to be implemented -- but from a quick look,
instead of libgomp directly referring to '__requires_mask_table',
shouldn't this use some "dynamic indirection scheme" (like we have for
the dynamic offloading code registering/loading function calls via
constructors, synthesized by the 'mkoffload's?), so that it also works
for shared objects ('*.so', etc.)  containing OpenMP code?  But maybe I
just have no clue what I'm talking about, and this is not applicable
here.  ;-)


'make check-target-libgomp':

    libgomp: while loading libgomp-plugin-hsa.so.1: [...]/libgomp-plugin-hsa.so.1: undefined symbol: GOMP_OFFLOAD_supported_features

I've pushed "OpenMP 5.0: requires directive: adjust libgomp HSA plugin"
to devel/omp/gcc-10 branch in commit
4ef4921cb10693c59b488002179db131683af8bc, see attached.  (The libgomp HSA
plugin has been removed in master branch, so not applicable there.)


Grüße
 Thomas


> 2021-01-13  Chung-Lin Tang  <cltang@codesourcery.com>
>
>       gcc/c/
>       * c-parser.c (c_parser_declaration_or_fndef): Set
>       OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
>       "omp declare target" attribute.
>       (c_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
>       omp_requires_mask.
>       (c_parser_omp_target_enter_data): Likewise.
>       (c_parser_omp_target_exit_data): Likewise.
>       (c_parser_omp_requires): Adjust to only mention "not implemented yet"
>       for OMP_REQUIRES_DYNAMIC_ALLOCATORS.
>
>       gcc/cp/
>       * parser.c (cp_parser_simple_declaration): Set
>       OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
>       "omp declare target" attribute.
>       (cp_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
>       omp_requires_mask.
>       (cp_parser_omp_target_enter_data): Likewise.
>       (cp_parser_omp_target_exit_data): Likewise.
>       (cp_parser_omp_requires): Adjust to only mention "not implemented yet"
>       for OMP_REQUIRES_DYNAMIC_ALLOCATORS.
>
>       gcc/fortran/
>       * openmp.c (gfc_check_omp_requires): Fix REVERSE_OFFLOAD typo.
>       (gfc_match_omp_requires): Adjust to only mention "not implemented yet"
>       for OMP_REQUIRES_DYNAMIC_ALLOCATORS.
>       * parse.c ("tree.h"): Add include.
>       ("omp-general.h"): Likewise.
>       (gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.
>
>       gcc/
>       * omp-offload.c (omp_finish_file): Add code to reate OpenMP requires
>       mask variable in .gnu.gomp_requires section if needed.
>
>       gcc/testsuite/
>       * c-c++-common/gomp/requires-4.c: Remove prune of "not supported yet".
>       * gcc/testsuite/gfortran.dg/gomp/requires-4.f90: Fix REVERSE_OFFLOAD typo.
>       * gcc/testsuite/gfortran.dg/gomp/requires-8.f90: Likewise.
>
>       include/
>       * gomp-constants.h (GOMP_REQUIRES_UNIFIED_ADDRESS): New symbol.
>       (GOMP_REQUIRES_UNIFIED_SHARED_MEMORY): Likewise.
>       (GOMP_REQUIRES_REVERSE_OFFLOAD): Likewise.
>
>       libgcc/
>       * offloadstuff.c (__requires_mask_table): New symbol to mark start of
>       .gnu.gomp_requires section.
>       (__requires_mask_table_end): New symbol to mark end of
>       .gnu.gomp_requires section.
>
>       libgomp/
>       * libgomp-plugin.h (GOMP_OFFLOAD_supported_features): New declaration.
>       * libgomp.h (struct gomp_device_descr): New 'supported_features_func'
>       plugin hook field.
>       * oacc-host.c (host_supported_features): New host hook function.
>       (host_dispatch): Initialize 'supported_features_func' host hook.
>       * plugin/plugin-gcn.c (GOMP_OFFLOAD_supported_features): New function.
>       * plugin/plugin-nvptx.c (GOMP_OFFLOAD_supported_features): Likewise.
>       * target.c (<stdio.h>): Add include of standard header.
>       (gomp_requires_mask): New static variable.
>       (__requires_mask_table): New declaration.
>       (__requires_mask_table_end): Likewise.
>       (gomp_load_plugin_for_device): Add loading of 'supported_features' hook.
>       (gomp_target_init): Add code to summarize .gnu._gomp_requires section
>       mask values, emit error if inconsistency found.
>
>       * testsuite/libgomp.c-c++-common/requires-1.c: New test.
>       * testsuite/libgomp.c-c++-common/requires-1-aux.c: New file linked with
>       above test.
>       * testsuite/libgomp.c-c++-common/requires-2.c: New test.
>       * testsuite/libgomp.c-c++-common/requires-2-aux.c: New file linked with
>       above test.
>
>       liboffloadmic/
>       * plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_supported_features):
>       New function.
> diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
> index c77d9fccdc2..e685b26746e 100644
> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c
> @@ -2475,6 +2475,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>         break;
>       }
>
> +      if (flag_openmp
> +       && lookup_attribute ("omp declare target",
> +                            DECL_ATTRIBUTES (current_function_decl)))
> +     omp_requires_mask
> +       = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>        if (DECL_DECLARED_INLINE_P (current_function_decl))
>          tv = TV_PARSE_INLINE;
>        else
> @@ -19556,6 +19562,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
>  static tree
>  c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
>  {
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
>                               "#pragma omp target data");
> @@ -19698,6 +19708,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
>        return NULL_TREE;
>      }
>
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
>                               "#pragma omp target enter data");
> @@ -19784,6 +19798,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
>        return NULL_TREE;
>      }
>
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
>                               "#pragma omp target exit data");
> @@ -21371,7 +21389,7 @@ c_parser_omp_requires (c_parser *parser)
>             c_parser_skip_to_pragma_eol (parser, false);
>             return;
>           }
> -       if (p)
> +       if (this_req == OMP_REQUIRES_DYNAMIC_ALLOCATORS)
>           sorry_at (cloc, "%qs clause on %<requires%> directive not "
>                           "supported yet", p);
>         if (p)
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index c713852fe93..afbc4e551d4 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -14455,6 +14455,11 @@ cp_parser_simple_declaration (cp_parser* parser,
>         /* Otherwise, we're done with the list of declarators.  */
>         else
>           {
> +           if (flag_openmp && lookup_attribute ("omp declare target",
> +                                                DECL_ATTRIBUTES (decl)))
> +             omp_requires_mask
> +               = (enum omp_requires) (omp_requires_mask
> +                                      | OMP_REQUIRES_TARGET_USED);
>             pop_deferring_access_checks ();
>             return;
>           }
> @@ -41432,6 +41437,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
>  static tree
>  cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
>  {
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
>                                "#pragma omp target data", pragma_tok);
> @@ -41535,6 +41544,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
>        return NULL_TREE;
>      }
>
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
>                                "#pragma omp target enter data", pragma_tok);
> @@ -41625,6 +41638,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
>        return NULL_TREE;
>      }
>
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
>                                "#pragma omp target exit data", pragma_tok);
> @@ -43819,7 +43836,7 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
>             cp_parser_skip_to_pragma_eol (parser, pragma_tok);
>             return false;
>           }
> -       if (p)
> +       if (this_req == OMP_REQUIRES_DYNAMIC_ALLOCATORS)
>           sorry_at (cloc, "%qs clause on %<requires%> directive not "
>                           "supported yet", p);
>         if (p)
> diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
> index cb166f956b7..c25531a4989 100644
> --- a/gcc/fortran/openmp.c
> +++ b/gcc/fortran/openmp.c
> @@ -3668,7 +3668,7 @@ gfc_check_omp_requires (gfc_namespace *ns, int ref_omp_requires)
>        if ((ref_omp_requires & OMP_REQ_REVERSE_OFFLOAD)
>         && !(ns->omp_requires & OMP_REQ_REVERSE_OFFLOAD))
>       gfc_error ("Program unit at %L has OpenMP device constructs/routines "
> -                "but does not set !$OMP REQUIRES REVERSE_OFFSET but other "
> +                "but does not set !$OMP REQUIRES REVERSE_OFFLOAD but other "
>                  "program units do", &ns->proc_name->declared_at);
>        if ((ref_omp_requires & OMP_REQ_UNIFIED_ADDRESS)
>         && !(ns->omp_requires & OMP_REQ_UNIFIED_ADDRESS))
> @@ -3855,7 +3855,8 @@ gfc_match_omp_requires (void)
>        else
>       goto error;
>
> -      if (requires_clause & ~OMP_REQ_ATOMIC_MEM_ORDER_MASK)
> +      /* Currently, everything except 'dynamic_allocators' is allowed.  */
> +      if (requires_clause == OMP_REQ_DYNAMIC_ALLOCATORS)
>       gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
>                      "yet supported", clause, &old_loc);
>        if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
> diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
> index 1549f8e1635..4731bca2cf7 100644
> --- a/gcc/fortran/parse.c
> +++ b/gcc/fortran/parse.c
> @@ -22,10 +22,12 @@ along with GCC; see the file COPYING3.  If not see
>  #include "system.h"
>  #include "coretypes.h"
>  #include "options.h"
> +#include "tree.h"
>  #include "gfortran.h"
>  #include <setjmp.h>
>  #include "match.h"
>  #include "parse.h"
> +#include "omp-general.h"
>
>  /* Current statement label.  Zero means no statement label.  Because new_st
>     can get wiped during statement matching, we have to keep it separate.  */
> @@ -6572,6 +6574,23 @@ done:
>         gfc_current_ns = gfc_current_ns->sibling)
>      gfc_check_omp_requires (gfc_current_ns, omp_requires);
>
> +  if (omp_requires)
> +    {
> +      omp_requires_mask = (enum omp_requires) OMP_REQUIRES_TARGET_USED;
> +      if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
> +     omp_requires_mask
> +       = (enum omp_requires) (omp_requires_mask
> +                              | OMP_REQUIRES_REVERSE_OFFLOAD);
> +      if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
> +     omp_requires_mask
> +       = (enum omp_requires) (omp_requires_mask
> +                              | OMP_REQUIRES_UNIFIED_ADDRESS);
> +      if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
> +     omp_requires_mask
> +       = (enum omp_requires) (omp_requires_mask
> +                              | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
> +    }
> +
>    /* Do the parse tree dump.  */
>    gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
>
> diff --git a/gcc/omp-offload.c b/gcc/omp-offload.c
> index ba0937fba94..9cc7d2945fc 100644
> --- a/gcc/omp-offload.c
> +++ b/gcc/omp-offload.c
> @@ -437,6 +437,24 @@ omp_finish_file (void)
>
>        varpool_node::finalize_decl (vars_decl);
>        varpool_node::finalize_decl (funcs_decl);
> +
> +      if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
> +     {
> +       const char *requires_section = ".gnu.gomp_requires";
> +       tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> +                                  get_identifier (".gomp_requires_mask"),
> +                                  unsigned_type_node);
> +       SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));
> +       TREE_STATIC (maskvar) = 1;
> +       DECL_INITIAL (maskvar)
> +         = build_int_cst (unsigned_type_node,
> +                          ((unsigned int) omp_requires_mask
> +                           & (OMP_REQUIRES_UNIFIED_ADDRESS
> +                              | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +                              | OMP_REQUIRES_REVERSE_OFFLOAD)));
> +       set_decl_section_name (maskvar, requires_section);
> +       varpool_node::finalize_decl (maskvar);
> +     }
>      }
>    else
>      {
> diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
> index 88ba7746cf8..8f45d83ea6e 100644
> --- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
> +++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
> @@ -9,5 +9,3 @@ foo (void)
>  #pragma omp requires unified_shared_memory   /* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
>  #pragma omp requires unified_address /* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
>  #pragma omp requires reverse_offload /* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
> -
> -/* { dg-prune-output "not supported yet" } */
> diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-4.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
> index b17aceb898b..c870a2840d3 100644
> --- a/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
> +++ b/gcc/testsuite/gfortran.dg/gomp/requires-4.f90
> @@ -9,7 +9,7 @@ end module m
>  subroutine foo
>    !$omp target
>    !$omp end target
> -! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFSET but other program units do" "" { target *-*-* } 9 }
> +! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" "" { target *-*-* } 9 }
>  ! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_ADDRESS but other program units do" "" { target *-*-* } 9 }
>  ! { dg-error "OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do" "" { target *-*-* } 9 }
>  end
> diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
> index 3c32ae9860e..3819b0c28cc 100644
> --- a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
> +++ b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
> @@ -13,7 +13,7 @@ contains
>   end subroutine foo
>  end module m
>
> -subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFSET but other program units do" }
> +subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
>    !use m
>    !$omp requires unified_shared_memory
>    !$omp declare target
> diff --git a/include/gomp-constants.h b/include/gomp-constants.h
> index 11a9308e3d2..d5a0b2c5ea7 100644
> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h
> @@ -301,6 +301,12 @@ enum gomp_map_kind
>  #define GOMP_DEPEND_INOUT            3
>  #define GOMP_DEPEND_MUTEXINOUTSET    4
>
> +/* Flag values for requires-directive features, must match corresponding
> +   OMP_REQUIRES_* values in gcc/omp-general.h.  */
> +#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
> +#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
> +#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
> +
>  /* HSA specific data structures.  */
>
>  /* Identifiers of device-specific target arguments.  */
> diff --git a/libgcc/offloadstuff.c b/libgcc/offloadstuff.c
> index b19428af6d8..78210a88f15 100644
> --- a/libgcc/offloadstuff.c
> +++ b/libgcc/offloadstuff.c
> @@ -54,6 +54,9 @@ const void *const __offload_var_table[0]
>    __attribute__ ((__used__, visibility ("hidden"),
>                 section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
>
> +const unsigned int const __requires_mask_table[0]
> +  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
> +
>  #elif defined CRT_END
>
>  const void *const __offload_funcs_end[0]
> @@ -63,6 +66,9 @@ const void *const __offload_vars_end[0]
>    __attribute__ ((__used__, visibility ("hidden"),
>                 section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
>
> +const unsigned int const __requires_mask_table_end[0]
> +  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
> +
>  #elif defined CRT_TABLE
>
>  extern const void *const __offload_func_table[];
> @@ -77,6 +83,9 @@ const void *const __OFFLOAD_TABLE__[]
>    &__offload_var_table, &__offload_vars_end
>  };
>
> +extern const unsigned int const __requires_mask_table[];
> +extern const unsigned int const __requires_mask_table_end[];
> +
>  #else /* ! CRT_BEGIN && ! CRT_END && ! CRT_TABLE  */
>  #error "One of CRT_BEGIN, CRT_END or CRT_TABLE must be defined."
>  #endif
> diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
> index 62645ce9954..f54469fdd6b 100644
> --- a/libgomp/libgomp-plugin.h
> +++ b/libgomp/libgomp-plugin.h
> @@ -122,6 +122,7 @@ extern int GOMP_OFFLOAD_get_type (void);
>  extern int GOMP_OFFLOAD_get_num_devices (void);
>  extern bool GOMP_OFFLOAD_init_device (int);
>  extern bool GOMP_OFFLOAD_fini_device (int);
> +extern bool GOMP_OFFLOAD_supported_features (unsigned *);
>  extern unsigned GOMP_OFFLOAD_version (void);
>  extern int GOMP_OFFLOAD_load_image (int, unsigned, const void *,
>                                   struct addr_pair **);
> diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
> index 305cba3aa02..09f2ac67943 100644
> --- a/libgomp/libgomp.h
> +++ b/libgomp/libgomp.h
> @@ -1130,6 +1130,7 @@ struct gomp_device_descr
>    __typeof (GOMP_OFFLOAD_get_num_devices) *get_num_devices_func;
>    __typeof (GOMP_OFFLOAD_init_device) *init_device_func;
>    __typeof (GOMP_OFFLOAD_fini_device) *fini_device_func;
> +  __typeof (GOMP_OFFLOAD_supported_features) *supported_features_func;
>    __typeof (GOMP_OFFLOAD_version) *version_func;
>    __typeof (GOMP_OFFLOAD_load_image) *load_image_func;
>    __typeof (GOMP_OFFLOAD_unload_image) *unload_image_func;
> diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
> index f3bbd2b9c61..94a7fac2a39 100644
> --- a/libgomp/oacc-host.c
> +++ b/libgomp/oacc-host.c
> @@ -71,6 +71,12 @@ host_fini_device (int n __attribute__ ((unused)))
>    return true;
>  }
>
> +static bool
> +host_supported_features (unsigned int *n)
> +{
> +  return (*n == 0);
> +}
> +
>  static unsigned
>  host_version (void)
>  {
> @@ -273,6 +279,7 @@ static struct gomp_device_descr host_dispatch =
>      .get_num_devices_func = host_get_num_devices,
>      .init_device_func = host_init_device,
>      .fini_device_func = host_fini_device,
> +    .supported_features_func = host_supported_features,
>      .version_func = host_version,
>      .load_image_func = host_load_image,
>      .unload_image_func = host_unload_image,
> diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
> index 47f0b6e25f8..718d78173fe 100644
> --- a/libgomp/plugin/plugin-gcn.c
> +++ b/libgomp/plugin/plugin-gcn.c
> @@ -3991,4 +3991,12 @@ GOMP_OFFLOAD_openacc_destroy_thread_data (void *data)
>    free (data);
>  }
>
> +/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
> +
> +bool
> +GOMP_OFFLOAD_supported_features (unsigned int *mask)
> +{
> +  return (*mask == 0);
> +}
> +
>  /* }}} */
> diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
> index 681c344b9c2..4cc25fbe232 100644
> --- a/libgomp/plugin/plugin-nvptx.c
> +++ b/libgomp/plugin/plugin-nvptx.c
> @@ -1236,6 +1236,14 @@ GOMP_OFFLOAD_fini_device (int n)
>    return true;
>  }
>
> +/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
> +
> +bool
> +GOMP_OFFLOAD_supported_features (unsigned int *mask)
> +{
> +  return (*mask == 0);
> +}
> +
>  /* Return the libgomp version number we're compatible with.  There is
>     no requirement for cross-version compatibility.  */
>
> diff --git a/libgomp/target.c b/libgomp/target.c
> index 4a4e1f80745..f06df7ba28d 100644
> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -31,6 +31,7 @@
>  #include "gomp-constants.h"
>  #include <limits.h>
>  #include <stdbool.h>
> +#include <stdio.h>
>  #include <stdlib.h>
>  #ifdef HAVE_INTTYPES_H
>  # include <inttypes.h>  /* For PRIu64.  */
> @@ -79,6 +80,16 @@ static int num_devices;
>  /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
>  static int num_devices_openmp;
>
> +/* Mask of requires directive clause values, summarized from .gnu.gomp.requires
> +   section. Offload plugins are queried with this mask to see if all required
> +   features are supported.  */
> +static unsigned int gomp_requires_mask;
> +
> +/* Start/end of .gnu.gomp.requires section of program, defined in
> +   crtoffloadbegin/end.o.  */
> +extern const unsigned int __requires_mask_table[];
> +extern const unsigned int __requires_mask_table_end[];
> +
>  /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
>
>  static void *
> @@ -1961,6 +1972,20 @@ gomp_init_device (struct gomp_device_descr *devicep)
>        gomp_fatal ("device initialization failed");
>      }
>
> +  unsigned int features = gomp_requires_mask;
> +  if (!devicep->supported_features_func (&features))
> +    {
> +      char buf[64], *end = buf + sizeof (buf), *p = buf;
> +      if (features & GOMP_REQUIRES_UNIFIED_ADDRESS)
> +     p += snprintf (p, end - p, "unified_address");
> +      if (features & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
> +     p += snprintf (p, end - p, "%sunified_shared_memory",
> +                    (p == buf ? "" : ", "));
> +      if (features & GOMP_REQUIRES_REVERSE_OFFLOAD)
> +     p += snprintf (p, end - p, "%sreverse_offload", (p == buf ? "" : ", "));
> +      gomp_error ("device does not support required features: %s", buf);
> +    }
> +
>    /* Load to device all images registered by the moment.  */
>    for (i = 0; i < num_offload_images; i++)
>      {
> @@ -3200,6 +3225,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
>    DLSYM (get_num_devices);
>    DLSYM (init_device);
>    DLSYM (fini_device);
> +  DLSYM (supported_features);
>    DLSYM (load_image);
>    DLSYM (unload_image);
>    DLSYM (alloc);
> @@ -3310,6 +3336,28 @@ gomp_target_init (void)
>    if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED)
>      return;
>
> +  gomp_requires_mask = 0;
> +  const unsigned int *mask_ptr = __requires_mask_table;
> +  bool error_emitted = false;
> +  while (mask_ptr != __requires_mask_table_end)
> +    {
> +      if (gomp_requires_mask == 0)
> +     gomp_requires_mask = *mask_ptr;
> +      else if (gomp_requires_mask != *mask_ptr)
> +     {
> +       if (!error_emitted)
> +         {
> +           gomp_error ("requires-directive clause inconsistency between "
> +                       "compilation units detected");
> +           error_emitted = true;
> +         }
> +       /* This is inconsistent, but still merge to query for all features
> +          later.  */
> +       gomp_requires_mask |= *mask_ptr;
> +     }
> +      mask_ptr++;
> +    }
> +
>    cur = OFFLOAD_PLUGINS;
>    if (*cur)
>      do
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
> new file mode 100644
> index 00000000000..8b9341523c6
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
> @@ -0,0 +1,11 @@
> +/* { dg-skip-if "" { *-*-* } } */
> +
> +#pragma omp requires reverse_offload
> +
> +int x;
> +
> +void foo (void)
> +{
> +  #pragma omp target
> +  x = 1;
> +}
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
> new file mode 100644
> index 00000000000..b5a3c512d28
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
> @@ -0,0 +1,21 @@
> +/* { dg-additional-sources requires-1-aux.c } */
> +
> +#pragma omp requires unified_shared_memory
> +
> +int a[10];
> +extern void foo (void);
> +
> +int
> +main (void)
> +{
> +  #pragma omp target
> +  for (int i = 0; i < 10; i++)
> +    a[i] = 0;
> +
> +  foo ();
> +  return 0;
> +}
> +
> +/* { dg-output "libgomp: requires-directive clause inconsistency between compilation units detected" } */
> +/* { dg-prune-output "device does not support required features" } */
> +/* { dg-shouldfail "" } */
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
> new file mode 100644
> index 00000000000..8b9341523c6
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
> @@ -0,0 +1,11 @@
> +/* { dg-skip-if "" { *-*-* } } */
> +
> +#pragma omp requires reverse_offload
> +
> +int x;
> +
> +void foo (void)
> +{
> +  #pragma omp target
> +  x = 1;
> +}
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
> new file mode 100644
> index 00000000000..6fb280baabd
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
> @@ -0,0 +1,20 @@
> +/* { dg-additional-sources requires-2-aux.c } */
> +
> +#pragma omp requires reverse_offload
> +
> +int a[10];
> +extern void foo (void);
> +
> +int
> +main (void)
> +{
> +  #pragma omp target
> +  for (int i = 0; i < 10; i++)
> +    a[i] = 0;
> +
> +  foo ();
> +  return 0;
> +}
> +
> +/* { dg-output "libgomp: device does not support required features: reverse_offload" } */
> +/* { dg-shouldfail "" } */
> diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
> index d1678d0514e..f92418fa416 100644
> --- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
> +++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
> @@ -233,6 +233,14 @@ GOMP_OFFLOAD_fini_device (int device)
>    return true;
>  }
>
> +/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
> +
> +extern "C" bool
> +GOMP_OFFLOAD_supported_features (unsigned int *mask)
> +{
> +  return (*mask == 0);
> +}
> +
>  static bool
>  get_target_table (int device, int &num_funcs, int &num_vars, void **&table)
>  {


-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstrasse 201, 80634 München Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Frank Thürauf

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-WIP-OpenMP-5.0-requires-directive-workaround-to.og10.patch --]
[-- Type: text/x-diff, Size: 2581 bytes --]

From ff77b4a0db75bc82a5519e31a882f9a25a02cd56 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Wed, 3 Mar 2021 22:37:58 +0100
Subject: [PATCH 1/2] [WIP] OpenMP 5.0: requires directive: workaround to fix
 libgomp IntelMIC plugin build

Fix-up for og10 commit c2e4a17adc0989f216c7fc3f93f150c66adba23a "OpenMP 5.0:
requires directive".

The GCC offloading target configurations don't build/use
'crtoffloadbegin.o'/'crtoffloadtable.o'/'crtoffloadend.o'
('libgcc/offloadstuff.c'), but the libgomp IntelMIC plugin still does link
against libgomp, and the latter unconditionally refers to
'__requires_mask_table', '__requires_mask_table_end':

    make[3]: Entering directory '[...]/build-gcc-offload-x86_64-intelmicemul-linux-gnu/x86_64-intelmicemul-linux-gnu/liboffloadmic/plugin'
    [...]/build-gcc-offload-x86_64-intelmicemul-linux-gnu/./gcc/xg++ [...] -loffloadmic_target -lcoi_device -lgomp -rdynamic ../ofldbegin.o offload_target_main.o ../ofldend.o -o offload_target_main
    ./../../libgomp/.libs/libgomp.so: undefined reference to `__requires_mask_table_end'
    ./../../libgomp/.libs/libgomp.so: undefined reference to `__requires_mask_table'
    collect2: error: ld returned 1 exit status
    Makefile:806: recipe for target 'offload_target_main' failed
    make[3]: *** [offload_target_main] Error 1

I have not researched what a proper fix would look like.

	libgomp/
	* target.c (__requires_mask_table, __requires_mask_table_end): Add
	'__attribute__((weak))'.
---
 libgomp/ChangeLog.omp | 5 +++++
 libgomp/target.c      | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/libgomp/ChangeLog.omp b/libgomp/ChangeLog.omp
index 0e3fd122f850..03ca74c8f3d5 100644
--- a/libgomp/ChangeLog.omp
+++ b/libgomp/ChangeLog.omp
@@ -1,3 +1,8 @@
+2021-03-25  Thomas Schwinge  <thomas@codesourcery.com>
+
+	* target.c (__requires_mask_table, __requires_mask_table_end): Add
+	'__attribute__((weak))'.
+
 2021-03-01  Kwok Cheung Yeung  <kcy@codesourcery.com>
 
 	* testsuite/libgomp.c-c++-common/collapse-4.c: New.
diff --git a/libgomp/target.c b/libgomp/target.c
index 699dc72cb722..9c7582635aa3 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -104,7 +104,9 @@ static unsigned int gomp_requires_mask;
 
 /* Start/end of .gnu.gomp.requires section of program, defined in
    crtoffloadbegin/end.o.  */
+__attribute__((weak))
 extern const unsigned int __requires_mask_table[];
+__attribute__((weak))
 extern const unsigned int __requires_mask_table_end[];
 
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
-- 
2.30.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-OpenMP-5.0-requires-directive-adjust-libgomp-HS.og10.patch --]
[-- Type: text/x-diff, Size: 1589 bytes --]

From 4ef4921cb10693c59b488002179db131683af8bc Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Wed, 3 Mar 2021 22:51:01 +0100
Subject: [PATCH 2/2] OpenMP 5.0: requires directive: adjust libgomp HSA plugin

Fix-up for og10 commit c2e4a17adc0989f216c7fc3f93f150c66adba23a "OpenMP 5.0:
requires directive".

    libgomp: while loading libgomp-plugin-hsa.so.1: [...]/libgomp-plugin-hsa.so.1: undefined symbol: GOMP_OFFLOAD_supported_features

	libgomp/
	* plugin/plugin-hsa.c (GOMP_OFFLOAD_supported_features): New
	function.
---
 libgomp/ChangeLog.omp       | 3 +++
 libgomp/plugin/plugin-hsa.c | 8 ++++++++
 2 files changed, 11 insertions(+)

diff --git a/libgomp/ChangeLog.omp b/libgomp/ChangeLog.omp
index 03ca74c8f3d5..19f48dc61202 100644
--- a/libgomp/ChangeLog.omp
+++ b/libgomp/ChangeLog.omp
@@ -1,5 +1,8 @@
 2021-03-25  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* plugin/plugin-hsa.c (GOMP_OFFLOAD_supported_features): New
+	function.
+
 	* target.c (__requires_mask_table, __requires_mask_table_end): Add
 	'__attribute__((weak))'.
 
diff --git a/libgomp/plugin/plugin-hsa.c b/libgomp/plugin/plugin-hsa.c
index abd3bc64163b..bddb690ca14f 100644
--- a/libgomp/plugin/plugin-hsa.c
+++ b/libgomp/plugin/plugin-hsa.c
@@ -1869,3 +1869,11 @@ GOMP_OFFLOAD_dev2dev (int ord, void *dst, const void *src, size_t n)
 		     "it should never be called");
   return false;
 }
+
+/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
+
+bool
+GOMP_OFFLOAD_supported_features (unsigned int *mask)
+{
+  return (*mask == 0);
+}
-- 
2.30.2


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH, OpenMP 5.0] More implementation of the requires directive
  2021-01-13 15:07 [PATCH, OpenMP 5.0] More implementation of the requires directive Chung-Lin Tang
  2021-01-13 15:27 ` Jakub Jelinek
  2021-03-25 11:18 ` Thomas Schwinge
@ 2022-03-29 13:42 ` Andrew Stubbs
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
  3 siblings, 0 replies; 42+ messages in thread
From: Andrew Stubbs @ 2022-03-29 13:42 UTC (permalink / raw)
  To: Chung-Lin Tang, gcc-patches, Jakub Jelinek, Catherine Moore

[-- Attachment #1: Type: text/plain, Size: 423 bytes --]

On 13/01/2021 15:07, Chung-Lin Tang wrote:
> We currently emit errors, but do not fatally cause exit of the program 
> if those
> are not met. We're still unsure if complete block-out of program 
> execution is the right
> thing for the user. This can be discussed later.

After the Unified Shared Memory patches are committed, this patch will 
need to be altered as attached.

I'll commit my patch to OG11 shortly.

Andrew

[-- Attachment #2: 220329-nvptx-usm-supported.patch --]
[-- Type: text/plain, Size: 796 bytes --]

libgomp, nvptx: report USM supported

libgomp/ChangeLog:

	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_supported_features): Allow
	GOMP_REQUIRES_UNIFIED_ADDRESS and GOMP_REQUIRES_UNIFIED_SHARED_MEMORY.

diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index dd490b2ae2a..e77c6a87930 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1260,11 +1260,14 @@ GOMP_OFFLOAD_fini_device (int n)
   return true;
 }
 
-/* Indicate which GOMP_REQUIRES_* features are supported, currently none.  */
+/* Indicate which GOMP_REQUIRES_* features are supported.  */
 
 bool
 GOMP_OFFLOAD_supported_features (unsigned int *mask)
 {
+  *mask &= ~(GOMP_REQUIRES_UNIFIED_ADDRESS
+             | GOMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+
   return (*mask == 0);
 }
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [Patch] OpenMP: Move omp requires checks to libgomp
@ 2022-06-08  3:56 ` Tobias Burnus
  2022-06-09 11:40   ` Jakub Jelinek
                     ` (3 more replies)
  0 siblings, 4 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-06-08  3:56 UTC (permalink / raw)
  To: gcc-patches, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 4063 bytes --]

This is based on Chung-Lin's patch at https://gcc.gnu.org/pipermail/gcc-patches/2021-January/563393.html

This is about code like:
   #pragma omp requires unified_shared_memory
   !$omp requires reverse_offload
which before was rejected with a sorry during parsing and is now
handled in libgomp (by excluding the devices from the available
device list).

Note: The requires-directive consistency check is nonfatal, i.e.
the program continues after the
  libgomp: requires-directive clause inconsistency between compilation units detected: 'unified_shared_memory' vs. 'reverse_offload'
message.

Changes compared to Chung-Lin's patch:
- I take the omp_* device API calls into account
- Rediffed because of changes done in the past year
- Included Thomas' fix for !ENABLE_OFFLOADING + intelmic, i.e. OG11 commit
   https://gcc.gnu.org/g:6da4ffd4a790f5f0abf290147217ca46a36f981e

On the libgomp side: The devices which do not fulfill the requirements are
now filtered out. That's in line how I understood the spec – and to make it
clearer, I spelled this out explicitly when adding (for other reasons) two
glossary items (passed 2nd vote but not yet in a released OpenMP spec):
- "accessible devices
    The host device and all non-host devices accessible for execution."
- "supported devices
    The host device and all non-host devices supported by the implementation
    for execution of target code for which the device-related requirements
    of the 'requires' directive are fulfilled."

Note:
* The FE only generates the requirement clauses when there is at least
   one declare target variable or function and offloading is used
   (target region, API call etc.)
   In particular, this implies that for !ENABLE_OFFLOADING, none is
   generated.
* libgomp only checks whether those values are consistent when
   env var OMP_TARGET_OFFLOAD != disable.

=> Thus, I protected the check for this (libgomp.c-c++-common/requires-1.c)
    by { dg-skip-if "" { ! offloading_enabled } }
    (and assume that OMP_TARGET_OFFLOAD is not set).

If env var OMP_TARGET_OFFLOAD != disable, it then runs for all configured
plugins and checks first whether devices are actually available and then
whether the requirement mask is fulfilled. Currently, none of the clauses
is supported (neither unified_shared_memory nor unified_shared_address nor
reverse_offload) even though there are patches submitted (and being worked on),
which add support for those.

I then unconditionally print a note like:
   libgomp: note: nvptx devices present but 'omp requires unified_shared_memory' cannot be fulfilled

This note is printed if env var OMP_TARGET_OFFLOAD != disable,
libgomp supports the device type, a device was found but omp requires
could not fulfilled.
This means that this message is also printed when compiled with
   -foffload=disable
or 'omp target if(0)' was used throught or ...

I think that's acceptable, but it could also be optimized further; however,
the initialization (e.g. GOMP_offload_register_ver) happens much later such
that the knowledge that a device is not needed (as with -foffload=disable)
is not available.

I hope the note is not too confusing, but otherwise:
* it could be postponed and then printed in context
   (requires device type <-> name association)
* it could only be printed with GOMP_DEBUG set
   but for the common case (why did it not run?), outputting it
   unconditionally surely helps to understand what went "wrong".

Thoughts? Comments? OK?

Tobias

PS: I have not fully tested the intelmic version.
PPS: I have not tried to implement the compile-time check to impose
consistent 'omp requires' – as proposed in the last review. I think I will
open a PR to track that proposal.
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Attachment #2: omp-requires-v3.diff --]
[-- Type: text/x-patch, Size: 51474 bytes --]

OpenMP: Move omp requires checks to libgomp

Handle reverse_offload, unified_address, and unified_shared_memory
requirements in libgomp by putting them into the .gnu.gomp_requires section.

For all in-principle supported devices, if a requirement cannot be fulfilled,
the device is excluded from the (supported) devices list. Currently, none of
those requirements are marked as supported for any of the non-host devices.

Additionally, libgomp checks for consistency across the entire
.gnu.gomp_requires section, matching the requirements set by the OpenMP spec.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_declaration_or_fndef): Set
	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
	"omp declare target" attribute.
	(c_parser_omp_target_data): Set	OMP_REQUIRES_TARGET_USED in
	omp_requires_mask.
	(c_parser_omp_target_enter_data): Likewise.
	(c_parser_omp_target_exit_data): Likewise.
	(c_parser_omp_requires): Remove sorry.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_simple_declaration): Set
	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
	"omp declare target" attribute.
	(cp_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
	omp_requires_mask.
	(cp_parser_omp_target_enter_data): Likewise.
	(cp_parser_omp_target_exit_data): Likewise.
	(cp_parser_omp_requires): Remove sorry.

gcc/fortran/ChangeLog:

	* openmp.cc (gfc_match_omp_requires): Remove "not implemented yet".
	* parse.cc: Include "tree.h" and "omp-general.h".
	(gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.

gcc/ChangeLog:

	* omp-general.h (omp_runtime_api_call): New prototype.
	* omp-general.cc (omp_runtime_api_call): Added device_api_only arg
	and moved from ...
	* omp-low.cc (omp_runtime_api_call): ... here.
	(scan_omp_1_stmt): Update call.
	* gimplify.cc (gimplify_call_expr): Call omp_runtime_api_call.
	* omp-offload.cc (omp_finish_file): Add code to create OpenMP requires
	mask variable in .gnu.gomp_requires section, if needed.

include/ChangeLog:

	* gomp-constants.h (GOMP_REQUIRES_UNIFIED_ADDRESS,
	GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
	GOMP_REQUIRES_REVERSE_OFFLOAD): New.

libgcc/ChangeLog:

	* offloadstuff.c (__requires_mask_table, __requires_mask_table_end):
	New symbols to mark start and end of the .gnu.gomp_requires section.


libgomp/ChangeLog:

	* libgomp-plugin.h (GOMP_OFFLOAD_get_num_devices): Add
	omp_requires_mask arg.
	* plugin/plugin-gcn.c (GOMP_OFFLOAD_get_num_devices): Likewise;
	return -1 when device available but omp_requires_mask != 0.
	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_get_num_devices): Likewise.
	* oacc-host.c (host_get_num_devices, host_openacc_get_property):
	Update call.
	* oacc-init.c (resolve_device, acc_init_1, acc_shutdown_1,
	goacc_attach_host_thread_to_device, acc_get_num_devices,
	acc_set_device_num, get_property_any): Likewise.
	* target.c: (__requires_mask_table, __requires_mask_table_end):
	Declare weak extern symbols.
	(gomp_requires_to_name): New.
	(gomp_target_init): Add code to check .gnu._gomp_requires section
	mask values for inconsistencies; warn when requirements makes an
	existing device unsupported.
	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2.c: New test.

liboffloadmic/ChangeLog:

	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_num_devices):
	Return -1 when device available but omp_requires_mask != 0.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/requires-4.c: Update dg-*.
	* c-c++-common/gomp/target-device-ancestor-2.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-3.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-4.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-5.c: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-3.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-4.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-2.f90: Likewise. Move post-FE
	checks to ...
	* gfortran.dg/gomp/target-device-ancestor-2a.f90: ... this new file.

Co-authored-by: Chung-Lin Tang <cltang@codesourcery.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>

 gcc/c/c-parser.cc                                  |  21 +++-
 gcc/cp/parser.cc                                   |  20 ++-
 gcc/fortran/openmp.cc                              |   4 -
 gcc/fortran/parse.cc                               |  21 ++++
 gcc/gimplify.cc                                    |   3 +
 gcc/omp-general.cc                                 | 137 +++++++++++++++++++++
 gcc/omp-general.h                                  |   1 +
 gcc/omp-low.cc                                     | 135 +-------------------
 gcc/omp-offload.cc                                 |  29 +++++
 gcc/testsuite/c-c++-common/gomp/requires-4.c       |   2 -
 .../c-c++-common/gomp/target-device-ancestor-2.c   |  10 +-
 .../c-c++-common/gomp/target-device-ancestor-3.c   |   2 +-
 .../c-c++-common/gomp/target-device-ancestor-4.c   |   4 +-
 .../c-c++-common/gomp/target-device-ancestor-5.c   |   2 +-
 .../gfortran.dg/gomp/target-device-ancestor-2.f90  |  70 +----------
 .../gfortran.dg/gomp/target-device-ancestor-2a.f90 |  80 ++++++++++++
 .../gfortran.dg/gomp/target-device-ancestor-3.f90  |   6 +-
 .../gfortran.dg/gomp/target-device-ancestor-4.f90  |   6 +-
 include/gomp-constants.h                           |   6 +
 libgcc/offloadstuff.c                              |   9 ++
 libgomp/libgomp-plugin.h                           |   2 +-
 libgomp/oacc-host.c                                |   4 +-
 libgomp/oacc-init.c                                |  16 +--
 libgomp/plugin/plugin-gcn.c                        |   6 +-
 libgomp/plugin/plugin-nvptx.c                      |   9 +-
 libgomp/target.c                                   |  66 +++++++++-
 .../libgomp.c-c++-common/requires-1-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-1.c    |  21 ++++
 .../libgomp.c-c++-common/requires-2-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-2.c    |  20 +++
 liboffloadmic/plugin/libgomp-plugin-intelmic.cpp   |   6 +-
 31 files changed, 499 insertions(+), 241 deletions(-)

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 1704a52be12..4748ce04737 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -2488,6 +2488,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	  break;
 	}
 
+      if (flag_openmp
+	  && lookup_attribute ("omp declare target",
+			       DECL_ATTRIBUTES (current_function_decl)))
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
       if (DECL_DECLARED_INLINE_P (current_function_decl))
         tv = TV_PARSE_INLINE;
       else
@@ -20915,6 +20921,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
 static tree
 c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				"#pragma omp target data");
@@ -21057,6 +21067,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				"#pragma omp target enter data");
@@ -21143,6 +21157,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				"#pragma omp target exit data");
@@ -22763,9 +22781,6 @@ c_parser_omp_requires (c_parser *parser)
 	      c_parser_skip_to_pragma_eol (parser, false);
 	      return;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    c_parser_consume_token (parser);
 	  if (this_req)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index da2f370cdca..6e26d123370 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -15389,6 +15389,11 @@ cp_parser_simple_declaration (cp_parser* parser,
 	  /* Otherwise, we're done with the list of declarators.  */
 	  else
 	    {
+	      if (flag_openmp && lookup_attribute ("omp declare target",
+						   DECL_ATTRIBUTES (decl)))
+		omp_requires_mask
+		  = (enum omp_requires) (omp_requires_mask
+					 | OMP_REQUIRES_TARGET_USED);
 	      pop_deferring_access_checks ();
 	      cp_finalize_omp_declare_simd (parser, &odsd);
 	      return;
@@ -44287,6 +44292,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
 static tree
 cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				 "#pragma omp target data", pragma_tok);
@@ -44390,6 +44399,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				 "#pragma omp target enter data", pragma_tok);
@@ -44481,6 +44494,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				 "#pragma omp target exit data", pragma_tok);
@@ -46861,9 +46878,6 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
 	      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	      return false;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    cp_lexer_consume_token (parser->lexer);
 	  if (this_req)
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index d12cec43d64..7790ef34664 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -5481,10 +5481,6 @@ gfc_match_omp_requires (void)
       else
 	goto error;
 
-      if (requires_clause & ~(OMP_REQ_ATOMIC_MEM_ORDER_MASK
-			      | OMP_REQ_DYNAMIC_ALLOCATORS))
-	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
-		       "yet supported", clause, &old_loc);
       if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
 	goto error;
       requires_clauses |= requires_clause;
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index 7356d1b5a3a..b142e169a5c 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -6908,6 +6908,27 @@ done:
       break;
     }
 
+  if (omp_requires & OMP_REQ_TARGET_MASK)
+    {
+      omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					       | OMP_REQUIRES_TARGET_USED);
+      if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_REVERSE_OFFLOAD);
+      if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_ADDRESS);
+      if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+    }
+
+  if (omp_requires & OMP_REQ_DYNAMIC_ALLOCATORS)
+    omp_requires_mask
+	= (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_DYNAMIC_ALLOCATORS);
   /* Do the parse tree dump.  */
   gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
 
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index cd1796643d7..3fe4571d677 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -3644,6 +3644,9 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 	  return GS_OK;
 	}
     }
+  if (fndecl && flag_openmp && omp_runtime_api_call (fndecl, true))
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
 
   /* Remember the original function pointer type.  */
   fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index a406c578f33..120bcaa10b2 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -89,6 +89,143 @@ omp_privatize_by_reference (tree decl)
   return lang_hooks.decls.omp_privatize_by_reference (decl);
 }
 
+/* Return true if FNDECL is an omp_* runtime API call; with device_api_only set,
+   returns true only for device API calls.  */
+
+bool
+omp_runtime_api_call (const_tree fndecl, bool device_api_only)
+{
+  tree declname = DECL_NAME (fndecl);
+  if (!declname
+      || (DECL_CONTEXT (fndecl) != NULL_TREE
+	  && TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL)
+      || !TREE_PUBLIC (fndecl))
+    return false;
+
+  const char *name = IDENTIFIER_POINTER (declname);
+  if (!startswith (name, "omp_"))
+    return false;
+
+  static const char *omp_runtime_apis[] =
+    {
+      /* This array has 6 sections.  First omp_* calls that don't
+	 have any suffixes and are non-device APIs.  */
+      "aligned_alloc",
+      "aligned_calloc",
+      "alloc",
+      "calloc",
+      "free",
+      "realloc",
+      NULL,
+      /* Now likewise but for device API. */
+      "get_mapped_ptr",
+      "target_alloc",
+      "target_associate_ptr",
+      "target_disassociate_ptr",
+      "target_free",
+      "target_is_accessible",
+      "target_is_present",
+      "target_memcpy",
+      "target_memcpy_async",
+      "target_memcpy_rect",
+      "target_memcpy_rect_async",
+      NULL,
+      /* Now omp_* calls that are available as omp_* and omp_*_; however, the
+	 DECL_NAME is always omp_* without tailing underscore. Non device.  */
+      "capture_affinity",
+      "destroy_allocator",
+      "destroy_lock",
+      "destroy_nest_lock",
+      "display_affinity",
+      "fulfill_event",
+      "get_active_level",
+      "get_affinity_format",
+      "get_cancellation",
+      "get_default_allocator",
+      "get_default_device",
+      "get_dynamic",
+      "get_level",
+      "get_max_active_levels",
+      "get_max_task_priority",
+      "get_max_teams",
+      "get_max_threads",
+      "get_nested",
+      "get_num_devices",
+      "get_num_places",
+      "get_num_procs",
+      "get_num_teams",
+      "get_num_threads",
+      "get_partition_num_places",
+      "get_place_num",
+      "get_proc_bind",
+      "get_supported_active_levels",
+      "get_team_num",
+      "get_teams_thread_limit",
+      "get_thread_limit",
+      "get_thread_num",
+      "get_wtick",
+      "get_wtime",
+      "in_final",
+      "in_parallel",
+      "init_lock",
+      "init_nest_lock",
+      "pause_resource",
+      "pause_resource_all",
+      "set_affinity_format",
+      "set_default_allocator",
+      "set_lock",
+      "set_nest_lock",
+      "test_lock",
+      "test_nest_lock",
+      "unset_lock",
+      "unset_nest_lock",
+      NULL,
+      /* And device APIs. */
+      "get_device_num",
+      "get_initial_device",
+      "is_initial_device",  /* Even if it does not require init'ed devices. */
+      NULL,
+      /* And finally calls available as omp_*, omp_*_ and omp_*_8_; however,
+	 as DECL_NAME only omp_* and omp_*_8 appear. For non device.  */
+      "display_env",
+      "get_ancestor_thread_num",
+      "init_allocator",
+      "get_partition_place_nums",
+      "get_place_num_procs",
+      "get_place_proc_ids",
+      "get_schedule",
+      "get_team_size",
+      "set_default_device",
+      "set_dynamic",
+      "set_max_active_levels",
+      "set_nested",
+      "set_num_teams",
+      "set_num_threads",
+      "set_schedule",
+      "set_teams_thread_limit",
+      NULL,
+      /* And for device APIs. (Currently none.)  */
+    };
+
+  int mode = 0;
+  for (unsigned i = 0; i < ARRAY_SIZE (omp_runtime_apis); i++)
+    {
+      if (omp_runtime_apis[i] == NULL)
+	{
+	  mode++;
+	  continue;
+	}
+      if (device_api_only && mode % 2 != 0)
+	continue;
+      size_t len = strlen (omp_runtime_apis[i]);
+      if (strncmp (name + 4, omp_runtime_apis[i], len) == 0
+	  && (name[4 + len] == '\0'
+	      || (mode > 1 && strcmp (name + 4 + len, "_8") == 0)))
+	return true;
+    }
+  return false;
+}
+
 /* Adjust *COND_CODE and *N2 so that the former is either LT_EXPR or GT_EXPR,
    given that V is the loop index variable and STEP is loop step. */
 
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 7a94831e8f5..f1be9f23ef7 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -95,6 +95,7 @@ extern tree omp_find_clause (tree clauses, enum omp_clause_code kind);
 extern bool omp_is_allocatable_or_ptr (tree decl);
 extern tree omp_check_optional_argument (tree decl, bool for_present_check);
 extern bool omp_privatize_by_reference (tree decl);
+extern bool omp_runtime_api_call (const_tree fndecl, bool device_api_only);
 extern void omp_adjust_for_condition (location_t loc, enum tree_code *cond_code,
 				      tree *n2, tree v, tree step);
 extern tree omp_get_for_step_from_incr (location_t loc, tree incr);
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index f976e3a1549..243fa27a62f 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -3989,134 +3989,6 @@ setjmp_or_longjmp_p (const_tree fndecl)
   return !strcmp (name, "setjmp") || !strcmp (name, "longjmp");
 }
 
-/* Return true if FNDECL is an omp_* runtime API call.  */
-
-static bool
-omp_runtime_api_call (const_tree fndecl)
-{
-  tree declname = DECL_NAME (fndecl);
-  if (!declname
-      || (DECL_CONTEXT (fndecl) != NULL_TREE
-          && TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL)
-      || !TREE_PUBLIC (fndecl))
-    return false;
-
-  const char *name = IDENTIFIER_POINTER (declname);
-  if (!startswith (name, "omp_"))
-    return false;
-
-  static const char *omp_runtime_apis[] =
-    {
-      /* This array has 3 sections.  First omp_* calls that don't
-	 have any suffixes.  */
-      "aligned_alloc",
-      "aligned_calloc",
-      "alloc",
-      "calloc",
-      "free",
-      "get_mapped_ptr",
-      "realloc",
-      "target_alloc",
-      "target_associate_ptr",
-      "target_disassociate_ptr",
-      "target_free",
-      "target_is_accessible",
-      "target_is_present",
-      "target_memcpy",
-      "target_memcpy_async",
-      "target_memcpy_rect",
-      "target_memcpy_rect_async",
-      NULL,
-      /* Now omp_* calls that are available as omp_* and omp_*_; however, the
-	 DECL_NAME is always omp_* without tailing underscore.  */
-      "capture_affinity",
-      "destroy_allocator",
-      "destroy_lock",
-      "destroy_nest_lock",
-      "display_affinity",
-      "fulfill_event",
-      "get_active_level",
-      "get_affinity_format",
-      "get_cancellation",
-      "get_default_allocator",
-      "get_default_device",
-      "get_device_num",
-      "get_dynamic",
-      "get_initial_device",
-      "get_level",
-      "get_max_active_levels",
-      "get_max_task_priority",
-      "get_max_teams",
-      "get_max_threads",
-      "get_nested",
-      "get_num_devices",
-      "get_num_places",
-      "get_num_procs",
-      "get_num_teams",
-      "get_num_threads",
-      "get_partition_num_places",
-      "get_place_num",
-      "get_proc_bind",
-      "get_supported_active_levels",
-      "get_team_num",
-      "get_teams_thread_limit",
-      "get_thread_limit",
-      "get_thread_num",
-      "get_wtick",
-      "get_wtime",
-      "in_final",
-      "in_parallel",
-      "init_lock",
-      "init_nest_lock",
-      "is_initial_device",
-      "pause_resource",
-      "pause_resource_all",
-      "set_affinity_format",
-      "set_default_allocator",
-      "set_lock",
-      "set_nest_lock",
-      "test_lock",
-      "test_nest_lock",
-      "unset_lock",
-      "unset_nest_lock",
-      NULL,
-      /* And finally calls available as omp_*, omp_*_ and omp_*_8_; however,
-	 as DECL_NAME only omp_* and omp_*_8 appear.  */
-      "display_env",
-      "get_ancestor_thread_num",
-      "init_allocator",
-      "get_partition_place_nums",
-      "get_place_num_procs",
-      "get_place_proc_ids",
-      "get_schedule",
-      "get_team_size",
-      "set_default_device",
-      "set_dynamic",
-      "set_max_active_levels",
-      "set_nested",
-      "set_num_teams",
-      "set_num_threads",
-      "set_schedule",
-      "set_teams_thread_limit"
-    };
-
-  int mode = 0;
-  for (unsigned i = 0; i < ARRAY_SIZE (omp_runtime_apis); i++)
-    {
-      if (omp_runtime_apis[i] == NULL)
-	{
-	  mode++;
-	  continue;
-	}
-      size_t len = strlen (omp_runtime_apis[i]);
-      if (strncmp (name + 4, omp_runtime_apis[i], len) == 0
-	  && (name[4 + len] == '\0'
-	      || (mode > 1 && strcmp (name + 4 + len, "_8") == 0)))
-	return true;
-    }
-  return false;
-}
-
 /* Helper function for scan_omp.
 
    Callback for walk_gimple_stmt used to scan for OMP directives in
@@ -4171,7 +4043,8 @@ scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
 	      omp_context *octx = ctx;
 	      if (gimple_code (ctx->stmt) == GIMPLE_OMP_SCAN && ctx->outer)
 		octx = ctx->outer;
-	      if (octx->order_concurrent && omp_runtime_api_call (fndecl))
+	      if (octx->order_concurrent
+		  && omp_runtime_api_call (fndecl, false))
 		{
 		  remove = true;
 		  error_at (gimple_location (stmt),
@@ -4179,7 +4052,7 @@ scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
 			    "%<order(concurrent)%> clause", fndecl);
 		}
 	      if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS
-		  && omp_runtime_api_call (fndecl)
+		  && omp_runtime_api_call (fndecl, false)
 		  && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl))
 		       != strlen ("omp_get_num_teams"))
 		      || strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)),
@@ -4197,7 +4070,7 @@ scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
 	      if (gimple_code (ctx->stmt) == GIMPLE_OMP_TARGET
 		  && (gimple_omp_target_kind (ctx->stmt)
 		      == GF_OMP_TARGET_KIND_REGION)
-		  && omp_runtime_api_call (fndecl))
+		  && omp_runtime_api_call (fndecl, false))
 		{
 		  tree tgt_clauses = gimple_omp_target_clauses (ctx->stmt);
 		  tree c = omp_find_clause (tgt_clauses, OMP_CLAUSE_DEVICE);
diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc
index ad4e772015e..998abab0f11 100644
--- a/gcc/omp-offload.cc
+++ b/gcc/omp-offload.cc
@@ -397,6 +397,27 @@ omp_finish_file (void)
   unsigned num_funcs = vec_safe_length (offload_funcs);
   unsigned num_vars = vec_safe_length (offload_vars);
 
+  if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
+    {
+      if (targetm_common.have_named_sections)
+	{
+	  const char *requires_section = ".gnu.gomp_requires";
+	  tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+				     get_identifier (".gomp_requires_mask"),
+				     unsigned_type_node);
+	  SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));
+	  TREE_STATIC (maskvar) = 1;
+	  DECL_INITIAL (maskvar)
+	    = build_int_cst (unsigned_type_node,
+			     ((unsigned int) omp_requires_mask
+			      & (OMP_REQUIRES_UNIFIED_ADDRESS
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+				 | OMP_REQUIRES_REVERSE_OFFLOAD)));
+	  set_decl_section_name (maskvar, requires_section);
+	  varpool_node::finalize_decl (maskvar);
+	}
+    }
+
   if (num_funcs == 0 && num_vars == 0)
     return;
 
@@ -442,6 +463,14 @@ omp_finish_file (void)
     }
   else
     {
+#ifndef ACCEL_COMPILER
+      if (flag_openmp
+	  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED)
+	  && (omp_requires_mask & (OMP_REQUIRES_UNIFIED_ADDRESS
+				   | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+				   | OMP_REQUIRES_REVERSE_OFFLOAD)))
+	sorry ("OpenMP device offloading is not supported for this target");
+#endif
       for (unsigned i = 0; i < num_funcs; i++)
 	{
 	  tree it = (*offload_funcs)[i];
diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
index 88ba7746cf8..8f45d83ea6e 100644
--- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
@@ -9,5 +9,3 @@ foo (void)
 #pragma omp requires unified_shared_memory	/* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires unified_address	/* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires reverse_offload	/* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
-
-/* { dg-prune-output "not supported yet" } */
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
index cf05c505004..b16e701bd5a 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
@@ -1,13 +1,11 @@
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (int n)
 {
-  /* The following test is marked with 'xfail' because a previous 'sorry' from
-     'reverse_offload' suppresses the 'sorry' for 'ancestor'.  */
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1)
   ;
 
 
@@ -19,9 +17,9 @@ foo (int n)
   #pragma omp target device (ancestor : 42) /* { dg-error "the 'device' clause expression must evaluate to '1'" } */
   ;
 
-  #pragma omp target device (ancestor : n) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n)
   ;
-  #pragma omp target device (ancestor : n + 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n + 1)
   ;
 
 
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
index ea6e5a0cf6c..d16590107d2 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
@@ -11,7 +11,7 @@ int bar (void);
 
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
index b4b5620bbc0..241234f8daf 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
@@ -4,12 +4,12 @@
   /* Test to ensure that device-modifier 'ancestor' is parsed correctly in
      device clauses. */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
 {
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1) /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
   ;
 
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
index b6ff84bcdab..b1520ff0636 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
@@ -1,4 +1,4 @@
-#pragma omp requires reverse_offload  /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo ()
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
index 117a1d000a5..230c690d84c 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
@@ -4,19 +4,16 @@ implicit none
 
 integer :: a, b, c
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
 
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor: 1)
 !$omp end target
 
-!$omp target device (ancestor : a)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a)
 !$omp end target
 
-!$omp target device (ancestor : a + 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a + 1)
 !$omp end target
 
 
@@ -32,61 +29,4 @@ integer :: a, b, c
 !$omp target device (42)
 !$omp end target
 
-
-! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)
-  !$omp teams  ! { dg-error "" "OpenMP constructs are not allowed in target region with 'ancestor'" { xfail *-*-* } }
-  !$omp end teams
-!$omp end target
-
-!$omp target device (device_num: 1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-!$omp target device (1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-
-! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
-! 'defaultmap', and 'map' clauses appear on the construct.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target nowait device (ancestor: 1)  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target device (ancestor: 1) nowait  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target nowait device (device_num: 1)
-!$omp end target
-
-!$omp target nowait device (1)
-!$omp end target
-
-!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
-!$omp end target
-
-
-! Ensure that 'ancestor' is only used with 'target' constructs (not with
-! 'target data', 'target update' etc.).
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target data map (a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp end target data
-
-!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-
-!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" "" { xfail *-*-* } }
-! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { xfail *-*-* } .-1 }
-
-
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
new file mode 100644
index 00000000000..feb76fe2144
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
@@ -0,0 +1,80 @@
+! { dg-do compile }
+
+implicit none
+
+integer :: a, b, c
+
+!$omp requires reverse_offload
+
+!$omp target device (ancestor: 1)
+!$omp end target
+
+!$omp target device (ancestor : a)
+!$omp end target
+
+!$omp target device (ancestor : a + 1)
+!$omp end target
+
+
+!$omp target device (device_num:42)
+!$omp end target
+
+!$omp target device (42)
+!$omp end target
+
+
+! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
+
+!$omp target device (ancestor: 1)
+  !$omp teams  ! { dg-error "OpenMP constructs are not allowed in target region with 'ancestor'" }
+  !$omp end teams
+!$omp end target
+
+!$omp target device (device_num: 1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+!$omp target device (1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+
+! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
+! 'defaultmap', and 'map' clauses appear on the construct.
+
+!$omp target nowait device (ancestor: 1)  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target device (ancestor: 1) nowait  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target nowait device (device_num: 1)
+!$omp end target
+
+!$omp target nowait device (1)
+!$omp end target
+
+!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
+!$omp end target
+
+
+! Ensure that 'ancestor' is only used with 'target' constructs (not with
+! 'target data', 'target update' etc.).
+! The following test case is marked with 'xfail' because a previous 'sorry' from
+! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
+
+!$omp target data map (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp end target data
+
+!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target device (ancestor: 1) if(.false.)
+! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { target *-*-* } .-1 }
+!$omp end target
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
index f1145bde2ec..e8975e6a08b 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
@@ -16,10 +16,10 @@ subroutine f1 ()
   implicit none
   integer :: n
 
-  !$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 
   !$omp target device (ancestor : 1)
-    n = omp_get_thread_num ()  ! { dg-error "" "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" { xfail *-*-* } }
+    n = omp_get_thread_num ()  ! { dg-error "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" }
   !$omp end target
 
   !$omp target device (device_num : 1)
@@ -30,4 +30,4 @@ subroutine f1 ()
     n = omp_get_thread_num ()
   !$omp end target
 
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
index 63872fa51fb..ab56e2d1d52 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
@@ -4,11 +4,11 @@
 ! Test to ensure that device-modifier 'ancestor' is parsed correctly in
 ! device clauses.
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
-!$omp target device (ancestor : 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : 1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
 !$omp end target
 
 end
 
-! TODO: dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 701d33dae49..ebf6978b697 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -330,6 +330,12 @@ enum gomp_map_kind
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 #define GOMP_DEPEND_INOUTSET		5
 
+/* Flag values for requires-directive features, must match corresponding
+   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
+#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
+#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgcc/offloadstuff.c b/libgcc/offloadstuff.c
index 10e1fe19c8e..b2282924fb4 100644
--- a/libgcc/offloadstuff.c
+++ b/libgcc/offloadstuff.c
@@ -54,6 +54,9 @@ const void *const __offload_var_table[0]
   __attribute__ ((__used__, visibility ("hidden"),
 		  section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
 
+const unsigned int const __requires_mask_table[0]
+  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
+
 #elif defined CRT_END
 
 const void *const __offload_funcs_end[0]
@@ -63,6 +66,9 @@ const void *const __offload_vars_end[0]
   __attribute__ ((__used__, visibility ("hidden"),
 		  section (OFFLOAD_VAR_TABLE_SECTION_NAME))) = { };
 
+const unsigned int const __requires_mask_table_end[0]
+  __attribute__ ((__used__, section (".gnu.gomp_requires"))) = { };
+
 #elif defined CRT_TABLE
 
 extern const void *const __offload_func_table[];
@@ -77,6 +83,9 @@ const void *const __OFFLOAD_TABLE__[]
   &__offload_var_table, &__offload_vars_end
 };
 
+extern const unsigned int const __requires_mask_table[];
+extern const unsigned int const __requires_mask_table_end[];
+
 #else /* ! CRT_BEGIN && ! CRT_END && ! CRT_TABLE  */
 #error "One of CRT_BEGIN, CRT_END or CRT_TABLE must be defined."
 #endif
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 07ab700b80c..ab3ed638475 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
 extern const char *GOMP_OFFLOAD_get_name (void);
 extern unsigned int GOMP_OFFLOAD_get_caps (void);
 extern int GOMP_OFFLOAD_get_type (void);
-extern int GOMP_OFFLOAD_get_num_devices (void);
+extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
 extern bool GOMP_OFFLOAD_init_device (int);
 extern bool GOMP_OFFLOAD_fini_device (int);
 extern unsigned GOMP_OFFLOAD_version (void);
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index 5bb889926d3..eb11b9cf16a 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -54,7 +54,7 @@ host_get_type (void)
 }
 
 static int
-host_get_num_devices (void)
+host_get_num_devices (unsigned int omp_requires_mask __attribute__((unused)))
 {
   return 1;
 }
@@ -229,7 +229,7 @@ host_openacc_get_property (int n, enum goacc_property prop)
 {
   union goacc_property_value nullval = { .val = 0 };
 
-  if (n >= host_get_num_devices ())
+  if (n >= host_get_num_devices (0))
     return nullval;
 
   switch (prop)
diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c
index 1565aa0f290..42c3e74e6ba 100644
--- a/libgomp/oacc-init.c
+++ b/libgomp/oacc-init.c
@@ -148,7 +148,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
 	      if (dispatchers[d]
 		  && !strcasecmp (goacc_device_type,
 				  get_openacc_name (dispatchers[d]->name))
-		  && dispatchers[d]->get_num_devices_func () > 0)
+		  && dispatchers[d]->get_num_devices_func (0) > 0)
 		goto found;
 
 	    if (fail_is_error)
@@ -169,7 +169,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
     case acc_device_not_host:
       /* Find the first available device after acc_device_not_host.  */
       while (known_device_type_p (++d))
-	if (dispatchers[d] && dispatchers[d]->get_num_devices_func () > 0)
+	if (dispatchers[d] && dispatchers[d]->get_num_devices_func (0) > 0)
 	  goto found;
       if (d_arg == acc_device_default)
 	{
@@ -302,7 +302,7 @@ acc_init_1 (acc_device_t d, acc_construct_t parent_construct, int implicit)
 
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   if (ndevs <= 0 || goacc_device_num >= ndevs)
     acc_dev_num_out_of_range (d, goacc_device_num, ndevs);
@@ -351,7 +351,7 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   /* Unload all the devices of this type that have been opened.  */
   for (i = 0; i < ndevs; i++)
@@ -520,7 +520,7 @@ goacc_attach_host_thread_to_device (int ord)
       base_dev = cached_base_dev;
     }
   
-  num_devices = base_dev->get_num_devices_func ();
+  num_devices = base_dev->get_num_devices_func (0);
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (acc_device_type (base_dev->type), ord,
 			      num_devices);
@@ -599,7 +599,7 @@ acc_get_num_devices (acc_device_t d)
   if (!acc_dev)
     return 0;
 
-  n = acc_dev->get_num_devices_func ();
+  n = acc_dev->get_num_devices_func (0);
   if (n < 0)
     n = 0;
 
@@ -779,7 +779,7 @@ acc_set_device_num (int ord, acc_device_t d)
 
       cached_base_dev = base_dev = resolve_device (d, true);
 
-      num_devices = base_dev->get_num_devices_func ();
+      num_devices = base_dev->get_num_devices_func (0);
 
       if (num_devices <= 0 || ord >= num_devices)
         acc_dev_num_out_of_range (d, ord, num_devices);
@@ -814,7 +814,7 @@ get_property_any (int ord, acc_device_t d, acc_device_property_t prop)
 
   struct gomp_device_descr *dev = resolve_device (d, true);
 
-  int num_devices = dev->get_num_devices_func ();
+  int num_devices = dev->get_num_devices_func (0);
 
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (d, ord, num_devices);
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 1c0436842da..ea327bf2ca0 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -3221,10 +3221,14 @@ GOMP_OFFLOAD_version (void)
 /* Return the number of GCN devices on the system.  */
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
   if (!init_hsa_context ())
     return 0;
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
+    return -1;
   return hsa_context.agent_count;
 }
 
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 387bcbbc52a..bc63e274cdf 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1175,9 +1175,14 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
-  return nvptx_get_num_devices ();
+  int num_devices = nvptx_get_num_devices ();
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
+  return num_devices;
 }
 
 bool
diff --git a/libgomp/target.c b/libgomp/target.c
index 4740f8a45d3..0fd3f7f47ad 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -36,6 +36,7 @@
 # include <inttypes.h>  /* For PRIu64.  */
 #endif
 #include <string.h>
+#include <stdio.h>  /* For snprintf. */
 #include <assert.h>
 #include <errno.h>
 
@@ -98,6 +99,13 @@ static int num_devices;
 /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
 static int num_devices_openmp;
 
+/* Start/end of .gnu.gomp.requires section of program, defined in
+   crtoffloadbegin/end.o.  */
+__attribute__((weak))
+extern const unsigned int __requires_mask_table[];
+__attribute__((weak))
+extern const unsigned int __requires_mask_table_end[];
+
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
 
 static void *
@@ -4085,6 +4093,20 @@ gomp_target_fini (void)
     }
 }
 
+static void
+gomp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* This function initializes the runtime for offloading.
    It parses the list of offload plugins, and tries to load these.
    On return, the variables NUM_DEVICES and NUM_DEVICES_OPENMP
@@ -4106,6 +4128,35 @@ gomp_target_init (void)
   if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED)
     return;
 
+  /* Mask of requires directive clause values, summarized from
+     .gnu.gomp.requires section. Offload plugins are queried with this mask to see
+     if all required features are supported.  */
+  unsigned int requires_mask = 0;
+  const unsigned int *mask_ptr = __requires_mask_table;
+  bool error_emitted = false;
+  while (mask_ptr != __requires_mask_table_end)
+    {
+      if (requires_mask == 0)
+	requires_mask = *mask_ptr;
+      else if (requires_mask != *mask_ptr)
+	{
+	  if (!error_emitted)
+	    {
+	      char buf[64], buf2[64];
+	      gomp_requires_to_name (buf, sizeof (buf), requires_mask);
+	      gomp_requires_to_name (buf2, sizeof (buf2), *mask_ptr);
+	      gomp_error ("requires-directive clause inconsistency between "
+			  "compilation units detected: '%s' vs. '%s'",
+			  buf, buf2);
+	      error_emitted = true;
+	    }
+	  /* This is inconsistent, but still merge to query for all features
+	     later.  */
+	  requires_mask |= *mask_ptr;
+	}
+      mask_ptr++;
+    }
+
   cur = OFFLOAD_PLUGINS;
   if (*cur)
     do
@@ -4132,8 +4183,19 @@ gomp_target_init (void)
 
 	if (gomp_load_plugin_for_device (&current_device, plugin_name))
 	  {
-	    new_num_devs = current_device.get_num_devices_func ();
-	    if (new_num_devs >= 1)
+	    new_num_devs = current_device.get_num_devices_func (requires_mask);
+	    if (new_num_devs < 0)
+	      {
+		char buf[64];
+		gomp_requires_to_name (buf, sizeof (buf), requires_mask);
+		char *name = (char *) malloc (cur_len + 1);
+		memcpy (name, cur, cur_len);
+		name[cur_len] = '\0';
+		GOMP_PLUGIN_error ("note: %s devices present but 'omp requires "
+				   "%s' cannot be fulfilled", name, buf);
+		free (name);
+	      }
+	    else if (new_num_devs >= 1)
 	      {
 		/* Augment DEVICES and NUM_DEVICES.  */
 
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
new file mode 100644
index 00000000000..8b9341523c6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
new file mode 100644
index 00000000000..990b4e9817d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -0,0 +1,21 @@
+/* { dg-skip-if "" { ! offloading_enabled } } */
+/* { dg-additional-sources requires-1-aux.c } */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "libgomp: requires-directive clause inconsistency between compilation units detected" } */
+/* { dg-prune-output "nvptx device present but 'omp requires unified_shared_memory, reverse_offload, reverse_offload' cannot be fulfilled" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
new file mode 100644
index 00000000000..4077648347d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_shared_memory
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
new file mode 100644
index 00000000000..bc55ab001e9
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -0,0 +1,20 @@
+/* { dg-additional-sources requires-2-aux.c } */
+/* { dg-require-effective-target offload_device } */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "devices present but 'omp requires unified_shared_memory' cannot be fulfilled" } */
diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
index d1678d0514e..33bae0650b4 100644
--- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
+++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
@@ -168,8 +168,12 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 extern "C" int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
   TRACE ("(): return %d", num_devices);
   return num_devices;
 }

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch] OpenMP: Move omp requires checks to libgomp
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
@ 2022-06-09 11:40   ` Jakub Jelinek
  2022-06-09 12:46     ` Tobias Burnus
  2022-07-06 10:30   ` Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] " Thomas Schwinge
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-06-09 11:40 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Wed, Jun 08, 2022 at 05:56:02AM +0200, Tobias Burnus wrote:
> gcc/c/ChangeLog:
> 
> 	* c-parser.cc (c_parser_declaration_or_fndef): Set
> 	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
> 	"omp declare target" attribute.
> 	(c_parser_omp_target_data): Set	OMP_REQUIRES_TARGET_USED in
> 	omp_requires_mask.
> 	(c_parser_omp_target_enter_data): Likewise.
> 	(c_parser_omp_target_exit_data): Likewise.
> 	(c_parser_omp_requires): Remove sorry.
> 
> gcc/cp/ChangeLog:
> 
> 	* parser.cc (cp_parser_simple_declaration): Set
> 	OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
> 	"omp declare target" attribute.
> 	(cp_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
> 	omp_requires_mask.
> 	(cp_parser_omp_target_enter_data): Likewise.
> 	(cp_parser_omp_target_exit_data): Likewise.
> 	(cp_parser_omp_requires): Remove sorry.
> 
> gcc/fortran/ChangeLog:
> 
> 	* openmp.cc (gfc_match_omp_requires): Remove "not implemented yet".
> 	* parse.cc: Include "tree.h" and "omp-general.h".
> 	(gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.
> 
> gcc/ChangeLog:
> 
> 	* omp-general.h (omp_runtime_api_call): New prototype.
> 	* omp-general.cc (omp_runtime_api_call): Added device_api_only arg
> 	and moved from ...
> 	* omp-low.cc (omp_runtime_api_call): ... here.
> 	(scan_omp_1_stmt): Update call.
> 	* gimplify.cc (gimplify_call_expr): Call omp_runtime_api_call.
> 	* omp-offload.cc (omp_finish_file): Add code to create OpenMP requires
> 	mask variable in .gnu.gomp_requires section, if needed.
> 
> include/ChangeLog:
> 
> 	* gomp-constants.h (GOMP_REQUIRES_UNIFIED_ADDRESS,
> 	GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
> 	GOMP_REQUIRES_REVERSE_OFFLOAD): New.
> 
> libgcc/ChangeLog:
> 
> 	* offloadstuff.c (__requires_mask_table, __requires_mask_table_end):
> 	New symbols to mark start and end of the .gnu.gomp_requires section.
> 
> 
> libgomp/ChangeLog:
> 
> 	* libgomp-plugin.h (GOMP_OFFLOAD_get_num_devices): Add
> 	omp_requires_mask arg.
> 	* plugin/plugin-gcn.c (GOMP_OFFLOAD_get_num_devices): Likewise;
> 	return -1 when device available but omp_requires_mask != 0.
> 	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_get_num_devices): Likewise.
> 	* oacc-host.c (host_get_num_devices, host_openacc_get_property):
> 	Update call.
> 	* oacc-init.c (resolve_device, acc_init_1, acc_shutdown_1,
> 	goacc_attach_host_thread_to_device, acc_get_num_devices,
> 	acc_set_device_num, get_property_any): Likewise.
> 	* target.c: (__requires_mask_table, __requires_mask_table_end):
> 	Declare weak extern symbols.
> 	(gomp_requires_to_name): New.
> 	(gomp_target_init): Add code to check .gnu._gomp_requires section
> 	mask values for inconsistencies; warn when requirements makes an
> 	existing device unsupported.
> 	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New test.
> 	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
> 	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New test.
> 	* testsuite/libgomp.c-c++-common/requires-2.c: New test.
> 
> liboffloadmic/ChangeLog:
> 
> 	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_num_devices):
> 	Return -1 when device available but omp_requires_mask != 0.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/gomp/requires-4.c: Update dg-*.
> 	* c-c++-common/gomp/target-device-ancestor-2.c: Likewise.
> 	* c-c++-common/gomp/target-device-ancestor-3.c: Likewise.
> 	* c-c++-common/gomp/target-device-ancestor-4.c: Likewise.
> 	* c-c++-common/gomp/target-device-ancestor-5.c: Likewise.
> 	* gfortran.dg/gomp/target-device-ancestor-3.f90: Likewise.
> 	* gfortran.dg/gomp/target-device-ancestor-4.f90: Likewise.
> 	* gfortran.dg/gomp/target-device-ancestor-2.f90: Likewise. Move post-FE
> 	checks to ...
> 	* gfortran.dg/gomp/target-device-ancestor-2a.f90: ... this new file.

> +      if (flag_openmp
> +         && lookup_attribute ("omp declare target",
> +                              DECL_ATTRIBUTES (current_function_decl)))
> +       omp_requires_mask
> +         = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);

I must admit it is unclear what the
"must appear lexically before any device constructs or device routines."
restriction actually means for device routines.
Is that lexically before definition of such device routines, or even their
declarations?

It wouldn't surprise me if some library packages started eventually adding
declare target directives in some headers around external declarations,
should that be the point after which we don't allow requires directives?

On the other side, for the definitions, we don't need to know when parsing
the definition whether it is a device routine.

void
foo (void)
{
}
#pragma omp declare target to (foo)

And yet another question: is
void bar (void);
#pragma omp declare target device_type (host) to (bar)
void
bar (void)
{
}
a device routine or not?

The above patch snippet is I believe for function definitions that were
arked as declare target before the definition somehow (another decl for
it merged with the new one or in between the begin/end).  And is true
even for device_type (host), to rule that out you'd need to check for
"omp declare target host" attribute not being present.
I'm not against the above snippet perhaps adjusted for device_type(host),
but IMHO we want clarifications from omp-lang.

> @@ -20915,6 +20921,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
>  static tree
>  c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
>  {
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
>  				"#pragma omp target data");

target update is also a device construct and the above snippet hasn't been
added for it, ditto for interop which we don't implement yet.
But, my preference would be instead of adding these snippets to
c_parser_omp_target_{data,enter_data,exit_data,update} etc. move it from
c_parser_omp_target to c_parser_omp_all_clauses:
  if (flag_openmp
      && (mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE)) != 0)
    omp_requires_mask
      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
(somewhere at the start of the function), because the definition of device
constructs is exactly like that:
"device construct	An OpenMP construct that accepts the device clause."

> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index da2f370cdca..6e26d123370 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -15389,6 +15389,11 @@ cp_parser_simple_declaration (cp_parser* parser,
>  	  /* Otherwise, we're done with the list of declarators.  */
>  	  else
>  	    {
> +	      if (flag_openmp && lookup_attribute ("omp declare target",
> +						   DECL_ATTRIBUTES (decl)))
> +		omp_requires_mask
> +		  = (enum omp_requires) (omp_requires_mask
> +					 | OMP_REQUIRES_TARGET_USED);
>  	      pop_deferring_access_checks ();
>  	      cp_finalize_omp_declare_simd (parser, &odsd);
>  	      return;

Ditto.

> @@ -44287,6 +44292,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
>  static tree
>  cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
>  {
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
>  				 "#pragma omp target data", pragma_tok);
> @@ -44390,6 +44399,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
>        return true;
>      }
>  
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
>  				 "#pragma omp target enter data", pragma_tok);
> @@ -44481,6 +44494,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
>        return true;
>      }
>  
> +  if (flag_openmp)
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>    tree clauses
>      = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
>  				 "#pragma omp target exit data", pragma_tok);

Ditto.

For Fortran, is the above mostly not needed because requires need to be in
the specification part and device constructs are executable and appear in
the part after it?  Do we allow requires in BLOCK's specification part?

> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -3644,6 +3644,9 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
>  	  return GS_OK;
>  	}
>      }
> +  if (fndecl && flag_openmp && omp_runtime_api_call (fndecl, true))
> +    omp_requires_mask
> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
>  
>    /* Remember the original function pointer type.  */
>    fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p));

I'm sure device APIs were discussed, but I must be blind and I can't find it
in either 5.0, 5.1 or 5.2.  All I see is device constructs or device routines
in those places where I'd also look for device related OpenMP runtime
library APIs.  Though, if some routine calls omp_get_num_devices (),
certainly the library at that point needs to know
reverse_offload/unified_shared_memory/etc. requires because that determines
how many devices it has.  So, what have I missed (aka on which place in the
standard the above snippet is based on)?
Perhaps I had in mind by "device routines" the OpenMP runtime APIs related
to devices, but unfortunately we have a different glossary for that term:
"device routine	A function (for C/C+ and Fortran) or subroutine (for Fortran)
		that can be executed on a target device, as part of a target region."

> +      /* Now likewise but for device API. */

Two spaces after .

> +      /* Now omp_* calls that are available as omp_* and omp_*_; however, the
> +	 DECL_NAME is always omp_* without tailing underscore. Non device.  */

Likewise.

> +      /* And device APIs. */
> +      "get_device_num",
> +      "get_initial_device",
> +      "is_initial_device",  /* Even if it does not require init'ed devices. */
> +      NULL,
> +      /* And finally calls available as omp_*, omp_*_ and omp_*_8_; however,
> +	 as DECL_NAME only omp_* and omp_*_8 appear. For non device.  */

Ditto 3x.

> --- a/gcc/omp-offload.cc
> +++ b/gcc/omp-offload.cc
> @@ -397,6 +397,27 @@ omp_finish_file (void)
>    unsigned num_funcs = vec_safe_length (offload_funcs);
>    unsigned num_vars = vec_safe_length (offload_vars);
>  
> +  if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
> +    {
> +      if (targetm_common.have_named_sections)
> +	{
> +	  const char *requires_section = ".gnu.gomp_requires";
> +	  tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> +				     get_identifier (".gomp_requires_mask"),
> +				     unsigned_type_node);
> +	  SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));

Don't we want also DECL_USER_ALIGN (maskvar) = 1; so that
we never try to increase its alignment?

Is it an allocated section, or should it be better non-allocated and then
dealt with by mkoffload?

Shouldn't the vars in that section be const, so that it is a read-only
section?

Is unsigned_type_node what we want (say wouldn't be just unsigned_char_node
be enough, currently we just need 3 bits).

Also, wonder if for HAVE_GAS_SHF_MERGE && flag_merge_constants
we shouldn't try to make that section mergeable.  If it goes away during
linking and is replaced by something, then it doesn't matter, but otherwise,
as we don't record which TU had what flags, all we care about is that
there were some TUs which used device construct/routines (and device APIs?)
and used bitmask 7, other TUs that used bitmask 3 and others that used
bitmask 4.

> +	  TREE_STATIC (maskvar) = 1;
> +	  DECL_INITIAL (maskvar)
> +	    = build_int_cst (unsigned_type_node,
> +			     ((unsigned int) omp_requires_mask
> +			      & (OMP_REQUIRES_UNIFIED_ADDRESS
> +				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +				 | OMP_REQUIRES_REVERSE_OFFLOAD)));
> +	  set_decl_section_name (maskvar, requires_section);
> +	  varpool_node::finalize_decl (maskvar);
> +	}
> +    }
> +
>    if (num_funcs == 0 && num_vars == 0)
>      return;
>  
> @@ -442,6 +463,14 @@ omp_finish_file (void)
>      }
>    else
>      {
> +#ifndef ACCEL_COMPILER
> +      if (flag_openmp
> +	  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED)
> +	  && (omp_requires_mask & (OMP_REQUIRES_UNIFIED_ADDRESS
> +				   | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +				   | OMP_REQUIRES_REVERSE_OFFLOAD)))
> +	sorry ("OpenMP device offloading is not supported for this target");
> +#endif

I don't understand this snippet.  Without named sections on the host,
I bet we simply don't support offloading at all,
the record_offload_symbol target hook is only non-trivially defined
for nvptx and nvptx isn't typical host for OpenMP offloading,
because we don't remember it anywhere.

> @@ -32,61 +29,4 @@ integer :: a, b, c
> -
> -
> -end
> \ No newline at end of file

Please avoid this in all files (unless it was there
previously and you are fixing it).

> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h
> @@ -330,6 +330,12 @@ enum gomp_map_kind
>  #define GOMP_DEPEND_MUTEXINOUTSET	4
>  #define GOMP_DEPEND_INOUTSET		5
>  
> +/* Flag values for requires-directive features, must match corresponding
> +   OMP_REQUIRES_* values in gcc/omp-general.h.  */
> +#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
> +#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
> +#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80

They don't have to much those, we can translate them
(and translating them to 1/2/4 might be a good idea).

> --- a/libgomp/libgomp-plugin.h
> +++ b/libgomp/libgomp-plugin.h
> @@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
>  extern const char *GOMP_OFFLOAD_get_name (void);
>  extern unsigned int GOMP_OFFLOAD_get_caps (void);
>  extern int GOMP_OFFLOAD_get_type (void);
> -extern int GOMP_OFFLOAD_get_num_devices (void);
> +extern int GOMP_OFFLOAD_get_num_devices (unsigned int);

I wonder if we shouldn't rename it when we change the arguments,
so that if one mixes an older plugin with newer libgomp or vice versa
this is easily caught.

> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -36,6 +36,7 @@
>  # include <inttypes.h>  /* For PRIu64.  */
>  #endif
>  #include <string.h>
> +#include <stdio.h>  /* For snprintf. */
>  #include <assert.h>
>  #include <errno.h>
>  
> @@ -98,6 +99,13 @@ static int num_devices;
>  /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
>  static int num_devices_openmp;
>  
> +/* Start/end of .gnu.gomp.requires section of program, defined in

Isn't it .gnu.gomp_requires ?

> +   crtoffloadbegin/end.o.  */
> +__attribute__((weak))
> +extern const unsigned int __requires_mask_table[];
> +__attribute__((weak))
> +extern const unsigned int __requires_mask_table_end[];

I must say it is unclear to me how this works.
It will only find one such array, say in the executable,
or if the executable doesn't have it, in one of the shared libraries.

I think we want some solution that will work with OpenMP code
at least in the executable and some shared libraries it is linked against
(obviously another case is when a library with certain #pragma omp requires
is dlopened after we've finalized the number of devices, bet the options
in that case are either warn or fatal error).

The choices could be e.g. make __requires_mask_table{,_end} .hidden
and in the crtoffloadbegin.o (or end) unconditionally call some new libgomp
routine to register the table, but the disadvantage of that is that we could
have many of those register calls even when there is nothing to register
(sure, the .ctor in crtoffloadbegin.o (or end) could compare the 2 addresses
and not call anything if the table is empty but there would be still code
executed during initialization of the library).

Yet another possibility is linker plugin case.  We already use it for the
case where we actually have some offloading LTO bytecode, transform it into
a data section and register with GOMP_offload_register*.
So, if we could e.g. at the same time also process those requires arrays,
diagnose at link time if multiple TUs with that set disagree on the mask
value and in the target data provide that mask to the library, that would
be much nicer.
And the masks either could be gathered from .gnu.gomp_requires or it can be
somehow encoded in the offloading LTO or its associated data.
What is important though is that it will work even if we actually don't have
any "omp declare target" functions or variables in the TU or the whole
executable or library, just the requires mask.  But that can be dealt with
e.g. by forcing the LTO sections even for that case or so.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch] OpenMP: Move omp requires checks to libgomp
  2022-06-09 11:40   ` Jakub Jelinek
@ 2022-06-09 12:46     ` Tobias Burnus
  2022-06-09 14:19       ` Jakub Jelinek
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-06-09 12:46 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 09.06.22 13:40, Jakub Jelinek via Gcc-patches wrote:
> On Wed, Jun 08, 2022 at 05:56:02AM +0200, Tobias Burnus wrote:
>> +         && lookup_attribute ("omp declare target",
>> +                              DECL_ATTRIBUTES (current_function_decl)))
>> +       omp_requires_mask
>> +         = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> I must admit it is unclear what the
> "must appear lexically before any device constructs or device routines."
> restriction actually means for device routines.
> Is that lexically before definition of such device routines, or even their
> declarations?
I have similar issues – also for Fortran (and C++) module use. Hence, I
had filled https://github.com/OpenMP/spec/issues/3240 (not publicly
accessible); I added your issues to the list.
> The above patch snippet is I believe for function definitions that were
> arked as declare target before the definition somehow (another decl for
> it merged with the new one or in between the begin/end).  And is true
> even for device_type (host), to rule that out you'd need to check for
> "omp declare target host" attribute not being present.
> I'm not against the above snippet perhaps adjusted for device_type(host),
> but IMHO we want clarifications from omp-lang
How to proceed for now? And does 'omp_is_initial_device()' on the host a
device function or not? It can be hard-coded to 'true' ...
> [...]
> target update is also a device construct and the above snippet hasn't been
> added for it, ditto for interop which we don't implement yet.
> But, my preference would be instead of adding these snippets to
> c_parser_omp_target_{data,enter_data,exit_data,update} etc. move it from
> c_parser_omp_target to c_parser_omp_all_clauses:
>    if (flag_openmp
>        && (mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE)) != 0)
>      omp_requires_mask
>        = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> (somewhere at the start of the function), because the definition of device
> constructs is exactly like that:
> "device construct     An OpenMP construct that accepts the device clause."

Makes sense.

[C++ cases]

> Ditto.
> For Fortran, is the above mostly not needed because requires need to be in
> the specification part and device constructs are executable and appear in
> the part after it?  Do we allow requires in BLOCK's specification part?
We don't allow it in BLOCK – but there are issues related to USE-ing
modules, cf. OpenMP issue.
>> --- a/gcc/gimplify.cc
>> +++ b/gcc/gimplify.cc
>> @@ -3644,6 +3644,9 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
>> +  if (fndecl && flag_openmp && omp_runtime_api_call (fndecl, true))
>> +    omp_requires_mask
>> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> I'm sure device APIs were discussed, but I must be blind and I can't find it
> in either 5.0, 5.1 or 5.2.  All I see is device constructs or device routines
> in those places where I'd also look for device related OpenMP runtime
> library APIs.  Though, if some routine calls omp_get_num_devices (),
> certainly the library at that point needs to know
> reverse_offload/unified_shared_memory/etc. requires because that determines
> how many devices it has.  So, what have I missed (aka on which place in the
> standard the above snippet is based on)?

It is based on your review comments from last year ("Something I miss in
the patch is that for the device API calls") plus what requires some
device initialization. But otherwise, I also did not see it.

In terms of parsing, it makes no difference – contrary to
'unified_shared_memory', where the parser could decide not to add
implicit mapping, the compiler part is not affected by API calls.

I cannot really make up my mind whether it should be required in this
case or not. Maybe, it is not needed.

> + const char *requires_section = ".gnu.gomp_requires";
>> +      tree maskvar = build_decl (UNKNOWN_LOCATION, VAR_DECL,
>> +                                 get_identifier (".gomp_requires_mask"),
>> +                                 unsigned_type_node);
>> +      SET_DECL_ALIGN (maskvar, TYPE_ALIGN (unsigned_type_node));
> Don't we want also DECL_USER_ALIGN (maskvar) = 1; so that
> we never try to increase its alignment?
Probably yes.
> Is it an allocated section, or should it be better non-allocated and then
> dealt with by mkoffload?
>
> Shouldn't the vars in that section be const, so that it is a read-only
> section?
>
> Is unsigned_type_node what we want (say wouldn't be just unsigned_char_node
> be enough, currently we just need 3 bits).

Probably -that would be 8 bits, leaving 5 spare. I have not checked what
Andrew et al. do with the pinned-memory support by -f<some-flag>, but
that will likely use only 1 to 3 bits, if any.

> Also, wonder if for HAVE_GAS_SHF_MERGE && flag_merge_constants
> we shouldn't try to make that section mergeable.  If it goes away during
> linking and is replaced by something, then it doesn't matter, but otherwise,
> as we don't record which TU had what flags, all we care about is that
> there were some TUs which used device construct/routines (and device APIs?)
> and used bitmask 7, other TUs that used bitmask 3 and others that used
> bitmask 4.
(maybe – I am not sure about this, either.)
> @@ -442,6 +463,14 @@ omp_finish_file (void)
>       }
>     else
>       {
> +#ifndef ACCEL_COMPILER
> +      if (flag_openmp
> +       && (omp_requires_mask & OMP_REQUIRES_TARGET_USED)
> +       && (omp_requires_mask & (OMP_REQUIRES_UNIFIED_ADDRESS
> +                                | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +                                | OMP_REQUIRES_REVERSE_OFFLOAD)))
> +     sorry ("OpenMP device offloading is not supported for this target");
> +#endif
> I don't understand this snippet.  Without named sections on the host,
> I bet we simply don't support offloading at all,
> the record_offload_symbol target hook is only non-trivially defined
> for nvptx and nvptx isn't typical host for OpenMP offloading,
> because we don't remember it anywhere.

I thought that would address your: "This probably needs to sorry if the
target doesn't support named sections. We probably don't support LTO in
that case either though."

> I wonder if we shouldn't rename it when we change the arguments,
> so that if one mixes an older plugin with newer libgomp or vice versa
> this is easily caught.
Ok.
>> +/* Start/end of .gnu.gomp.requires section of program, defined in
> Isn't it .gnu.gomp_requires ?
Yes.
>> +   crtoffloadbegin/end.o.  */
>> +__attribute__((weak))
>> +extern const unsigned int __requires_mask_table[];
>> +__attribute__((weak))
>> +extern const unsigned int __requires_mask_table_end[];
> I must say it is unclear to me how this works.
> It will only find one such array, say in the executable,
> or if the executable doesn't have it, in one of the shared libraries.
>
> I think we want some solution that will work with OpenMP code
> at least in the executable and some shared libraries it is linked against
> (obviously another case is when a library with certain #pragma omp requires
> is dlopened after we've finalized the number of devices, bet the options
> in that case are either warn or fatal error).

There is the general problem that GCC does not work well with having
device routines in a shared library;  it works fine if the device part
is only in the library – but calling from an .exe program's target
region a declare-target function in a library won't work.

Thus, we may need to find a more general solution for this.

> The choices could be e.g. make __requires_mask_table{,_end} .hidden
> and in the crtoffloadbegin.o (or end) unconditionally call some new libgomp
> routine to register the table, but the disadvantage of that is that we could
> have many of those register calls even when there is nothing to register
> (sure, the .ctor in crtoffloadbegin.o (or end) could compare the 2 addresses
> and not call anything if the table is empty but there would be still code
> executed during initialization of the library).
>
> Yet another possibility is linker plugin case.  We already use it for the
> case where we actually have some offloading LTO bytecode, transform it into
> a data section and register with GOMP_offload_register*.

> So, if we could e.g. at the same time also process those requires arrays,
> diagnose at link time if multiple TUs with that set disagree on the mask
> value and in the target data provide that mask to the library, that would
> be much nicer.
> And the masks either could be gathered from .gnu.gomp_requires or it can be
> somehow encoded in the offloading LTO or its associated data.
> What is important though is that it will work even if we actually don't have
> any "omp declare target" functions or variables in the TU or the whole
> executable or library, just the requires mask.  But that can be dealt with
> e.g. by forcing the LTO sections even for that case or so.
Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch] OpenMP: Move omp requires checks to libgomp
  2022-06-09 12:46     ` Tobias Burnus
@ 2022-06-09 14:19       ` Jakub Jelinek
  2022-06-29 14:33         ` [Patch][v4] " Tobias Burnus
  0 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-06-09 14:19 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Thu, Jun 09, 2022 at 02:46:34PM +0200, Tobias Burnus wrote:
> On 09.06.22 13:40, Jakub Jelinek via Gcc-patches wrote:
> > On Wed, Jun 08, 2022 at 05:56:02AM +0200, Tobias Burnus wrote:
> > > +         && lookup_attribute ("omp declare target",
> > > +                              DECL_ATTRIBUTES (current_function_decl)))
> > > +       omp_requires_mask
> > > +         = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> > I must admit it is unclear what the
> > "must appear lexically before any device constructs or device routines."
> > restriction actually means for device routines.
> > Is that lexically before definition of such device routines, or even their
> > declarations?
> I have similar issues – also for Fortran (and C++) module use. Hence, I
> had filled https://github.com/OpenMP/spec/issues/3240 (not publicly
> accessible); I added your issues to the list.
> > The above patch snippet is I believe for function definitions that were
> > arked as declare target before the definition somehow (another decl for
> > it merged with the new one or in between the begin/end).  And is true
> > even for device_type (host), to rule that out you'd need to check for
> > "omp declare target host" attribute not being present.
> > I'm not against the above snippet perhaps adjusted for device_type(host),
> > but IMHO we want clarifications from omp-lang
> How to proceed for now? And does 'omp_is_initial_device()' on the host a
> device function or not? It can be hard-coded to 'true' ...

If it is from me, bet it was because of that (mis)understanding that
device routines are device related runtime API calls.
I'd suggest to only mark in the patch what is clear (which is device
constructs) and defer the rest until it is clarified.

> > For Fortran, is the above mostly not needed because requires need to be in
> > the specification part and device constructs are executable and appear in
> > the part after it?  Do we allow requires in BLOCK's specification part?
> We don't allow it in BLOCK – but there are issues related to USE-ing
> modules, cf. OpenMP issue.

Ack.

> In terms of parsing, it makes no difference – contrary to
> 'unified_shared_memory', where the parser could decide not to add
> implicit mapping, the compiler part is not affected by API calls.

Yeah.  So perhaps on the standard side we should just keep the
lexically before device constructs (and metadirective/declare variant
device related resolution) in the restriction, but say that TUs
that have device constructs and device runtime APIs (or whatever is agreed)
imply that requires mask must be the same in all of them.

> > Shouldn't the vars in that section be const, so that it is a read-only
> > section?
> > 
> > Is unsigned_type_node what we want (say wouldn't be just unsigned_char_node
> > be enough, currently we just need 3 bits).
> 
> Probably -that would be 8 bits, leaving 5 spare. I have not checked what
> Andrew et al. do with the pinned-memory support by -f<some-flag>, but
> that will likely use only 1 to 3 bits, if any.

If it is SHF_MERGE, even 16-bit or 32-bit wouldn't be the end of the world,
or if it is in LTO streamed out stuff, we can use a bitpack for it...

> > Also, wonder if for HAVE_GAS_SHF_MERGE && flag_merge_constants
> > we shouldn't try to make that section mergeable.  If it goes away during
> > linking and is replaced by something, then it doesn't matter, but otherwise,
> > as we don't record which TU had what flags, all we care about is that
> > there were some TUs which used device construct/routines (and device APIs?)
> > and used bitmask 7, other TUs that used bitmask 3 and others that used
> > bitmask 4.
> (maybe – I am not sure about this, either.)
> > @@ -442,6 +463,14 @@ omp_finish_file (void)
> >       }
> >     else
> >       {
> > +#ifndef ACCEL_COMPILER
> > +      if (flag_openmp
> > +       && (omp_requires_mask & OMP_REQUIRES_TARGET_USED)
> > +       && (omp_requires_mask & (OMP_REQUIRES_UNIFIED_ADDRESS
> > +                                | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> > +                                | OMP_REQUIRES_REVERSE_OFFLOAD)))
> > +     sorry ("OpenMP device offloading is not supported for this target");
> > +#endif
> > I don't understand this snippet.  Without named sections on the host,
> > I bet we simply don't support offloading at all,
> > the record_offload_symbol target hook is only non-trivially defined
> > for nvptx and nvptx isn't typical host for OpenMP offloading,
> > because we don't remember it anywhere.
> 
> I thought that would address your: "This probably needs to sorry if the
> target doesn't support named sections. We probably don't support LTO in
> that case either though."

But sorry means we will fail to compile it.  Perhaps
inform would be better, but then we don't complain (warn/inform)
if no offloading targets are configured.  And, presence of requires
unified*/reverse_offload  as the reason for the diagnostics rather than
say presence of declare target functions would be weird.

I think best would be a fatal error if people try to configure
offloading targets for a compiler that doesn't support named sections,
or perhaps that and presence of anything that should be offloaded.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v4] OpenMP: Move omp requires checks to libgomp
  2022-06-09 14:19       ` Jakub Jelinek
@ 2022-06-29 14:33         ` Tobias Burnus
  2022-06-29 17:02           ` Jakub Jelinek
                             ` (2 more replies)
  0 siblings, 3 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-06-29 14:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 3986 bytes --]

Hi Jakub, hi all,

new version attached. It now checks during lto1 whether the values are
consistent – and fails with a hard error.

The actually used value (by libgomp) is stored as a scalar weak symbol –
while for checking, each translation unit stores the integer value for
lto (alongside the offload table). This is both used for checking and in
lto1 (device + host lto1), to restore the value of 'omp_requires_mask'
for further use. – Currently, it is only used on the host to make the
value available to libgomp. – However, a device lto1 could also use it.
(Usage: cf. Andrew's USM gcn patch.)

Unchanged from previous version, libgomp outputs an warning/note if a
device could be found but the requires prevented libgomp from using it.
This message is also shown with -foffload=disable but it is not shown
for OMP_TARGET_OFFLOAD=disable.

Other change is that API calls no longer count as relevant for 'omp
requires' – such that compilation units which only contain those will
not output anything (independent whether there is an 'omp requires' or not.)

On 09.06.22 16:19, Jakub Jelinek wrote:
> On Thu, Jun 09, 2022 at 02:46:34PM +0200, Tobias Burnus wrote:
>> On 09.06.22 13:40, Jakub Jelinek via Gcc-patches wrote:
> If it is from me, bet it was because of that (mis)understanding that
> device routines are device related runtime API calls.
> I'd suggest to only mark in the patch what is clear (which is device
> constructs) and defer the rest until it is clarified.
Done so.
>>> Shouldn't the vars in that section be const, so that it is a read-only
>>> section?
>>>
>>> Is unsigned_type_node what we want (say wouldn't be just unsigned_char_node
>>> be enough, currently we just need 3 bits).
>> Probably -that would be 8 bits, leaving 5 spare. I have not checked what
>> Andrew et al. do with the pinned-memory support by -f<some-flag>, but
>> that will likely use only 1 to 3 bits, if any.
> If it is SHF_MERGE, even 16-bit or 32-bit wouldn't be the end of the world,
> or if it is in LTO streamed out stuff, we can use a bitpack for it...

As the final binary will only contain a single variable, the size should
not matter much. I currently use 'unsigned' but it could surely be
shorter.  For the .o files, it also outputs the unsigned value for each
TU, but that's also small. I was thinking about adding more data (like
location data, be it location_t or __FILENAME__). However, it uses a
stripped-down stream writer - and to do so, location/string writing
requires a different object (and reading it, data_in). I did not regard
this as worthwhile and, thus, I only output the used requires clauses
and not where they were used.

> I think best would be a fatal error if people try to configure
> offloading targets for a compiler that doesn't support named sections,
> or perhaps that and presence of anything that should be offloaded.

I do not use any named section – but I could if it makes sense. In any
case, the question is whether the current weak symbol makes sense or
not. And whether there are problems in using weak symbols (in libgomp's
target.c + for non-ACCEL_COMPILER, but only when the symbol needs to be
written). I am also not sure about the best naming. – Thoughts?

Otherwise, tested with no offloading configured + with offloading to
nvptx (fully tested) and gcn (partially) [all x86_64-gnu-linux)

Tobias

PS: At some point, we need to think about handling calling from a
program's target region a declare-target device function which is inside
a shared library. I am sure, we currently do not handle it. – For that,
we then also have to think about how to handle the requires clauses.
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Attachment #2: omp-requires-v4.diff --]
[-- Type: text/x-patch, Size: 49650 bytes --]

OpenMP: Move omp requires checks to libgomp

Handle reverse_offload, unified_address, and unified_shared_memory
requirements in libgomp by putting them into the '__offload_requires_mask'
weak variable. Additionally, store the value alongside the offload table
in lto - to permit checking the value for consistency in lto1. The value
is only stored when actually required due to 'omp (declare) target ...'.

In lto1 (either the host one, with -flto [+ ENABLE_OFFLOADING], or in the
offload-device lto1), the consistency check is done, erroring out when
an inconistency is found.

For all in-principle supported devices, if a requirement cannot be fulfilled,
the device is excluded from the (supported) devices list. Currently, none of
those requirements are marked as supported for any of the non-host devices.

gcc/c/ChangeLog:

        * c-parser.cc (c_parser_declaration_or_fndef): Set
        OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
        "omp declare target" attribute.
        (c_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
        omp_requires_mask.
        (c_parser_omp_target_enter_data): Likewise.
        (c_parser_omp_target_exit_data): Likewise.
        (c_parser_omp_requires): Remove sorry.

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_simple_declaration): Set
        OMP_REQUIRES_TARGET_USED in omp_requires_mask if function has
        "omp declare target" attribute.
        (cp_parser_omp_target_data): Set OMP_REQUIRES_TARGET_USED in
        omp_requires_mask.
        (cp_parser_omp_target_enter_data): Likewise.
        (cp_parser_omp_target_exit_data): Likewise.
        (cp_parser_omp_requires): Remove sorry.

gcc/fortran/ChangeLog:

        * openmp.cc (gfc_match_omp_requires): Remove "not implemented yet".
        * parse.cc: Include "tree.h" and "omp-general.h".
        (gfc_parse_file): Add code to merge omp_requires to omp_requires_mask.

gcc/ChangeLog:

	* lto-cgraph.cc (output_offload_tables): Output omp_requires_mask,
	but only if OMP_REQUIRES_TARGET_USED.
	(omp_requires_to_name): New.
	(input_offload_tables): Read omp_requires_mask and check whether
	all compilation units use the same value.
	* omp-offload.cc (omp_finish_file): Output omp_requires_mask as
	weak symbol '__offload_requires_mask'.

include/ChangeLog:

	* gomp-constants.h (GOMP_REQUIRES_UNIFIED_ADDRESS,
	GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
	GOMP_REQUIRES_REVERSE_OFFLOAD): New.

libgomp/ChangeLog:

        * libgomp-plugin.h (GOMP_OFFLOAD_get_num_devices): Add
        omp_requires_mask arg.
        * plugin/plugin-gcn.c (GOMP_OFFLOAD_get_num_devices): Likewise;
        return -1 when device available but omp_requires_mask != 0.
        * plugin/plugin-nvptx.c (GOMP_OFFLOAD_get_num_devices): Likewise.
        * oacc-host.c (host_get_num_devices, host_openacc_get_property):
        Update call.
        * oacc-init.c (resolve_device, acc_init_1, acc_shutdown_1,
        goacc_attach_host_thread_to_device, acc_get_num_devices,
        acc_set_device_num, get_property_any): Likewise.
	* target.c (__offload_requires_mask): Declare extern weak var.
	(gomp_requires_to_name): New.
	(gomp_target_init): Pass __offload_requires_mask to
	get_num_devices_func, warn if devices present which do not fulfill
	requirements.
	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5.c: New test.
	* testsuite/libgomp.c-c++-common/requires-6.c: New test.

liboffloadmic/ChangeLog:

	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_num_devices):
	Return -1 when device available but omp_requires_mask != 0.

gcc/testsuite/ChangeLog:

        * c-c++-common/gomp/requires-4.c: Update dg-*.
	* c-c++-common/gomp/reverse-offload-1.c: Likewise.
        * c-c++-common/gomp/target-device-ancestor-2.c: Likewise.
        * c-c++-common/gomp/target-device-ancestor-3.c: Likewise.
        * c-c++-common/gomp/target-device-ancestor-4.c: Likewise.
        * c-c++-common/gomp/target-device-ancestor-5.c: Likewise.
        * gfortran.dg/gomp/target-device-ancestor-3.f90: Likewise.
        * gfortran.dg/gomp/target-device-ancestor-4.f90: Likewise.
        * gfortran.dg/gomp/target-device-ancestor-2.f90: Likewise. Move post-FE
        checks to ...
        * gfortran.dg/gomp/target-device-ancestor-2a.f90: ... this new file.

Co-authored-by: Chung-Lin Tang <cltang@codesourcery.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>

 gcc/c/c-parser.cc                                  | 21 +++++-
 gcc/cp/parser.cc                                   | 20 +++++-
 gcc/fortran/openmp.cc                              |  4 --
 gcc/fortran/parse.cc                               | 21 ++++++
 gcc/lto-cgraph.cc                                  | 53 +++++++++++++-
 gcc/omp-offload.cc                                 | 21 ++++++
 gcc/testsuite/c-c++-common/gomp/requires-4.c       |  2 -
 .../c-c++-common/gomp/reverse-offload-1.c          |  2 +-
 .../c-c++-common/gomp/target-device-ancestor-2.c   | 10 ++-
 .../c-c++-common/gomp/target-device-ancestor-3.c   |  2 +-
 .../c-c++-common/gomp/target-device-ancestor-4.c   |  4 +-
 .../c-c++-common/gomp/target-device-ancestor-5.c   |  2 +-
 .../gfortran.dg/gomp/target-device-ancestor-2.f90  | 70 ++-----------------
 .../gfortran.dg/gomp/target-device-ancestor-2a.f90 | 80 ++++++++++++++++++++++
 .../gfortran.dg/gomp/target-device-ancestor-3.f90  |  6 +-
 .../gfortran.dg/gomp/target-device-ancestor-4.f90  |  6 +-
 .../gfortran.dg/gomp/target-device-ancestor-5.f90  |  8 +--
 include/gomp-constants.h                           |  6 ++
 libgomp/libgomp-plugin.h                           |  2 +-
 libgomp/oacc-host.c                                |  4 +-
 libgomp/oacc-init.c                                | 16 ++---
 libgomp/plugin/plugin-gcn.c                        |  6 +-
 libgomp/plugin/plugin-nvptx.c                      |  9 ++-
 libgomp/target.c                                   | 40 ++++++++++-
 .../libgomp.c-c++-common/requires-1-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-1.c    | 24 +++++++
 .../libgomp.c-c++-common/requires-2-aux.c          |  9 +++
 .../testsuite/libgomp.c-c++-common/requires-2.c    | 25 +++++++
 .../libgomp.c-c++-common/requires-3-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-3.c    | 24 +++++++
 .../libgomp.c-c++-common/requires-4-aux.c          | 13 ++++
 .../testsuite/libgomp.c-c++-common/requires-4.c    | 23 +++++++
 .../libgomp.c-c++-common/requires-5-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-5.c    | 20 ++++++
 .../testsuite/libgomp.c-c++-common/requires-6.c    | 17 +++++
 liboffloadmic/plugin/libgomp-plugin-intelmic.cpp   |  6 +-
 36 files changed, 493 insertions(+), 116 deletions(-)

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 1704a52be12..4748ce04737 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -2488,6 +2488,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	  break;
 	}
 
+      if (flag_openmp
+	  && lookup_attribute ("omp declare target",
+			       DECL_ATTRIBUTES (current_function_decl)))
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
       if (DECL_DECLARED_INLINE_P (current_function_decl))
         tv = TV_PARSE_INLINE;
       else
@@ -20915,6 +20921,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
 static tree
 c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				"#pragma omp target data");
@@ -21057,6 +21067,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				"#pragma omp target enter data");
@@ -21143,6 +21157,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				"#pragma omp target exit data");
@@ -22763,9 +22781,6 @@ c_parser_omp_requires (c_parser *parser)
 	      c_parser_skip_to_pragma_eol (parser, false);
 	      return;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    c_parser_consume_token (parser);
 	  if (this_req)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index da2f370cdca..6e26d123370 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -15389,6 +15389,11 @@ cp_parser_simple_declaration (cp_parser* parser,
 	  /* Otherwise, we're done with the list of declarators.  */
 	  else
 	    {
+	      if (flag_openmp && lookup_attribute ("omp declare target",
+						   DECL_ATTRIBUTES (decl)))
+		omp_requires_mask
+		  = (enum omp_requires) (omp_requires_mask
+					 | OMP_REQUIRES_TARGET_USED);
 	      pop_deferring_access_checks ();
 	      cp_finalize_omp_declare_simd (parser, &odsd);
 	      return;
@@ -44287,6 +44292,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
 static tree
 cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				 "#pragma omp target data", pragma_tok);
@@ -44390,6 +44399,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				 "#pragma omp target enter data", pragma_tok);
@@ -44481,6 +44494,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				 "#pragma omp target exit data", pragma_tok);
@@ -46861,9 +46878,6 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
 	      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	      return false;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    cp_lexer_consume_token (parser->lexer);
 	  if (this_req)
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index aeb8a43e12e..a68711081e2 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -5488,10 +5488,6 @@ gfc_match_omp_requires (void)
       else
 	goto error;
 
-      if (requires_clause & ~(OMP_REQ_ATOMIC_MEM_ORDER_MASK
-			      | OMP_REQ_DYNAMIC_ALLOCATORS))
-	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
-		       "yet supported", clause, &old_loc);
       if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
 	goto error;
       requires_clauses |= requires_clause;
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index 7356d1b5a3a..b142e169a5c 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -6908,6 +6908,27 @@ done:
       break;
     }
 
+  if (omp_requires & OMP_REQ_TARGET_MASK)
+    {
+      omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					       | OMP_REQUIRES_TARGET_USED);
+      if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_REVERSE_OFFLOAD);
+      if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_ADDRESS);
+      if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
+	omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+    }
+
+  if (omp_requires & OMP_REQ_DYNAMIC_ALLOCATORS)
+    omp_requires_mask
+	= (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_DYNAMIC_ALLOCATORS);
   /* Do the parse tree dump.  */
   gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
 
diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 237743ef0ba..1b67c4916c4 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pass_manager.h"
 #include "ipa-utils.h"
 #include "omp-offload.h"
+#include "omp-general.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "alloc-pool.h"
@@ -1068,12 +1069,28 @@ read_string (class lto_input_block *ib)
 void
 output_offload_tables (void)
 {
-  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars))
+  bool output_requires = (flag_openmp
+			  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0);
+  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars)
+      && !output_requires)
     return;
 
   struct lto_simple_output_block *ob
     = lto_create_simple_output_block (LTO_section_offload_table);
 
+  if (output_requires)
+    {
+      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
+			   & (OMP_REQUIRES_UNIFIED_ADDRESS
+			      | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+			      | OMP_REQUIRES_REVERSE_OFFLOAD
+			      | OMP_REQUIRES_TARGET_USED));
+      /* (Mis)use LTO_symtab_edge for this variable.  */
+      streamer_write_enum (ob->main_stream, LTO_symtab_tags,
+			   LTO_symtab_last_tag, LTO_symtab_edge);
+      streamer_write_hwi_stream (ob->main_stream, val);
+    }
+
   for (unsigned i = 0; i < vec_safe_length (offload_funcs); i++)
     {
       symtab_node *node = symtab_node::get ((*offload_funcs)[i]);
@@ -1764,6 +1781,20 @@ input_symtab (void)
     }
 }
 
+static void
+omp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* Input function/variable tables that will allow libgomp to look up offload
    target code, and store them into OFFLOAD_FUNCS and OFFLOAD_VARS.  */
 
@@ -1774,6 +1805,8 @@ input_offload_tables (bool do_force_output)
   struct lto_file_decl_data *file_data;
   unsigned int j = 0;
 
+  omp_requires_mask = (omp_requires) 0;
+
   while ((file_data = file_data_vec[j++]))
     {
       const char *data;
@@ -1811,6 +1844,24 @@ input_offload_tables (bool do_force_output)
 	      if (do_force_output)
 		varpool_node::get (var_decl)->force_output = 1;
 	    }
+	  else if (tag == LTO_symtab_edge)
+	    {
+	      static bool error_emitted = false;
+	      HOST_WIDE_INT val = streamer_read_hwi (ib);
+
+	      if (omp_requires_mask == 0)
+		omp_requires_mask = (omp_requires) val;
+	      else if (omp_requires_mask != val && !error_emitted)
+		{
+		  char buf[64], buf2[64];
+		  omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
+		  omp_requires_to_name (buf2, sizeof (buf2), val);
+		  error ("OpenMP %<requires%> directive with non-identical "
+			 "clauses in multiple compilation units: %qs vs. %qs",
+			 buf, buf2);
+		  error_emitted = true;
+		}
+	    }
 	  else
 	    fatal_error (input_location,
 			 "invalid offload table in %s", file_data->file_name);
diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc
index 3a89119371c..68e4f6e0993 100644
--- a/gcc/omp-offload.cc
+++ b/gcc/omp-offload.cc
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "context.h"
 #include "convert.h"
 #include "opts.h"
+#include "varasm.h"
 
 /* Describe the OpenACC looping structure of a function.  The entire
    function is held in a 'NULL' loop.  */
@@ -398,6 +399,26 @@ omp_finish_file (void)
   unsigned num_funcs = vec_safe_length (offload_funcs);
   unsigned num_vars = vec_safe_length (offload_vars);
 
+#ifndef ACCEL_COMPILER
+  if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
+    {
+      tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+				 get_identifier ("__offload_requires_mask"),
+				 unsigned_type_node);
+      TREE_PUBLIC (var) = 1;
+      TREE_STATIC (var) = 1;
+      TREE_READONLY (var) = 1;
+      DECL_INITIAL (var)
+	= build_int_cst (unsigned_type_node,
+			 ((unsigned int) omp_requires_mask
+			  & (OMP_REQUIRES_UNIFIED_ADDRESS
+			     | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+			     | OMP_REQUIRES_REVERSE_OFFLOAD)));
+      declare_weak (var);
+      varpool_node::finalize_decl (var);
+    }
+#endif
+
   if (num_funcs == 0 && num_vars == 0)
     return;
 
diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
index 88ba7746cf8..8f45d83ea6e 100644
--- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
@@ -9,5 +9,3 @@ foo (void)
 #pragma omp requires unified_shared_memory	/* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires unified_address	/* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires reverse_offload	/* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
-
-/* { dg-prune-output "not supported yet" } */
diff --git a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
index 9a3fa5230f8..3452156f948 100644
--- a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
@@ -43,7 +43,7 @@ tg_fn (int *x, int *y)
   x2 = x2 + 2 + called_in_target1 ();
   y2 = y2 + 7;
 
-  #pragma omp target device(ancestor : 1) map(tofrom: x2)
+  #pragma omp target device(ancestor : 1) map(tofrom: x2)  /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
     check_offload(&x2, &y2);
 
   if (x2 != 2+2+3+42 || y2 != 3 + 7)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
index cf05c505004..b16e701bd5a 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
@@ -1,13 +1,11 @@
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (int n)
 {
-  /* The following test is marked with 'xfail' because a previous 'sorry' from
-     'reverse_offload' suppresses the 'sorry' for 'ancestor'.  */
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1)
   ;
 
 
@@ -19,9 +17,9 @@ foo (int n)
   #pragma omp target device (ancestor : 42) /* { dg-error "the 'device' clause expression must evaluate to '1'" } */
   ;
 
-  #pragma omp target device (ancestor : n) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n)
   ;
-  #pragma omp target device (ancestor : n + 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n + 1)
   ;
 
 
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
index ea6e5a0cf6c..d16590107d2 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
@@ -11,7 +11,7 @@ int bar (void);
 
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
index b4b5620bbc0..241234f8daf 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
@@ -4,12 +4,12 @@
   /* Test to ensure that device-modifier 'ancestor' is parsed correctly in
      device clauses. */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
 {
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1) /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
   ;
 
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
index b6ff84bcdab..b1520ff0636 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
@@ -1,4 +1,4 @@
-#pragma omp requires reverse_offload  /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo ()
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
index 117a1d000a5..230c690d84c 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
@@ -4,19 +4,16 @@ implicit none
 
 integer :: a, b, c
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
 
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor: 1)
 !$omp end target
 
-!$omp target device (ancestor : a)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a)
 !$omp end target
 
-!$omp target device (ancestor : a + 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a + 1)
 !$omp end target
 
 
@@ -32,61 +29,4 @@ integer :: a, b, c
 !$omp target device (42)
 !$omp end target
 
-
-! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)
-  !$omp teams  ! { dg-error "" "OpenMP constructs are not allowed in target region with 'ancestor'" { xfail *-*-* } }
-  !$omp end teams
-!$omp end target
-
-!$omp target device (device_num: 1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-!$omp target device (1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-
-! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
-! 'defaultmap', and 'map' clauses appear on the construct.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target nowait device (ancestor: 1)  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target device (ancestor: 1) nowait  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target nowait device (device_num: 1)
-!$omp end target
-
-!$omp target nowait device (1)
-!$omp end target
-
-!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
-!$omp end target
-
-
-! Ensure that 'ancestor' is only used with 'target' constructs (not with
-! 'target data', 'target update' etc.).
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target data map (a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp end target data
-
-!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-
-!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" "" { xfail *-*-* } }
-! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { xfail *-*-* } .-1 }
-
-
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
new file mode 100644
index 00000000000..feb76fe2144
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
@@ -0,0 +1,80 @@
+! { dg-do compile }
+
+implicit none
+
+integer :: a, b, c
+
+!$omp requires reverse_offload
+
+!$omp target device (ancestor: 1)
+!$omp end target
+
+!$omp target device (ancestor : a)
+!$omp end target
+
+!$omp target device (ancestor : a + 1)
+!$omp end target
+
+
+!$omp target device (device_num:42)
+!$omp end target
+
+!$omp target device (42)
+!$omp end target
+
+
+! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
+
+!$omp target device (ancestor: 1)
+  !$omp teams  ! { dg-error "OpenMP constructs are not allowed in target region with 'ancestor'" }
+  !$omp end teams
+!$omp end target
+
+!$omp target device (device_num: 1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+!$omp target device (1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+
+! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
+! 'defaultmap', and 'map' clauses appear on the construct.
+
+!$omp target nowait device (ancestor: 1)  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target device (ancestor: 1) nowait  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target nowait device (device_num: 1)
+!$omp end target
+
+!$omp target nowait device (1)
+!$omp end target
+
+!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
+!$omp end target
+
+
+! Ensure that 'ancestor' is only used with 'target' constructs (not with
+! 'target data', 'target update' etc.).
+! The following test case is marked with 'xfail' because a previous 'sorry' from
+! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
+
+!$omp target data map (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp end target data
+
+!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target device (ancestor: 1) if(.false.)
+! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { target *-*-* } .-1 }
+!$omp end target
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
index f1145bde2ec..e8975e6a08b 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
@@ -16,10 +16,10 @@ subroutine f1 ()
   implicit none
   integer :: n
 
-  !$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 
   !$omp target device (ancestor : 1)
-    n = omp_get_thread_num ()  ! { dg-error "" "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" { xfail *-*-* } }
+    n = omp_get_thread_num ()  ! { dg-error "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" }
   !$omp end target
 
   !$omp target device (device_num : 1)
@@ -30,4 +30,4 @@ subroutine f1 ()
     n = omp_get_thread_num ()
   !$omp end target
 
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
index 63872fa51fb..ab56e2d1d52 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
@@ -4,11 +4,11 @@
 ! Test to ensure that device-modifier 'ancestor' is parsed correctly in
 ! device clauses.
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
-!$omp target device (ancestor : 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : 1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
 !$omp end target
 
 end
 
-! TODO: dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
index 06a11eb5092..ca8d4b282a0 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
@@ -6,7 +6,7 @@
 !
 
 module m
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
@@ -17,7 +17,7 @@ contains
     block
       block
         block
-          !$omp target device(ancestor:1)
+          !$omp target device(ancestor:1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
           !$omp end target
         end block
       end block
@@ -26,7 +26,7 @@ contains
 end module m
 
 subroutine foo()
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
   block
     block
       block
@@ -49,7 +49,7 @@ contains
 end subroutine foo
 
 program main
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index e4dd8ef3e1d..24804aa551f 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -341,6 +341,12 @@ enum gomp_map_kind
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 #define GOMP_DEPEND_INOUTSET		5
 
+/* Flag values for requires-directive features, must match corresponding
+   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
+#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
+#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 07ab700b80c..ab3ed638475 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
 extern const char *GOMP_OFFLOAD_get_name (void);
 extern unsigned int GOMP_OFFLOAD_get_caps (void);
 extern int GOMP_OFFLOAD_get_type (void);
-extern int GOMP_OFFLOAD_get_num_devices (void);
+extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
 extern bool GOMP_OFFLOAD_init_device (int);
 extern bool GOMP_OFFLOAD_fini_device (int);
 extern unsigned GOMP_OFFLOAD_version (void);
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index 5bb889926d3..eb11b9cf16a 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -54,7 +54,7 @@ host_get_type (void)
 }
 
 static int
-host_get_num_devices (void)
+host_get_num_devices (unsigned int omp_requires_mask __attribute__((unused)))
 {
   return 1;
 }
@@ -229,7 +229,7 @@ host_openacc_get_property (int n, enum goacc_property prop)
 {
   union goacc_property_value nullval = { .val = 0 };
 
-  if (n >= host_get_num_devices ())
+  if (n >= host_get_num_devices (0))
     return nullval;
 
   switch (prop)
diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c
index 1565aa0f290..42c3e74e6ba 100644
--- a/libgomp/oacc-init.c
+++ b/libgomp/oacc-init.c
@@ -148,7 +148,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
 	      if (dispatchers[d]
 		  && !strcasecmp (goacc_device_type,
 				  get_openacc_name (dispatchers[d]->name))
-		  && dispatchers[d]->get_num_devices_func () > 0)
+		  && dispatchers[d]->get_num_devices_func (0) > 0)
 		goto found;
 
 	    if (fail_is_error)
@@ -169,7 +169,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
     case acc_device_not_host:
       /* Find the first available device after acc_device_not_host.  */
       while (known_device_type_p (++d))
-	if (dispatchers[d] && dispatchers[d]->get_num_devices_func () > 0)
+	if (dispatchers[d] && dispatchers[d]->get_num_devices_func (0) > 0)
 	  goto found;
       if (d_arg == acc_device_default)
 	{
@@ -302,7 +302,7 @@ acc_init_1 (acc_device_t d, acc_construct_t parent_construct, int implicit)
 
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   if (ndevs <= 0 || goacc_device_num >= ndevs)
     acc_dev_num_out_of_range (d, goacc_device_num, ndevs);
@@ -351,7 +351,7 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   /* Unload all the devices of this type that have been opened.  */
   for (i = 0; i < ndevs; i++)
@@ -520,7 +520,7 @@ goacc_attach_host_thread_to_device (int ord)
       base_dev = cached_base_dev;
     }
   
-  num_devices = base_dev->get_num_devices_func ();
+  num_devices = base_dev->get_num_devices_func (0);
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (acc_device_type (base_dev->type), ord,
 			      num_devices);
@@ -599,7 +599,7 @@ acc_get_num_devices (acc_device_t d)
   if (!acc_dev)
     return 0;
 
-  n = acc_dev->get_num_devices_func ();
+  n = acc_dev->get_num_devices_func (0);
   if (n < 0)
     n = 0;
 
@@ -779,7 +779,7 @@ acc_set_device_num (int ord, acc_device_t d)
 
       cached_base_dev = base_dev = resolve_device (d, true);
 
-      num_devices = base_dev->get_num_devices_func ();
+      num_devices = base_dev->get_num_devices_func (0);
 
       if (num_devices <= 0 || ord >= num_devices)
         acc_dev_num_out_of_range (d, ord, num_devices);
@@ -814,7 +814,7 @@ get_property_any (int ord, acc_device_t d, acc_device_property_t prop)
 
   struct gomp_device_descr *dev = resolve_device (d, true);
 
-  int num_devices = dev->get_num_devices_func ();
+  int num_devices = dev->get_num_devices_func (0);
 
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (d, ord, num_devices);
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 1c0436842da..ea327bf2ca0 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -3221,10 +3221,14 @@ GOMP_OFFLOAD_version (void)
 /* Return the number of GCN devices on the system.  */
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
   if (!init_hsa_context ())
     return 0;
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
+    return -1;
   return hsa_context.agent_count;
 }
 
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 387bcbbc52a..bc63e274cdf 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1175,9 +1175,14 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
-  return nvptx_get_num_devices ();
+  int num_devices = nvptx_get_num_devices ();
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
+  return num_devices;
 }
 
 bool
diff --git a/libgomp/target.c b/libgomp/target.c
index c0844f2265a..12e6df28198 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -36,6 +36,7 @@
 # include <inttypes.h>  /* For PRIu64.  */
 #endif
 #include <string.h>
+#include <stdio.h>  /* For snprintf. */
 #include <assert.h>
 #include <errno.h>
 
@@ -98,6 +99,12 @@ static int num_devices;
 /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
 static int num_devices_openmp;
 
+/* Mask of requires directive clause values.  Offload plugins are queried
+   with this mask to see if all required features are supported.  */
+__attribute__((weak))
+extern unsigned int __offload_requires_mask;
+
+
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
 
 static void *
@@ -4078,6 +4085,20 @@ gomp_target_fini (void)
     }
 }
 
+static void
+gomp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* This function initializes the runtime for offloading.
    It parses the list of offload plugins, and tries to load these.
    On return, the variables NUM_DEVICES and NUM_DEVICES_OPENMP
@@ -4125,8 +4146,23 @@ gomp_target_init (void)
 
 	if (gomp_load_plugin_for_device (&current_device, plugin_name))
 	  {
-	    new_num_devs = current_device.get_num_devices_func ();
-	    if (new_num_devs >= 1)
+	    int requires_mask = 0;
+	    if (&__offload_requires_mask != NULL)
+	      requires_mask = __offload_requires_mask;
+	    new_num_devs
+	      = current_device.get_num_devices_func (requires_mask);
+	    if (new_num_devs < 0)
+	      {
+		char buf[64];
+		gomp_requires_to_name (buf, sizeof (buf), requires_mask);
+		char *name = (char *) malloc (cur_len + 1);
+		memcpy (name, cur, cur_len);
+		name[cur_len] = '\0';
+		GOMP_PLUGIN_error ("note: %s devices present but 'omp requires "
+				   "%s' cannot be fulfilled", name, buf);
+		free (name);
+	      }
+	    else if (new_num_devs >= 1)
 	      {
 		/* Augment DEVICES and NUM_DEVICES.  */
 
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
new file mode 100644
index 00000000000..fedf9779769
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-1-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
new file mode 100644
index 00000000000..617577448ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
@@ -0,0 +1,9 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
new file mode 100644
index 00000000000..ac7f3ef512c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -0,0 +1,25 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-foffload=disable -flto" } */
+/* { dg-additional-sources requires-2-aux.c } */
+
+/* Check diagnostic by host's lto1.
+   Other file does not have any 'omp requires'. */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. ''" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
new file mode 100644
index 00000000000..4b07ffdd09b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-sources requires-3-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
new file mode 100644
index 00000000000..b8b51ae8ca7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
@@ -0,0 +1,13 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+/* Note: The file does not have neither of:
+   declare target directives, device constructs or device routines.  */
+
+int x;
+
+void foo (void)
+{
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
new file mode 100644
index 00000000000..128fdbb8463
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
@@ -0,0 +1,23 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources requires-4-aux.c } */
+
+/* Check diagnostic by device-compiler's or host compiler's lto1.
+   Other file uses: 'requires reverse_offload', but that's inactive as
+   there are no declare target directives, device constructs nor device routines  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
new file mode 100644
index 00000000000..d223749f0a1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
new file mode 100644
index 00000000000..3d15bde21f0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -0,0 +1,20 @@
+/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-5-aux.c } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-6.c b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
new file mode 100644
index 00000000000..b00c7459bbc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
@@ -0,0 +1,17 @@
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+/* The requires line is not active as there is none of:
+     declare target directives, device constructs or device routines.
+   Thus, this code is expected to work everywhere.  */
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  return 0;
+}
diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
index d1678d0514e..33bae0650b4 100644
--- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
+++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
@@ -168,8 +168,12 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 extern "C" int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
   TRACE ("(): return %d", num_devices);
   return num_devices;
 }

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v4] OpenMP: Move omp requires checks to libgomp
  2022-06-29 14:33         ` [Patch][v4] " Tobias Burnus
@ 2022-06-29 17:02           ` Jakub Jelinek
  2022-06-29 18:10             ` Tobias Burnus
  2022-07-07  8:37           ` Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
  2022-07-07  8:42           ` Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing " Thomas Schwinge
  2 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-06-29 17:02 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Wed, Jun 29, 2022 at 04:33:02PM +0200, Tobias Burnus wrote:
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -2488,6 +2488,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>  	  break;
>  	}
>  
> +      if (flag_openmp
> +	  && lookup_attribute ("omp declare target",
> +			       DECL_ATTRIBUTES (current_function_decl)))
> +	omp_requires_mask
> +	  = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
> +
>        if (DECL_DECLARED_INLINE_P (current_function_decl))
>          tv = TV_PARSE_INLINE;
>        else

I thought the above would be left out, at least until clarified what exactly
we mean with device routines in the restrictions.

> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -15389,6 +15389,11 @@ cp_parser_simple_declaration (cp_parser* parser,
>  	  /* Otherwise, we're done with the list of declarators.  */
>  	  else
>  	    {
> +	      if (flag_openmp && lookup_attribute ("omp declare target",
> +						   DECL_ATTRIBUTES (decl)))
> +		omp_requires_mask
> +		  = (enum omp_requires) (omp_requires_mask
> +					 | OMP_REQUIRES_TARGET_USED);
>  	      pop_deferring_access_checks ();
>  	      cp_finalize_omp_declare_simd (parser, &odsd);
>  	      return;

And the above too.

On the Fortran side I don't see it being done.
> --- a/gcc/lto-cgraph.cc
> +++ b/gcc/lto-cgraph.cc
> @@ -1068,12 +1069,28 @@ read_string (class lto_input_block *ib)
>  void
>  output_offload_tables (void)
>  {
> -  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars))
> +  bool output_requires = (flag_openmp
> +			  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0);
> +  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars)
> +      && !output_requires)
>      return;
>  
>    struct lto_simple_output_block *ob
>      = lto_create_simple_output_block (LTO_section_offload_table);
>  
> +  if (output_requires)
> +    {
> +      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
> +			   & (OMP_REQUIRES_UNIFIED_ADDRESS
> +			      | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +			      | OMP_REQUIRES_REVERSE_OFFLOAD
> +			      | OMP_REQUIRES_TARGET_USED));

Why is the OMP_REQUIRES_TARGET_USED bit saved there?  It is always set
if output_requires...
If we want to make sure it is set in omp_requires_mask after streaming
in, we can just or it in back there.

> @@ -1811,6 +1844,24 @@ input_offload_tables (bool do_force_output)
>  	      if (do_force_output)
>  		varpool_node::get (var_decl)->force_output = 1;
>  	    }
> +	  else if (tag == LTO_symtab_edge)
> +	    {
> +	      static bool error_emitted = false;
> +	      HOST_WIDE_INT val = streamer_read_hwi (ib);
> +
> +	      if (omp_requires_mask == 0)
> +		omp_requires_mask = (omp_requires) val;

I mean here: (omp_requires) (val | OMP_REQUIRES_TARGET_USED);

> +	      else if (omp_requires_mask != val && !error_emitted)
> +		{
> +		  char buf[64], buf2[64];
> +		  omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
> +		  omp_requires_to_name (buf2, sizeof (buf2), val);
> +		  error ("OpenMP %<requires%> directive with non-identical "
> +			 "clauses in multiple compilation units: %qs vs. %qs",
> +			 buf, buf2);

I think the user should be told also where, so file_data->file_name and
saved another filename from the if (omp_requires_mask == 0) body.
I admit I haven't investigated whether it would be enough to just record
the const char * file_data->file_name or whether what it points to might be
freed before we report it.

> +		  error_emitted = true;

> --- a/gcc/omp-offload.cc
> +++ b/gcc/omp-offload.cc
> @@ -398,6 +399,26 @@ omp_finish_file (void)
>    unsigned num_funcs = vec_safe_length (offload_funcs);
>    unsigned num_vars = vec_safe_length (offload_vars);
>  
> +#ifndef ACCEL_COMPILER
> +  if (flag_openmp && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0)
> +    {
> +      tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> +				 get_identifier ("__offload_requires_mask"),
> +				 unsigned_type_node);
> +      TREE_PUBLIC (var) = 1;
> +      TREE_STATIC (var) = 1;
> +      TREE_READONLY (var) = 1;
> +      DECL_INITIAL (var)
> +	= build_int_cst (unsigned_type_node,
> +			 ((unsigned int) omp_requires_mask
> +			  & (OMP_REQUIRES_UNIFIED_ADDRESS
> +			     | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> +			     | OMP_REQUIRES_REVERSE_OFFLOAD)));
> +      declare_weak (var);
> +      varpool_node::finalize_decl (var);
> +    }
> +#endif

I think this __offload_requires_mask can't work reliably, not only because
it is a single var per process while one can have target regions in
multiple shared libraries (I know we've discussed that it doesn't always
work reliably, but we shouldn't hardcode something that will prevent it from
working properly altogether), but also because one can e.g. use symbol
versioning or simple linker script and __offload_requires_mask won't be
visible to libgomp.

Can't we arrange for GOMP_offload_register_ver to see the bitmasks somewhere
instead (and force GOMP_offload_register_ver even if there are no vars or
funcs and just the requires mask)?

GOMP_offload_register_ver passes a version number, perhaps we could bump
GOMP_VERSION from 1 to 2 and store the bitmask somewhere in the target data
and on the libgomp side handle both GOMP_VERSION_LIB (version) 1 and 2,
the former by pretending there is 0 bitmask?

There can be various ways how to get the bitmask into
config/*/*mkoffload.cc so that it can add it there.

Could be the weak __offload_requires_mask (but we probably e.g. can't assume
declare_weak will work), which we'd make also hidden and the *mkoffload.cc
generated source would refer to its address (but have it declared hidden so
that if it isn't present in current TU, we get NULL).  Disadvantage is the
relocation.

Or we could ask for the offloading lto1 when it merges those from different
offloadng TUs to emit some magic symbol in what it emits and have mkoffload
search for it.

Or mkoffload could pass some option to the offloading lto compilation, say
filename of a temporary file, let lto1 save into that file the bitmask and
let mkoffload read it.  Or env var.

> --- a/libgomp/libgomp-plugin.h
> +++ b/libgomp/libgomp-plugin.h
> @@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
>  extern const char *GOMP_OFFLOAD_get_name (void);
>  extern unsigned int GOMP_OFFLOAD_get_caps (void);
>  extern int GOMP_OFFLOAD_get_type (void);
> -extern int GOMP_OFFLOAD_get_num_devices (void);
> +extern int GOMP_OFFLOAD_get_num_devices (unsigned int);

This is an ABI change for plugins, don't we want to e.g. rename
the symbol as well, so that plugin mismatches are caught more easily?

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v4] OpenMP: Move omp requires checks to libgomp
  2022-06-29 17:02           ` Jakub Jelinek
@ 2022-06-29 18:10             ` Tobias Burnus
  2022-06-29 20:18               ` Jakub Jelinek
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-06-29 18:10 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

Hi Jakub,

On 29.06.22 19:02, Jakub Jelinek wrote:
> On Wed, Jun 29, 2022 at 04:33:02PM +0200, Tobias Burnus wrote:
>> +      if (flag_openmp
>> +      && lookup_attribute ("omp declare target",
>> +                           DECL_ATTRIBUTES (current_function_decl)))
>> +    omp_requires_mask
>> +      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);

> I thought the above would be left out, at least until clarified what exactly
> we mean with device routines in the restrictions.
Missed that – I thought mostly of the API calls. However, I concur that
for 'declare target', it is not really needed as no data transfers or
reverse offloads can happen. - Additionally, I took this part from
Chung-Lin's patch and did not really think about this part ...

>> --- a/gcc/cp/parser.cc
>> +++ b/gcc/cp/parser.cc
> And the above too.
>
> On the Fortran side I don't see it being done.
(Likewise.)
>> --- a/gcc/lto-cgraph.cc
>> +++ b/gcc/lto-cgraph.cc
>> ...
>> +  if (output_requires)
>> +    {
>> +      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
>> +                       & (OMP_REQUIRES_UNIFIED_ADDRESS
>> +                          | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
>> +                          | OMP_REQUIRES_REVERSE_OFFLOAD
>> +                          | OMP_REQUIRES_TARGET_USED));
> Why is the OMP_REQUIRES_TARGET_USED bit saved there?  It is always set
> if output_requires...
> If we want to make sure it is set in omp_requires_mask after streaming
> in, we can just or it in back there.

It is there because it is later needed: With -flto, we otherwise
wouldn't generate the variable in omp-offload.cc. And as this value is
only used internally, I thought it could just stay there. However, it
could also be excluded here and ...

>> @@ -1811,6 +1844,24 @@ input_offload_tables (bool do_force_output)
>>            if (do_force_output)
>>              varpool_node::get (var_decl)->force_output = 1;
>>          }
>> +      else if (tag == LTO_symtab_edge)
>> +        {
>> +          static bool error_emitted = false;
>> +          HOST_WIDE_INT val = streamer_read_hwi (ib);
>> +
>> +          if (omp_requires_mask == 0)
>> +            omp_requires_mask = (omp_requires) val;
... added here as:

> I mean here: (omp_requires) (val | OMP_REQUIRES_TARGET_USED);
... but that also requires ...
>> +          else if (omp_requires_mask != val && !error_emitted)

... something like:

(omp_requires_mask & ~OMP_REQUIRES_TARGET_USED) != val

or something like that.

>> +            {
>> +              char buf[64], buf2[64];
>> +              omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
>> +              omp_requires_to_name (buf2, sizeof (buf2), val);
>> +              error ("OpenMP %<requires%> directive with non-identical "
>> +                     "clauses in multiple compilation units: %qs vs. %qs",
>> +                     buf, buf2);
> I think the user should be told also where, so file_data->file_name

With -save-temps, the filename is indeed helpful, e.g.:
"a-requires-1.o". However, without, I get names like: "/tmp/ccIgRkOW.o".

As mentioned, I was thinking of storing after the value some location
data, e.g. DECL_SOURCE_FILE() or even DECL_SOURCE_LOCATION() – by
keeping track of the first 'omp requires' in a translation unit.

Those can be streamed with  streamer_write_string and
lto_output_location. However, the both require as argument an "struct
output_block" and in lto-cgraph.cc, I only have a "struct
lto_simple_output_block".

And for reading, I additionally need a "class data_in" argument.

Thus, I think it is doable – however, I was not quite sure whether it
made sense to do all the effort or not. (And it was not immediately
clear to me how to create a "data_in" class and ...)

> Can't we arrange for GOMP_offload_register_ver to see the bitmasks somewhere
> instead (and force GOMP_offload_register_ver even if there are no vars or
> funcs and just the requires mask)?

This probably works – but means some restructuring.
GOMP_offload_register_ver assumes that num_devices is already available
– and we need to exclude those for which the 'omp requires' cannot be
fulfilled.

I think this could be either be done in GOMP_offload_register_ver by
decrementing num_offload_images + shifting the offload_images[i] entries
(or have some table to match user-visible numbers to the original number) .

Or it could just be done by setting a flag – and num_offload_images
updated later. We probably need something which is run later to exclude
those devices for which no image exists (existing but not supported
devices) – and for OpenMP 6's OMP_AVAILABLE_DEVICES env, which permits
to sort the devices and filter out some of them.

> Could be the weak __offload_requires_mask (but we probably e.g. can't assume
> declare_weak will work),
I assume that it does work in practice, given that it is only needed on
the host – and given which systems effectively support offloading. –
With -flto, we even would know that there is only one variable, but
unfortunately, we cannot count on a host lto1 run.
> Or we could ask for the offloading lto1 when it merges those from different
> offloadng TUs to emit some magic symbol in what it emits and have mkoffload
> search for it.
>
> Or mkoffload could pass some option to the offloading lto compilation, say
> filename of a temporary file, let lto1 save into that file the bitmask and
> let mkoffload read it.  Or env var.
(Can surely be done – having a constant in the GOMP_offload_register_ver
call would be surely useful.)
>> --- a/libgomp/libgomp-plugin.h
>> +++ b/libgomp/libgomp-plugin.h
>> ...
>> -extern int GOMP_OFFLOAD_get_num_devices (void);
>> +extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
> This is an ABI change for plugins, don't we want to e.g. rename
> the symbol as well, so that plugin mismatches are caught more easily?

Yes, I recall that we talked about it, but I obviously missed to
actually change it. If we go for GOMP_offload_register_ver + updating
the list later, this function can probably stay as is – and we need
another func to query whether the requirements are fulfillable or not.

Tobias


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v4] OpenMP: Move omp requires checks to libgomp
  2022-06-29 18:10             ` Tobias Burnus
@ 2022-06-29 20:18               ` Jakub Jelinek
  2022-07-01 13:06                 ` [Patch][v5] " Tobias Burnus
  0 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-06-29 20:18 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Wed, Jun 29, 2022 at 08:10:10PM +0200, Tobias Burnus wrote:
> > > +  if (output_requires)
> > > +    {
> > > +      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
> > > +                       & (OMP_REQUIRES_UNIFIED_ADDRESS
> > > +                          | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
> > > +                          | OMP_REQUIRES_REVERSE_OFFLOAD
> > > +                          | OMP_REQUIRES_TARGET_USED));
> > Why is the OMP_REQUIRES_TARGET_USED bit saved there?  It is always set
> > if output_requires...
> > If we want to make sure it is set in omp_requires_mask after streaming
> > in, we can just or it in back there.
> 
> It is there because it is later needed: With -flto, we otherwise
> wouldn't generate the variable in omp-offload.cc. And as this value is
> only used internally, I thought it could just stay there. However, it
> could also be excluded here and ...

Ok, let's keep it then.  Wanted to save that 1 bit somewhere, but as it
isn't a pack, it is insignificant anyway.
More could be saved by reordering the bitmasks such that the atomic stuff is
in upper bits.

> > > +              char buf[64], buf2[64];
> > > +              omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
> > > +              omp_requires_to_name (buf2, sizeof (buf2), val);
> > > +              error ("OpenMP %<requires%> directive with non-identical "
> > > +                     "clauses in multiple compilation units: %qs vs. %qs",
> > > +                     buf, buf2);
> > I think the user should be told also where, so file_data->file_name
> 
> With -save-temps, the filename is indeed helpful, e.g.:
> "a-requires-1.o". However, without, I get names like: "/tmp/ccIgRkOW.o".

Even when using
gcc -fopenmp -c foo.c -o foo.o
gcc -fopenmp -c bar.c -o bar.o
gcc -fopenmp -o foo foo.o bar.o
?
Anyway, I think it would be good to ask Richi or Honza what is the best to
get at the TU's filename.
Perhaps location_t from TRANSLATION_UNIT_DECL or something similar?

> > Can't we arrange for GOMP_offload_register_ver to see the bitmasks somewhere
> > instead (and force GOMP_offload_register_ver even if there are no vars or
> > funcs and just the requires mask)?
> 
> This probably works – but means some restructuring.
> GOMP_offload_register_ver assumes that num_devices is already available
> – and we need to exclude those for which the 'omp requires' cannot be
> fulfilled.

GOMP_offload_register_ver is called from initializers of programs and shared
libraries.  For the program and non-dlopened shared libraries, the usual
case is that it is called before we initialize devices, so we just record it
in offload_images and continue.  When the devices are initialized, we push
it to those devices.

Another case is registration after number of devices is initialized.

The former should just enqueue also those bitmasks, and when we initialize
devices we should complain on mismatches and/or filter out unsuitable
devices.

For the latter case, in theory (at least when we also catch calls to the
device routines with the meaning of device related omp_* APIs) that means
some TU already had the mask and so the later loaded masks should be the
same, so we should just complain on mismatches.

Now, the target data I'm afraid is device specific, but for GOMP_VERSION 2
we could e.g. have the pointer passed to the function point to target data
with the mask at offset -4 bytes before it, or can just have always the
bitmask followed by real target data.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-06-29 20:18               ` Jakub Jelinek
@ 2022-07-01 13:06                 ` Tobias Burnus
  2022-07-01 14:34                   ` Jakub Jelinek
                                     ` (2 more replies)
  0 siblings, 3 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-01 13:06 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1258 bytes --]

Attached is the updated patch. Main changes:
- File names shown that violate the requires-clause-must-be-same requirement
   Taken from the offload_vars/funcs context (if available), otherwise
   (that's no 'omp target'/'omp declare target' but just 'omp target update/data'
   in the TU), the *.o file name is used.
(thanks to richi + jakub for the susggestions!)
- Uses GOMP_register_var to pass the mask to libgomp
(and no longer a weak variable)
- 'omp declare target' is not regarded as being used -> pending OpenMP lang spec clarification
- 'omp target update' is for C/C++
- Properly handle is used by-target constucts for Fortran
- Save requires (and empty offload table) in the *.o file, even if it is only
   using 'omp target (enter/exit) data'

Thanks goes to Jakub for many useful suggestions!

Tested without offloading configured and with nvptx and amdgcn offloading (all on x86_64-gnu-linux).

OK? Or does anyone have more useful suggestions?

Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Attachment #2: omp-requires-v5.diff --]
[-- Type: text/x-patch, Size: 65789 bytes --]

OpenMP: Move omp requires checks to libgomp

Handle reverse_offload, unified_address, and unified_shared_memory
requirements in libgomp by saving them alongside the offload table.
When the device lto1 runs, it extracts the data for mkoffload. The
latter than passes the value on to GOMP_offload_register_ver.

lto1 (either the host one, with -flto [+ ENABLE_OFFLOADING], or in the
offload-device lto1) also does the the consistency check is done,
erroring out when the 'omp requires' clause use is inconsistent.

For all in-principle supported devices, if a requirement cannot be fulfilled,
the device is excluded from the (supported) devices list. Currently, none of
those requirements are marked as supported for any of the non-host devices.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_omp_target_data, c_parser_omp_target_update,
	c_parser_omp_target_enter_data, c_parser_omp_target_exit_data): Set
	OMP_REQUIRES_TARGET_USED.
	(c_parser_omp_requires): Remove sorry.

gcc/ChangeLog:

	* config/gcn/mkoffload.cc (process_asm): Write '#include <stdint.h>'.
	(process_obj): Pass omp_requires_mask to GOMP_offload_register_ver.
	(main): Ask lto1 to obtain omp_requires_mask and pass it on.
	* config/nvptx/mkoffload.cc (process, main): Likewise.
	* lto-cgraph.cc (omp_requires_to_name): New.
	(input_offload_tables): Save omp_requires_mask.
	(output_offload_tables): Read it, check for consistency,
	save value for mkoffload.
	* omp-low.cc (lower_omp_target): Force output_offloadtables
	call for OMP_REQUIRES_TARGET_USED.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_omp_target_data,
	cp_parser_omp_target_enter_data, cp_parser_omp_target_exit_data,
	cp_parser_omp_target_update): Set OMP_REQUIRES_TARGET_USED.
	(cp_parser_omp_requires): Remove sorry.

gcc/fortran/ChangeLog:

	* openmp.cc (gfc_match_omp_requires): Remove sorry.
	* parse.cc (decode_omp_directive): Don't regard 'declare target'
	as target usage for 'omp requires'; add more flags to
	omp_requires_mask.

include/ChangeLog:

	* gomp-constants.h (GOMP_VERSION): Bump to 2.
	(GOMP_REQUIRES_UNIFIED_ADDRESS, GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
	GOMP_REQUIRES_REVERSE_OFFLOAD): New defines.

libgomp/ChangeLog:

	* libgomp-plugin.h (GOMP_OFFLOAD_get_num_devices): Add
	omp_requires_mask arg.
	* plugin/plugin-gcn.c (GOMP_OFFLOAD_get_num_devices): Likewise;
	return -1 when device available but omp_requires_mask != 0.
	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_get_num_devices): Likewise.
	* oacc-host.c (host_get_num_devices, host_openacc_get_property):
	Update call.
	* oacc-init.c (resolve_device, acc_init_1, acc_shutdown_1,
	goacc_attach_host_thread_to_device, acc_get_num_devices,
	acc_set_device_num, get_property_any): Likewise.
	* target.c (omp_requires_mask): New global var.
	(gomp_requires_to_name): New.
	(GOMP_offload_register_ver): Handle passed omp_requires_mask.
	(gomp_target_init): Handle omp_requires_mask.
	* libgomp.texi (OpenMP 5.0): Update requires impl. status.
	(OpenMP 5.1): Add a missed item.
	(OpenMP 5.2): Mark linear-clause change as supported in C/C++.
	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5.c: New test.
	* testsuite/libgomp.c-c++-common/requires-6.c: New test.
	* testsuite/libgomp.c-c++-common/requires-7-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-7.c: New test.
	* testsuite/libgomp.fortran/requires-1-aux.f90: New test.
	* testsuite/libgomp.fortran/requires-1.f90: New test.

liboffloadmic/ChangeLog:

	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_num_devices):
	Return -1 when device available but omp_requires_mask != 0.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/requires-4.c: Update dg-*.
	* c-c++-common/gomp/reverse-offload-1.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-2.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-3.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-4.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-5.c: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-3.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-4.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-5.f90: Likewise.
        * gfortran.dg/gomp/target-device-ancestor-2.f90: Likewise. Move
	post-FE checks to ...
        * gfortran.dg/gomp/target-device-ancestor-2a.f90: ... this new file.
	* gfortran.dg/gomp/requires-8.f90: Update as we don't regard
	'declare target' for the 'requires' usage requirement.

Co-authored-by: Chung-Lin Tang <cltang@codesourcery.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>
 gcc/c/c-parser.cc                                  | 19 ++++-
 gcc/config/gcn/mkoffload.cc                        | 27 +++++-
 gcc/config/nvptx/mkoffload.cc                      | 29 ++++++-
 gcc/cp/parser.cc                                   | 19 ++++-
 gcc/fortran/openmp.cc                              |  4 -
 gcc/fortran/parse.cc                               | 22 ++++-
 gcc/lto-cgraph.cc                                  | 99 +++++++++++++++++++++-
 gcc/omp-low.cc                                     |  5 ++
 gcc/testsuite/c-c++-common/gomp/requires-4.c       |  2 -
 .../c-c++-common/gomp/reverse-offload-1.c          |  2 +-
 .../c-c++-common/gomp/target-device-ancestor-2.c   | 10 +--
 .../c-c++-common/gomp/target-device-ancestor-3.c   |  2 +-
 .../c-c++-common/gomp/target-device-ancestor-4.c   |  4 +-
 .../c-c++-common/gomp/target-device-ancestor-5.c   |  2 +-
 gcc/testsuite/gfortran.dg/gomp/requires-8.f90      | 14 ++-
 .../gfortran.dg/gomp/target-device-ancestor-2.f90  | 70 ++-------------
 .../gfortran.dg/gomp/target-device-ancestor-2a.f90 | 80 +++++++++++++++++
 .../gfortran.dg/gomp/target-device-ancestor-3.f90  |  6 +-
 .../gfortran.dg/gomp/target-device-ancestor-4.f90  |  6 +-
 .../gfortran.dg/gomp/target-device-ancestor-5.f90  |  8 +-
 include/gomp-constants.h                           |  8 +-
 libgomp/libgomp-plugin.h                           |  2 +-
 libgomp/libgomp.texi                               |  8 +-
 libgomp/oacc-host.c                                |  4 +-
 libgomp/oacc-init.c                                | 16 ++--
 libgomp/plugin/plugin-gcn.c                        |  6 +-
 libgomp/plugin/plugin-nvptx.c                      |  9 +-
 libgomp/target.c                                   | 64 +++++++++++++-
 .../libgomp.c-c++-common/requires-1-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-1.c    | 24 ++++++
 .../libgomp.c-c++-common/requires-2-aux.c          |  9 ++
 .../testsuite/libgomp.c-c++-common/requires-2.c    | 25 ++++++
 .../libgomp.c-c++-common/requires-3-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-3.c    | 24 ++++++
 .../libgomp.c-c++-common/requires-4-aux.c          | 13 +++
 .../testsuite/libgomp.c-c++-common/requires-4.c    | 23 +++++
 .../libgomp.c-c++-common/requires-5-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-5.c    | 20 +++++
 .../testsuite/libgomp.c-c++-common/requires-6.c    | 17 ++++
 .../libgomp.c-c++-common/requires-7-aux.c          | 11 +++
 .../testsuite/libgomp.c-c++-common/requires-7.c    | 24 ++++++
 .../testsuite/libgomp.fortran/requires-1-aux.f90   | 14 +++
 libgomp/testsuite/libgomp.fortran/requires-1.f90   | 26 ++++++
 liboffloadmic/plugin/libgomp-plugin-intelmic.cpp   |  6 +-
 44 files changed, 684 insertions(+), 132 deletions(-)

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 1704a52be12..9894b010446 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -20915,6 +20915,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
 static tree
 c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				"#pragma omp target data");
@@ -21010,6 +21014,10 @@ c_parser_omp_target_update (location_t loc, c_parser *parser,
       return false;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree stmt = make_node (OMP_TARGET_UPDATE);
   TREE_TYPE (stmt) = void_type_node;
   OMP_TARGET_UPDATE_CLAUSES (stmt) = clauses;
@@ -21057,6 +21065,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				"#pragma omp target enter data");
@@ -21143,6 +21155,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				"#pragma omp target exit data");
@@ -22763,9 +22779,6 @@ c_parser_omp_requires (c_parser *parser)
 	      c_parser_skip_to_pragma_eol (parser, false);
 	      return;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    c_parser_consume_token (parser);
 	  if (this_req)
diff --git a/gcc/config/gcn/mkoffload.cc b/gcc/config/gcn/mkoffload.cc
index ed93ae844e4..b8b3fecfcb4 100644
--- a/gcc/config/gcn/mkoffload.cc
+++ b/gcc/config/gcn/mkoffload.cc
@@ -611,6 +611,7 @@ process_asm (FILE *in, FILE *out, FILE *cfile)
   struct regcount *regcounts = XOBFINISH (&regcounts_os, struct regcount *);
 
   fprintf (cfile, "#include <stdlib.h>\n");
+  fprintf (cfile, "#include <stdint.h>\n");
   fprintf (cfile, "#include <stdbool.h>\n\n");
 
   fprintf (cfile, "static const int gcn_num_vars = %d;\n\n", var_count);
@@ -664,7 +665,7 @@ process_asm (FILE *in, FILE *out, FILE *cfile)
 /* Embed an object file into a C source file.  */
 
 static void
-process_obj (FILE *in, FILE *cfile)
+process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
 {
   size_t len = 0;
   const char *input = read_file (in, &len);
@@ -692,16 +693,18 @@ process_obj (FILE *in, FILE *cfile)
 
   fprintf (cfile,
 	   "static const struct gcn_image_desc {\n"
+	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct gcn_image *gcn_image;\n"
 	   "  unsigned kernel_count;\n"
 	   "  const struct hsa_kernel_description *kernel_infos;\n"
 	   "  unsigned global_variable_count;\n"
 	   "} target_data = {\n"
+	   "  %d,\n"
 	   "  &gcn_image,\n"
 	   "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
 	   "  gcn_kernels,\n"
 	   "  gcn_num_vars\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (cfile,
 	   "#ifdef __cplusplus\n"
@@ -1077,9 +1080,27 @@ main (int argc, char **argv)
       unsetenv ("COMPILER_PATH");
       unsetenv ("LIBRARY_PATH");
 
+      char *omp_requires_file;
+      if (save_temps)
+	omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+      else
+	omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+
       /* Run the compiler pass.  */
+      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
       fork_execute (cc_argv[0], CONST_CAST (char **, cc_argv), true, ".gcc_args");
       obstack_free (&cc_argv_obstack, NULL);
+      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+
+      in = fopen (omp_requires_file, "rb");
+      if (!in)
+	fatal_error (input_location, "cannot open omp_requires file %qs",
+		     omp_requires_file);
+      uint32_t omp_requires;
+      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
+	fatal_error (input_location, "cannot read omp_requires file %qs",
+		     omp_requires_file);
+      fclose (in);
 
       in = fopen (gcn_s1_name, "r");
       if (!in)
@@ -1102,7 +1123,7 @@ main (int argc, char **argv)
       if (!in)
 	fatal_error (input_location, "cannot open intermediate gcn obj file");
 
-      process_obj (in, cfile);
+      process_obj (in, cfile, omp_requires);
 
       fclose (in);
 
diff --git a/gcc/config/nvptx/mkoffload.cc b/gcc/config/nvptx/mkoffload.cc
index b28c1a32292..d8c81eb0547 100644
--- a/gcc/config/nvptx/mkoffload.cc
+++ b/gcc/config/nvptx/mkoffload.cc
@@ -231,7 +231,7 @@ access_check (const char *name, int mode)
 }
 
 static void
-process (FILE *in, FILE *out)
+process (FILE *in, FILE *out, uint32_t omp_requires)
 {
   size_t len = 0;
   const char *input = read_file (in, &len);
@@ -240,6 +240,8 @@ process (FILE *in, FILE *out)
   unsigned obj_count = 0;
   unsigned ix;
 
+  fprintf (out, "#include <stdint.h>\n\n");
+
   /* Dump out char arrays for each PTX object file.  These are
      terminated by a NUL.  */
   for (size_t i = 0; i != len;)
@@ -309,6 +311,7 @@ process (FILE *in, FILE *out)
 
   fprintf (out,
 	   "static const struct nvptx_tdata {\n"
+	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct ptx_obj *ptx_objs;\n"
 	   "  unsigned ptx_num;\n"
 	   "  const char *const *var_names;\n"
@@ -316,12 +319,12 @@ process (FILE *in, FILE *out)
 	   "  const struct nvptx_fn *fn_names;\n"
 	   "  unsigned fn_num;\n"
 	   "} target_data = {\n"
-	   "  ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
+	   "  %d, ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
 	   "  var_mappings,"
 	   "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
 	   "  func_mappings,"
 	   "  sizeof (func_mappings) / sizeof (func_mappings[0])\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (out, "#ifdef __cplusplus\n"
 	   "extern \"C\" {\n"
@@ -583,19 +586,37 @@ main (int argc, char **argv)
       unsetenv ("COMPILER_PATH");
       unsetenv ("LIBRARY_PATH");
 
+      char *omp_requires_file;
+      if (save_temps)
+	omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+      else
+	omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+
+      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
       fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true,
 		    ".gcc_args");
       obstack_free (&argv_obstack, NULL);
+      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
 
       xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
       xputenv (concat ("COMPILER_PATH=", cpath, NULL));
       xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
 
+      in = fopen (omp_requires_file, "rb");
+      if (!in)
+	fatal_error (input_location, "cannot open omp_requires file %qs",
+		     omp_requires_file);
+      uint32_t omp_requires;
+      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
+	fatal_error (input_location, "cannot read omp_requires file %qs",
+		     omp_requires_file);
+      fclose (in);
+
       in = fopen (ptx_name, "r");
       if (!in)
 	fatal_error (input_location, "cannot open intermediate ptx file");
 
-      process (in, out);
+      process (in, out, omp_requires);
       fclose (in);
     }
 
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index da2f370cdca..089b75121ed 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -44287,6 +44287,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
 static tree
 cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				 "#pragma omp target data", pragma_tok);
@@ -44390,6 +44394,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				 "#pragma omp target enter data", pragma_tok);
@@ -44481,6 +44489,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				 "#pragma omp target exit data", pragma_tok);
@@ -44567,6 +44579,10 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree stmt = make_node (OMP_TARGET_UPDATE);
   TREE_TYPE (stmt) = void_type_node;
   OMP_TARGET_UPDATE_CLAUSES (stmt) = clauses;
@@ -46861,9 +46877,6 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
 	      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	      return false;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    cp_lexer_consume_token (parser->lexer);
 	  if (this_req)
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index aeb8a43e12e..a68711081e2 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -5488,10 +5488,6 @@ gfc_match_omp_requires (void)
       else
 	goto error;
 
-      if (requires_clause & ~(OMP_REQ_ATOMIC_MEM_ORDER_MASK
-			      | OMP_REQ_DYNAMIC_ALLOCATORS))
-	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
-		       "yet supported", clause, &old_loc);
       if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
 	goto error;
       requires_clauses |= requires_clause;
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index 7356d1b5a3a..0b4c596996c 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -1168,7 +1168,8 @@ decode_omp_directive (void)
     }
   switch (ret)
     {
-    case ST_OMP_DECLARE_TARGET:
+    /* Set omp_target_seen; exclude ST_OMP_DECLARE_TARGET.
+       FIXME: Get clarification, cf. OpenMP Spec Issue #3240.  */
     case ST_OMP_TARGET:
     case ST_OMP_TARGET_DATA:
     case ST_OMP_TARGET_ENTER_DATA:
@@ -6879,11 +6880,14 @@ done:
 
   /* Fixup for external procedures and resolve 'omp requires'.  */
   int omp_requires;
+  bool omp_target_seen;
   omp_requires = 0;
+  omp_target_seen = false;
   for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
        gfc_current_ns = gfc_current_ns->sibling)
     {
       omp_requires |= gfc_current_ns->omp_requires;
+      omp_target_seen |= gfc_current_ns->omp_target_seen;
       gfc_check_externals (gfc_current_ns);
     }
   for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
@@ -6908,6 +6912,22 @@ done:
       break;
     }
 
+  if (omp_target_seen)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_TARGET_USED);
+  if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_REVERSE_OFFLOAD);
+  if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_UNIFIED_ADDRESS);
+  if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
+    omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+  if (omp_requires & OMP_REQ_DYNAMIC_ALLOCATORS)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_DYNAMIC_ALLOCATORS);
   /* Do the parse tree dump.  */
   gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
 
diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 237743ef0ba..87f01cbd2af 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pass_manager.h"
 #include "ipa-utils.h"
 #include "omp-offload.h"
+#include "omp-general.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "alloc-pool.h"
@@ -1068,7 +1069,10 @@ read_string (class lto_input_block *ib)
 void
 output_offload_tables (void)
 {
-  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars))
+  bool output_requires = (flag_openmp
+			  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0);
+  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars)
+      && !output_requires)
     return;
 
   struct lto_simple_output_block *ob
@@ -1098,6 +1102,19 @@ output_offload_tables (void)
 			       (*offload_vars)[i]);
     }
 
+  if (output_requires)
+    {
+      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
+			   & (OMP_REQUIRES_UNIFIED_ADDRESS
+			      | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+			      | OMP_REQUIRES_REVERSE_OFFLOAD
+			      | OMP_REQUIRES_TARGET_USED));
+      /* (Mis)use LTO_symtab_edge for this variable.  */
+      streamer_write_enum (ob->main_stream, LTO_symtab_tags,
+			   LTO_symtab_last_tag, LTO_symtab_edge);
+      streamer_write_hwi_stream (ob->main_stream, val);
+    }
+
   streamer_write_uhwi_stream (ob->main_stream, 0);
   lto_destroy_simple_output_block (ob);
 
@@ -1764,6 +1781,20 @@ input_symtab (void)
     }
 }
 
+static void
+omp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* Input function/variable tables that will allow libgomp to look up offload
    target code, and store them into OFFLOAD_FUNCS and OFFLOAD_VARS.  */
 
@@ -1773,6 +1804,10 @@ input_offload_tables (bool do_force_output)
   struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
   struct lto_file_decl_data *file_data;
   unsigned int j = 0;
+  const char *requires_fn = NULL;
+  tree requires_decl = NULL_TREE;
+
+  omp_requires_mask = (omp_requires) 0;
 
   while ((file_data = file_data_vec[j++]))
     {
@@ -1784,6 +1819,7 @@ input_offload_tables (bool do_force_output)
       if (!ib)
 	continue;
 
+      tree tmp_decl = NULL_TREE;
       enum LTO_symtab_tags tag
 	= streamer_read_enum (ib, LTO_symtab_tags, LTO_symtab_last_tag);
       while (tag)
@@ -1799,6 +1835,7 @@ input_offload_tables (bool do_force_output)
 		 LTO mode.  */
 	      if (do_force_output)
 		cgraph_node::get (fn_decl)->mark_force_output ();
+	      tmp_decl = fn_decl;
 	    }
 	  else if (tag == LTO_symtab_variable)
 	    {
@@ -1810,6 +1847,54 @@ input_offload_tables (bool do_force_output)
 		 may be no refs to var_decl in offload LTO mode.  */
 	      if (do_force_output)
 		varpool_node::get (var_decl)->force_output = 1;
+	      tmp_decl = var_decl;
+	    }
+	  else if (tag == LTO_symtab_edge)
+	    {
+	      static bool error_emitted = false;
+	      HOST_WIDE_INT val = streamer_read_hwi (ib);
+
+	      if (omp_requires_mask == 0)
+		{
+		  omp_requires_mask = (omp_requires) val;
+		  requires_decl = tmp_decl;
+		  requires_fn = file_data->file_name;
+		}
+	      else if (omp_requires_mask != val && !error_emitted)
+		{
+		  char buf[64], buf2[64];
+		  omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
+		  omp_requires_to_name (buf2, sizeof (buf2), val);
+		  error ("OpenMP %<requires%> directive with non-identical "
+			 "clauses in multiple compilation units: %qs vs. %qs",
+			 buf, buf2);
+		  if (requires_decl != NULL_TREE)
+		    {
+		      while (DECL_CONTEXT (requires_decl) != NULL_TREE
+			     && TREE_CODE (requires_decl) != TRANSLATION_UNIT_DECL)
+			requires_decl = DECL_CONTEXT (requires_decl);
+		      if (requires_decl != NULL_TREE)
+			inform (UNKNOWN_LOCATION, "%qs has %qs",
+				IDENTIFIER_POINTER (DECL_NAME (requires_decl)),
+				buf);
+		    }
+		  else
+		    inform (UNKNOWN_LOCATION, "%qs has %qs", requires_fn, buf);
+		  if (tmp_decl != NULL_TREE)
+		    {
+		      while (DECL_CONTEXT (tmp_decl) != NULL_TREE
+			     && TREE_CODE (tmp_decl) != TRANSLATION_UNIT_DECL)
+			tmp_decl = DECL_CONTEXT (tmp_decl);
+		      if (tmp_decl != NULL_TREE)
+			inform (UNKNOWN_LOCATION, "%qs has %qs",
+				IDENTIFIER_POINTER (DECL_NAME (tmp_decl)),
+				buf2);
+		    }
+		  else
+		    inform (UNKNOWN_LOCATION, "%qs has %qs",
+			    file_data->file_name, buf2);
+		  error_emitted = true;
+		}
 	    }
 	  else
 	    fatal_error (input_location,
@@ -1821,6 +1906,18 @@ input_offload_tables (bool do_force_output)
       lto_destroy_simple_input_block (file_data, LTO_section_offload_table,
 				      ib, data, len);
     }
+#ifdef ACCEL_COMPILER
+  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
+    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");
+  FILE *f = fopen (omp_requires_file, "wb");
+  if (!f)
+    fatal_error (input_location, "Cannot open omp_requires file %qs",
+		 omp_requires_file);
+  uint32_t req_mask = omp_requires_mask & ~OMP_REQUIRES_TARGET_USED;
+  fwrite (&req_mask, sizeof (req_mask), 1, f);
+  fclose (f);
+#endif
 }
 
 /* True when we need optimization summary for NODE.  */
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index b9d5529f212..d73c165f029 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -12701,6 +12701,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
       gcc_unreachable ();
     }
 
+  /* Ensure that requires map is written via output_offload_tables, even if only
+     'target (enter/exit) data' is used in the translation unit.  */
+  if (ENABLE_OFFLOADING && (omp_requires_mask & OMP_REQUIRES_TARGET_USED))
+    g->have_offload = true;
+
   clauses = gimple_omp_target_clauses (stmt);
 
   gimple_seq dep_ilist = NULL;
diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
index 88ba7746cf8..8f45d83ea6e 100644
--- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
@@ -9,5 +9,3 @@ foo (void)
 #pragma omp requires unified_shared_memory	/* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires unified_address	/* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires reverse_offload	/* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
-
-/* { dg-prune-output "not supported yet" } */
diff --git a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
index 9a3fa5230f8..3452156f948 100644
--- a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
@@ -43,7 +43,7 @@ tg_fn (int *x, int *y)
   x2 = x2 + 2 + called_in_target1 ();
   y2 = y2 + 7;
 
-  #pragma omp target device(ancestor : 1) map(tofrom: x2)
+  #pragma omp target device(ancestor : 1) map(tofrom: x2)  /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
     check_offload(&x2, &y2);
 
   if (x2 != 2+2+3+42 || y2 != 3 + 7)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
index cf05c505004..b16e701bd5a 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
@@ -1,13 +1,11 @@
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (int n)
 {
-  /* The following test is marked with 'xfail' because a previous 'sorry' from
-     'reverse_offload' suppresses the 'sorry' for 'ancestor'.  */
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1)
   ;
 
 
@@ -19,9 +17,9 @@ foo (int n)
   #pragma omp target device (ancestor : 42) /* { dg-error "the 'device' clause expression must evaluate to '1'" } */
   ;
 
-  #pragma omp target device (ancestor : n) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n)
   ;
-  #pragma omp target device (ancestor : n + 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n + 1)
   ;
 
 
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
index ea6e5a0cf6c..d16590107d2 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
@@ -11,7 +11,7 @@ int bar (void);
 
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
index b4b5620bbc0..241234f8daf 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
@@ -4,12 +4,12 @@
   /* Test to ensure that device-modifier 'ancestor' is parsed correctly in
      device clauses. */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
 {
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1) /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
   ;
 
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
index b6ff84bcdab..b1520ff0636 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
@@ -1,4 +1,4 @@
-#pragma omp requires reverse_offload  /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo ()
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
index e84d609ad29..583c5a56b32 100644
--- a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
@@ -1,3 +1,7 @@
+module m0
+  integer :: x
+end module m0
+
 module m  !  { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do" }
   !$omp requires reverse_offload
 contains
@@ -13,10 +17,14 @@ contains
  end subroutine foo
 end module m
 
-subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
+subroutine bar
   !use m
-  !$omp requires unified_shared_memory
+  !$omp requires unified_shared_memory  ! Possibly OK - needs OpenMP Lang Spec clarification (-> #3240)
   !$omp declare target
 end subroutine bar
 
-! { dg-prune-output "not yet supported" }
+subroutine foobar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
+  use m0
+  !$omp requires unified_shared_memory
+  !$omp target enter data map(to:x)
+end subroutine foobar
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
index 117a1d000a5..230c690d84c 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
@@ -4,19 +4,16 @@ implicit none
 
 integer :: a, b, c
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
 
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor: 1)
 !$omp end target
 
-!$omp target device (ancestor : a)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a)
 !$omp end target
 
-!$omp target device (ancestor : a + 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a + 1)
 !$omp end target
 
 
@@ -32,61 +29,4 @@ integer :: a, b, c
 !$omp target device (42)
 !$omp end target
 
-
-! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)
-  !$omp teams  ! { dg-error "" "OpenMP constructs are not allowed in target region with 'ancestor'" { xfail *-*-* } }
-  !$omp end teams
-!$omp end target
-
-!$omp target device (device_num: 1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-!$omp target device (1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-
-! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
-! 'defaultmap', and 'map' clauses appear on the construct.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target nowait device (ancestor: 1)  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target device (ancestor: 1) nowait  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target nowait device (device_num: 1)
-!$omp end target
-
-!$omp target nowait device (1)
-!$omp end target
-
-!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
-!$omp end target
-
-
-! Ensure that 'ancestor' is only used with 'target' constructs (not with
-! 'target data', 'target update' etc.).
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target data map (a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp end target data
-
-!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-
-!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" "" { xfail *-*-* } }
-! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { xfail *-*-* } .-1 }
-
-
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
new file mode 100644
index 00000000000..feb76fe2144
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
@@ -0,0 +1,80 @@
+! { dg-do compile }
+
+implicit none
+
+integer :: a, b, c
+
+!$omp requires reverse_offload
+
+!$omp target device (ancestor: 1)
+!$omp end target
+
+!$omp target device (ancestor : a)
+!$omp end target
+
+!$omp target device (ancestor : a + 1)
+!$omp end target
+
+
+!$omp target device (device_num:42)
+!$omp end target
+
+!$omp target device (42)
+!$omp end target
+
+
+! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
+
+!$omp target device (ancestor: 1)
+  !$omp teams  ! { dg-error "OpenMP constructs are not allowed in target region with 'ancestor'" }
+  !$omp end teams
+!$omp end target
+
+!$omp target device (device_num: 1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+!$omp target device (1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+
+! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
+! 'defaultmap', and 'map' clauses appear on the construct.
+
+!$omp target nowait device (ancestor: 1)  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target device (ancestor: 1) nowait  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target nowait device (device_num: 1)
+!$omp end target
+
+!$omp target nowait device (1)
+!$omp end target
+
+!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
+!$omp end target
+
+
+! Ensure that 'ancestor' is only used with 'target' constructs (not with
+! 'target data', 'target update' etc.).
+! The following test case is marked with 'xfail' because a previous 'sorry' from
+! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
+
+!$omp target data map (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp end target data
+
+!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target device (ancestor: 1) if(.false.)
+! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { target *-*-* } .-1 }
+!$omp end target
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
index f1145bde2ec..e8975e6a08b 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
@@ -16,10 +16,10 @@ subroutine f1 ()
   implicit none
   integer :: n
 
-  !$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 
   !$omp target device (ancestor : 1)
-    n = omp_get_thread_num ()  ! { dg-error "" "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" { xfail *-*-* } }
+    n = omp_get_thread_num ()  ! { dg-error "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" }
   !$omp end target
 
   !$omp target device (device_num : 1)
@@ -30,4 +30,4 @@ subroutine f1 ()
     n = omp_get_thread_num ()
   !$omp end target
 
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
index 63872fa51fb..ab56e2d1d52 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
@@ -4,11 +4,11 @@
 ! Test to ensure that device-modifier 'ancestor' is parsed correctly in
 ! device clauses.
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
-!$omp target device (ancestor : 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : 1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
 !$omp end target
 
 end
 
-! TODO: dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
index 06a11eb5092..ca8d4b282a0 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
@@ -6,7 +6,7 @@
 !
 
 module m
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
@@ -17,7 +17,7 @@ contains
     block
       block
         block
-          !$omp target device(ancestor:1)
+          !$omp target device(ancestor:1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
           !$omp end target
         end block
       end block
@@ -26,7 +26,7 @@ contains
 end module m
 
 subroutine foo()
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
   block
     block
       block
@@ -49,7 +49,7 @@ contains
 end subroutine foo
 
 program main
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index e4dd8ef3e1d..5aab183c69a 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -282,7 +282,7 @@ enum gomp_map_kind
 /* Versions of libgomp and device-specific plugins.  GOMP_VERSION
    should be incremented whenever an ABI-incompatible change is introduced
    to the plugin interface defined in libgomp/libgomp.h.  */
-#define GOMP_VERSION	1
+#define GOMP_VERSION	2
 #define GOMP_VERSION_NVIDIA_PTX 1
 #define GOMP_VERSION_INTEL_MIC 0
 #define GOMP_VERSION_GCN 2
@@ -341,6 +341,12 @@ enum gomp_map_kind
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 #define GOMP_DEPEND_INOUTSET		5
 
+/* Flag values for requires-directive features, must match corresponding
+   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
+#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
+#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 07ab700b80c..ab3ed638475 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
 extern const char *GOMP_OFFLOAD_get_name (void);
 extern unsigned int GOMP_OFFLOAD_get_caps (void);
 extern int GOMP_OFFLOAD_get_type (void);
-extern int GOMP_OFFLOAD_get_num_devices (void);
+extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
 extern bool GOMP_OFFLOAD_init_device (int);
 extern bool GOMP_OFFLOAD_fini_device (int);
 extern unsigned GOMP_OFFLOAD_version (void);
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 2c4622c1092..c12bdd8bd28 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -189,8 +189,8 @@ The OpenMP 4.5 specification is fully supported.
       env variable @tab Y @tab
 @item Nested-parallel changes to @emph{max-active-levels-var} ICV @tab Y @tab
 @item @code{requires} directive @tab P
-      @tab Only fulfillable requirement are @code{atomic_default_mem_order}
-      and @code{dynamic_allocators}
+      @tab complete but no non-host devices provides @code{unified_address},
+      @code{unified_shared_memory} or @code{reverse_offload}
 @item @code{teams} construct outside an enclosing target region @tab Y @tab
 @item Non-rectangular loop nests @tab Y @tab
 @item @code{!=} as relational-op in canonical loop form for C/C++ @tab Y @tab
@@ -344,6 +344,8 @@ The OpenMP 4.5 specification is fully supported.
 @item @code{unconstrained} and @code{reproducible} modifiers on @code{order}
       clause @tab Y @tab
 @item Support @code{begin/end declare target} syntax in C/C++ @tab N @tab
+@item Pointer predetermined firstprivate getting initialized
+to address of matching mapped list item per 5.1, Sect. 2.21.7.2 @tab N @tab
 @end multitable
 
 
@@ -361,7 +363,7 @@ The OpenMP 4.5 specification is fully supported.
 @item Clauses on @code{end} directive can be on directive @tab N @tab
 @item Deprecation of no-argument @code{destroy} clause on @code{depobj}
       @tab N @tab
-@item @code{linear} clause syntax changes and @code{step} modifier @tab N @tab
+@item @code{linear} clause syntax changes and @code{step} modifier @tab P @tab only C/C++
 @item Deprecation of minus operator for reductions @tab N @tab
 @item Deprecation of separating @code{map} modifiers without comma @tab N @tab
 @item @code{declare mapper} with iterator and @code{present} modifiers
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index 5bb889926d3..eb11b9cf16a 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -54,7 +54,7 @@ host_get_type (void)
 }
 
 static int
-host_get_num_devices (void)
+host_get_num_devices (unsigned int omp_requires_mask __attribute__((unused)))
 {
   return 1;
 }
@@ -229,7 +229,7 @@ host_openacc_get_property (int n, enum goacc_property prop)
 {
   union goacc_property_value nullval = { .val = 0 };
 
-  if (n >= host_get_num_devices ())
+  if (n >= host_get_num_devices (0))
     return nullval;
 
   switch (prop)
diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c
index 1565aa0f290..42c3e74e6ba 100644
--- a/libgomp/oacc-init.c
+++ b/libgomp/oacc-init.c
@@ -148,7 +148,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
 	      if (dispatchers[d]
 		  && !strcasecmp (goacc_device_type,
 				  get_openacc_name (dispatchers[d]->name))
-		  && dispatchers[d]->get_num_devices_func () > 0)
+		  && dispatchers[d]->get_num_devices_func (0) > 0)
 		goto found;
 
 	    if (fail_is_error)
@@ -169,7 +169,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
     case acc_device_not_host:
       /* Find the first available device after acc_device_not_host.  */
       while (known_device_type_p (++d))
-	if (dispatchers[d] && dispatchers[d]->get_num_devices_func () > 0)
+	if (dispatchers[d] && dispatchers[d]->get_num_devices_func (0) > 0)
 	  goto found;
       if (d_arg == acc_device_default)
 	{
@@ -302,7 +302,7 @@ acc_init_1 (acc_device_t d, acc_construct_t parent_construct, int implicit)
 
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   if (ndevs <= 0 || goacc_device_num >= ndevs)
     acc_dev_num_out_of_range (d, goacc_device_num, ndevs);
@@ -351,7 +351,7 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   /* Unload all the devices of this type that have been opened.  */
   for (i = 0; i < ndevs; i++)
@@ -520,7 +520,7 @@ goacc_attach_host_thread_to_device (int ord)
       base_dev = cached_base_dev;
     }
   
-  num_devices = base_dev->get_num_devices_func ();
+  num_devices = base_dev->get_num_devices_func (0);
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (acc_device_type (base_dev->type), ord,
 			      num_devices);
@@ -599,7 +599,7 @@ acc_get_num_devices (acc_device_t d)
   if (!acc_dev)
     return 0;
 
-  n = acc_dev->get_num_devices_func ();
+  n = acc_dev->get_num_devices_func (0);
   if (n < 0)
     n = 0;
 
@@ -779,7 +779,7 @@ acc_set_device_num (int ord, acc_device_t d)
 
       cached_base_dev = base_dev = resolve_device (d, true);
 
-      num_devices = base_dev->get_num_devices_func ();
+      num_devices = base_dev->get_num_devices_func (0);
 
       if (num_devices <= 0 || ord >= num_devices)
         acc_dev_num_out_of_range (d, ord, num_devices);
@@ -814,7 +814,7 @@ get_property_any (int ord, acc_device_t d, acc_device_property_t prop)
 
   struct gomp_device_descr *dev = resolve_device (d, true);
 
-  int num_devices = dev->get_num_devices_func ();
+  int num_devices = dev->get_num_devices_func (0);
 
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (d, ord, num_devices);
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 1c0436842da..ea327bf2ca0 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -3221,10 +3221,14 @@ GOMP_OFFLOAD_version (void)
 /* Return the number of GCN devices on the system.  */
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
   if (!init_hsa_context ())
     return 0;
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
+    return -1;
   return hsa_context.agent_count;
 }
 
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 387bcbbc52a..bc63e274cdf 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1175,9 +1175,14 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
-  return nvptx_get_num_devices ();
+  int num_devices = nvptx_get_num_devices ();
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
+  return num_devices;
 }
 
 bool
diff --git a/libgomp/target.c b/libgomp/target.c
index c0844f2265a..5a23aad92c9 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -36,6 +36,7 @@
 # include <inttypes.h>  /* For PRIu64.  */
 #endif
 #include <string.h>
+#include <stdio.h>  /* For snprintf. */
 #include <assert.h>
 #include <errno.h>
 
@@ -98,6 +99,9 @@ static int num_devices;
 /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
 static int num_devices_openmp;
 
+/* OpenMP requires mask.  */
+static int omp_requires_mask;
+
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
 
 static void *
@@ -2314,6 +2318,20 @@ gomp_unload_image_from_device (struct gomp_device_descr *devicep,
     }
 }
 
+static void
+gomp_requires_to_name (char *buf, size_t size, int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* This function should be called from every offload image while loading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
    the target, and TARGET_DATA needed by target plugin.  */
@@ -2323,11 +2341,29 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
 			   int target_type, const void *target_data)
 {
   int i;
+  int omp_req = omp_requires_mask;
 
   if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
     gomp_fatal ("Library too old for offload (version %u < %u)",
 		GOMP_VERSION, GOMP_VERSION_LIB (version));
-  
+
+  if (GOMP_VERSION_LIB (version) > 1)
+    {
+      omp_req = (int) (size_t) ((void **) target_data)[0];
+      target_data = &((void **) target_data)[1];
+      if (num_devices && (omp_req & ~omp_requires_mask))
+	{
+	  char buf[64];
+	  gomp_requires_to_name (buf, sizeof (buf),
+				 omp_req & ~omp_requires_mask);
+	  gomp_error ("devices already initialized when registering additional "
+		      "offload images that use the additional OpenMP 'requires'"
+		      " directive clauses %s. Therefore, the program might not "
+		      "run correctly", buf);
+	}
+      omp_requires_mask |= omp_req;
+    }
+
   gomp_mutex_lock (&register_lock);
 
   /* Load image to all initialized devices.  */
@@ -4125,8 +4161,30 @@ gomp_target_init (void)
 
 	if (gomp_load_plugin_for_device (&current_device, plugin_name))
 	  {
-	    new_num_devs = current_device.get_num_devices_func ();
-	    if (new_num_devs >= 1)
+	    new_num_devs
+	      = current_device.get_num_devices_func (omp_requires_mask);
+	    if (new_num_devs < 0)
+	      {
+		bool found = false;
+		int type = current_device.get_type_func ();
+		for (int img = 0; img < num_offload_images; img++)
+		  if (type == offload_images[img].type)
+		    found = true;
+		if (found)
+		  {
+		    char buf[64];
+		    gomp_requires_to_name (buf, sizeof (buf),
+					   omp_requires_mask);
+		    char *name = (char *) malloc (cur_len + 1);
+		    memcpy (name, cur, cur_len);
+		    name[cur_len] = '\0';
+		    GOMP_PLUGIN_error ("note: %s devices present but 'omp "
+				       "requires %s' cannot be fulfilled",
+				       name, buf);
+		    free (name);
+		  }
+	      }
+	    else if (new_num_devs >= 1)
 	      {
 		/* Augment DEVICES and NUM_DEVICES.  */
 
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
new file mode 100644
index 00000000000..fedf9779769
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-1-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
new file mode 100644
index 00000000000..617577448ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
@@ -0,0 +1,9 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
new file mode 100644
index 00000000000..ac7f3ef512c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -0,0 +1,25 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-foffload=disable -flto" } */
+/* { dg-additional-sources requires-2-aux.c } */
+
+/* Check diagnostic by host's lto1.
+   Other file does not have any 'omp requires'. */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. ''" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
new file mode 100644
index 00000000000..4b07ffdd09b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-sources requires-3-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
new file mode 100644
index 00000000000..b8b51ae8ca7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
@@ -0,0 +1,13 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+/* Note: The file does not have neither of:
+   declare target directives, device constructs or device routines.  */
+
+int x;
+
+void foo (void)
+{
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
new file mode 100644
index 00000000000..128fdbb8463
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
@@ -0,0 +1,23 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources requires-4-aux.c } */
+
+/* Check diagnostic by device-compiler's or host compiler's lto1.
+   Other file uses: 'requires reverse_offload', but that's inactive as
+   there are no declare target directives, device constructs nor device routines  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
new file mode 100644
index 00000000000..d223749f0a1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
new file mode 100644
index 00000000000..3d15bde21f0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -0,0 +1,20 @@
+/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-5-aux.c } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-6.c b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
new file mode 100644
index 00000000000..b00c7459bbc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
@@ -0,0 +1,17 @@
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+/* The requires line is not active as there is none of:
+     declare target directives, device constructs or device routines.
+   Thus, this code is expected to work everywhere.  */
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c
new file mode 100644
index 00000000000..0916db8a0ce
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  x = 1;
+  #pragma omp target enter data map(always,to: x)
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
new file mode 100644
index 00000000000..c94a4c10846
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-7-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90 b/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90
new file mode 100644
index 00000000000..a18caeb4c69
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90
@@ -0,0 +1,14 @@
+! { dg-skip-if "" { *-*-* } }
+
+module m
+  integer x
+end module m
+
+subroutine foo
+  use m
+  implicit none
+  !$omp requires unified_address
+
+  x = 1
+  !$omp target enter data map(always,to: x)
+end
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
new file mode 100644
index 00000000000..33741af15f1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -0,0 +1,26 @@
+! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
+! { dg-additional-sources requires-1-aux.f90 }
+
+! Check diagnostic by device-compiler's lto1.
+!   Other file uses: 'requires unified_address'.
+
+module m
+  integer :: a(10)
+  interface
+    subroutine foo
+    end
+  end interface
+end
+
+program main
+  !$omp requires unified_shared_memory
+
+  !$omp target
+    a = 0
+  !$omp end target
+
+  call foo ()
+end
+
+! { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+! { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" }
diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
index d1678d0514e..33bae0650b4 100644
--- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
+++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
@@ -168,8 +168,12 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 extern "C" int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
   TRACE ("(): return %d", num_devices);
   return num_devices;
 }

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 13:06                 ` [Patch][v5] " Tobias Burnus
@ 2022-07-01 14:34                   ` Jakub Jelinek
  2022-07-01 16:31                     ` Tobias Burnus
  2022-07-06 10:42                   ` Restore 'GOMP_offload_unregister_ver' functionality " Thomas Schwinge
  2023-09-15  9:41                   ` [Patch][v5] OpenMP: Move omp requires checks to libgomp Thomas Schwinge
  2 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-07-01 14:34 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Fri, Jul 01, 2022 at 03:06:05PM +0200, Tobias Burnus wrote:
> --- a/gcc/fortran/parse.cc
> +++ b/gcc/fortran/parse.cc
> @@ -1168,7 +1168,8 @@ decode_omp_directive (void)
>      }
>    switch (ret)
>      {
> -    case ST_OMP_DECLARE_TARGET:
> +    /* Set omp_target_seen; exclude ST_OMP_DECLARE_TARGET.
> +       FIXME: Get clarification, cf. OpenMP Spec Issue #3240.  */
>      case ST_OMP_TARGET:
>      case ST_OMP_TARGET_DATA:
>      case ST_OMP_TARGET_ENTER_DATA:
> @@ -6879,11 +6880,14 @@ done:
>  
>    /* Fixup for external procedures and resolve 'omp requires'.  */
>    int omp_requires;
> +  bool omp_target_seen;
>    omp_requires = 0;
> +  omp_target_seen = false;
>    for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
>         gfc_current_ns = gfc_current_ns->sibling)
>      {
>        omp_requires |= gfc_current_ns->omp_requires;
> +      omp_target_seen |= gfc_current_ns->omp_target_seen;
>        gfc_check_externals (gfc_current_ns);
>      }
>    for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
> @@ -6908,6 +6912,22 @@ done:
>        break;
>      }
>  
> +  if (omp_target_seen)
> +    omp_requires_mask = (enum omp_requires) (omp_requires_mask
> +					     | OMP_REQUIRES_TARGET_USED);
> +  if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
> +    omp_requires_mask = (enum omp_requires) (omp_requires_mask
> +					     | OMP_REQUIRES_REVERSE_OFFLOAD);
> +  if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
> +    omp_requires_mask = (enum omp_requires) (omp_requires_mask
> +					     | OMP_REQUIRES_UNIFIED_ADDRESS);
> +  if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
> +    omp_requires_mask
> +	  = (enum omp_requires) (omp_requires_mask
> +				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
> +  if (omp_requires & OMP_REQ_DYNAMIC_ALLOCATORS)
> +    omp_requires_mask = (enum omp_requires) (omp_requires_mask
> +					     | OMP_REQUIRES_DYNAMIC_ALLOCATORS);
>    /* Do the parse tree dump.  */
>    gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;

Will Fortran diagnose:
subroutine foo
!$omp requires unified_shared_memory
!$omp target
!$omp end target
end subroutine foo
subroutine bar
!$omp requires reverse_offload
!$omp target
!$omp end target
end subroutine bar

or just merge it from the different namespaces?
This is something that can be handled separately if it isn't resolved
and might need clarification from omp-lang.

> @@ -1764,6 +1781,20 @@ input_symtab (void)
>      }
>  }
>  
> +static void
> +omp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
> +{
> +  char *end = buf + size, *p = buf;
> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
> +    p += snprintf (p, end - p, "unified_address");
> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
> +    p += snprintf (p, end - p, "%sunified_shared_memory",
> +		   (p == buf ? "" : ", "));
> +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
> +    p += snprintf (p, end - p, "%sreverse_offload",
> +		   (p == buf ? "" : ", "));

So, what does this print if requires_mask is 0 (or just the target used bit
set but not unified_address, unified_shared_memory nor reverse_offload)?
Say in case of:
a.c
#pragma omp requires unified_address
void foo (void) {
#pragma omp target
;
}
b.c:
void bar (void) {
#pragma omp target
;
}
gcc -fopenmp -shared -o a.so a.c b.c
?

> @@ -1810,6 +1847,54 @@ input_offload_tables (bool do_force_output)
>  		 may be no refs to var_decl in offload LTO mode.  */
>  	      if (do_force_output)
>  		varpool_node::get (var_decl)->force_output = 1;
> +	      tmp_decl = var_decl;
> +	    }
> +	  else if (tag == LTO_symtab_edge)
> +	    {
> +	      static bool error_emitted = false;
> +	      HOST_WIDE_INT val = streamer_read_hwi (ib);
> +
> +	      if (omp_requires_mask == 0)
> +		{
> +		  omp_requires_mask = (omp_requires) val;
> +		  requires_decl = tmp_decl;
> +		  requires_fn = file_data->file_name;

And similarly here, if some device construct is seen but requires
directive isn't, not sure if in this version val would be 0 or something
with the TARGET_USED bit set.  In the latter case, only what is printed
for no requires or just atomic related requires is a problem, in the former
case due to the == 0 check mixing of 0 with non-zero would be ignored
but mixing of non-zero with 0 wouldn't be.

> +		}
> +	      else if (omp_requires_mask != val && !error_emitted)
> +		{
> +		  char buf[64], buf2[64];

Perhaps cleaner would be to size the buffers as
sizeof ("unified_address,unified_shared_memory,reverse_offload")
64 is more, but just a wild guess and if further clauses are added later,
it might be too small.

> +                (p == buf ? "" : ", "));
> +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
> +    p += snprintf (p, end - p, "%sreverse_offload
> +		  omp_requires_to_name (buf, sizeof (buf), omp_requires_mask);
> +		  omp_requires_to_name (buf2, sizeof (buf2), val);
> +		  error ("OpenMP %<requires%> directive with non-identical "
> +			 "clauses in multiple compilation units: %qs vs. %qs",
> +			 buf, buf2);

> @@ -1821,6 +1906,18 @@ input_offload_tables (bool do_force_output)
>        lto_destroy_simple_input_block (file_data, LTO_section_offload_table,
>  				      ib, data, len);
>      }
> +#ifdef ACCEL_COMPILER
> +  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
> +  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
> +    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");
> +  FILE *f = fopen (omp_requires_file, "wb");
> +  if (!f)
> +    fatal_error (input_location, "Cannot open omp_requires file %qs",
> +		 omp_requires_file);
> +  uint32_t req_mask = omp_requires_mask & ~OMP_REQUIRES_TARGET_USED;

Perhaps it is better to also store the TARGET_USED bit and on the library
side completely ignore values of 0.

> --- a/gcc/omp-low.cc
> +++ b/gcc/omp-low.cc
> @@ -12701,6 +12701,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>        gcc_unreachable ();
>      }
>  
> +  /* Ensure that requires map is written via output_offload_tables, even if only
> +     'target (enter/exit) data' is used in the translation unit.  */
> +  if (ENABLE_OFFLOADING && (omp_requires_mask & OMP_REQUIRES_TARGET_USED))
> +    g->have_offload = true;

Is
c.c:
#pragma omp requires unified_shared_memory
d.c:
void baz (void) {
  #pragma omp target
  ;
}
ok?  Pedantically reading current standard probably yes, but perhaps again
something to be discussed.  The question is what the requires directive
in that case would do, nothing at all as there are no device constructs
etc.?  In that case omp_requires_mask & OMP_REQUIRES_TARGET_USED is right.
But if it should influence the behavior anyway, the restriction should be
Either all compilation units of a program that contain ... device
constructs ... should include also requires directive with one of the
unified_shared_memory, unified_address or reverse_offload clauses.
In that case the test would be
omp_requires_mask & (OMP_REQUIRES_TARGET_USED | OMP_REQUIRES_UNIFIED* | OMP_REQUIRES_REV*)

> +static void
> +gomp_requires_to_name (char *buf, size_t size, int requires_mask)
> +{
> +  char *end = buf + size, *p = buf;
> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
> +    p += snprintf (p, end - p, "unified_address");
> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
> +    p += snprintf (p, end - p, "%sunified_shared_memory",
> +		   (p == buf ? "" : ", "));
> +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
> +    p += snprintf (p, end - p, "%sreverse_offload",
> +		   (p == buf ? "" : ", "));
> +}

Same question as earlier.

>  /* This function should be called from every offload image while loading.
>     It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
>     the target, and TARGET_DATA needed by target plugin.  */
> @@ -2323,11 +2341,29 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
>  			   int target_type, const void *target_data)
>  {
>    int i;
> +  int omp_req = omp_requires_mask;
>  
>    if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
>      gomp_fatal ("Library too old for offload (version %u < %u)",
>  		GOMP_VERSION, GOMP_VERSION_LIB (version));
> -  
> +
> +  if (GOMP_VERSION_LIB (version) > 1)
> +    {
> +      omp_req = (int) (size_t) ((void **) target_data)[0];
> +      target_data = &((void **) target_data)[1];
> +      if (num_devices && (omp_req & ~omp_requires_mask))
> +	{
> +	  char buf[64];
> +	  gomp_requires_to_name (buf, sizeof (buf),
> +				 omp_req & ~omp_requires_mask);
> +	  gomp_error ("devices already initialized when registering additional "
> +		      "offload images that use the additional OpenMP 'requires'"
> +		      " directive clauses %s. Therefore, the program might not "
> +		      "run correctly", buf);
> +	}
> +      omp_requires_mask |= omp_req;
> +    }

Both omp_requires_mask and num_devices are global vars that would be
modified concurrently in some other thread, so the above is racy.

What I'd do is int omp_req = 0; early, just the omp_req + target_data in
if (GOMP_VERSION_LIB (version) > 1) otherwise.  That computes
the local omp_req only.

> +
>    gomp_mutex_lock (&register_lock);

Then under the lock, you can do the merging.
But, IMHO the runtime library should repeat what is done in the offloading
lto1, diagnose if there are differences between the masks in between
different TUs, here at runtime on the program/shared library level.
And IMHO the error you emit above is unnecessary, because (at least
hopefully) the num_devices computation / device initialization should
only happen on behalf of some device construct or device related OpenMP API
routine, so at that point the shared library or program that does that
should have its own mask and if something is dlopened later, it should
either have compatible mask (nothing is diagnosed) or incompatible, but then
it should be diagnosed like any other incompatibilities.
If you want further diagnostics after devices are initialized, it could be
just a note only in case there would be some extra devices available that
don't match it.  If all available devices satisfy it, the extra message
wouldn't tell user anything interesting.

> @@ -4125,8 +4161,30 @@ gomp_target_init (void)
>  
>  	if (gomp_load_plugin_for_device (&current_device, plugin_name))
>  	  {
> -	    new_num_devs = current_device.get_num_devices_func ();
> -	    if (new_num_devs >= 1)
> +	    new_num_devs
> +	      = current_device.get_num_devices_func (omp_requires_mask);
> +	    if (new_num_devs < 0)
> +	      {
> +		bool found = false;
> +		int type = current_device.get_type_func ();
> +		for (int img = 0; img < num_offload_images; img++)
> +		  if (type == offload_images[img].type)
> +		    found = true;
> +		if (found)
> +		  {
> +		    char buf[64];
> +		    gomp_requires_to_name (buf, sizeof (buf),
> +					   omp_requires_mask);
> +		    char *name = (char *) malloc (cur_len + 1);
> +		    memcpy (name, cur, cur_len);
> +		    name[cur_len] = '\0';
> +		    GOMP_PLUGIN_error ("note: %s devices present but 'omp "
> +				       "requires %s' cannot be fulfilled",
> +				       name, buf);
> +		    free (name);
> +		  }

This isn't an error, so IMNSHO it should be at least guarded with
GOMP_DEBUG=true in the environment, not all programs want the library to be
talkative and break its standard error...
Why do you need the malloc?  Can't you just use %.*s ... cur_len, cur
?  If malloc would be necessary, it would need to be gomp_malloc, so that
the program doesn't silently crash if malloc fails, or should handle malloc
failure itself.

> --- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
> +++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
> @@ -168,8 +168,12 @@ GOMP_OFFLOAD_get_type (void)
>  }
>  
>  extern "C" int
> -GOMP_OFFLOAD_get_num_devices (void)
> +GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
>  {
> +  /* Return -1 if no omp_requires_mask cannot be fulfilled but
> +     devices were present.  */
> +  if (num_devices > 0 && omp_requires_mask != 0)
> +    return -1;
>    TRACE ("(): return %d", num_devices);
>    return num_devices;
>  }

I thought I've mentioned earlier it would be nice to rename the
get_num_devices plugin hook because its API has changed, so that
if one mixes old plugin with new libgomp or vice versa it doesn't
break silently.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 14:34                   ` Jakub Jelinek
@ 2022-07-01 16:31                     ` Tobias Burnus
  2022-07-01 16:55                       ` Jakub Jelinek
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-07-01 16:31 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 01.07.22 16:34, Jakub Jelinek wrote:
> On Fri, Jul 01, 2022 at 03:06:05PM +0200, Tobias Burnus wrote:
> [...]
> Will Fortran diagnose:
> subroutine foo
> !$omp requires unified_shared_memory
> !$omp target
> !$omp end target
> end subroutine foo
> subroutine bar
> !$omp requires reverse_offload
> !$omp target
> !$omp end target
> end subroutine bar
>
> or just merge it from the different namespaces?

This is done in openmp.cc during parsing. The merging you quoted (in parse.cc) happens
after the whole input file has been parsed and resolved. For your test case, the
following error is shown:

test.f90:1:15:

     1 |  subroutine foo
       |               1
Error: Program unit at (1) has OpenMP device constructs/routines but does not set !$OMP REQUIRES REVERSE_OFFLOAD but other program units do
test.f90:6:14:

     6 | subroutine bar
       |              1
Error: Program unit at (1) has OpenMP device constructs/routines but does not set !$OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do


> @@ -1764,6 +1781,20 @@ input_symtab (void)
>>       }
>>   }
>>
>> +static void
>> +omp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
>> +{
>> +  char *end = buf + size, *p = buf;
>> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
>> +    p += snprintf (p, end - p, "unified_address");
>> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
>> +    p += snprintf (p, end - p, "%sunified_shared_memory",
>> +               (p == buf ? "" : ", "));
>> +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
>> +    p += snprintf (p, end - p, "%sreverse_offload",
>> +               (p == buf ? "" : ", "));
> So, what does this print if requires_mask is 0 (or just the target used bit
> set but not unified_address, unified_shared_memory nor reverse_offload)?

Well, that's what libgomp/testsuite/libgomp.c-c++-common/requires-2.c (+ *-2-aux.c)
tests:

/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. ''" "" { target *-*-* } 0 }  */

I hope the '' vs. 'unified_shared_memory' is clear - but if you have a better wording.

Note that both:
   no 'omp requires'
and
   'omp requires' with other clauses (such as the atomic ones or dynamic_allocators)
will lead to 0. Thus, if the wording is changed, it should fit for both cases.

>> @@ -1810,6 +1847,54 @@ input_offload_tables (bool do_force_output)
>>               may be no refs to var_decl in offload LTO mode.  */
>>            if (do_force_output)
>>              varpool_node::get (var_decl)->force_output = 1;
>> +          tmp_decl = var_decl;
>> +        }
>> +      else if (tag == LTO_symtab_edge)
>> +        {
>> +          static bool error_emitted = false;
>> +          HOST_WIDE_INT val = streamer_read_hwi (ib);
>> +
>> +          if (omp_requires_mask == 0)
>> +            {
>> +              omp_requires_mask = (omp_requires) val;
>> +              requires_decl = tmp_decl;
>> +              requires_fn = file_data->file_name;
> And similarly here, if some device construct is seen but requires
> directive isn't, not sure if in this version val would be 0 or something
> with the TARGET_USED bit set.  In the latter case, only what is printed
> for no requires or just atomic related requires is a problem, in the former
> case due to the == 0 check mixing of 0 with non-zero would be ignored
> but mixing of non-zero with 0 wouldn't be.

Here: 0 = "unset" in the sense that either TARGET_USE nor USM/UA/RO was
specified. If any of those is set, we get != 0.

For mkoffload, the single results are merged - and TARGET_USE is stripped,
such that it is either 0 or a combination of USM/UA/RO

>> +            }
>> +          else if (omp_requires_mask != val && !error_emitted)
>> +            {
>> +              char buf[64], buf2[64];
> Perhaps cleaner would be to size the buffers as
> sizeof ("unified_address,unified_shared_memory,reverse_offload")
> 64 is more, but just a wild guess and if further clauses are added later,
> it might be too small.

I concur – except that ',' should be ', '.
(Likewise in libgomp/target.c)

> @@ -1821,6 +1906,18 @@ input_offload_tables (bool do_force_output)
>>         lto_destroy_simple_input_block (file_data, LTO_section_offload_table,
>>                                    ib, data, len);
>>       }
>> +#ifdef ACCEL_COMPILER
>> +  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
>> +  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
>> +    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");
>> +  FILE *f = fopen (omp_requires_file, "wb");
>> +  if (!f)
>> +    fatal_error (input_location, "Cannot open omp_requires file %qs",
>> +             omp_requires_file);
>> +  uint32_t req_mask = omp_requires_mask & ~OMP_REQUIRES_TARGET_USED;
> Perhaps it is better to also store the TARGET_USED bit and on the library
> side completely ignore values of 0.

For the compiler side, we need to distinguish no requires vs. some
requires when checking multiple TU (to distinguish it from TU which do
not use target constructs).

But for libgomp only the result counts: no requires or some requires.
Thus, passing 0 if there are no USM/UA/RO should be fine – and the code
does so. This 0 is then passed on to the plugin to check against it.

If we pass target_used to libgomp, we need to filter it out at some point.

>> --- a/gcc/omp-low.cc
>> +++ b/gcc/omp-low.cc
>> @@ -12701,6 +12701,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>>         gcc_unreachable ();
>>       }
>>
>> +  /* Ensure that requires map is written via output_offload_tables, even if only
>> +     'target (enter/exit) data' is used in the translation unit.  */
>> +  if (ENABLE_OFFLOADING && (omp_requires_mask & OMP_REQUIRES_TARGET_USED))
>> +    g->have_offload = true;
> Is
> c.c:
> #pragma omp requires unified_shared_memory
> d.c:
> void baz (void) {
>    #pragma omp target
>    ;
> }
> ok?

This one is *already* streamed out as it creates a symbol and entry in
in offload_functions (baz.omp_fn.0).

The code is rather for '#pragma omp target enter data map(x)' as this
only adds a library call and no symbol.

> Pedantically reading current standard probably yes, but perhaps again
> something to be discussed.  The question is what the requires directive
> in that case would do, nothing at all as there are no device constructs
> etc.?

Isn't there a device construct – which happens to be empty?

With 'omp target map(always, to: x)' it would be even observable that
the code is run.

> In that case omp_requires_mask & OMP_REQUIRES_TARGET_USED is right.
> But if it should influence the behavior anyway, the restriction should be
> Either all compilation units of a program that contain ... device
> constructs ... should include also requires directive with one of the
> unified_shared_memory, unified_address or reverse_offload clauses.
> In that case the test would be
> omp_requires_mask & (OMP_REQUIRES_TARGET_USED | OMP_REQUIRES_UNIFIED* | OMP_REQUIRES_REV*)

I think I am lost – don't we effectively test this? We filter out
everything else in output_offload_tables. Thus, in input_offload_tables,
a single '==' will do. (We additionally know that TARGET_USED is set -
as otherwise there wouldn't be a symbol in the offload table.)

Thus, it is unclear to me what you propose here.

>> +static void
>> +gomp_requires_to_name (char *buf, size_t size, int requires_mask)
>> +{
>> +  char *end = buf + size, *p = buf;
>> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
>> +    p += snprintf (p, end - p, "unified_address");
>> +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
>> +    p += snprintf (p, end - p, "%sunified_shared_memory",
>> +               (p == buf ? "" : ", "));
>> +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
>> +    p += snprintf (p, end - p, "%sreverse_offload",
>> +               (p == buf ? "" : ", "));
>> +}
> Same question as earlier.

Same answer, except that in libgomp, this code is effectively only
reachable when omp_requires_mask != 0 as it reaches this code only if
either some additional flag was added (in register_ver) or when devices
were available, but those do not support a flag.

We just have to remember to update this, if we ever add additional flags.

>>   /* This function should be called from every offload image while loading.
>>      It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
>>      the target, and TARGET_DATA needed by target plugin.  */
>> @@ -2323,11 +2341,29 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
>>                         int target_type, const void *target_data)
>>   {
>>     int i;
>> +  int omp_req = omp_requires_mask;
>>
>>     if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
>>       gomp_fatal ("Library too old for offload (version %u < %u)",
>>              GOMP_VERSION, GOMP_VERSION_LIB (version));
>> -
>> +
>> +  if (GOMP_VERSION_LIB (version) > 1)
>> +    {
>> +      omp_req = (int) (size_t) ((void **) target_data)[0];
>> +      target_data = &((void **) target_data)[1];
>> +      if (num_devices && (omp_req & ~omp_requires_mask))
>> +    {
>> +      char buf[64];
>> +      gomp_requires_to_name (buf, sizeof (buf),
>> +                             omp_req & ~omp_requires_mask);
>> +      gomp_error ("devices already initialized when registering additional "
>> +                  "offload images that use the additional OpenMP 'requires'"
>> +                  " directive clauses %s. Therefore, the program might not "
>> +                  "run correctly", buf);
>> +    }
>> +      omp_requires_mask |= omp_req;
>> +    }
> Both omp_requires_mask and num_devices are global vars that would be
> modified concurrently in some other thread, so the above is racy.
>
> What I'd do is int omp_req = 0; early, just the omp_req + target_data in
> if (GOMP_VERSION_LIB (version) > 1) otherwise.  That computes
> the local omp_req only.
>
>> +
>>     gomp_mutex_lock (&register_lock);
> Then under the lock, you can do the merging.
> But, IMHO the runtime library should repeat what is done in the offloading
> lto1, diagnose if there are differences between the masks in between
> different TUs, here at runtime on the program/shared library level.
> And IMHO the error you emit above is unnecessary, because (at least
> hopefully) the num_devices computation / device initialization should
> only happen on behalf of some device construct or device related OpenMP API
> routine, so at that point the shared library or program that does that
> should have its own mask and if something is dlopened later, it should
> either have compatible mask (nothing is diagnosed) or incompatible, but then
> it should be diagnosed like any other incompatibilities.

OK – I will diagnose it always.

Question: If it is not the same, should there just be a message to
stderr (gomp_error) or should libgomp abort (gomp_fatal)?

Downside is that I cannot really provide much data where it fails. But
on the other hand, it will probably only rarely occur.

> I thought I've mentioned earlier it would be nice to rename the
> get_num_devices plugin hook because its API has changed, so that
> if one mixes old plugin with new libgomp or vice versa it doesn't
> break silently.

As discussed off list, gomp_load_plugin_for_device calls     if
(device->version_func () != GOMP_VERSION) and we did bump the GOMP_VERSION.

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 16:31                     ` Tobias Burnus
@ 2022-07-01 16:55                       ` Jakub Jelinek
  2022-07-01 21:08                         ` Tobias Burnus
  0 siblings, 1 reply; 42+ messages in thread
From: Jakub Jelinek @ 2022-07-01 16:55 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Fri, Jul 01, 2022 at 06:31:48PM +0200, Tobias Burnus wrote:
> This is done in openmp.cc during parsing. The merging you quoted (in parse.cc) happens
> after the whole input file has been parsed and resolved. For your test case, the
> following error is shown:
> 
> test.f90:1:15:
> 
>     1 |  subroutine foo
>       |               1
> Error: Program unit at (1) has OpenMP device constructs/routines but does not set !$OMP REQUIRES REVERSE_OFFLOAD but other program units do
> test.f90:6:14:
> 
>     6 | subroutine bar
>       |              1
> Error: Program unit at (1) has OpenMP device constructs/routines but does not set !$OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do

Great.

> > @@ -1764,6 +1781,20 @@ input_symtab (void)
> > >       }
> > >   }
> > > 
> > > +static void
> > > +omp_requires_to_name (char *buf, size_t size, unsigned int requires_mask)
> > > +{
> > > +  char *end = buf + size, *p = buf;
> > > +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
> > > +    p += snprintf (p, end - p, "unified_address");
> > > +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
> > > +    p += snprintf (p, end - p, "%sunified_shared_memory",
> > > +               (p == buf ? "" : ", "));
> > > +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
> > > +    p += snprintf (p, end - p, "%sreverse_offload",
> > > +               (p == buf ? "" : ", "));
> > So, what does this print if requires_mask is 0 (or just the target used bit
> > set but not unified_address, unified_shared_memory nor reverse_offload)?
> 
> Well, that's what libgomp/testsuite/libgomp.c-c++-common/requires-2.c (+ *-2-aux.c)
> tests:
> 
> /* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. ''" "" { target *-*-* } 0 }  */
> 
> I hope the '' vs. 'unified_shared_memory' is clear - but if you have a better wording.

I must be missing how that works.  Because the buf in the callers is
uninitialized and this function doesn't store there anything if
requires_mask == 0.
Perhaps you're just lucky and the stack contains '\0' there?

> Note that both:
>   no 'omp requires'
> and
>   'omp requires' with other clauses (such as the atomic ones or dynamic_allocators)
> will lead to 0. Thus, if the wording is changed, it should fit for both cases.

Maybe it would be better to simply use different error message for the
0 vs. non-0 case, canonicalized to non-0 vs. 0 order so that it is just
2 messages vs. 3 and wording like
"OpenMP 'requires' directive with '....' clauses specified only in some compilation units"
note: specified here ...
note: but not here ...

> > > +          if (omp_requires_mask == 0)
> > > +            {
> > > +              omp_requires_mask = (omp_requires) val;
> > > +              requires_decl = tmp_decl;
> > > +              requires_fn = file_data->file_name;
> > And similarly here, if some device construct is seen but requires
> > directive isn't, not sure if in this version val would be 0 or something
> > with the TARGET_USED bit set.  In the latter case, only what is printed
> > for no requires or just atomic related requires is a problem, in the former
> > case due to the == 0 check mixing of 0 with non-zero would be ignored
> > but mixing of non-zero with 0 wouldn't be.
> 
> Here: 0 = "unset" in the sense that either TARGET_USE nor USM/UA/RO was
> specified. If any of those is set, we get != 0.

Ok.
> 
> For mkoffload, the single results are merged - and TARGET_USE is stripped,
> such that it is either 0 or a combination of USM/UA/RO

I'd find it clearer if we never stripped that, so that even the library knows.
The details will depend on the resolution of #3240.
Whether say declare target and no device constructs and device related API
calls etc. force it too or not.  If not, you could get 0 even if you are
actually registering something, just not target regions.
If anything that will lead to GOMP_offload_register_ver actually means
TARGET_USED, then it isn't necessary.  But even if it isn't necessary,
e.g. for backwards compatibility with GOMP_VERSION == 1 it will be easier
to have that bit in.  0 will then mean older gcc built library or binary.

> > > +            }
> > > +          else if (omp_requires_mask != val && !error_emitted)
> > > +            {
> > > +              char buf[64], buf2[64];
> > Perhaps cleaner would be to size the buffers as
> > sizeof ("unified_address,unified_shared_memory,reverse_offload")
> > 64 is more, but just a wild guess and if further clauses are added later,
> > it might be too small.
> 
> I concur – except that ',' should be ', '.
> (Likewise in libgomp/target.c)

Good catch.

> > Is
> > c.c:
> > #pragma omp requires unified_shared_memory
> > d.c:
> > void baz (void) {
> >    #pragma omp target
> >    ;
> > }
> > ok?
> 
> This one is *already* streamed out as it creates a symbol and entry in
> in offload_functions (baz.omp_fn.0).
> 
> The code is rather for '#pragma omp target enter data map(x)' as this
> only adds a library call and no symbol.
> 
> > Pedantically reading current standard probably yes, but perhaps again
> > something to be discussed.  The question is what the requires directive
> > in that case would do, nothing at all as there are no device constructs
> > etc.?
> 
> Isn't there a device construct – which happens to be empty?

In d.c there is.  But in c.c there isn't.
So, the question is if the directive in c.c is just completely ignored
(ok, aside from semantic checking) or if it should mean that if it is
specified there, it must be specified elsewhere where device constructs etc.
are used too.

> > In that case omp_requires_mask & OMP_REQUIRES_TARGET_USED is right.
> > But if it should influence the behavior anyway, the restriction should be
> > Either all compilation units of a program that contain ... device
> > constructs ... should include also requires directive with one of the
> > unified_shared_memory, unified_address or reverse_offload clauses.
> > In that case the test would be
> > omp_requires_mask & (OMP_REQUIRES_TARGET_USED | OMP_REQUIRES_UNIFIED* | OMP_REQUIRES_REV*)
> 
> I think I am lost – don't we effectively test this? We filter out
> everything else in output_offload_tables. Thus, in input_offload_tables,
> a single '==' will do. (We additionally know that TARGET_USED is set -
> as otherwise there wouldn't be a symbol in the offload table.)
> 
> Thus, it is unclear to me what you propose here.

We want to get clarification from omp-lang on what is the intent.
If the TARGET_USED bit is explicit, we can easily tweak the checks.
> 
> > > +static void
> > > +gomp_requires_to_name (char *buf, size_t size, int requires_mask)
> > > +{
> > > +  char *end = buf + size, *p = buf;
> > > +  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
> > > +    p += snprintf (p, end - p, "unified_address");
> > > +  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
> > > +    p += snprintf (p, end - p, "%sunified_shared_memory",
> > > +               (p == buf ? "" : ", "));
> > > +  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
> > > +    p += snprintf (p, end - p, "%sreverse_offload",
> > > +               (p == buf ? "" : ", "));
> > > +}
> > Same question as earlier.
> 
> Same answer, except that in libgomp, this code is effectively only
> reachable when omp_requires_mask != 0 as it reaches this code only if
> either some additional flag was added (in register_ver) or when devices
> were available, but those do not support a flag.

I don't understand.  Won't
z.c:
int v;
void
foo (void)
{
  v++;
}
#pragma omp declare target enter (v, foo)
void
bar (void)
{
  #pragma omp target
  foo ();
}
have omp_requires_mask == 0 (if TARGET_USED isn't explicit) but will
GOMP_offload_register_var?

> > Then under the lock, you can do the merging.
> > But, IMHO the runtime library should repeat what is done in the offloading
> > lto1, diagnose if there are differences between the masks in between
> > different TUs, here at runtime on the program/shared library level.
> > And IMHO the error you emit above is unnecessary, because (at least
> > hopefully) the num_devices computation / device initialization should
> > only happen on behalf of some device construct or device related OpenMP API
> > routine, so at that point the shared library or program that does that
> > should have its own mask and if something is dlopened later, it should
> > either have compatible mask (nothing is diagnosed) or incompatible, but then
> > it should be diagnosed like any other incompatibilities.
> 
> OK – I will diagnose it always.
> 
> Question: If it is not the same, should there just be a message to
> stderr (gomp_error) or should libgomp abort (gomp_fatal)?

I'd say gomp_fatal.
It is an error rather than warning in lto1 too...

> > I thought I've mentioned earlier it would be nice to rename the
> > get_num_devices plugin hook because its API has changed, so that
> > if one mixes old plugin with new libgomp or vice versa it doesn't
> > break silently.
> 
> As discussed off list, gomp_load_plugin_for_device calls     if
> (device->version_func () != GOMP_VERSION) and we did bump the GOMP_VERSION.

Yeah, sorry for that.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 16:55                       ` Jakub Jelinek
@ 2022-07-01 21:08                         ` Tobias Burnus
  2022-07-04  8:31                           ` Jakub Jelinek
  2022-07-07 13:26                           ` Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
  0 siblings, 2 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-01 21:08 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1833 bytes --]

Updated version attached – I hope I got everything right, but I start to
get tired, I am not 100% sure.

On 01.07.22 18:55, Jakub Jelinek wrote:
> Perhaps you're just lucky and the stack contains '\0' there?
Probably.
> Maybe it would be better to simply use different error message for the
> 0 vs. non-0 case,
Done so.
>> For mkoffload, the single results are merged - and TARGET_USE is stripped,
>> such that it is either 0 or a combination of USM/UA/RO
> I'd find it clearer if we never stripped that, so that even the library knows.
I have done so – and I concur that the check works then better in
libgomp as well.
>>> Pedantically reading current standard probably yes, but perhaps again
>>> something to be discussed.  The question is what the requires directive
>>> in that case would do, nothing at all as there are no device constructs
>>> etc.?
>> Isn't there a device construct – which happens to be empty?
> In d.c there is.  But in c.c there isn't.
> So, the question is if the directive in c.c is just completely ignored
> (ok, aside from semantic checking) or if it should mean that if it is
> specified there, it must be specified elsewhere where device constructs etc.
> are used too.

Good question. The current code follows the wording of the spec and
ignores it. I think that's fine but still feels a bit odd.
>> Question: If it is not the same, should there just be a message to
>> stderr (gomp_error) or should libgomp abort (gomp_fatal)?
> I'd say gomp_fatal.
Done so - it makes life easier.

Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Attachment #2: omp-requires-v6.1.diff --]
[-- Type: text/x-patch, Size: 67132 bytes --]

OpenMP: Move omp requires checks to libgomp

Handle reverse_offload, unified_address, and unified_shared_memory
requirements in libgomp by saving them alongside the offload table.
When the device lto1 runs, it extracts the data for mkoffload. The
latter than passes the value on to GOMP_offload_register_ver.

lto1 (either the host one, with -flto [+ ENABLE_OFFLOADING], or in the
offload-device lto1) also does the the consistency check is done,
erroring out when the 'omp requires' clause use is inconsistent.

For all in-principle supported devices, if a requirement cannot be fulfilled,
the device is excluded from the (supported) devices list. Currently, none of
those requirements are marked as supported for any of the non-host devices.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_omp_target_data, c_parser_omp_target_update,
	c_parser_omp_target_enter_data, c_parser_omp_target_exit_data): Set
	OMP_REQUIRES_TARGET_USED.
	(c_parser_omp_requires): Remove sorry.

gcc/ChangeLog:

	* config/gcn/mkoffload.cc (process_asm): Write '#include <stdint.h>'.
	(process_obj): Pass omp_requires_mask to GOMP_offload_register_ver.
	(main): Ask lto1 to obtain omp_requires_mask and pass it on.
	* config/nvptx/mkoffload.cc (process, main): Likewise.
	* lto-cgraph.cc (omp_requires_to_name): New.
	(input_offload_tables): Save omp_requires_mask.
	(output_offload_tables): Read it, check for consistency,
	save value for mkoffload.
	* omp-low.cc (lower_omp_target): Force output_offloadtables
	call for OMP_REQUIRES_TARGET_USED.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_omp_target_data,
	cp_parser_omp_target_enter_data, cp_parser_omp_target_exit_data,
	cp_parser_omp_target_update): Set OMP_REQUIRES_TARGET_USED.
	(cp_parser_omp_requires): Remove sorry.

gcc/fortran/ChangeLog:

	* openmp.cc (gfc_match_omp_requires): Remove sorry.
	* parse.cc (decode_omp_directive): Don't regard 'declare target'
	as target usage for 'omp requires'; add more flags to
	omp_requires_mask.

include/ChangeLog:

	* gomp-constants.h (GOMP_VERSION): Bump to 2.
	(GOMP_REQUIRES_UNIFIED_ADDRESS, GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
	GOMP_REQUIRES_REVERSE_OFFLOAD, GOMP_REQUIRES_TARGET_USED):
	New defines.

libgomp/ChangeLog:

	* libgomp-plugin.h (GOMP_OFFLOAD_get_num_devices): Add
	omp_requires_mask arg.
	* plugin/plugin-gcn.c (GOMP_OFFLOAD_get_num_devices): Likewise;
	return -1 when device available but omp_requires_mask != 0.
	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_get_num_devices): Likewise.
	* oacc-host.c (host_get_num_devices, host_openacc_get_property):
	Update call.
	* oacc-init.c (resolve_device, acc_init_1, acc_shutdown_1,
	goacc_attach_host_thread_to_device, acc_get_num_devices,
	acc_set_device_num, get_property_any): Likewise.
	* target.c (omp_requires_mask): New global var.
	(gomp_requires_to_name): New.
	(GOMP_offload_register_ver): Handle passed omp_requires_mask.
	(gomp_target_init): Handle omp_requires_mask.
	* libgomp.texi (OpenMP 5.0): Update requires impl. status.
	(OpenMP 5.1): Add a missed item.
	(OpenMP 5.2): Mark linear-clause change as supported in C/C++.
	* testsuite/libgomp.c-c++-common/requires-1-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-1.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-2.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-3.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-4.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-5.c: New test.
	* testsuite/libgomp.c-c++-common/requires-6.c: New test.
	* testsuite/libgomp.c-c++-common/requires-7-aux.c: New test.
	* testsuite/libgomp.c-c++-common/requires-7.c: New test.
	* testsuite/libgomp.fortran/requires-1-aux.f90: New test.
	* testsuite/libgomp.fortran/requires-1.f90: New test.

liboffloadmic/ChangeLog:

	* plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_num_devices):
	Return -1 when device available but omp_requires_mask != 0.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/requires-4.c: Update dg-*.
	* c-c++-common/gomp/reverse-offload-1.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-2.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-3.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-4.c: Likewise.
	* c-c++-common/gomp/target-device-ancestor-5.c: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-3.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-4.f90: Likewise.
	* gfortran.dg/gomp/target-device-ancestor-5.f90: Likewise.
        * gfortran.dg/gomp/target-device-ancestor-2.f90: Likewise. Move
	post-FE checks to ...
        * gfortran.dg/gomp/target-device-ancestor-2a.f90: ... this new file.
	* gfortran.dg/gomp/requires-8.f90: Update as we don't regard
	'declare target' for the 'requires' usage requirement.

Co-authored-by: Chung-Lin Tang <cltang@codesourcery.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>

 gcc/c/c-parser.cc                                  |  19 +++-
 gcc/config/gcn/mkoffload.cc                        |  27 ++++-
 gcc/config/nvptx/mkoffload.cc                      |  29 ++++-
 gcc/cp/parser.cc                                   |  19 +++-
 gcc/fortran/openmp.cc                              |   4 -
 gcc/fortran/parse.cc                               |  22 +++-
 gcc/lto-cgraph.cc                                  | 117 ++++++++++++++++++++-
 gcc/omp-low.cc                                     |   5 +
 gcc/testsuite/c-c++-common/gomp/requires-4.c       |   2 -
 .../c-c++-common/gomp/reverse-offload-1.c          |   2 +-
 .../c-c++-common/gomp/target-device-ancestor-2.c   |  10 +-
 .../c-c++-common/gomp/target-device-ancestor-3.c   |   2 +-
 .../c-c++-common/gomp/target-device-ancestor-4.c   |   4 +-
 .../c-c++-common/gomp/target-device-ancestor-5.c   |   2 +-
 gcc/testsuite/gfortran.dg/gomp/requires-8.f90      |  14 ++-
 .../gfortran.dg/gomp/target-device-ancestor-2.f90  |  70 +-----------
 .../gfortran.dg/gomp/target-device-ancestor-2a.f90 |  80 ++++++++++++++
 .../gfortran.dg/gomp/target-device-ancestor-3.f90  |   6 +-
 .../gfortran.dg/gomp/target-device-ancestor-4.f90  |   6 +-
 .../gfortran.dg/gomp/target-device-ancestor-5.f90  |   8 +-
 include/gomp-constants.h                           |   9 +-
 libgomp/libgomp-plugin.h                           |   2 +-
 libgomp/libgomp.texi                               |   8 +-
 libgomp/oacc-host.c                                |   4 +-
 libgomp/oacc-init.c                                |  16 +--
 libgomp/plugin/plugin-gcn.c                        |   6 +-
 libgomp/plugin/plugin-nvptx.c                      |   9 +-
 libgomp/target.c                                   |  76 ++++++++++++-
 .../libgomp.c-c++-common/requires-1-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-1.c    |  24 +++++
 .../libgomp.c-c++-common/requires-2-aux.c          |   9 ++
 .../testsuite/libgomp.c-c++-common/requires-2.c    |  25 +++++
 .../libgomp.c-c++-common/requires-3-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-3.c    |  24 +++++
 .../libgomp.c-c++-common/requires-4-aux.c          |  13 +++
 .../testsuite/libgomp.c-c++-common/requires-4.c    |  23 ++++
 .../libgomp.c-c++-common/requires-5-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-5.c    |  20 ++++
 .../testsuite/libgomp.c-c++-common/requires-6.c    |  17 +++
 .../libgomp.c-c++-common/requires-7-aux.c          |  11 ++
 .../testsuite/libgomp.c-c++-common/requires-7.c    |  24 +++++
 .../testsuite/libgomp.fortran/requires-1-aux.f90   |  14 +++
 libgomp/testsuite/libgomp.fortran/requires-1.f90   |  26 +++++
 liboffloadmic/plugin/libgomp-plugin-intelmic.cpp   |   6 +-
 44 files changed, 715 insertions(+), 132 deletions(-)

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 97e3b23b5d2..9c02141e2c6 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -20915,6 +20915,10 @@ c_parser_omp_teams (location_t loc, c_parser *parser,
 static tree
 c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				"#pragma omp target data");
@@ -21010,6 +21014,10 @@ c_parser_omp_target_update (location_t loc, c_parser *parser,
       return false;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree stmt = make_node (OMP_TARGET_UPDATE);
   TREE_TYPE (stmt) = void_type_node;
   OMP_TARGET_UPDATE_CLAUSES (stmt) = clauses;
@@ -21057,6 +21065,10 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				"#pragma omp target enter data");
@@ -21151,6 +21163,10 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				"#pragma omp target exit data");
@@ -22779,9 +22795,6 @@ c_parser_omp_requires (c_parser *parser)
 	      c_parser_skip_to_pragma_eol (parser, false);
 	      return;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    c_parser_consume_token (parser);
 	  if (this_req)
diff --git a/gcc/config/gcn/mkoffload.cc b/gcc/config/gcn/mkoffload.cc
index ed93ae844e4..b8b3fecfcb4 100644
--- a/gcc/config/gcn/mkoffload.cc
+++ b/gcc/config/gcn/mkoffload.cc
@@ -611,6 +611,7 @@ process_asm (FILE *in, FILE *out, FILE *cfile)
   struct regcount *regcounts = XOBFINISH (&regcounts_os, struct regcount *);
 
   fprintf (cfile, "#include <stdlib.h>\n");
+  fprintf (cfile, "#include <stdint.h>\n");
   fprintf (cfile, "#include <stdbool.h>\n\n");
 
   fprintf (cfile, "static const int gcn_num_vars = %d;\n\n", var_count);
@@ -664,7 +665,7 @@ process_asm (FILE *in, FILE *out, FILE *cfile)
 /* Embed an object file into a C source file.  */
 
 static void
-process_obj (FILE *in, FILE *cfile)
+process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
 {
   size_t len = 0;
   const char *input = read_file (in, &len);
@@ -692,16 +693,18 @@ process_obj (FILE *in, FILE *cfile)
 
   fprintf (cfile,
 	   "static const struct gcn_image_desc {\n"
+	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct gcn_image *gcn_image;\n"
 	   "  unsigned kernel_count;\n"
 	   "  const struct hsa_kernel_description *kernel_infos;\n"
 	   "  unsigned global_variable_count;\n"
 	   "} target_data = {\n"
+	   "  %d,\n"
 	   "  &gcn_image,\n"
 	   "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
 	   "  gcn_kernels,\n"
 	   "  gcn_num_vars\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (cfile,
 	   "#ifdef __cplusplus\n"
@@ -1077,9 +1080,27 @@ main (int argc, char **argv)
       unsetenv ("COMPILER_PATH");
       unsetenv ("LIBRARY_PATH");
 
+      char *omp_requires_file;
+      if (save_temps)
+	omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+      else
+	omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+
       /* Run the compiler pass.  */
+      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
       fork_execute (cc_argv[0], CONST_CAST (char **, cc_argv), true, ".gcc_args");
       obstack_free (&cc_argv_obstack, NULL);
+      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+
+      in = fopen (omp_requires_file, "rb");
+      if (!in)
+	fatal_error (input_location, "cannot open omp_requires file %qs",
+		     omp_requires_file);
+      uint32_t omp_requires;
+      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
+	fatal_error (input_location, "cannot read omp_requires file %qs",
+		     omp_requires_file);
+      fclose (in);
 
       in = fopen (gcn_s1_name, "r");
       if (!in)
@@ -1102,7 +1123,7 @@ main (int argc, char **argv)
       if (!in)
 	fatal_error (input_location, "cannot open intermediate gcn obj file");
 
-      process_obj (in, cfile);
+      process_obj (in, cfile, omp_requires);
 
       fclose (in);
 
diff --git a/gcc/config/nvptx/mkoffload.cc b/gcc/config/nvptx/mkoffload.cc
index b28c1a32292..d8c81eb0547 100644
--- a/gcc/config/nvptx/mkoffload.cc
+++ b/gcc/config/nvptx/mkoffload.cc
@@ -231,7 +231,7 @@ access_check (const char *name, int mode)
 }
 
 static void
-process (FILE *in, FILE *out)
+process (FILE *in, FILE *out, uint32_t omp_requires)
 {
   size_t len = 0;
   const char *input = read_file (in, &len);
@@ -240,6 +240,8 @@ process (FILE *in, FILE *out)
   unsigned obj_count = 0;
   unsigned ix;
 
+  fprintf (out, "#include <stdint.h>\n\n");
+
   /* Dump out char arrays for each PTX object file.  These are
      terminated by a NUL.  */
   for (size_t i = 0; i != len;)
@@ -309,6 +311,7 @@ process (FILE *in, FILE *out)
 
   fprintf (out,
 	   "static const struct nvptx_tdata {\n"
+	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct ptx_obj *ptx_objs;\n"
 	   "  unsigned ptx_num;\n"
 	   "  const char *const *var_names;\n"
@@ -316,12 +319,12 @@ process (FILE *in, FILE *out)
 	   "  const struct nvptx_fn *fn_names;\n"
 	   "  unsigned fn_num;\n"
 	   "} target_data = {\n"
-	   "  ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
+	   "  %d, ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
 	   "  var_mappings,"
 	   "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
 	   "  func_mappings,"
 	   "  sizeof (func_mappings) / sizeof (func_mappings[0])\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (out, "#ifdef __cplusplus\n"
 	   "extern \"C\" {\n"
@@ -583,19 +586,37 @@ main (int argc, char **argv)
       unsetenv ("COMPILER_PATH");
       unsetenv ("LIBRARY_PATH");
 
+      char *omp_requires_file;
+      if (save_temps)
+	omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+      else
+	omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+
+      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
       fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true,
 		    ".gcc_args");
       obstack_free (&argv_obstack, NULL);
+      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
 
       xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
       xputenv (concat ("COMPILER_PATH=", cpath, NULL));
       xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
 
+      in = fopen (omp_requires_file, "rb");
+      if (!in)
+	fatal_error (input_location, "cannot open omp_requires file %qs",
+		     omp_requires_file);
+      uint32_t omp_requires;
+      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
+	fatal_error (input_location, "cannot read omp_requires file %qs",
+		     omp_requires_file);
+      fclose (in);
+
       in = fopen (ptx_name, "r");
       if (!in)
 	fatal_error (input_location, "cannot open intermediate ptx file");
 
-      process (in, out);
+      process (in, out, omp_requires);
       fclose (in);
     }
 
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 212ed1445d5..74af165e7d8 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -44327,6 +44327,10 @@ cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok,
 static tree
 cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 {
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
 				 "#pragma omp target data", pragma_tok);
@@ -44430,6 +44434,10 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
 				 "#pragma omp target enter data", pragma_tok);
@@ -44529,6 +44537,10 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
 				 "#pragma omp target exit data", pragma_tok);
@@ -44623,6 +44635,10 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
       return true;
     }
 
+  if (flag_openmp)
+    omp_requires_mask
+      = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED);
+
   tree stmt = make_node (OMP_TARGET_UPDATE);
   TREE_TYPE (stmt) = void_type_node;
   OMP_TARGET_UPDATE_CLAUSES (stmt) = clauses;
@@ -46917,9 +46933,6 @@ cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok)
 	      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	      return false;
 	    }
-	  if (p && this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-	    sorry_at (cloc, "%qs clause on %<requires%> directive not "
-			    "supported yet", p);
 	  if (p)
 	    cp_lexer_consume_token (parser->lexer);
 	  if (this_req)
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 93e40f25f82..51b429a597c 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -5488,10 +5488,6 @@ gfc_match_omp_requires (void)
       else
 	goto error;
 
-      if (requires_clause & ~(OMP_REQ_ATOMIC_MEM_ORDER_MASK
-			      | OMP_REQ_DYNAMIC_ALLOCATORS))
-	gfc_error_now ("Sorry, %qs clause at %L on REQUIRES directive is not "
-		       "yet supported", clause, &old_loc);
       if (!gfc_omp_requires_add_clause (requires_clause, clause, &old_loc, NULL))
 	goto error;
       requires_clauses |= requires_clause;
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index 7356d1b5a3a..0b4c596996c 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -1168,7 +1168,8 @@ decode_omp_directive (void)
     }
   switch (ret)
     {
-    case ST_OMP_DECLARE_TARGET:
+    /* Set omp_target_seen; exclude ST_OMP_DECLARE_TARGET.
+       FIXME: Get clarification, cf. OpenMP Spec Issue #3240.  */
     case ST_OMP_TARGET:
     case ST_OMP_TARGET_DATA:
     case ST_OMP_TARGET_ENTER_DATA:
@@ -6879,11 +6880,14 @@ done:
 
   /* Fixup for external procedures and resolve 'omp requires'.  */
   int omp_requires;
+  bool omp_target_seen;
   omp_requires = 0;
+  omp_target_seen = false;
   for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
        gfc_current_ns = gfc_current_ns->sibling)
     {
       omp_requires |= gfc_current_ns->omp_requires;
+      omp_target_seen |= gfc_current_ns->omp_target_seen;
       gfc_check_externals (gfc_current_ns);
     }
   for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
@@ -6908,6 +6912,22 @@ done:
       break;
     }
 
+  if (omp_target_seen)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_TARGET_USED);
+  if (omp_requires & OMP_REQ_REVERSE_OFFLOAD)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_REVERSE_OFFLOAD);
+  if (omp_requires & OMP_REQ_UNIFIED_ADDRESS)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_UNIFIED_ADDRESS);
+  if (omp_requires & OMP_REQ_UNIFIED_SHARED_MEMORY)
+    omp_requires_mask
+	  = (enum omp_requires) (omp_requires_mask
+				 | OMP_REQUIRES_UNIFIED_SHARED_MEMORY);
+  if (omp_requires & OMP_REQ_DYNAMIC_ALLOCATORS)
+    omp_requires_mask = (enum omp_requires) (omp_requires_mask
+					     | OMP_REQUIRES_DYNAMIC_ALLOCATORS);
   /* Do the parse tree dump.  */
   gfc_current_ns = flag_dump_fortran_original ? gfc_global_ns_list : NULL;
 
diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 237743ef0ba..48629651e31 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pass_manager.h"
 #include "ipa-utils.h"
 #include "omp-offload.h"
+#include "omp-general.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "alloc-pool.h"
@@ -1068,7 +1069,10 @@ read_string (class lto_input_block *ib)
 void
 output_offload_tables (void)
 {
-  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars))
+  bool output_requires = (flag_openmp
+			  && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0);
+  if (vec_safe_is_empty (offload_funcs) && vec_safe_is_empty (offload_vars)
+      && !output_requires)
     return;
 
   struct lto_simple_output_block *ob
@@ -1098,6 +1102,19 @@ output_offload_tables (void)
 			       (*offload_vars)[i]);
     }
 
+  if (output_requires)
+    {
+      HOST_WIDE_INT val = ((HOST_WIDE_INT) omp_requires_mask
+			   & (OMP_REQUIRES_UNIFIED_ADDRESS
+			      | OMP_REQUIRES_UNIFIED_SHARED_MEMORY
+			      | OMP_REQUIRES_REVERSE_OFFLOAD
+			      | OMP_REQUIRES_TARGET_USED));
+      /* (Mis)use LTO_symtab_edge for this variable.  */
+      streamer_write_enum (ob->main_stream, LTO_symtab_tags,
+			   LTO_symtab_last_tag, LTO_symtab_edge);
+      streamer_write_hwi_stream (ob->main_stream, val);
+    }
+
   streamer_write_uhwi_stream (ob->main_stream, 0);
   lto_destroy_simple_output_block (ob);
 
@@ -1764,6 +1781,20 @@ input_symtab (void)
     }
 }
 
+static void
+omp_requires_to_name (char *buf, size_t size, HOST_WIDE_INT requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* Input function/variable tables that will allow libgomp to look up offload
    target code, and store them into OFFLOAD_FUNCS and OFFLOAD_VARS.  */
 
@@ -1773,6 +1804,10 @@ input_offload_tables (bool do_force_output)
   struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
   struct lto_file_decl_data *file_data;
   unsigned int j = 0;
+  const char *requires_fn = NULL;
+  tree requires_decl = NULL_TREE;
+
+  omp_requires_mask = (omp_requires) 0;
 
   while ((file_data = file_data_vec[j++]))
     {
@@ -1784,6 +1819,7 @@ input_offload_tables (bool do_force_output)
       if (!ib)
 	continue;
 
+      tree tmp_decl = NULL_TREE;
       enum LTO_symtab_tags tag
 	= streamer_read_enum (ib, LTO_symtab_tags, LTO_symtab_last_tag);
       while (tag)
@@ -1799,6 +1835,7 @@ input_offload_tables (bool do_force_output)
 		 LTO mode.  */
 	      if (do_force_output)
 		cgraph_node::get (fn_decl)->mark_force_output ();
+	      tmp_decl = fn_decl;
 	    }
 	  else if (tag == LTO_symtab_variable)
 	    {
@@ -1810,6 +1847,72 @@ input_offload_tables (bool do_force_output)
 		 may be no refs to var_decl in offload LTO mode.  */
 	      if (do_force_output)
 		varpool_node::get (var_decl)->force_output = 1;
+	      tmp_decl = var_decl;
+	    }
+	  else if (tag == LTO_symtab_edge)
+	    {
+	      static bool error_emitted = false;
+	      HOST_WIDE_INT val = streamer_read_hwi (ib);
+
+	      if (omp_requires_mask == 0)
+		{
+		  omp_requires_mask = (omp_requires) val;
+		  requires_decl = tmp_decl;
+		  requires_fn = file_data->file_name;
+		}
+	      else if (omp_requires_mask != val && !error_emitted)
+		{
+		  const char *fn1 = requires_fn;
+		  if (requires_decl != NULL_TREE)
+		    {
+		      while (DECL_CONTEXT (requires_decl) != NULL_TREE
+			     && TREE_CODE (requires_decl) != TRANSLATION_UNIT_DECL)
+			requires_decl = DECL_CONTEXT (requires_decl);
+		      if (requires_decl != NULL_TREE)
+			fn1 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
+		    }
+
+		  const char *fn2 = file_data->file_name;
+		  if (tmp_decl != NULL_TREE)
+		    {
+		      while (DECL_CONTEXT (tmp_decl) != NULL_TREE
+			     && TREE_CODE (tmp_decl) != TRANSLATION_UNIT_DECL)
+			tmp_decl = DECL_CONTEXT (tmp_decl);
+		      if (tmp_decl != NULL_TREE)
+			fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
+		    }
+
+		  char buf1[sizeof ("unified_address, unified_shared_memory, "
+				    "reverse_offload")];
+		  char buf2[sizeof ("unified_address, unified_shared_memory, "
+				    "reverse_offload")];
+		  omp_requires_to_name (buf2, sizeof (buf2),
+					val != OMP_REQUIRES_TARGET_USED
+					? val
+					: (HOST_WIDE_INT) omp_requires_mask);
+		  if (val != OMP_REQUIRES_TARGET_USED
+		      && omp_requires_mask != OMP_REQUIRES_TARGET_USED)
+		    {
+		      omp_requires_to_name (buf1, sizeof (buf1),
+					    omp_requires_mask);
+		      error ("OpenMP %<requires%> directive with non-identical "
+			     "clauses in multiple compilation units: %qs vs. "
+			     "%qs", buf1, buf2);
+		      inform (UNKNOWN_LOCATION, "%qs has %qs", fn1, buf1);
+		      inform (UNKNOWN_LOCATION, "%qs has %qs", fn2, buf2);
+		    }
+		  else
+		    {
+		      error ("OpenMP %<requires%> directive with %qs specified "
+			     "only in some compilation units", buf2);
+		      inform (UNKNOWN_LOCATION, "%qs has %qs",
+			      val != OMP_REQUIRES_TARGET_USED ? fn2 : fn1,
+			      buf2);
+		      inform (UNKNOWN_LOCATION, "but %qs has not",
+			      val != OMP_REQUIRES_TARGET_USED ? fn1 : fn2);
+		    }
+		  error_emitted = true;
+		}
 	    }
 	  else
 	    fatal_error (input_location,
@@ -1821,6 +1924,18 @@ input_offload_tables (bool do_force_output)
       lto_destroy_simple_input_block (file_data, LTO_section_offload_table,
 				      ib, data, len);
     }
+#ifdef ACCEL_COMPILER
+  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
+    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");
+  FILE *f = fopen (omp_requires_file, "wb");
+  if (!f)
+    fatal_error (input_location, "Cannot open omp_requires file %qs",
+		 omp_requires_file);
+  uint32_t req_mask = omp_requires_mask;
+  fwrite (&req_mask, sizeof (req_mask), 1, f);
+  fclose (f);
+#endif
 }
 
 /* True when we need optimization summary for NODE.  */
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index b9d5529f212..d73c165f029 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -12701,6 +12701,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
       gcc_unreachable ();
     }
 
+  /* Ensure that requires map is written via output_offload_tables, even if only
+     'target (enter/exit) data' is used in the translation unit.  */
+  if (ENABLE_OFFLOADING && (omp_requires_mask & OMP_REQUIRES_TARGET_USED))
+    g->have_offload = true;
+
   clauses = gimple_omp_target_clauses (stmt);
 
   gimple_seq dep_ilist = NULL;
diff --git a/gcc/testsuite/c-c++-common/gomp/requires-4.c b/gcc/testsuite/c-c++-common/gomp/requires-4.c
index 88ba7746cf8..8f45d83ea6e 100644
--- a/gcc/testsuite/c-c++-common/gomp/requires-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/requires-4.c
@@ -9,5 +9,3 @@ foo (void)
 #pragma omp requires unified_shared_memory	/* { dg-error "'unified_shared_memory' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires unified_address	/* { dg-error "'unified_address' clause used lexically after first target construct or offloading API" } */
 #pragma omp requires reverse_offload	/* { dg-error "'reverse_offload' clause used lexically after first target construct or offloading API" } */
-
-/* { dg-prune-output "not supported yet" } */
diff --git a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
index 9a3fa5230f8..3452156f948 100644
--- a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c
@@ -43,7 +43,7 @@ tg_fn (int *x, int *y)
   x2 = x2 + 2 + called_in_target1 ();
   y2 = y2 + 7;
 
-  #pragma omp target device(ancestor : 1) map(tofrom: x2)
+  #pragma omp target device(ancestor : 1) map(tofrom: x2)  /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
     check_offload(&x2, &y2);
 
   if (x2 != 2+2+3+42 || y2 != 3 + 7)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
index cf05c505004..b16e701bd5a 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-2.c
@@ -1,13 +1,11 @@
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (int n)
 {
-  /* The following test is marked with 'xfail' because a previous 'sorry' from
-     'reverse_offload' suppresses the 'sorry' for 'ancestor'.  */
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1)
   ;
 
 
@@ -19,9 +17,9 @@ foo (int n)
   #pragma omp target device (ancestor : 42) /* { dg-error "the 'device' clause expression must evaluate to '1'" } */
   ;
 
-  #pragma omp target device (ancestor : n) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n)
   ;
-  #pragma omp target device (ancestor : n + 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor : n + 1)
   ;
 
 
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
index ea6e5a0cf6c..d16590107d2 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-3.c
@@ -11,7 +11,7 @@ int bar (void);
 
 /* { dg-do compile } */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
index b4b5620bbc0..241234f8daf 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-4.c
@@ -4,12 +4,12 @@
   /* Test to ensure that device-modifier 'ancestor' is parsed correctly in
      device clauses. */
 
-#pragma omp requires reverse_offload /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo (void)
 {
-  #pragma omp target device (ancestor: 1) /* { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } } */
+  #pragma omp target device (ancestor: 1) /* { dg-message "sorry, unimplemented: 'ancestor' not yet supported" } */
   ;
 
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
index b6ff84bcdab..b1520ff0636 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-ancestor-5.c
@@ -1,4 +1,4 @@
-#pragma omp requires reverse_offload  /* { dg-message "sorry, unimplemented: 'reverse_offload' clause on 'requires' directive not supported yet" } */
+#pragma omp requires reverse_offload
 
 void
 foo ()
diff --git a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90 b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
index e84d609ad29..583c5a56b32 100644
--- a/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/requires-8.f90
@@ -1,3 +1,7 @@
+module m0
+  integer :: x
+end module m0
+
 module m  !  { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES UNIFIED_SHARED_MEMORY but other program units do" }
   !$omp requires reverse_offload
 contains
@@ -13,10 +17,14 @@ contains
  end subroutine foo
 end module m
 
-subroutine bar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
+subroutine bar
   !use m
-  !$omp requires unified_shared_memory
+  !$omp requires unified_shared_memory  ! Possibly OK - needs OpenMP Lang Spec clarification (-> #3240)
   !$omp declare target
 end subroutine bar
 
-! { dg-prune-output "not yet supported" }
+subroutine foobar  ! { dg-error "has OpenMP device constructs/routines but does not set !.OMP REQUIRES REVERSE_OFFLOAD but other program units do" }
+  use m0
+  !$omp requires unified_shared_memory
+  !$omp target enter data map(to:x)
+end subroutine foobar
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
index 117a1d000a5..230c690d84c 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2.f90
@@ -4,19 +4,16 @@ implicit none
 
 integer :: a, b, c
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
 
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor: 1)
 !$omp end target
 
-!$omp target device (ancestor : a)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a)
 !$omp end target
 
-!$omp target device (ancestor : a + 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : a + 1)
 !$omp end target
 
 
@@ -32,61 +29,4 @@ integer :: a, b, c
 !$omp target device (42)
 !$omp end target
 
-
-! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target device (ancestor: 1)
-  !$omp teams  ! { dg-error "" "OpenMP constructs are not allowed in target region with 'ancestor'" { xfail *-*-* } }
-  !$omp end teams
-!$omp end target
-
-!$omp target device (device_num: 1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-!$omp target device (1)
-  !$omp teams
-  !$omp end teams
-!$omp end target
-
-
-! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
-! 'defaultmap', and 'map' clauses appear on the construct.
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target nowait device (ancestor: 1)  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target device (ancestor: 1) nowait  ! { dg-error "" "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" { xfail *-*-* } }
-!$omp end target
-
-!$omp target nowait device (device_num: 1)
-!$omp end target
-
-!$omp target nowait device (1)
-!$omp end target
-
-!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
-!$omp end target
-
-
-! Ensure that 'ancestor' is only used with 'target' constructs (not with
-! 'target data', 'target update' etc.).
-! The following test case is marked with 'xfail' because a previous 'sorry' from
-! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
-
-!$omp target data map (a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp end target data
-
-!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "" "'device' clause with 'ancestor' is only allowed on 'target' construct" { xfail *-*-* } }
-
-!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" "" { xfail *-*-* } }
-! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { xfail *-*-* } .-1 }
-
-
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
new file mode 100644
index 00000000000..feb76fe2144
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-2a.f90
@@ -0,0 +1,80 @@
+! { dg-do compile }
+
+implicit none
+
+integer :: a, b, c
+
+!$omp requires reverse_offload
+
+!$omp target device (ancestor: 1)
+!$omp end target
+
+!$omp target device (ancestor : a)
+!$omp end target
+
+!$omp target device (ancestor : a + 1)
+!$omp end target
+
+
+!$omp target device (device_num:42)
+!$omp end target
+
+!$omp target device (42)
+!$omp end target
+
+
+! Ensure that no OpenMP constructs appear inside target regions with 'ancestor'.
+
+!$omp target device (ancestor: 1)
+  !$omp teams  ! { dg-error "OpenMP constructs are not allowed in target region with 'ancestor'" }
+  !$omp end teams
+!$omp end target
+
+!$omp target device (device_num: 1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+!$omp target device (1)
+  !$omp teams
+  !$omp end teams
+!$omp end target
+
+
+! Ensure that with 'ancestor' only the 'device', 'firstprivate', 'private',
+! 'defaultmap', and 'map' clauses appear on the construct.
+
+!$omp target nowait device (ancestor: 1)  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target device (ancestor: 1) nowait  ! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" }
+!$omp end target
+
+!$omp target nowait device (device_num: 1)
+!$omp end target
+
+!$omp target nowait device (1)
+!$omp end target
+
+!$omp target device (ancestor: 1) firstprivate (a) private (b) defaultmap (none) map (c)
+!$omp end target
+
+
+! Ensure that 'ancestor' is only used with 'target' constructs (not with
+! 'target data', 'target update' etc.).
+! The following test case is marked with 'xfail' because a previous 'sorry' from
+! 'reverse_offload' suppresses the 'sorry' for 'ancestor'.
+
+!$omp target data map (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp end target data
+
+!$omp target enter data map (to: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+!$omp target exit data map (from: a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target update to (a) device (ancestor: 1)  ! { dg-error "'device' clause with 'ancestor' is only allowed on 'target' construct" }
+
+!$omp target device (ancestor: 1) if(.false.)
+! { dg-error "with 'ancestor', only the 'device', 'firstprivate', 'private', 'defaultmap', and 'map' clauses may appear on the construct" "" { target *-*-* } .-1 }
+!$omp end target
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
index f1145bde2ec..e8975e6a08b 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-3.f90
@@ -16,10 +16,10 @@ subroutine f1 ()
   implicit none
   integer :: n
 
-  !$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 
   !$omp target device (ancestor : 1)
-    n = omp_get_thread_num ()  ! { dg-error "" "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" { xfail *-*-* } }
+    n = omp_get_thread_num ()  ! { dg-error "OpenMP runtime API call 'omp_get_thread_num' in a region with 'device\\(ancestor\\)' clause" }
   !$omp end target
 
   !$omp target device (device_num : 1)
@@ -30,4 +30,4 @@ subroutine f1 ()
     n = omp_get_thread_num ()
   !$omp end target
 
-end
\ No newline at end of file
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
index 63872fa51fb..ab56e2d1d52 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-4.f90
@@ -4,11 +4,11 @@
 ! Test to ensure that device-modifier 'ancestor' is parsed correctly in
 ! device clauses.
 
-!$omp requires reverse_offload  ! { dg-error "Sorry, 'reverse_offload' clause at \\(1\\) on REQUIRES directive is not yet supported" }
+!$omp requires reverse_offload
 
-!$omp target device (ancestor : 1)  ! { dg-message "" "sorry, unimplemented: 'ancestor' not yet supported" { xfail *-*-* } }
+!$omp target device (ancestor : 1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
 !$omp end target
 
 end
 
-! TODO: dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "pragma omp target \[^\n\r)]*device\\(ancestor:1\\)" 1 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90 b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
index 06a11eb5092..ca8d4b282a0 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-ancestor-5.f90
@@ -6,7 +6,7 @@
 !
 
 module m
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
@@ -17,7 +17,7 @@ contains
     block
       block
         block
-          !$omp target device(ancestor:1)
+          !$omp target device(ancestor:1)  ! { dg-message "sorry, unimplemented: 'ancestor' not yet supported" }
           !$omp end target
         end block
       end block
@@ -26,7 +26,7 @@ contains
 end module m
 
 subroutine foo()
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
   block
     block
       block
@@ -49,7 +49,7 @@ contains
 end subroutine foo
 
 program main
-  !$omp requires reverse_offload  ! { dg-error "REQUIRES directive is not yet supported" }
+  !$omp requires reverse_offload
 contains
   subroutine foo()
     !$omp target device(ancestor:1)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index e4dd8ef3e1d..3e3078f082e 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -282,7 +282,7 @@ enum gomp_map_kind
 /* Versions of libgomp and device-specific plugins.  GOMP_VERSION
    should be incremented whenever an ABI-incompatible change is introduced
    to the plugin interface defined in libgomp/libgomp.h.  */
-#define GOMP_VERSION	1
+#define GOMP_VERSION	2
 #define GOMP_VERSION_NVIDIA_PTX 1
 #define GOMP_VERSION_INTEL_MIC 0
 #define GOMP_VERSION_GCN 2
@@ -341,6 +341,13 @@ enum gomp_map_kind
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 #define GOMP_DEPEND_INOUTSET		5
 
+/* Flag values for requires-directive features, must match corresponding
+   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
+#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
+#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
+#define GOMP_REQUIRES_TARGET_USED           0x200
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 07ab700b80c..ab3ed638475 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -125,7 +125,7 @@ extern void GOMP_PLUGIN_fatal (const char *, ...)
 extern const char *GOMP_OFFLOAD_get_name (void);
 extern unsigned int GOMP_OFFLOAD_get_caps (void);
 extern int GOMP_OFFLOAD_get_type (void);
-extern int GOMP_OFFLOAD_get_num_devices (void);
+extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
 extern bool GOMP_OFFLOAD_init_device (int);
 extern bool GOMP_OFFLOAD_fini_device (int);
 extern unsigned GOMP_OFFLOAD_version (void);
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index a75cd244a83..39426ff7fbf 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -189,8 +189,8 @@ The OpenMP 4.5 specification is fully supported.
       env variable @tab Y @tab
 @item Nested-parallel changes to @emph{max-active-levels-var} ICV @tab Y @tab
 @item @code{requires} directive @tab P
-      @tab Only fulfillable requirement are @code{atomic_default_mem_order}
-      and @code{dynamic_allocators}
+      @tab complete but no non-host devices provides @code{unified_address},
+      @code{unified_shared_memory} or @code{reverse_offload}
 @item @code{teams} construct outside an enclosing target region @tab Y @tab
 @item Non-rectangular loop nests @tab Y @tab
 @item @code{!=} as relational-op in canonical loop form for C/C++ @tab Y @tab
@@ -344,6 +344,8 @@ The OpenMP 4.5 specification is fully supported.
 @item @code{unconstrained} and @code{reproducible} modifiers on @code{order}
       clause @tab Y @tab
 @item Support @code{begin/end declare target} syntax in C/C++ @tab N @tab
+@item Pointer predetermined firstprivate getting initialized
+to address of matching mapped list item per 5.1, Sect. 2.21.7.2 @tab N @tab
 @end multitable
 
 
@@ -361,7 +363,7 @@ The OpenMP 4.5 specification is fully supported.
 @item Clauses on @code{end} directive can be on directive @tab N @tab
 @item Deprecation of no-argument @code{destroy} clause on @code{depobj}
       @tab N @tab
-@item @code{linear} clause syntax changes and @code{step} modifier @tab N @tab
+@item @code{linear} clause syntax changes and @code{step} modifier @tab P @tab only C/C++
 @item Deprecation of minus operator for reductions @tab N @tab
 @item Deprecation of separating @code{map} modifiers without comma @tab N @tab
 @item @code{declare mapper} with iterator and @code{present} modifiers
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index 5bb889926d3..eb11b9cf16a 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -54,7 +54,7 @@ host_get_type (void)
 }
 
 static int
-host_get_num_devices (void)
+host_get_num_devices (unsigned int omp_requires_mask __attribute__((unused)))
 {
   return 1;
 }
@@ -229,7 +229,7 @@ host_openacc_get_property (int n, enum goacc_property prop)
 {
   union goacc_property_value nullval = { .val = 0 };
 
-  if (n >= host_get_num_devices ())
+  if (n >= host_get_num_devices (0))
     return nullval;
 
   switch (prop)
diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c
index 1565aa0f290..42c3e74e6ba 100644
--- a/libgomp/oacc-init.c
+++ b/libgomp/oacc-init.c
@@ -148,7 +148,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
 	      if (dispatchers[d]
 		  && !strcasecmp (goacc_device_type,
 				  get_openacc_name (dispatchers[d]->name))
-		  && dispatchers[d]->get_num_devices_func () > 0)
+		  && dispatchers[d]->get_num_devices_func (0) > 0)
 		goto found;
 
 	    if (fail_is_error)
@@ -169,7 +169,7 @@ resolve_device (acc_device_t d, bool fail_is_error)
     case acc_device_not_host:
       /* Find the first available device after acc_device_not_host.  */
       while (known_device_type_p (++d))
-	if (dispatchers[d] && dispatchers[d]->get_num_devices_func () > 0)
+	if (dispatchers[d] && dispatchers[d]->get_num_devices_func (0) > 0)
 	  goto found;
       if (d_arg == acc_device_default)
 	{
@@ -302,7 +302,7 @@ acc_init_1 (acc_device_t d, acc_construct_t parent_construct, int implicit)
 
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   if (ndevs <= 0 || goacc_device_num >= ndevs)
     acc_dev_num_out_of_range (d, goacc_device_num, ndevs);
@@ -351,7 +351,7 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
-  ndevs = base_dev->get_num_devices_func ();
+  ndevs = base_dev->get_num_devices_func (0);
 
   /* Unload all the devices of this type that have been opened.  */
   for (i = 0; i < ndevs; i++)
@@ -520,7 +520,7 @@ goacc_attach_host_thread_to_device (int ord)
       base_dev = cached_base_dev;
     }
   
-  num_devices = base_dev->get_num_devices_func ();
+  num_devices = base_dev->get_num_devices_func (0);
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (acc_device_type (base_dev->type), ord,
 			      num_devices);
@@ -599,7 +599,7 @@ acc_get_num_devices (acc_device_t d)
   if (!acc_dev)
     return 0;
 
-  n = acc_dev->get_num_devices_func ();
+  n = acc_dev->get_num_devices_func (0);
   if (n < 0)
     n = 0;
 
@@ -779,7 +779,7 @@ acc_set_device_num (int ord, acc_device_t d)
 
       cached_base_dev = base_dev = resolve_device (d, true);
 
-      num_devices = base_dev->get_num_devices_func ();
+      num_devices = base_dev->get_num_devices_func (0);
 
       if (num_devices <= 0 || ord >= num_devices)
         acc_dev_num_out_of_range (d, ord, num_devices);
@@ -814,7 +814,7 @@ get_property_any (int ord, acc_device_t d, acc_device_property_t prop)
 
   struct gomp_device_descr *dev = resolve_device (d, true);
 
-  int num_devices = dev->get_num_devices_func ();
+  int num_devices = dev->get_num_devices_func (0);
 
   if (num_devices <= 0 || ord >= num_devices)
     acc_dev_num_out_of_range (d, ord, num_devices);
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 1c0436842da..ea327bf2ca0 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -3221,10 +3221,14 @@ GOMP_OFFLOAD_version (void)
 /* Return the number of GCN devices on the system.  */
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
   if (!init_hsa_context ())
     return 0;
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
+    return -1;
   return hsa_context.agent_count;
 }
 
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 387bcbbc52a..bc63e274cdf 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1175,9 +1175,14 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
-  return nvptx_get_num_devices ();
+  int num_devices = nvptx_get_num_devices ();
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
+  return num_devices;
 }
 
 bool
diff --git a/libgomp/target.c b/libgomp/target.c
index c0844f2265a..766e487fce9 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -36,6 +36,7 @@
 # include <inttypes.h>  /* For PRIu64.  */
 #endif
 #include <string.h>
+#include <stdio.h>  /* For snprintf. */
 #include <assert.h>
 #include <errno.h>
 
@@ -98,6 +99,9 @@ static int num_devices;
 /* Number of GOMP_OFFLOAD_CAP_OPENMP_400 devices.  */
 static int num_devices_openmp;
 
+/* OpenMP requires mask.  */
+static int omp_requires_mask;
+
 /* Similar to gomp_realloc, but release register_lock before gomp_fatal.  */
 
 static void *
@@ -2314,6 +2318,20 @@ gomp_unload_image_from_device (struct gomp_device_descr *devicep,
     }
 }
 
+static void
+gomp_requires_to_name (char *buf, size_t size, int requires_mask)
+{
+  char *end = buf + size, *p = buf;
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_ADDRESS)
+    p += snprintf (p, end - p, "unified_address");
+  if (requires_mask & GOMP_REQUIRES_UNIFIED_SHARED_MEMORY)
+    p += snprintf (p, end - p, "%sunified_shared_memory",
+		   (p == buf ? "" : ", "));
+  if (requires_mask & GOMP_REQUIRES_REVERSE_OFFLOAD)
+    p += snprintf (p, end - p, "%sreverse_offload",
+		   (p == buf ? "" : ", "));
+}
+
 /* This function should be called from every offload image while loading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
    the target, and TARGET_DATA needed by target plugin.  */
@@ -2323,13 +2341,43 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
 			   int target_type, const void *target_data)
 {
   int i;
+  int omp_req = 0;
 
   if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
     gomp_fatal ("Library too old for offload (version %u < %u)",
 		GOMP_VERSION, GOMP_VERSION_LIB (version));
-  
+
+  if (GOMP_VERSION_LIB (version) > 1)
+    {
+      omp_req = (int) (size_t) ((void **) target_data)[0];
+      target_data = &((void **) target_data)[1];
+    }
+
   gomp_mutex_lock (&register_lock);
 
+  if (omp_requires_mask && omp_requires_mask != omp_req)
+    {
+      char buf1[sizeof ("unified_address, unified_shared_memory, "
+			"reverse_offload")];
+      char buf2[sizeof ("unified_address, unified_shared_memory, "
+			"reverse_offload")];
+      gomp_requires_to_name (buf2, sizeof (buf2),
+			     omp_req != GOMP_REQUIRES_TARGET_USED
+			     ? omp_req : omp_requires_mask);
+      if (omp_req != GOMP_REQUIRES_TARGET_USED
+	  && omp_requires_mask != GOMP_REQUIRES_TARGET_USED)
+	{
+	  gomp_requires_to_name (buf1, sizeof (buf1), omp_requires_mask);
+	  gomp_fatal ("OpenMP 'requires' directive with non-identical clauses "
+		      "in multiple compilation units: '%s' vs. '%s'",
+		      buf1, buf2);
+	}
+      else
+	gomp_fatal ("OpenMP 'requires' directive with '%s' specified only in "
+		    "some compilation units", buf2);
+    }
+  omp_requires_mask = omp_req;
+
   /* Load image to all initialized devices.  */
   for (i = 0; i < num_devices; i++)
     {
@@ -4125,8 +4173,30 @@ gomp_target_init (void)
 
 	if (gomp_load_plugin_for_device (&current_device, plugin_name))
 	  {
-	    new_num_devs = current_device.get_num_devices_func ();
-	    if (new_num_devs >= 1)
+	    int omp_req = omp_requires_mask & ~GOMP_REQUIRES_TARGET_USED;
+	    new_num_devs = current_device.get_num_devices_func (omp_req);
+	    if (new_num_devs < 0)
+	      {
+		bool found = false;
+		int type = current_device.get_type_func ();
+		for (int img = 0; img < num_offload_images; img++)
+		  if (type == offload_images[img].type)
+		    found = true;
+		if (found)
+		  {
+		    char buf[sizeof ("unified_address, unified_shared_memory, "
+				     "reverse_offload")];
+		    gomp_requires_to_name (buf, sizeof (buf), omp_req);
+		    char *name = (char *) malloc (cur_len + 1);
+		    memcpy (name, cur, cur_len);
+		    name[cur_len] = '\0';
+		    GOMP_PLUGIN_error ("note: %s devices present but 'omp "
+				       "requires %s' cannot be fulfilled",
+				       name, buf);
+		    free (name);
+		  }
+	      }
+	    else if (new_num_devs >= 1)
 	      {
 		/* Augment DEVICES and NUM_DEVICES.  */
 
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
new file mode 100644
index 00000000000..fedf9779769
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-1-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
new file mode 100644
index 00000000000..617577448ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2-aux.c
@@ -0,0 +1,9 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
new file mode 100644
index 00000000000..be1830d0c46
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -0,0 +1,25 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-foffload=disable -flto" } */
+/* { dg-additional-sources requires-2-aux.c } */
+
+/* Check diagnostic by host's lto1.
+   Other file does not have any 'omp requires'. */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with 'unified_shared_memory' specified only in some compilation units" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
new file mode 100644
index 00000000000..bdca662e42f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
new file mode 100644
index 00000000000..4b07ffdd09b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-sources requires-3-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
new file mode 100644
index 00000000000..b8b51ae8ca7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
@@ -0,0 +1,13 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires reverse_offload
+
+/* Note: The file does not have neither of:
+   declare target directives, device constructs or device routines.  */
+
+int x;
+
+void foo (void)
+{
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
new file mode 100644
index 00000000000..128fdbb8463
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
@@ -0,0 +1,23 @@
+/* { dg-do link { target offloading_enabled } } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources requires-4-aux.c } */
+
+/* Check diagnostic by device-compiler's or host compiler's lto1.
+   Other file uses: 'requires reverse_offload', but that's inactive as
+   there are no declare target directives, device constructs nor device routines  */
+
+#pragma omp requires unified_address,unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
new file mode 100644
index 00000000000..d223749f0a1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int x;
+
+void foo (void)
+{
+  #pragma omp target
+  x = 1;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
new file mode 100644
index 00000000000..3d15bde21f0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -0,0 +1,20 @@
+/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-5-aux.c } */
+
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-6.c b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
new file mode 100644
index 00000000000..b00c7459bbc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-6.c
@@ -0,0 +1,17 @@
+#pragma omp requires unified_shared_memory, unified_address, reverse_offload
+
+/* The requires line is not active as there is none of:
+     declare target directives, device constructs or device routines.
+   Thus, this code is expected to work everywhere.  */
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c
new file mode 100644
index 00000000000..0916db8a0ce
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7-aux.c
@@ -0,0 +1,11 @@
+/* { dg-skip-if "" { *-*-* } } */
+
+#pragma omp requires unified_address
+
+int x;
+
+void foo (void)
+{
+  x = 1;
+  #pragma omp target enter data map(always,to: x)
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
new file mode 100644
index 00000000000..c94a4c10846
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-additional-sources requires-7-aux.c } */
+
+/* Check diagnostic by device-compiler's lto1.
+   Other file uses: 'requires unified_address'.  */
+
+#pragma omp requires unified_shared_memory
+
+int a[10];
+extern void foo (void);
+
+int
+main (void)
+{
+  #pragma omp target
+  for (int i = 0; i < 10; i++)
+    a[i] = 0;
+
+  foo ();
+  return 0;
+}
+
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90 b/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90
new file mode 100644
index 00000000000..a18caeb4c69
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/requires-1-aux.f90
@@ -0,0 +1,14 @@
+! { dg-skip-if "" { *-*-* } }
+
+module m
+  integer x
+end module m
+
+subroutine foo
+  use m
+  implicit none
+  !$omp requires unified_address
+
+  x = 1
+  !$omp target enter data map(always,to: x)
+end
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
new file mode 100644
index 00000000000..33741af15f1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -0,0 +1,26 @@
+! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
+! { dg-additional-sources requires-1-aux.f90 }
+
+! Check diagnostic by device-compiler's lto1.
+!   Other file uses: 'requires unified_address'.
+
+module m
+  integer :: a(10)
+  interface
+    subroutine foo
+    end
+  end interface
+end
+
+program main
+  !$omp requires unified_shared_memory
+
+  !$omp target
+    a = 0
+  !$omp end target
+
+  call foo ()
+end
+
+! { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+! { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" }
diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
index d1678d0514e..33bae0650b4 100644
--- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
+++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
@@ -168,8 +168,12 @@ GOMP_OFFLOAD_get_type (void)
 }
 
 extern "C" int
-GOMP_OFFLOAD_get_num_devices (void)
+GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
 {
+  /* Return -1 if no omp_requires_mask cannot be fulfilled but
+     devices were present.  */
+  if (num_devices > 0 && omp_requires_mask != 0)
+    return -1;
   TRACE ("(): return %d", num_devices);
   return num_devices;
 }

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 21:08                         ` Tobias Burnus
@ 2022-07-04  8:31                           ` Jakub Jelinek
  2022-07-07 13:26                           ` Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
  1 sibling, 0 replies; 42+ messages in thread
From: Jakub Jelinek @ 2022-07-04  8:31 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches

On Fri, Jul 01, 2022 at 11:08:16PM +0200, Tobias Burnus wrote:
>    gomp_mutex_lock (&register_lock);
>  
> +  if (omp_requires_mask && omp_requires_mask != omp_req)

I'd use if (omp_req && omp_requires_mask && omp_requires_mask != omp_req)
e.g. for the case of mixing GCC <= 12 compiled code with GCC 13,
treat omp_req 0 as "don't know" while GOMP_REQUIRES_TARGET_USED
as "known and no requires uni*/rev* specified".

> +    {
> +      char buf1[sizeof ("unified_address, unified_shared_memory, "
> +			"reverse_offload")];
> +      char buf2[sizeof ("unified_address, unified_shared_memory, "
> +			"reverse_offload")];
> +      gomp_requires_to_name (buf2, sizeof (buf2),
> +			     omp_req != GOMP_REQUIRES_TARGET_USED
> +			     ? omp_req : omp_requires_mask);
> +      if (omp_req != GOMP_REQUIRES_TARGET_USED
> +	  && omp_requires_mask != GOMP_REQUIRES_TARGET_USED)
> +	{
> +	  gomp_requires_to_name (buf1, sizeof (buf1), omp_requires_mask);
> +	  gomp_fatal ("OpenMP 'requires' directive with non-identical clauses "
> +		      "in multiple compilation units: '%s' vs. '%s'",
> +		      buf1, buf2);
> +	}
> +      else
> +	gomp_fatal ("OpenMP 'requires' directive with '%s' specified only in "
> +		    "some compilation units", buf2);
> +    }
> +  omp_requires_mask = omp_req;
> +
>    /* Load image to all initialized devices.  */
>    for (i = 0; i < num_devices; i++)
>      {
> @@ -4125,8 +4173,30 @@ gomp_target_init (void)
>  
>  	if (gomp_load_plugin_for_device (&current_device, plugin_name))
>  	  {
> -	    new_num_devs = current_device.get_num_devices_func ();
> -	    if (new_num_devs >= 1)
> +	    int omp_req = omp_requires_mask & ~GOMP_REQUIRES_TARGET_USED;
> +	    new_num_devs = current_device.get_num_devices_func (omp_req);
> +	    if (new_num_devs < 0)

Can this be if (gomp_debug && new_num_devs < 0) - i.e. be verbose only
when the user asks for it?

> +	      {
> +		bool found = false;
> +		int type = current_device.get_type_func ();
> +		for (int img = 0; img < num_offload_images; img++)
> +		  if (type == offload_images[img].type)
> +		    found = true;
> +		if (found)
> +		  {
> +		    char buf[sizeof ("unified_address, unified_shared_memory, "
> +				     "reverse_offload")];
> +		    gomp_requires_to_name (buf, sizeof (buf), omp_req);
> +		    char *name = (char *) malloc (cur_len + 1);
> +		    memcpy (name, cur, cur_len);
> +		    name[cur_len] = '\0';
> +		    GOMP_PLUGIN_error ("note: %s devices present but 'omp "
> +				       "requires %s' cannot be fulfilled",
> +				       name, buf);
> +		    free (name);
> +		  }
> +	      }
> +	    else if (new_num_devs >= 1)
>  	      {
>  		/* Augment DEVICES and NUM_DEVICES.  */
>  

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
  2022-06-09 11:40   ` Jakub Jelinek
@ 2022-07-06 10:30   ` Thomas Schwinge
  2022-07-06 13:40     ` Tobias Burnus
  2022-07-06 11:04   ` Fix Intel MIC 'mkoffload' for OpenMP 'requires' " Thomas Schwinge
  2024-03-07 12:38   ` nvptx: 'cuDeviceGetCount' failure is fatal " Thomas Schwinge
  3 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-06 10:30 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 987 bytes --]

Hi!

On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com> wrote:
> This is based on Chung-Lin's patch at https://gcc.gnu.org/pipermail/gcc-patches/2021-January/563393.html

> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h

> +/* Flag values for requires-directive features, must match corresponding
> +   OMP_REQUIRES_* values in gcc/omp-general.h.  */
> +#define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
> +#define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
> +#define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80

To make things more failure proof, OK to push the attached
"Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place"?


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Define-OMP_REQUIRES_-.-GOMP_REQUIRES_-.-in-a-single-.patch --]
[-- Type: text/x-diff, Size: 2120 bytes --]

From bd1aa5bc96e141a85bb53d61a5c7531e09ea3cf6 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 5 Jul 2022 11:04:46 +0200
Subject: [PATCH] Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a
 single place

Clean up for recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	gcc/
	* omp-general.h (enum omp_requires): Use 'GOMP_REQUIRES_[...]'.
	include/
	* gomp-constants.h (OMP_REQUIRES_[...]): Update comment.
---
 gcc/omp-general.h        | 8 ++++----
 include/gomp-constants.h | 3 +--
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 7a94831e8f5..74e90e1a71a 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -126,12 +126,12 @@ extern int oacc_get_ifn_dim_arg (const gimple *stmt);
 
 enum omp_requires {
   OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER = 0xf,
-  OMP_REQUIRES_UNIFIED_ADDRESS = 0x10,
-  OMP_REQUIRES_UNIFIED_SHARED_MEMORY = 0x20,
+  OMP_REQUIRES_UNIFIED_ADDRESS = GOMP_REQUIRES_UNIFIED_ADDRESS,
+  OMP_REQUIRES_UNIFIED_SHARED_MEMORY = GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
   OMP_REQUIRES_DYNAMIC_ALLOCATORS = 0x40,
-  OMP_REQUIRES_REVERSE_OFFLOAD = 0x80,
+  OMP_REQUIRES_REVERSE_OFFLOAD = GOMP_REQUIRES_REVERSE_OFFLOAD,
   OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER_USED = 0x100,
-  OMP_REQUIRES_TARGET_USED = 0x200
+  OMP_REQUIRES_TARGET_USED = GOMP_REQUIRES_TARGET_USED,
 };
 
 extern GTY(()) enum omp_requires omp_requires_mask;
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 3e3078f082e..84316f953d0 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -341,8 +341,7 @@ enum gomp_map_kind
 #define GOMP_DEPEND_MUTEXINOUTSET	4
 #define GOMP_DEPEND_INOUTSET		5
 
-/* Flag values for requires-directive features, must match corresponding
-   OMP_REQUIRES_* values in gcc/omp-general.h.  */
+/* Flag values for OpenMP 'requires' directive features.  */
 #define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
 #define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
 #define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Restore 'GOMP_offload_unregister_ver' functionality (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-01 13:06                 ` [Patch][v5] " Tobias Burnus
  2022-07-01 14:34                   ` Jakub Jelinek
@ 2022-07-06 10:42                   ` Thomas Schwinge
  2022-07-06 13:59                     ` Tobias Burnus
  2023-09-15  9:41                   ` [Patch][v5] OpenMP: Move omp requires checks to libgomp Thomas Schwinge
  2 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-06 10:42 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]

Hi!

On 2022-07-01T15:06:05+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> Attached is the updated patch. Main changes: [...]

This is now a great implementation of cross-component communication
(host/offloading compilers, runtime), thanks!  I'm sure this will be
usable (or at least instructing) for further purposes, too.

> - Uses GOMP_register_var to pass the mask to libgomp

Like 'GOMP_offload_register_ver', also 'GOMP_offload_unregister_ver'
needs to be adjusted correspondingly.  OK to push the attached
"Restore 'GOMP_offload_unregister_ver' functionality"?  (Currently
testing.)

> (and no longer a weak variable)

... which actually removed my "contribution" (hack!) to this patch.  ;-)


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Restore-GOMP_offload_unregister_ver-functionality.patch --]
[-- Type: text/x-diff, Size: 6871 bytes --]

From 9a49a3e1e4d3def7b48beccdde6fa9f218719244 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 5 Jul 2022 18:23:15 +0200
Subject: [PATCH] Restore 'GOMP_offload_unregister_ver' functionality

The recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp" changed the
'GOMP_offload_register_ver' interface but didn't change
'GOMP_offload_unregister_ver' accordingly, so we're no longer
actually unregistering.

	gcc/
	* config/gcn/mkoffload.cc (process_obj): Clarify 'target_data' ->
	'[...]_data'.
	* config/nvptx/mkoffload.cc (process): Likewise.
	libgomp/
	* target.c (GOMP_offload_register_ver): Clarify 'target_data' ->
	'data'.
	(GOMP_offload_unregister_ver): Likewise.  Fix up 'target_data',
	and add 'assert'.
---
 gcc/config/gcn/mkoffload.cc   |  8 ++++----
 gcc/config/nvptx/mkoffload.cc |  8 ++++----
 libgomp/target.c              | 33 ++++++++++++++++++++++++++-------
 3 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/gcc/config/gcn/mkoffload.cc b/gcc/config/gcn/mkoffload.cc
index b8b3fecfcb4..d2464332275 100644
--- a/gcc/config/gcn/mkoffload.cc
+++ b/gcc/config/gcn/mkoffload.cc
@@ -692,13 +692,13 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
 	   len);
 
   fprintf (cfile,
-	   "static const struct gcn_image_desc {\n"
+	   "static const struct gcn_data {\n"
 	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct gcn_image *gcn_image;\n"
 	   "  unsigned kernel_count;\n"
 	   "  const struct hsa_kernel_description *kernel_infos;\n"
 	   "  unsigned global_variable_count;\n"
-	   "} target_data = {\n"
+	   "} gcn_data = {\n"
 	   "  %d,\n"
 	   "  &gcn_image,\n"
 	   "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
@@ -723,7 +723,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
   fprintf (cfile, "static __attribute__((constructor)) void init (void)\n"
 	   "{\n"
 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*GCN*/, &target_data);\n"
+	   " %d/*GCN*/, &gcn_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
 	   GOMP_DEVICE_GCN);
@@ -731,7 +731,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
   fprintf (cfile, "static __attribute__((destructor)) void fini (void)\n"
 	   "{\n"
 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*GCN*/, &target_data);\n"
+	   " %d/*GCN*/, &gcn_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
 	   GOMP_DEVICE_GCN);
diff --git a/gcc/config/nvptx/mkoffload.cc b/gcc/config/nvptx/mkoffload.cc
index d8c81eb0547..0fa5f4423bf 100644
--- a/gcc/config/nvptx/mkoffload.cc
+++ b/gcc/config/nvptx/mkoffload.cc
@@ -310,7 +310,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "\n};\n\n");
 
   fprintf (out,
-	   "static const struct nvptx_tdata {\n"
+	   "static const struct nvptx_data {\n"
 	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct ptx_obj *ptx_objs;\n"
 	   "  unsigned ptx_num;\n"
@@ -318,7 +318,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
 	   "  unsigned var_num;\n"
 	   "  const struct nvptx_fn *fn_names;\n"
 	   "  unsigned fn_num;\n"
-	   "} target_data = {\n"
+	   "} nvptx_data = {\n"
 	   "  %d, ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
 	   "  var_mappings,"
 	   "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
@@ -344,7 +344,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "static __attribute__((constructor)) void init (void)\n"
 	   "{\n"
 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*NVIDIA_PTX*/, &target_data);\n"
+	   " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
 	   GOMP_DEVICE_NVIDIA_PTX);
@@ -352,7 +352,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "static __attribute__((destructor)) void fini (void)\n"
 	   "{\n"
 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*NVIDIA_PTX*/, &target_data);\n"
+	   " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
 	   GOMP_DEVICE_NVIDIA_PTX);
diff --git a/libgomp/target.c b/libgomp/target.c
index 4dac81862d7..288b748b9e8 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2334,23 +2334,29 @@ gomp_requires_to_name (char *buf, size_t size, int requires_mask)
 
 /* This function should be called from every offload image while loading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
-   the target, and TARGET_DATA needed by target plugin.  */
+   the target, and DATA.  */
 
 void
 GOMP_offload_register_ver (unsigned version, const void *host_table,
-			   int target_type, const void *target_data)
+			   int target_type, const void *data)
 {
   int i;
-  int omp_req = 0;
 
   if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
     gomp_fatal ("Library too old for offload (version %u < %u)",
 		GOMP_VERSION, GOMP_VERSION_LIB (version));
 
+  int omp_req;
+  const void *target_data;
   if (GOMP_VERSION_LIB (version) > 1)
     {
-      omp_req = (int) (size_t) ((void **) target_data)[0];
-      target_data = &((void **) target_data)[1];
+      omp_req = (int) (size_t) ((void **) data)[0];
+      target_data = &((void **) data)[1];
+    }
+  else
+    {
+      omp_req = 0;
+      target_data = data;
     }
 
   gomp_mutex_lock (&register_lock);
@@ -2413,14 +2419,24 @@ GOMP_offload_register (const void *host_table, int target_type,
 
 /* This function should be called from every offload image while unloading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
-   the target, and TARGET_DATA needed by target plugin.  */
+   the target, and DATA.  */
 
 void
 GOMP_offload_unregister_ver (unsigned version, const void *host_table,
-			     int target_type, const void *target_data)
+			     int target_type, const void *data)
 {
   int i;
 
+  if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
+    gomp_fatal ("Library too old for offload (version %u < %u)",
+		GOMP_VERSION, GOMP_VERSION_LIB (version));
+
+  const void *target_data;
+  if (GOMP_VERSION_LIB (version) > 1)
+    target_data = &((void **) data)[1];
+  else
+    target_data = data;
+
   gomp_mutex_lock (&register_lock);
 
   /* Unload image from all initialized devices.  */
@@ -2436,12 +2452,15 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
     }
 
   /* Remove image from array of pending images.  */
+  bool found = false;
   for (i = 0; i < num_offload_images; i++)
     if (offload_images[i].target_data == target_data)
       {
 	offload_images[i] = offload_images[--num_offload_images];
+	found = true;
 	break;
       }
+  assert (found);
 
   gomp_mutex_unlock (&register_lock);
 }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
  2022-06-09 11:40   ` Jakub Jelinek
  2022-07-06 10:30   ` Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] " Thomas Schwinge
@ 2022-07-06 11:04   ` Thomas Schwinge
  2022-07-06 11:29     ` Tobias Burnus
  2022-07-06 14:19     ` Tobias Burnus
  2024-03-07 12:38   ` nvptx: 'cuDeviceGetCount' failure is fatal " Thomas Schwinge
  3 siblings, 2 replies; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-06 11:04 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1412 bytes --]

Hi!

On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com> wrote:
> This is based on Chung-Lin's patch at https://gcc.gnu.org/pipermail/gcc-patches/2021-January/563393.html

> PS: I have not fully tested the intelmic version.

As part of my standard testing, I'm reporting that it got completely
broken.  ;'-) Your commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp" states:
"When the device lto1 runs, it extracts the data for mkoffload. The
latter than passes the value on to GOMP_offload_register_ver."
That's not implemented for Intel MIC 'mkoffload', so we always run into
'gcc/lto-cgraph.cc:input_offload_tables':

    +#ifdef ACCEL_COMPILER
    +  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
    +  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
    +    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");

..., and all offloading compilation fail with that 'fatal_error'.  OK to
push the attached "Fix Intel MIC 'mkoffload' for OpenMP 'requires'"?
(Currently testing.)


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-Intel-MIC-mkoffload-for-OpenMP-requires.patch --]
[-- Type: text/x-diff, Size: 9201 bytes --]

From afd77646d7ced9f58fb49667e37ee4e21dd6fc53 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 5 Jul 2022 12:21:33 +0200
Subject: [PATCH] Fix Intel MIC 'mkoffload' for OpenMP 'requires'

Similar to how the other 'mkoffload's got changed in
recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

This also means finally switching Intel MIC 'mkoffload' to
'GOMP_offload_register_ver', 'GOMP_offload_unregister_ver',
making 'GOMP_offload_register', 'GOMP_offload_unregister'
legacy entry points.

	gcc/
	* config/i386/intelmic-mkoffload.cc (generate_host_descr_file)
	(prepare_target_image, main): Handle OpenMP 'requires'.
	(generate_host_descr_file): Switch to 'GOMP_offload_register_ver',
	'GOMP_offload_unregister_ver'.
	libgomp/
	* target.c (GOMP_offload_register, GOMP_offload_unregister):
	Denote as legacy entry points.
	* testsuite/libgomp.c-c++-common/requires-1.c: Enable for all
	'target offloading_enabled'.
	* testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
	* testsuite/libgomp.fortran/requires-1.f90: Likewise.
---
 gcc/config/i386/intelmic-mkoffload.cc         | 56 +++++++++++++++----
 libgomp/target.c                              |  4 ++
 .../libgomp.c-c++-common/requires-1.c         |  2 +-
 .../libgomp.c-c++-common/requires-5.c         |  2 +-
 .../libgomp.c-c++-common/requires-7.c         |  2 +-
 .../testsuite/libgomp.fortran/requires-1.f90  |  2 +-
 6 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/gcc/config/i386/intelmic-mkoffload.cc b/gcc/config/i386/intelmic-mkoffload.cc
index c683d6f473e..596f6f107b8 100644
--- a/gcc/config/i386/intelmic-mkoffload.cc
+++ b/gcc/config/i386/intelmic-mkoffload.cc
@@ -370,7 +370,7 @@ generate_target_offloadend_file (const char *target_compiler)
 
 /* Generates object file with the host side descriptor.  */
 static const char *
-generate_host_descr_file (const char *host_compiler)
+generate_host_descr_file (const char *host_compiler, uint32_t omp_requires)
 {
   char *dump_filename = concat (dumppfx, "_host_descr.c", NULL);
   const char *src_filename = save_temps
@@ -386,39 +386,50 @@ generate_host_descr_file (const char *host_compiler)
   if (!src_file)
     fatal_error (input_location, "cannot open '%s'", src_filename);
 
+  fprintf (src_file, "#include <stdint.h>\n\n");
+
   fprintf (src_file,
 	   "extern const void *const __OFFLOAD_TABLE__;\n"
 	   "extern const void *const __offload_image_intelmic_start;\n"
 	   "extern const void *const __offload_image_intelmic_end;\n\n"
 
-	   "static const void *const __offload_target_data[] = {\n"
+	   "static const struct intelmic_data {\n"
+	   "  uintptr_t omp_requires_mask;\n"
+	   "  const void *const image_start;\n"
+	   "  const void *const image_end;\n"
+	   "} intelmic_data = {\n"
+	   "  %d,\n"
 	   "  &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (src_file,
 	   "#ifdef __cplusplus\n"
 	   "extern \"C\"\n"
 	   "#endif\n"
-	   "void GOMP_offload_register (const void *, int, const void *);\n"
+	   "void GOMP_offload_register_ver (unsigned, const void *, int, const void *);\n"
 	   "#ifdef __cplusplus\n"
 	   "extern \"C\"\n"
 	   "#endif\n"
-	   "void GOMP_offload_unregister (const void *, int, const void *);\n\n"
+	   "void GOMP_offload_unregister_ver (unsigned, const void *, int, const void *);\n\n"
 
 	   "__attribute__((constructor))\n"
 	   "static void\n"
 	   "init (void)\n"
 	   "{\n"
-	   "  GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
-	   "}\n\n", GOMP_DEVICE_INTEL_MIC);
+	   "  GOMP_offload_register_ver (%#x, &__OFFLOAD_TABLE__, %d, &intelmic_data);\n"
+	   "}\n\n",
+	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
+	   GOMP_DEVICE_INTEL_MIC);
 
   fprintf (src_file,
 	   "__attribute__((destructor))\n"
 	   "static void\n"
 	   "fini (void)\n"
 	   "{\n"
-	   "  GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
-	   "}\n", GOMP_DEVICE_INTEL_MIC);
+	   "  GOMP_offload_unregister_ver (%#x, &__OFFLOAD_TABLE__, %d, &intelmic_data);\n"
+	   "}\n",
+	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
+	   GOMP_DEVICE_INTEL_MIC);
 
   fclose (src_file);
 
@@ -462,7 +473,7 @@ generate_host_descr_file (const char *host_compiler)
 }
 
 static const char *
-prepare_target_image (const char *target_compiler, int argc, char **argv)
+prepare_target_image (const char *target_compiler, int argc, char **argv, uint32_t *omp_requires)
 {
   const char *target_descr_filename
     = generate_target_descr_file (target_compiler);
@@ -509,8 +520,26 @@ prepare_target_image (const char *target_compiler, int argc, char **argv)
   obstack_ptr_grow (&argv_obstack, "");
   obstack_ptr_grow (&argv_obstack, "-o");
   obstack_ptr_grow (&argv_obstack, target_so_filename);
+
+  char *omp_requires_file;
+  if (save_temps)
+    omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+  else
+    omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+  xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
+
   compile_for_target (&argv_obstack);
 
+  unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+  FILE *in = fopen (omp_requires_file, "rb");
+  if (!in)
+    fatal_error (input_location, "cannot open omp_requires file %qs",
+		 omp_requires_file);
+  if (fread (omp_requires, sizeof (*omp_requires), 1, in) != 1)
+    fatal_error (input_location, "cannot read omp_requires file %qs",
+		 omp_requires_file);
+  fclose (in);
+
   /* Run objcopy.  */
   char *rename_section_opt
     = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
@@ -643,10 +672,13 @@ main (int argc, char **argv)
   if (!dumppfx)
     dumppfx = out_obj_filename;
 
+  uint32_t omp_requires;
+
   const char *target_so_filename
-    = prepare_target_image (target_compiler, argc, argv);
+    = prepare_target_image (target_compiler, argc, argv, &omp_requires);
 
-  const char *host_descr_filename = generate_host_descr_file (host_compiler);
+  const char *host_descr_filename
+    = generate_host_descr_file (host_compiler, omp_requires);
 
   /* Perform partial linking for the target image and host side descriptor.
      As a result we'll get a finalized object file with all offload data.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index 288b748b9e8..18c5f6e27db 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2410,6 +2410,8 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
   gomp_mutex_unlock (&register_lock);
 }
 
+/* Legacy entry point.  */
+
 void
 GOMP_offload_register (const void *host_table, int target_type,
 		       const void *target_data)
@@ -2465,6 +2467,8 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
   gomp_mutex_unlock (&register_lock);
 }
 
+/* Legacy entry point.  */
+
 void
 GOMP_offload_unregister (const void *host_table, int target_type,
 			 const void *target_data)
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
index fedf9779769..8eaac54e187 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do link { target offloading_enabled } } */
 /* { dg-additional-sources requires-1-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
index c1e5540cfc5..5aa04a5a604 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -1,4 +1,4 @@
-/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do run { target offloading_enabled } } */
 /* { dg-additional-sources requires-5-aux.c } */
 
 #pragma omp requires unified_shared_memory, unified_address, reverse_offload
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
index c94a4c10846..31c6f73c6da 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do link { target offloading_enabled } } */
 /* { dg-additional-sources requires-7-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
index 33741af15f1..1020ebb4277 100644
--- a/libgomp/testsuite/libgomp.fortran/requires-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -1,4 +1,4 @@
-! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
+! { dg-do link { target offloading_enabled } }
 ! { dg-additional-sources requires-1-aux.f90 }
 
 ! Check diagnostic by device-compiler's lto1.
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 11:04   ` Fix Intel MIC 'mkoffload' for OpenMP 'requires' " Thomas Schwinge
@ 2022-07-06 11:29     ` Tobias Burnus
  2022-07-06 12:38       ` Thomas Schwinge
  2022-07-06 14:19     ` Tobias Burnus
  1 sibling, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-07-06 11:29 UTC (permalink / raw)
  To: Thomas Schwinge, Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas,

On 06.07.22 13:04, Thomas Schwinge wrote:
> On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com> wrote:
>> PS: I have not fully tested the intelmic version.
> As part of my standard testing, I'm reporting that it got completely
> broken.  ;'-)

Interesting. Because intelmic-mkoffload.cc calls GOMP_offload_register
and not GOMP_offload_register_ver - and that call path should be unchanged.

However, I missed that I had an assert that GCC_OFFLOAD_OMP_REQUIRES_FILE is
set. - Thus, an alternative is to change that into an 'if'.

But I concur that updating intelmic-mkoffload.cc is nicer! Thanks!


Regarding:
> -! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
> +! { dg-do link { target offloading_enabled } }
This patch looks wrong. We are not interested whether there is an offloading device
available or not - but whether the offloading compiler is running.

Those are completely independent. Obviously, offloading can be configured but not
being present. (That's the usual case for testing distro builds but also can
occur elsewhere.)
And also the reverse if possible - usually because of -foffload=... but when GCC is
configured with --enable-offload-defaulted, also other combinations are possible.


I think the proper check would be write and use an 'offload_target_any',
i.e. OFFLOAD_TARGET_NAMES= being present and nonempty.

Cf. check_effective_target_offload_target_nvptx / ..._amdgcn and
libgomp_check_effective_target_offload_target
in libgomp/testsuite/lib/libgomp.exp

Possible patch (untested):

# Return 1 if compiling for some offload target(s)
proc check_effective_target_offload_target_any { } {
     return [libgomp_check_effective_target_offload_target ""]
}

At least if I understand the following correctly, "" should work:
         return [string match "*:$target_name*:*" ":$gcc_offload_targets:"]


Thanks for taking care of the patch fallout!

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 11:29     ` Tobias Burnus
@ 2022-07-06 12:38       ` Thomas Schwinge
  2022-07-06 13:30         ` Tobias Burnus
  0 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-06 12:38 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches, Jakub Jelinek

Hi Tobias!

On 2022-07-06T13:29:14+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 06.07.22 13:04, Thomas Schwinge wrote:
>> On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com> wrote:
>>> PS: I have not fully tested the intelmic version.
>> As part of my standard testing, I'm reporting that it got completely
>> broken.  ;'-)
>
> Interesting. Because intelmic-mkoffload.cc calls GOMP_offload_register
> and not GOMP_offload_register_ver - and that call path should be unchanged.

True indeed for that code path...

> However, I missed that I had an assert that GCC_OFFLOAD_OMP_REQUIRES_FILE is
> set.

..., but not for that one.

> Thus, an alternative is to change that into an 'if'.
> But I concur that updating intelmic-mkoffload.cc is nicer! Thanks!

ACK.


> Regarding:
>> -! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
>> +! { dg-do link { target offloading_enabled } }
> This patch looks wrong. We are not interested whether there is an offloading device
> available or not - but whether the offloading compiler is running.
>
> Those are completely independent. Obviously, offloading can be configured but not
> being present. (That's the usual case for testing distro builds but also can
> occur elsewhere.)
> And also the reverse if possible - usually because of -foffload=... but when GCC is
> configured with --enable-offload-defaulted, also other combinations are possible.
>
>
> I think the proper check would be write and use an 'offload_target_any',
> i.e. OFFLOAD_TARGET_NAMES= being present and nonempty.
>
> Cf. check_effective_target_offload_target_nvptx / ..._amdgcn and
> libgomp_check_effective_target_offload_target
> in libgomp/testsuite/lib/libgomp.exp
>
> Possible patch (untested):
>
> # Return 1 if compiling for some offload target(s)
> proc check_effective_target_offload_target_any { } {
>      return [libgomp_check_effective_target_offload_target ""]
> }
>
> At least if I understand the following correctly, "" should work:
>          return [string match "*:$target_name*:*" ":$gcc_offload_targets:"]

:-) Haha, that's actually *exactly* what I had implemented first!  But
then I realized that 'target offloading_enabled' is doing exactly that:
check that offloading compilation is configured -- not that "there is an
offloading device available or not" as you seem to understand?  Or am I
confused there?

I do however agree that (generally) replacing 'target offloading_enabled'
with a new 'target offload_target_any' would seem appropriate (as a
separate patch), because that would also do the right thing when running
libgomp testing with non-default '-foffload=[...]', including
'-foffload=disable'.

For checking "offloading device available" we'd use
'check_effective_target_offload_device[...]'.


Grüße
 Thomas


> Thanks for taking care of the patch fallout!
>
> Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 12:38       ` Thomas Schwinge
@ 2022-07-06 13:30         ` Tobias Burnus
  2022-07-07 10:46           ` Thomas Schwinge
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-07-06 13:30 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: gcc-patches, Jakub Jelinek

Hi Thomas,

On 06.07.22 14:38, Thomas Schwinge wrote:
> :-) Haha, that's actually *exactly* what I had implemented first!  But
> then I realized that 'target offloading_enabled' is doing exactly that:
> check that offloading compilation is configured -- not that "there is an
> offloading device available or not" as you seem to understand?  Or am I
> confused there?

I think as you mentioned below – there is a difference. And that difference,
I explicitly maked use of:
  - libgomp.c-c++-common/requires-{1,5,7}.c test the device lto1 compiler,
    which requires that it is actually available. Thus, I used:
    /* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
while
  - libgomp.c-c++-common/requires-2.c checks that ENABLE_OFFLOADING == true, i.e.
    the host lto1 compiler is configured to create the offload tables and, thus,
    can diagnose the omp-requires mismatch. Hence, the testcase has:
    /* { dg-do link { target offloading_enabled } } */
    /* { dg-additional-options "-foffload=disable -flto" } */

Granted, as the other files do not use -foffload=..., it should not
make a difference - but, still, replacing it unconditionally
with 'target offloading_enabled' feels wrong.

I was about to write again about --enable-offload-defaulted and
having no offloading compilers installed. But that comes too late.
gcc.cc (the driver) will set
   OFFLOAD_TARGET_NAMES=
to the configured offload-target compilers (after -foffload= filtering),
but the is-available check is only done in lto-wrapper.cc, which comes
too late. But I admit it is unlikely that someone configures + builds
offload compilers and, then, for testing have no offload compilers
available.

> I do however agree that (generally) replacing 'target offloading_enabled'
> with a new 'target offload_target_any' would seem appropriate (as a
> separate patch), because that would also do the right thing when running
> libgomp testing with non-default '-foffload=[...]', including
> '-foffload=disable'.

I concur – but I would prefer to have that lib/libgomp.exp patch committed
before the testsuite-part of this patch is committed. Albeit, I do not feel
very strong about this.

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 10:30   ` Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] " Thomas Schwinge
@ 2022-07-06 13:40     ` Tobias Burnus
  0 siblings, 0 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-06 13:40 UTC (permalink / raw)
  To: Thomas Schwinge, Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas,

On 06.07.22 12:30, Thomas Schwinge wrote:
> On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com>
> wrote:
>> [..]
> To make things more failure proof, OK to push the attached
> "Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place"?

I concur that it makes sense to avoid having multiple definitions,
especially as I can imagine that we might want to use this flag to
pass other options to the library - e.g. some of the command-line
pinning flags, proposed (and used in the OG11/OG12 branch).

Thus, the following is ok/makes sense from my point of view.

> diff --git a/gcc/omp-general.h b/gcc/omp-general.h
> index 7a94831e8f5..74e90e1a71a 100644
> --- a/gcc/omp-general.h
> +++ b/gcc/omp-general.h
> @@ -126,12 +126,12 @@ extern int oacc_get_ifn_dim_arg (const gimple *stmt);
>
>   enum omp_requires {
>     OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER = 0xf,
> -  OMP_REQUIRES_UNIFIED_ADDRESS = 0x10,
> -  OMP_REQUIRES_UNIFIED_SHARED_MEMORY = 0x20,
> +  OMP_REQUIRES_UNIFIED_ADDRESS = GOMP_REQUIRES_UNIFIED_ADDRESS,
> +  OMP_REQUIRES_UNIFIED_SHARED_MEMORY = GOMP_REQUIRES_UNIFIED_SHARED_MEMORY,
>     OMP_REQUIRES_DYNAMIC_ALLOCATORS = 0x40,
> -  OMP_REQUIRES_REVERSE_OFFLOAD = 0x80,
> +  OMP_REQUIRES_REVERSE_OFFLOAD = GOMP_REQUIRES_REVERSE_OFFLOAD,
>     OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER_USED = 0x100,
> -  OMP_REQUIRES_TARGET_USED = 0x200
> +  OMP_REQUIRES_TARGET_USED = GOMP_REQUIRES_TARGET_USED,
>   };
>
>   extern GTY(()) enum omp_requires omp_requires_mask;
> diff --git a/include/gomp-constants.h b/include/gomp-constants.h
> index 3e3078f082e..84316f953d0 100644
> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h
> @@ -341,8 +341,7 @@ enum gomp_map_kind
>   #define GOMP_DEPEND_MUTEXINOUTSET   4
>   #define GOMP_DEPEND_INOUTSET                5
>
> -/* Flag values for requires-directive features, must match corresponding
> -   OMP_REQUIRES_* values in gcc/omp-general.h.  */
> +/* Flag values for OpenMP 'requires' directive features.  */
>   #define GOMP_REQUIRES_UNIFIED_ADDRESS       0x10
>   #define GOMP_REQUIRES_UNIFIED_SHARED_MEMORY 0x20
>   #define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Restore 'GOMP_offload_unregister_ver' functionality (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 10:42                   ` Restore 'GOMP_offload_unregister_ver' functionality " Thomas Schwinge
@ 2022-07-06 13:59                     ` Tobias Burnus
  2022-07-06 21:08                       ` Thomas Schwinge
  2022-08-17 11:45                       ` Jakub Jelinek
  0 siblings, 2 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-06 13:59 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas, hello all,

On 06.07.22 12:42, Thomas Schwinge wrote:
> On 2022-07-01T15:06:05+0200, Tobias Burnus <tobias@codesourcery.com>
> wrote:
>> Attached is the updated patch. Main changes: [...]
> This is now a great implementation of cross-component communication
> (host/offloading compilers, runtime), thanks!  I'm sure this will be
> usable (or at least instructing) for further purposes, too.
I also see potential use for other tools.
>> - Uses GOMP_register_var to pass the mask to libgomp
> Like 'GOMP_offload_register_ver', also 'GOMP_offload_unregister_ver'
> needs to be adjusted correspondingly.
Ups! Thanks for catching it and the patch.
> OK to push the attached

Fixing the data handling in GOMP_offload_unregister_ver,
i.e. the 'target_data = &((void **) data)[1];' bit,
is obvious and a good bug fix.

Regarding the renaming of (..._)'target_data' to (..._)'data',
I do not have a real opinion as I also regard the requires part
as being part of the target(-related) data. Thus, I don't think
it harms but I also do not think that there is a large benefit.

Regarding the assert ... (continued below)

>       gcc/
>       * config/gcn/mkoffload.cc (process_obj): Clarify 'target_data' ->
>       '[...]_data'.
>       * config/nvptx/mkoffload.cc (process): Likewise.
>       libgomp/
>       * target.c (GOMP_offload_register_ver): Clarify 'target_data' ->
>       'data'.
>       (GOMP_offload_unregister_ver): Likewise.  Fix up 'target_data',
>       and add 'assert'.
...
> diff --git a/gcc/config/gcn/mkoffload.cc b/gcc/config/gcn/mkoffload.cc
> index b8b3fecfcb4..d2464332275 100644
> --- a/gcc/config/gcn/mkoffload.cc
> +++ b/gcc/config/gcn/mkoffload.cc
> @@ -692,13 +692,13 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
>          len);
>
>     fprintf (cfile,
> -        "static const struct gcn_image_desc {\n"
> +        "static const struct gcn_data {\n"
>          "  uintptr_t omp_requires_mask;\n"
>          "  const struct gcn_image *gcn_image;\n"
>          "  unsigned kernel_count;\n"
>          "  const struct hsa_kernel_description *kernel_infos;\n"
>          "  unsigned global_variable_count;\n"
> -        "} target_data = {\n"
> +        "} gcn_data = {\n"
>          "  %d,\n"
>          "  &gcn_image,\n"
>          "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
> @@ -723,7 +723,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
>     fprintf (cfile, "static __attribute__((constructor)) void init (void)\n"
>          "{\n"
>          "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
> -        " %d/*GCN*/, &target_data);\n"
> +        " %d/*GCN*/, &gcn_data);\n"
>          "};\n",
>          GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
>          GOMP_DEVICE_GCN);
> @@ -731,7 +731,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
>     fprintf (cfile, "static __attribute__((destructor)) void fini (void)\n"
>          "{\n"
>          "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
> -        " %d/*GCN*/, &target_data);\n"
> +        " %d/*GCN*/, &gcn_data);\n"
>          "};\n",
>          GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
>          GOMP_DEVICE_GCN);
> diff --git a/gcc/config/nvptx/mkoffload.cc b/gcc/config/nvptx/mkoffload.cc
> index d8c81eb0547..0fa5f4423bf 100644
> --- a/gcc/config/nvptx/mkoffload.cc
> +++ b/gcc/config/nvptx/mkoffload.cc
> @@ -310,7 +310,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
>     fprintf (out, "\n};\n\n");
>
>     fprintf (out,
> -        "static const struct nvptx_tdata {\n"
> +        "static const struct nvptx_data {\n"
>          "  uintptr_t omp_requires_mask;\n"
>          "  const struct ptx_obj *ptx_objs;\n"
>          "  unsigned ptx_num;\n"
> @@ -318,7 +318,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
>          "  unsigned var_num;\n"
>          "  const struct nvptx_fn *fn_names;\n"
>          "  unsigned fn_num;\n"
> -        "} target_data = {\n"
> +        "} nvptx_data = {\n"
>          "  %d, ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
>          "  var_mappings,"
>          "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
> @@ -344,7 +344,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
>     fprintf (out, "static __attribute__((constructor)) void init (void)\n"
>          "{\n"
>          "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
> -        " %d/*NVIDIA_PTX*/, &target_data);\n"
> +        " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
>          "};\n",
>          GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
>          GOMP_DEVICE_NVIDIA_PTX);
> @@ -352,7 +352,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
>     fprintf (out, "static __attribute__((destructor)) void fini (void)\n"
>          "{\n"
>          "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
> -        " %d/*NVIDIA_PTX*/, &target_data);\n"
> +        " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
>          "};\n",
>          GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
>          GOMP_DEVICE_NVIDIA_PTX);
> diff --git a/libgomp/target.c b/libgomp/target.c
> index 4dac81862d7..288b748b9e8 100644
> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -2334,23 +2334,29 @@ gomp_requires_to_name (char *buf, size_t size, int requires_mask)
>
>   /* This function should be called from every offload image while loading.
>      It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
> -   the target, and TARGET_DATA needed by target plugin.  */
> +   the target, and DATA.  */
>
>   void
>   GOMP_offload_register_ver (unsigned version, const void *host_table,
> -                        int target_type, const void *target_data)
> +                        int target_type, const void *data)
>   {
>     int i;
> -  int omp_req = 0;
>
>     if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
>       gomp_fatal ("Library too old for offload (version %u < %u)",
>               GOMP_VERSION, GOMP_VERSION_LIB (version));
>
> +  int omp_req;
> +  const void *target_data;
>     if (GOMP_VERSION_LIB (version) > 1)
>       {
> -      omp_req = (int) (size_t) ((void **) target_data)[0];
> -      target_data = &((void **) target_data)[1];
> +      omp_req = (int) (size_t) ((void **) data)[0];
> +      target_data = &((void **) data)[1];
> +    }
> +  else
> +    {
> +      omp_req = 0;
> +      target_data = data;
>       }
>
>     gomp_mutex_lock (&register_lock);
> @@ -2413,14 +2419,24 @@ GOMP_offload_register (const void *host_table, int target_type,
>
>   /* This function should be called from every offload image while unloading.
>      It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
> -   the target, and TARGET_DATA needed by target plugin.  */
> +   the target, and DATA.  */
>
>   void
>   GOMP_offload_unregister_ver (unsigned version, const void *host_table,
> -                          int target_type, const void *target_data)
> +                          int target_type, const void *data)
>   {
>     int i;
>
> +  if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
> +    gomp_fatal ("Library too old for offload (version %u < %u)",
> +             GOMP_VERSION, GOMP_VERSION_LIB (version));
> +
> +  const void *target_data;
> +  if (GOMP_VERSION_LIB (version) > 1)
> +    target_data = &((void **) data)[1];
> +  else
> +    target_data = data;
> +
>     gomp_mutex_lock (&register_lock);
>
>     /* Unload image from all initialized devices.  */
> @@ -2436,12 +2452,15 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
>       }
>
>     /* Remove image from array of pending images.  */
> +  bool found = false;
>     for (i = 0; i < num_offload_images; i++)
>       if (offload_images[i].target_data == target_data)
>         {
>       offload_images[i] = offload_images[--num_offload_images];
> +     found = true;
>       break;
>         }
> +  assert (found);
>
>     gomp_mutex_unlock (&register_lock);
>   }

... I don't like that libgomp crashes without any helpful message in that case.

In my opinion:
* Either we assume that it is unlikely to occur - ignore it.
   (Matches the current implementation: do nothing.)
* Or we want to have some diagnostic in case it occurs. But in that case,
   it should be some explicit diagnostic printed by gomp_error or gomp_fatal.
   IMHO, gomp_error is better than gomp_fatal as libgomp then continues cleaning
   up after this error, which IMHO makes more sense that just aborting.

To conclude, I think the '&((void **) data)[1]' shall go in - it is an obvious
bugfix! While the 'data' change is IMHO fine (but not doing it is also fine.
And, for the assert change, I think it shouldn't be done - either keep it as is
or use a gomp_error instead.

Thanks,

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 11:04   ` Fix Intel MIC 'mkoffload' for OpenMP 'requires' " Thomas Schwinge
  2022-07-06 11:29     ` Tobias Burnus
@ 2022-07-06 14:19     ` Tobias Burnus
  1 sibling, 0 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-06 14:19 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas, hello all,

On 06.07.22 13:04, Thomas Schwinge wrote:
> On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com>
> wrote:
>> PS: I have not fully tested the intelmic version.
...
> Subject: [PATCH] Fix Intel MIC 'mkoffload' for OpenMP 'requires'
...
> This also means finally switching Intel MIC 'mkoffload' to
> 'GOMP_offload_register_ver', 'GOMP_offload_unregister_ver',
> making 'GOMP_offload_register', 'GOMP_offload_unregister'
> legacy entry points.
...
>       gcc/
>       * config/i386/intelmic-mkoffload.cc (generate_host_descr_file)
>       (prepare_target_image, main): Handle OpenMP 'requires'.
>       (generate_host_descr_file): Switch to 'GOMP_offload_register_ver',
>       'GOMP_offload_unregister_ver'.
>       libgomp/
>       * target.c (GOMP_offload_register, GOMP_offload_unregister):
>       Denote as legacy entry points.
>       * testsuite/libgomp.c-c++-common/requires-1.c: Enable for all
>       'target offloading_enabled'.
>       * testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
>       * testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
>       * testsuite/libgomp.fortran/requires-1.f90: Likewise.
...
> diff --git a/gcc/config/i386/intelmic-mkoffload.cc b/gcc/config/i386/intelmic-mkoffload.cc
> index c683d6f473e..596f6f107b8 100644
> --- a/gcc/config/i386/intelmic-mkoffload.cc
> +++ b/gcc/config/i386/intelmic-mkoffload.cc
> @@ -370,7 +370,7 @@ generate_target_offloadend_file (const char *target_compiler)
>
>   /* Generates object file with the host side descriptor.  */
>   static const char *
> -generate_host_descr_file (const char *host_compiler)
> +generate_host_descr_file (const char *host_compiler, uint32_t omp_requires)
>   {
>     char *dump_filename = concat (dumppfx, "_host_descr.c", NULL);
>     const char *src_filename = save_temps
> @@ -386,39 +386,50 @@ generate_host_descr_file (const char *host_compiler)
>     if (!src_file)
>       fatal_error (input_location, "cannot open '%s'", src_filename);
>
> +  fprintf (src_file, "#include <stdint.h>\n\n");
> +
>     fprintf (src_file,
>          "extern const void *const __OFFLOAD_TABLE__;\n"
>          "extern const void *const __offload_image_intelmic_start;\n"
>          "extern const void *const __offload_image_intelmic_end;\n\n"
>
> -        "static const void *const __offload_target_data[] = {\n"
> +        "static const struct intelmic_data {\n"
> +        "  uintptr_t omp_requires_mask;\n"
> +        "  const void *const image_start;\n"
> +        "  const void *const image_end;\n"
> +        "} intelmic_data = {\n"
> +        "  %d,\n"
>          "  &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
> -        "};\n\n");
> +        "};\n\n", omp_requires);
>
>     fprintf (src_file,
>          "#ifdef __cplusplus\n"
>          "extern \"C\"\n"
>          "#endif\n"
> -        "void GOMP_offload_register (const void *, int, const void *);\n"
> +        "void GOMP_offload_register_ver (unsigned, const void *, int, const void *);\n"
This line now ends in column 90, I think you want to wrap it.
>          "#ifdef __cplusplus\n"
>          "extern \"C\"\n"
>          "#endif\n"
> -        "void GOMP_offload_unregister (const void *, int, const void *);\n\n"
> +        "void GOMP_offload_unregister_ver (unsigned, const void *, int, const void *);\n\n"
Likewise - before col 80, now col 94.
>
>          "__attribute__((constructor))\n"
>          "static void\n"
>          "init (void)\n"
>          "{\n"
> -        "  GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
> -        "}\n\n", GOMP_DEVICE_INTEL_MIC);
> +        "  GOMP_offload_register_ver (%#x, &__OFFLOAD_TABLE__, %d, &intelmic_data);\n"
Likewise - albeit before already col 87, now col 89.
> +        "}\n\n",
> +        GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
> +        GOMP_DEVICE_INTEL_MIC);
>
>     fprintf (src_file,
>          "__attribute__((destructor))\n"
>          "static void\n"
>          "fini (void)\n"
>          "{\n"
> -        "  GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
> -        "}\n", GOMP_DEVICE_INTEL_MIC);
>
> +        "}\n",
> +        GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
> +        GOMP_DEVICE_INTEL_MIC);
>
>     fclose (src_file);
>
> @@ -462,7 +473,7 @@ generate_host_descr_file (const char *host_compiler)
>   }
>
>   static const char *
> -prepare_target_image (const char *target_compiler, int argc, char **argv)
> +prepare_target_image (const char *target_compiler, int argc, char **argv, uint32_t *omp_requires)
Likewise now too long.
>   {
>     const char *target_descr_filename
>       = generate_target_descr_file (target_compiler);
> @@ -509,8 +520,26 @@ prepare_target_image (const char *target_compiler, int argc, char **argv)
>     obstack_ptr_grow (&argv_obstack, "");
>     obstack_ptr_grow (&argv_obstack, "-o");
>     obstack_ptr_grow (&argv_obstack, target_so_filename);
> +
> +  char *omp_requires_file;
> +  if (save_temps)
> +    omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
> +  else
> +    omp_requires_file = make_temp_file (".mkoffload.omp_requires");
> +  xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
> +
>     compile_for_target (&argv_obstack);
>
> +  unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
> +  FILE *in = fopen (omp_requires_file, "rb");
> +  if (!in)
> +    fatal_error (input_location, "cannot open omp_requires file %qs",
> +              omp_requires_file);
> +  if (fread (omp_requires, sizeof (*omp_requires), 1, in) != 1)
> +    fatal_error (input_location, "cannot read omp_requires file %qs",
> +              omp_requires_file);
> +  fclose (in);
> +
>     /* Run objcopy.  */
>     char *rename_section_opt
>       = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
> @@ -643,10 +672,13 @@ main (int argc, char **argv)
>     if (!dumppfx)
>       dumppfx = out_obj_filename;
>
> +  uint32_t omp_requires;
> +
>     const char *target_so_filename
> -    = prepare_target_image (target_compiler, argc, argv);
> +    = prepare_target_image (target_compiler, argc, argv, &omp_requires);
>
> -  const char *host_descr_filename = generate_host_descr_file (host_compiler);
> +  const char *host_descr_filename
> +    = generate_host_descr_file (host_compiler, omp_requires);
>
>     /* Perform partial linking for the target image and host side descriptor.
>        As a result we'll get a finalized object file with all offload data.  */
> diff --git a/libgomp/target.c b/libgomp/target.c
> index 288b748b9e8..18c5f6e27db 100644
> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -2410,6 +2410,8 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
>     gomp_mutex_unlock (&register_lock);
>   }
>
> +/* Legacy entry point.  */
> +
>   void
>   GOMP_offload_register (const void *host_table, int target_type,
>                      const void *target_data)
> @@ -2465,6 +2467,8 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
>     gomp_mutex_unlock (&register_lock);
>   }
>
> +/* Legacy entry point.  */
> +
>   void
>   GOMP_offload_unregister (const void *host_table, int target_type,
>                        const void *target_data)

Except for the too-long lines, it LGTM up to here.

However, regarding the following, I prefer to have something like 'offload_target_any'
(or similar), instead of (ab)using 'offloading_enabled', as discussed elsewhere in this thread.

While it does not make currently any difference, I could imagine to do the same
for OpenMP as done in libgomp.oacc-*/*exp. Namely: Compile/run all 'omp target'
testcases in addition with -foffload=disable.

As we don't do this, yet, I think it is fine to apply the patch for now. However, if
you don't want to fix it as follow up (please do), can you at least open a PR about it?

> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
> index fedf9779769..8eaac54e187 100644
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
> @@ -1,4 +1,4 @@
> -/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
> +/* { dg-do link { target offloading_enabled } } */
>   /* { dg-additional-sources requires-1-aux.c } */
>
>   /* Check diagnostic by device-compiler's lto1.
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> index c1e5540cfc5..5aa04a5a604 100644
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> @@ -1,4 +1,4 @@
> -/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
> +/* { dg-do run { target offloading_enabled } } */
>   /* { dg-additional-sources requires-5-aux.c } */
>
>   #pragma omp requires unified_shared_memory, unified_address, reverse_offload
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
> index c94a4c10846..31c6f73c6da 100644
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
> @@ -1,4 +1,4 @@
> -/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
> +/* { dg-do link { target offloading_enabled } } */
>   /* { dg-additional-sources requires-7-aux.c } */
>
>   /* Check diagnostic by device-compiler's lto1.
> diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
> index 33741af15f1..1020ebb4277 100644
> --- a/libgomp/testsuite/libgomp.fortran/requires-1.f90
> +++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
> @@ -1,4 +1,4 @@
> -! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
> +! { dg-do link { target offloading_enabled } }
>   ! { dg-additional-sources requires-1-aux.f90 }
>
>   ! Check diagnostic by device-compiler's lto1.

Thanks,

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Restore 'GOMP_offload_unregister_ver' functionality (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 13:59                     ` Tobias Burnus
@ 2022-07-06 21:08                       ` Thomas Schwinge
  2022-08-17 11:45                       ` Jakub Jelinek
  1 sibling, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-06 21:08 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 2979 bytes --]

Hi Tobias!

On 2022-07-06T15:59:59+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 06.07.22 12:42, Thomas Schwinge wrote:
>> --- a/libgomp/target.c
>> +++ b/libgomp/target.c

>>   /* This function should be called from every offload image while unloading.

>>   GOMP_offload_unregister_ver (unsigned version, const void *host_table,

>>     /* Remove image from array of pending images.  */
>> +  bool found = false;
>>     for (i = 0; i < num_offload_images; i++)
>>       if (offload_images[i].target_data == target_data)
>>         {
>>      offload_images[i] = offload_images[--num_offload_images];
>> +    found = true;
>>      break;
>>         }
>> +  assert (found);
>>
>>     gomp_mutex_unlock (&register_lock);
>>   }
>
> ... I don't like that libgomp crashes without any helpful message in that case.
>
> In my opinion:
> * Either we assume that it is unlikely to occur - ignore it.
>    (Matches the current implementation: do nothing.)
> * Or we want to have some diagnostic in case it occurs. But in that case,
>    it should be some explicit diagnostic printed by gomp_error or gomp_fatal.
>    IMHO, gomp_error is better than gomp_fatal as libgomp then continues cleaning
>    up after this error, which IMHO makes more sense that just aborting.

I'd be fine to change this into a 'gomp_error', but I don't think it's
necessary.  Maybe that wasn't obvious (and I should add a source code
comment), but my point here is that this situation really should never
arise (hence, if it does: internal error, thus 'assert').  Or, in other
words, such a check should've been present in the original implementation
already -- and would then have flagged your patch as being incomplete in
that function.

Thinking about it again, shouldn't we also add a corresponding
sanity-check ('assert') to 'GOMP_offload_register_ver', such that the
newly registered offload image must not already be present in
'offload_images'?  (Isn't that understanding also supported by the
'break' in 'if (offload_images[i].target_data == target_data)' in
'GOMP_offload_unregister_ver', as cited above: that no duplicates are
expected?)

That's at least my understanding of the situation; happy to hear if I'm
wrong.  (It's a pity that we're totally devoid of test cases for dynamic
registration/unregistration of offload images...)

Anyway: it's totally fine to address (or not, if so desired) this
sanity-check aspect independently of the other changes, so I've backed
that out, and then pushed to master branch
commit 3f05e03d6cfdf723ca0556318b6a9aa37be438e7
"Restore 'GOMP_offload_unregister_ver' functionality", see attached.


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Restore-GOMP_offload_unregister_ver-functionality.patch --]
[-- Type: text/x-diff, Size: 6404 bytes --]

From 3f05e03d6cfdf723ca0556318b6a9aa37be438e7 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 5 Jul 2022 18:23:15 +0200
Subject: [PATCH] Restore 'GOMP_offload_unregister_ver' functionality

The recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp" changed the
'GOMP_offload_register_ver' interface but didn't change
'GOMP_offload_unregister_ver' accordingly, so we're no longer
actually unregistering.

	gcc/
	* config/gcn/mkoffload.cc (process_obj): Clarify 'target_data' ->
	'[...]_data'.
	* config/nvptx/mkoffload.cc (process): Likewise.
	libgomp/
	* target.c (GOMP_offload_register_ver): Clarify 'target_data' ->
	'data'.
	(GOMP_offload_unregister_ver): Likewise.  Fix up 'target_data'.
---
 gcc/config/gcn/mkoffload.cc   |  8 ++++----
 gcc/config/nvptx/mkoffload.cc |  8 ++++----
 libgomp/target.c              | 30 +++++++++++++++++++++++-------
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/gcc/config/gcn/mkoffload.cc b/gcc/config/gcn/mkoffload.cc
index b8b3fecfcb4..d2464332275 100644
--- a/gcc/config/gcn/mkoffload.cc
+++ b/gcc/config/gcn/mkoffload.cc
@@ -692,13 +692,13 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
 	   len);
 
   fprintf (cfile,
-	   "static const struct gcn_image_desc {\n"
+	   "static const struct gcn_data {\n"
 	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct gcn_image *gcn_image;\n"
 	   "  unsigned kernel_count;\n"
 	   "  const struct hsa_kernel_description *kernel_infos;\n"
 	   "  unsigned global_variable_count;\n"
-	   "} target_data = {\n"
+	   "} gcn_data = {\n"
 	   "  %d,\n"
 	   "  &gcn_image,\n"
 	   "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
@@ -723,7 +723,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
   fprintf (cfile, "static __attribute__((constructor)) void init (void)\n"
 	   "{\n"
 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*GCN*/, &target_data);\n"
+	   " %d/*GCN*/, &gcn_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
 	   GOMP_DEVICE_GCN);
@@ -731,7 +731,7 @@ process_obj (FILE *in, FILE *cfile, uint32_t omp_requires)
   fprintf (cfile, "static __attribute__((destructor)) void fini (void)\n"
 	   "{\n"
 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*GCN*/, &target_data);\n"
+	   " %d/*GCN*/, &gcn_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
 	   GOMP_DEVICE_GCN);
diff --git a/gcc/config/nvptx/mkoffload.cc b/gcc/config/nvptx/mkoffload.cc
index d8c81eb0547..0fa5f4423bf 100644
--- a/gcc/config/nvptx/mkoffload.cc
+++ b/gcc/config/nvptx/mkoffload.cc
@@ -310,7 +310,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "\n};\n\n");
 
   fprintf (out,
-	   "static const struct nvptx_tdata {\n"
+	   "static const struct nvptx_data {\n"
 	   "  uintptr_t omp_requires_mask;\n"
 	   "  const struct ptx_obj *ptx_objs;\n"
 	   "  unsigned ptx_num;\n"
@@ -318,7 +318,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
 	   "  unsigned var_num;\n"
 	   "  const struct nvptx_fn *fn_names;\n"
 	   "  unsigned fn_num;\n"
-	   "} target_data = {\n"
+	   "} nvptx_data = {\n"
 	   "  %d, ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
 	   "  var_mappings,"
 	   "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
@@ -344,7 +344,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "static __attribute__((constructor)) void init (void)\n"
 	   "{\n"
 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*NVIDIA_PTX*/, &target_data);\n"
+	   " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
 	   GOMP_DEVICE_NVIDIA_PTX);
@@ -352,7 +352,7 @@ process (FILE *in, FILE *out, uint32_t omp_requires)
   fprintf (out, "static __attribute__((destructor)) void fini (void)\n"
 	   "{\n"
 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
-	   " %d/*NVIDIA_PTX*/, &target_data);\n"
+	   " %d/*NVIDIA_PTX*/, &nvptx_data);\n"
 	   "};\n",
 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
 	   GOMP_DEVICE_NVIDIA_PTX);
diff --git a/libgomp/target.c b/libgomp/target.c
index 4dac81862d7..c66c61b0621 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2334,23 +2334,29 @@ gomp_requires_to_name (char *buf, size_t size, int requires_mask)
 
 /* This function should be called from every offload image while loading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
-   the target, and TARGET_DATA needed by target plugin.  */
+   the target, and DATA.  */
 
 void
 GOMP_offload_register_ver (unsigned version, const void *host_table,
-			   int target_type, const void *target_data)
+			   int target_type, const void *data)
 {
   int i;
-  int omp_req = 0;
 
   if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
     gomp_fatal ("Library too old for offload (version %u < %u)",
 		GOMP_VERSION, GOMP_VERSION_LIB (version));
 
+  int omp_req;
+  const void *target_data;
   if (GOMP_VERSION_LIB (version) > 1)
     {
-      omp_req = (int) (size_t) ((void **) target_data)[0];
-      target_data = &((void **) target_data)[1];
+      omp_req = (int) (size_t) ((void **) data)[0];
+      target_data = &((void **) data)[1];
+    }
+  else
+    {
+      omp_req = 0;
+      target_data = data;
     }
 
   gomp_mutex_lock (&register_lock);
@@ -2413,14 +2419,24 @@ GOMP_offload_register (const void *host_table, int target_type,
 
 /* This function should be called from every offload image while unloading.
    It gets the descriptor of the host func and var tables HOST_TABLE, TYPE of
-   the target, and TARGET_DATA needed by target plugin.  */
+   the target, and DATA.  */
 
 void
 GOMP_offload_unregister_ver (unsigned version, const void *host_table,
-			     int target_type, const void *target_data)
+			     int target_type, const void *data)
 {
   int i;
 
+  if (GOMP_VERSION_LIB (version) > GOMP_VERSION)
+    gomp_fatal ("Library too old for offload (version %u < %u)",
+		GOMP_VERSION, GOMP_VERSION_LIB (version));
+
+  const void *target_data;
+  if (GOMP_VERSION_LIB (version) > 1)
+    target_data = &((void **) data)[1];
+  else
+    target_data = data;
+
   gomp_mutex_lock (&register_lock);
 
   /* Unload image from all initialized devices.  */
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp)
  2022-06-29 14:33         ` [Patch][v4] " Tobias Burnus
  2022-06-29 17:02           ` Jakub Jelinek
@ 2022-07-07  8:37           ` Thomas Schwinge
  2022-07-07  9:02             ` Tobias Burnus
  2022-07-07  8:42           ` Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing " Thomas Schwinge
  2 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-07  8:37 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1778 bytes --]

Hi!

In preparation for other changes:

On 2022-06-29T16:33:02+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3-aux.c
> @@ -0,0 +1,11 @@
> +/* { dg-skip-if "" { *-*-* } } */
> +
> +#pragma omp requires unified_address
> +
> +int x;
> +
> +void foo (void)
> +{
> +  #pragma omp target
> +  x = 1;
> +}

> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
> @@ -0,0 +1,24 @@
> +/* { dg-do link { target offloading_enabled } } */

Not expected to see 'offloading_enabled' here...

> +/* { dg-additional-sources requires-3-aux.c } */
> +
> +/* Check diagnostic by device-compiler's lto1.

..., because of this note ^.

> +   Other file uses: 'requires unified_address'.  */
> +
> +#pragma omp requires unified_address,unified_shared_memory
> +
> +int a[10];
> +extern void foo (void);
> +
> +int
> +main (void)
> +{
> +  #pragma omp target
> +  for (int i = 0; i < 10; i++)
> +    a[i] = 0;
> +
> +  foo ();
> +  return 0;
> +}
> +
> +/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
> +/* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */

OK to push the attached "Adjust 'libgomp.c-c++-common/requires-3.c'"?


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Adjust-libgomp.c-c-common-requires-3.c.patch --]
[-- Type: text/x-diff, Size: 1300 bytes --]

From 6a4031b351680bdbfe3cdb9ac4e4a3aa59e4ca84 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 7 Jul 2022 09:59:45 +0200
Subject: [PATCH] Adjust 'libgomp.c-c++-common/requires-3.c'

As documented, this one does "Check diagnostic by device-compiler's lto1".
Indeed there are none when compiling with '-foffload=disable' with an
offloading-enabled compiler, so we should use 'offload_target_[...]', as
used in other similar test cases.

Follow-up to recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	libgomp/
	* testsuite/libgomp.c-c++-common/requires-3.c: Adjust.
---
 libgomp/testsuite/libgomp.c-c++-common/requires-3.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
index 4b07ffdd09b..7091f400ef0 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target offloading_enabled } } */
+/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
 /* { dg-additional-sources requires-3-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp)
  2022-06-29 14:33         ` [Patch][v4] " Tobias Burnus
  2022-06-29 17:02           ` Jakub Jelinek
  2022-07-07  8:37           ` Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
@ 2022-07-07  8:42           ` Thomas Schwinge
  2022-07-07  9:36             ` Tobias Burnus
  2 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-07  8:42 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 2738 bytes --]

Hi!

In preparation for other changes:

On 2022-06-29T16:33:02+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4-aux.c
> @@ -0,0 +1,13 @@
> +/* { dg-skip-if "" { *-*-* } } */
> +
> +#pragma omp requires reverse_offload
> +
> +/* Note: The file does not have neither of:
> +   declare target directives, device constructs or device routines.  */
> +
> +int x;
> +
> +void foo (void)
> +{
> +  x = 1;
> +}

> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
> @@ -0,0 +1,23 @@
> +/* { dg-do link { target offloading_enabled } } */
> +/* { dg-additional-options "-flto" } */
> +/* { dg-additional-sources requires-4-aux.c } */
> +
> +/* Check diagnostic by device-compiler's or host compiler's lto1.
> +   Other file uses: 'requires reverse_offload', but that's inactive as
> +   there are no declare target directives, device constructs nor device routines  */
> +
> +#pragma omp requires unified_address,unified_shared_memory
> +
> +int a[10];
> +extern void foo (void);
> +
> +int
> +main (void)
> +{
> +  #pragma omp target
> +  for (int i = 0; i < 10; i++)
> +    a[i] = 0;
> +
> +  foo ();
> +  return 0;
> +}

> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5-aux.c
> @@ -0,0 +1,11 @@
> +/* { dg-skip-if "" { *-*-* } } */
> +
> +#pragma omp requires unified_shared_memory, unified_address, reverse_offload
> +
> +int x;
> +
> +void foo (void)
> +{
> +  #pragma omp target
> +  x = 1;
> +}

> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> @@ -0,0 +1,20 @@
> +/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
> +/* { dg-additional-sources requires-5-aux.c } */
> +
> +#pragma omp requires unified_shared_memory, unified_address, reverse_offload
> +
> +int a[10];
> +extern void foo (void);
> +
> +int
> +main (void)
> +{
> +  #pragma omp target
> +  for (int i = 0; i < 10; i++)
> +    a[i] = 0;
> +
> +  foo ();
> +  return 0;
> +}
> +
> +/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */

(The latter diagnostic later got conditionalized by 'GOMP_DEBUG=1'.)

OK to push the attached "Enhance 'libgomp.c-c++-common/requires-4.c',
'libgomp.c-c++-common/requires-5.c' testing"?


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Enhance-libgomp.c-c-common-requires-4.c-libgomp.c-c-.patch --]
[-- Type: text/x-diff, Size: 3426 bytes --]

From ae14ccbd050d0b49073d5ea09de3e2af63f8c674 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 7 Jul 2022 09:45:42 +0200
Subject: [PATCH] Enhance 'libgomp.c-c++-common/requires-4.c',
 'libgomp.c-c++-common/requires-5.c' testing

These should compile and link and execute in all configurations; host-fallback
execution, which we may actually verify.

Follow-up to recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	libgomp/
	* testsuite/libgomp.c-c++-common/requires-4.c: Enhance testing.
	* testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
---
 .../libgomp.c-c++-common/requires-4.c          | 17 ++++++++++++-----
 .../libgomp.c-c++-common/requires-5.c          | 18 +++++++++++-------
 2 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
index 128fdbb8463..deb04368108 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
@@ -1,22 +1,29 @@
-/* { dg-do link { target offloading_enabled } } */
 /* { dg-additional-options "-flto" } */
 /* { dg-additional-sources requires-4-aux.c } */
 
-/* Check diagnostic by device-compiler's or host compiler's lto1.
+/* Check no diagnostic by device-compiler's or host compiler's lto1.
    Other file uses: 'requires reverse_offload', but that's inactive as
    there are no declare target directives, device constructs nor device routines  */
 
+/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
+   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
+   and does host-fallback execution.  */
+
 #pragma omp requires unified_address,unified_shared_memory
 
-int a[10];
+int a[10] = { 0 };
 extern void foo (void);
 
 int
 main (void)
 {
-  #pragma omp target
+  #pragma omp target map(to: a)
+  for (int i = 0; i < 10; i++)
+    a[i] = i;
+
   for (int i = 0; i < 10; i++)
-    a[i] = 0;
+    if (a[i] != i)
+      __builtin_abort ();
 
   foo ();
   return 0;
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
index c1e5540cfc5..68816314b94 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -1,21 +1,25 @@
-/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
 /* { dg-additional-sources requires-5-aux.c } */
 
+/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
+   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
+   and does host-fallback execution.  */
+
 #pragma omp requires unified_shared_memory, unified_address, reverse_offload
 
-int a[10];
+int a[10] = { 0 };
 extern void foo (void);
 
 int
 main (void)
 {
-  #pragma omp target
+  #pragma omp target map(to: a)
+  for (int i = 0; i < 10; i++)
+    a[i] = i;
+
   for (int i = 0; i < 10; i++)
-    a[i] = 0;
+    if (a[i] != i)
+      __builtin_abort ();
 
   foo ();
   return 0;
 }
-
-/* (Only) if GOMP_DEBUG=1, should print at runtime the following:
-   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" */
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp)
  2022-07-07  8:37           ` Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
@ 2022-07-07  9:02             ` Tobias Burnus
  0 siblings, 0 replies; 42+ messages in thread
From: Tobias Burnus @ 2022-07-07  9:02 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas, hello all,

On 07.07.22 10:37, Thomas Schwinge wrote:
> In preparation for other changes:
>
> On 2022-06-29T16:33:02+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
>> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
>> @@ -0,0 +1,24 @@
>> +/* { dg-do link { target offloading_enabled } } */
> Not expected to see 'offloading_enabled' here...
>
>> +/* { dg-additional-sources requires-3-aux.c } */
>> +
>> +/* Check diagnostic by device-compiler's lto1.
> ..., because of this note ^.
...
> Subject: [PATCH] Adjust 'libgomp.c-c++-common/requires-3.c'
...
>       libgomp/
>       * testsuite/libgomp.c-c++-common/requires-3.c: Adjust.
...
> index 4b07ffdd09b..7091f400ef0 100644
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
> @@ -1,4 +1,4 @@
> -/* { dg-do link { target offloading_enabled } } */
> +/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */

LGTM.

Tobias
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp)
  2022-07-07  8:42           ` Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing " Thomas Schwinge
@ 2022-07-07  9:36             ` Tobias Burnus
  2022-07-07 10:42               ` Thomas Schwinge
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-07-07  9:36 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

On 07.07.22 10:42, Thomas Schwinge wrote:
> In preparation for other changes:
...
> On 2022-06-29T16:33:02+0200, Tobias Burnus<tobias@codesourcery.com>  wrote:
>> +/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */
> (The latter diagnostic later got conditionalized by 'GOMP_DEBUG=1'.)
> OK to push the attached "Enhance 'libgomp.c-c++-common/requires-4.c',
> 'libgomp.c-c++-common/requires-5.c' testing"?
...
>       libgomp/
>       * testsuite/libgomp.c-c++-common/requires-4.c: Enhance testing.
>       * testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
...
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
> @@ -1,22 +1,29 @@
> -/* { dg-do link { target offloading_enabled } } */
>   /* { dg-additional-options "-flto" } */
>   /* { dg-additional-sources requires-4-aux.c } */
>
> -/* Check diagnostic by device-compiler's or host compiler's lto1.
> +/* Check no diagnostic by device-compiler's or host compiler's lto1.

I note that without ENABLE_OFFLOADING that there is never any lto1
diagnostic.

However, given that no diagnostic is expected, it also works for "!
offloading_enabled".

Thus, the change fine.

>      Other file uses: 'requires reverse_offload', but that's inactive as
>      there are no declare target directives, device constructs nor device routines  */
>
> +/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
> +   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
> +   and does host-fallback execution.  */

The latter is only true when also device code is produced – and a device
is available for that/those device types. I think that's what you imply
by "For actual offload execution", but it is a bit hidden.

Maybe s/For actual offload execution, prints/It may print/ is clearer?

In principle, it would be nice if we could test for the output, but
currently setting an env var for remote execution does not work, yet.
Cf. https://gcc.gnu.org/pipermail/gcc-patches/2022-July/597773.html -
When set, we could use offload_target_nvptx etc. (..._amdgcn, ..._any)
to test – as this guarantees that it is compiled for that device + the
device is available.
> +
>   #pragma omp requires unified_address,unified_shared_memory
>
> -int a[10];
> +int a[10] = { 0 };
>   extern void foo (void);
>
>   int
>   main (void)
>   {
> -  #pragma omp target
> +  #pragma omp target map(to: a)

Hmm, I wonder whether I like it or not. Without, there is an implicit
"map(tofrom:a)". On the other hand, OpenMP permits that – even with
unified-shared memory – the implementation my copy the data to the
device. (For instance, to permit faster access to "a".)

Thus, ...

> +  for (int i = 0; i < 10; i++)
> +    a[i] = i;
> +
>     for (int i = 0; i < 10; i++)
> -    a[i] = 0;
> +    if (a[i] != i)
> +      __builtin_abort ();
... this condition (back on the host) could also fail with USM. However,
given that to my knowledge no USM implementation actually copies the
data, I believe it is fine. (Disclaimer: I have not checked what OG12,
but I guess it also does not copy it.)
> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
> @@ -1,21 +1,25 @@
> -/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
>   /* { dg-additional-sources requires-5-aux.c } */
>
> +/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
> +   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
> +   and does host-fallback execution.  */
> +
This wording is correct with the now-removed check – but if you remove
the offload_target..., it only "might" print it, depending, well, on the
conditions set by offload_target...
>   #pragma omp requires unified_shared_memory, unified_address, reverse_offload
>
> -int a[10];
> +int a[10] = { 0 };
>   extern void foo (void);
>
>   int
>   main (void)
>   {
> -  #pragma omp target
> +  #pragma omp target map(to: a)
> +  for (int i = 0; i < 10; i++)
> +    a[i] = i;
> +
>     for (int i = 0; i < 10; i++)
> -    a[i] = 0;
> +    if (a[i] != i)
> +      __builtin_abort ();
>
>     foo ();
>     return 0;
>   }

Thus: LGTM, if you update the GOMP_DEBUG=... wording, either using
"might" (etc.) or by being more explicit.

Once we have remote setenv, we probably want to add another testcase to
check for the GOMP_DEBUG=1, copying an existing one, adding dg-output
and restricting it to target offload_target_...

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp)
  2022-07-07  9:36             ` Tobias Burnus
@ 2022-07-07 10:42               ` Thomas Schwinge
  0 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-07 10:42 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 6459 bytes --]

Hi Tobias!

On 2022-07-07T11:36:34+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 07.07.22 10:42, Thomas Schwinge wrote:
>> In preparation for other changes:
> ...
>> On 2022-06-29T16:33:02+0200, Tobias Burnus<tobias@codesourcery.com>  wrote:
>>> +/* { dg-output "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" } */
>> (The latter diagnostic later got conditionalized by 'GOMP_DEBUG=1'.)
>> OK to push the attached "Enhance 'libgomp.c-c++-common/requires-4.c',
>> 'libgomp.c-c++-common/requires-5.c' testing"?
> ...
>>      libgomp/
>>      * testsuite/libgomp.c-c++-common/requires-4.c: Enhance testing.
>>      * testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
> ...
>> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
>> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
>> @@ -1,22 +1,29 @@
>> -/* { dg-do link { target offloading_enabled } } */
>>   /* { dg-additional-options "-flto" } */
>>   /* { dg-additional-sources requires-4-aux.c } */
>>
>> -/* Check diagnostic by device-compiler's or host compiler's lto1.
>> +/* Check no diagnostic by device-compiler's or host compiler's lto1.
>
> I note that without ENABLE_OFFLOADING that there is never any lto1
> diagnostic.
>
> However, given that no diagnostic is expected, it also works for "!
> offloading_enabled".
>
> Thus, the change fine.

ACK.

>>      Other file uses: 'requires reverse_offload', but that's inactive as
>>      there are no declare target directives, device constructs nor device routines  */
>>
>> +/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
>> +   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
>> +   and does host-fallback execution.  */
>
> The latter is only true when also device code is produced – and a device
> is available for that/those device types. I think that's what you imply
> by "For actual offload execution"

ACK.

> but it is a bit hidden.
>
> Maybe s/For actual offload execution, prints/It may print/ is clearer?

I've settled on:

    /* Depending on offload device capabilities, it may print something like the
       following (only) if GOMP_DEBUG=1:
       "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
       and in that case does host-fallback execution.  */

> In principle, it would be nice if we could test for the output, but
> currently setting an env var for remote execution does not work, yet.
> Cf. https://gcc.gnu.org/pipermail/gcc-patches/2022-July/597773.html

Right, I'm aware of that issue with remote testing, and that's why I
didn't propose such output verification.  (In a few other test cases, we
do have 'dg-set-target-env-var GOMP_DEBUG "1"', which then at present are
UNSUPPORTED for remote testing.)

> When set, we could use offload_target_nvptx etc. (..._amdgcn, ..._any)
> to test – as this guarantees that it is compiled for that device + the
> device is available.

Use 'target offload_device_nvptx', not 'target offload_target_nvptx',
etc.  ;-)

>> +
>>   #pragma omp requires unified_address,unified_shared_memory
>>
>> -int a[10];
>> +int a[10] = { 0 };
>>   extern void foo (void);
>>
>>   int
>>   main (void)
>>   {
>> -  #pragma omp target
>> +  #pragma omp target map(to: a)
>
> Hmm, I wonder whether I like it or not. Without, there is an implicit
> "map(tofrom:a)". On the other hand, OpenMP permits that – even with
> unified-shared memory – the implementation my copy the data to the
> device. (For instance, to permit faster access to "a".)
>
> Thus, ...
>
>> +  for (int i = 0; i < 10; i++)
>> +    a[i] = i;
>> +
>>     for (int i = 0; i < 10; i++)
>> -    a[i] = 0;
>> +    if (a[i] != i)
>> +      __builtin_abort ();
> ... this condition (back on the host) could also fail with USM. However,
> given that to my knowledge no USM implementation actually copies the
> data, I believe it is fine.

Right, this is meant to describe/test the current GCC master branch
behavior, where USM isn't supported, so I didn't consider that.  But I
agree, a source code comment should be added:

       As no offload devices support USM at present, we may verify host-fallback
       execution by absence of separate memory spaces.  */

> (Disclaimer: I have not checked what OG12,
> but I guess it also does not copy it.)

>> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
>> +++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
>> @@ -1,21 +1,25 @@
>> -/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
>>   /* { dg-additional-sources requires-5-aux.c } */
>>
>> +/* For actual offload execution, prints the following (only) if GOMP_DEBUG=1:
>> +   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
>> +   and does host-fallback execution.  */
>> +
> This wording is correct with the now-removed check – but if you remove
> the offload_target..., it only "might" print it, depending, well, on the
> conditions set by offload_target...
>>   #pragma omp requires unified_shared_memory, unified_address, reverse_offload
>>
>> -int a[10];
>> +int a[10] = { 0 };
>>   extern void foo (void);
>>
>>   int
>>   main (void)
>>   {
>> -  #pragma omp target
>> +  #pragma omp target map(to: a)
>> +  for (int i = 0; i < 10; i++)
>> +    a[i] = i;
>> +
>>     for (int i = 0; i < 10; i++)
>> -    a[i] = 0;
>> +    if (a[i] != i)
>> +      __builtin_abort ();
>>
>>     foo ();
>>     return 0;
>>   }
>
> Thus: LGTM, if you update the GOMP_DEBUG=... wording, either using
> "might" (etc.) or by being more explicit.

Used the wording as in 'libgomp.c-c++-common/requires-4.c'.

Thanks for the review!  Pushed to master branch
commit 5647e2c3853cbd51a6536a84b8eb0eb3c210dfbf
"Enhance 'libgomp.c-c++-common/requires-4.c',
'libgomp.c-c++-common/requires-5.c' testing", see attached.


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Enhance-libgomp.c-c-common-requires-4.c-libgomp.c-c-.patch --]
[-- Type: text/x-diff, Size: 3791 bytes --]

From 5647e2c3853cbd51a6536a84b8eb0eb3c210dfbf Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 7 Jul 2022 09:45:42 +0200
Subject: [PATCH] Enhance 'libgomp.c-c++-common/requires-4.c',
 'libgomp.c-c++-common/requires-5.c' testing

These should compile and link and execute in all configurations; host-fallback
execution, which we may actually verify.

Follow-up to recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	libgomp/
	* testsuite/libgomp.c-c++-common/requires-4.c: Enhance testing.
	* testsuite/libgomp.c-c++-common/requires-5.c: Likewise.
---
 .../libgomp.c-c++-common/requires-4.c         | 21 +++++++++++++-----
 .../libgomp.c-c++-common/requires-5.c         | 22 +++++++++++++------
 2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
index 128fdbb8463..6ed5a5f647a 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-4.c
@@ -1,22 +1,33 @@
-/* { dg-do link { target offloading_enabled } } */
 /* { dg-additional-options "-flto" } */
 /* { dg-additional-sources requires-4-aux.c } */
 
-/* Check diagnostic by device-compiler's or host compiler's lto1.
+/* Check no diagnostic by device-compiler's or host compiler's lto1.
    Other file uses: 'requires reverse_offload', but that's inactive as
    there are no declare target directives, device constructs nor device routines  */
 
+/* Depending on offload device capabilities, it may print something like the
+   following (only) if GOMP_DEBUG=1:
+   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
+   and in that case does host-fallback execution.
+
+   No offload devices support USM at present, so we may verify host-fallback
+   execution by presence of separate memory spaces.  */
+
 #pragma omp requires unified_address,unified_shared_memory
 
-int a[10];
+int a[10] = { 0 };
 extern void foo (void);
 
 int
 main (void)
 {
-  #pragma omp target
+  #pragma omp target map(to: a)
+  for (int i = 0; i < 10; i++)
+    a[i] = i;
+
   for (int i = 0; i < 10; i++)
-    a[i] = 0;
+    if (a[i] != i)
+      __builtin_abort ();
 
   foo ();
   return 0;
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
index c1e5540cfc5..7fe0c735d27 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-5.c
@@ -1,21 +1,29 @@
-/* { dg-do run { target { offload_target_nvptx || offload_target_amdgcn } } } */
 /* { dg-additional-sources requires-5-aux.c } */
 
+/* Depending on offload device capabilities, it may print something like the
+   following (only) if GOMP_DEBUG=1:
+   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled"
+   and in that case does host-fallback execution.
+
+   As no offload devices support USM at present, we may verify host-fallback
+   execution by absence of separate memory spaces.  */
+
 #pragma omp requires unified_shared_memory, unified_address, reverse_offload
 
-int a[10];
+int a[10] = { 0 };
 extern void foo (void);
 
 int
 main (void)
 {
-  #pragma omp target
+  #pragma omp target map(to: a)
   for (int i = 0; i < 10; i++)
-    a[i] = 0;
+    a[i] = i;
+
+  for (int i = 0; i < 10; i++)
+    if (a[i] != i)
+      __builtin_abort ();
 
   foo ();
   return 0;
 }
-
-/* (Only) if GOMP_DEBUG=1, should print at runtime the following:
-   "devices present but 'omp requires unified_address, unified_shared_memory, reverse_offload' cannot be fulfilled" */
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix Intel MIC 'mkoffload' for OpenMP 'requires' (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 13:30         ` Tobias Burnus
@ 2022-07-07 10:46           ` Thomas Schwinge
  0 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-07 10:46 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1361 bytes --]

Hi Tobias!

On 2022-07-06T15:30:57+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 06.07.22 14:38, Thomas Schwinge wrote:
>> :-) Haha, that's actually *exactly* what I had implemented first!  But
>> then I realized that 'target offloading_enabled' is doing exactly that:
>> check that offloading compilation is configured -- not that "there is an
>> offloading device available or not" as you seem to understand?  Or am I
>> confused there?
>
> I think as you mentioned below – there is a difference.

Eh, thanks for un-confusing me on that aspect!  There's a reason after
all that 'offloading_enabled' lives in 'gcc/testsuite/lib/'...

> And that difference,
> I explicitly maked use of: [...]

> Granted, as the other files do not use -foffload=..., it should not
> make a difference - but, still, replacing it unconditionally
> with 'target offloading_enabled' feels wrong.

ACK!

I've pushed to master branch
commit 9ef714539cb7cc1cd746312fd5dcc987bf167471
"Fix Intel MIC 'mkoffload' for OpenMP 'requires'", see attached.


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-Intel-MIC-mkoffload-for-OpenMP-requires.patch --]
[-- Type: text/x-diff, Size: 9958 bytes --]

From 9ef714539cb7cc1cd746312fd5dcc987bf167471 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 5 Jul 2022 12:21:33 +0200
Subject: [PATCH] Fix Intel MIC 'mkoffload' for OpenMP 'requires'

Similar to how the other 'mkoffload's got changed in
recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

This also means finally switching Intel MIC 'mkoffload' to
'GOMP_offload_register_ver', 'GOMP_offload_unregister_ver',
making 'GOMP_offload_register', 'GOMP_offload_unregister'
legacy entry points.

	gcc/
	* config/i386/intelmic-mkoffload.cc (generate_host_descr_file)
	(prepare_target_image, main): Handle OpenMP 'requires'.
	(generate_host_descr_file): Switch to 'GOMP_offload_register_ver',
	'GOMP_offload_unregister_ver'.
	libgomp/
	* target.c (GOMP_offload_register, GOMP_offload_unregister):
	Denote as legacy entry points.
	* testsuite/lib/libgomp.exp
	(check_effective_target_offload_target_any): New proc.
	* testsuite/libgomp.c-c++-common/requires-1.c: Enable for
	'offload_target_any'.
	* testsuite/libgomp.c-c++-common/requires-3.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
	* testsuite/libgomp.fortran/requires-1.f90: Likewise.
---
 gcc/config/i386/intelmic-mkoffload.cc         | 56 +++++++++++++++----
 libgomp/target.c                              |  4 ++
 libgomp/testsuite/lib/libgomp.exp             |  5 ++
 .../libgomp.c-c++-common/requires-1.c         |  2 +-
 .../libgomp.c-c++-common/requires-3.c         |  2 +-
 .../libgomp.c-c++-common/requires-7.c         |  2 +-
 .../testsuite/libgomp.fortran/requires-1.f90  |  2 +-
 7 files changed, 57 insertions(+), 16 deletions(-)

diff --git a/gcc/config/i386/intelmic-mkoffload.cc b/gcc/config/i386/intelmic-mkoffload.cc
index c683d6f473e..596f6f107b8 100644
--- a/gcc/config/i386/intelmic-mkoffload.cc
+++ b/gcc/config/i386/intelmic-mkoffload.cc
@@ -370,7 +370,7 @@ generate_target_offloadend_file (const char *target_compiler)
 
 /* Generates object file with the host side descriptor.  */
 static const char *
-generate_host_descr_file (const char *host_compiler)
+generate_host_descr_file (const char *host_compiler, uint32_t omp_requires)
 {
   char *dump_filename = concat (dumppfx, "_host_descr.c", NULL);
   const char *src_filename = save_temps
@@ -386,39 +386,50 @@ generate_host_descr_file (const char *host_compiler)
   if (!src_file)
     fatal_error (input_location, "cannot open '%s'", src_filename);
 
+  fprintf (src_file, "#include <stdint.h>\n\n");
+
   fprintf (src_file,
 	   "extern const void *const __OFFLOAD_TABLE__;\n"
 	   "extern const void *const __offload_image_intelmic_start;\n"
 	   "extern const void *const __offload_image_intelmic_end;\n\n"
 
-	   "static const void *const __offload_target_data[] = {\n"
+	   "static const struct intelmic_data {\n"
+	   "  uintptr_t omp_requires_mask;\n"
+	   "  const void *const image_start;\n"
+	   "  const void *const image_end;\n"
+	   "} intelmic_data = {\n"
+	   "  %d,\n"
 	   "  &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
-	   "};\n\n");
+	   "};\n\n", omp_requires);
 
   fprintf (src_file,
 	   "#ifdef __cplusplus\n"
 	   "extern \"C\"\n"
 	   "#endif\n"
-	   "void GOMP_offload_register (const void *, int, const void *);\n"
+	   "void GOMP_offload_register_ver (unsigned, const void *, int, const void *);\n"
 	   "#ifdef __cplusplus\n"
 	   "extern \"C\"\n"
 	   "#endif\n"
-	   "void GOMP_offload_unregister (const void *, int, const void *);\n\n"
+	   "void GOMP_offload_unregister_ver (unsigned, const void *, int, const void *);\n\n"
 
 	   "__attribute__((constructor))\n"
 	   "static void\n"
 	   "init (void)\n"
 	   "{\n"
-	   "  GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
-	   "}\n\n", GOMP_DEVICE_INTEL_MIC);
+	   "  GOMP_offload_register_ver (%#x, &__OFFLOAD_TABLE__, %d, &intelmic_data);\n"
+	   "}\n\n",
+	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
+	   GOMP_DEVICE_INTEL_MIC);
 
   fprintf (src_file,
 	   "__attribute__((destructor))\n"
 	   "static void\n"
 	   "fini (void)\n"
 	   "{\n"
-	   "  GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
-	   "}\n", GOMP_DEVICE_INTEL_MIC);
+	   "  GOMP_offload_unregister_ver (%#x, &__OFFLOAD_TABLE__, %d, &intelmic_data);\n"
+	   "}\n",
+	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_INTEL_MIC),
+	   GOMP_DEVICE_INTEL_MIC);
 
   fclose (src_file);
 
@@ -462,7 +473,7 @@ generate_host_descr_file (const char *host_compiler)
 }
 
 static const char *
-prepare_target_image (const char *target_compiler, int argc, char **argv)
+prepare_target_image (const char *target_compiler, int argc, char **argv, uint32_t *omp_requires)
 {
   const char *target_descr_filename
     = generate_target_descr_file (target_compiler);
@@ -509,8 +520,26 @@ prepare_target_image (const char *target_compiler, int argc, char **argv)
   obstack_ptr_grow (&argv_obstack, "");
   obstack_ptr_grow (&argv_obstack, "-o");
   obstack_ptr_grow (&argv_obstack, target_so_filename);
+
+  char *omp_requires_file;
+  if (save_temps)
+    omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
+  else
+    omp_requires_file = make_temp_file (".mkoffload.omp_requires");
+  xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
+
   compile_for_target (&argv_obstack);
 
+  unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
+  FILE *in = fopen (omp_requires_file, "rb");
+  if (!in)
+    fatal_error (input_location, "cannot open omp_requires file %qs",
+		 omp_requires_file);
+  if (fread (omp_requires, sizeof (*omp_requires), 1, in) != 1)
+    fatal_error (input_location, "cannot read omp_requires file %qs",
+		 omp_requires_file);
+  fclose (in);
+
   /* Run objcopy.  */
   char *rename_section_opt
     = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
@@ -643,10 +672,13 @@ main (int argc, char **argv)
   if (!dumppfx)
     dumppfx = out_obj_filename;
 
+  uint32_t omp_requires;
+
   const char *target_so_filename
-    = prepare_target_image (target_compiler, argc, argv);
+    = prepare_target_image (target_compiler, argc, argv, &omp_requires);
 
-  const char *host_descr_filename = generate_host_descr_file (host_compiler);
+  const char *host_descr_filename
+    = generate_host_descr_file (host_compiler, omp_requires);
 
   /* Perform partial linking for the target image and host side descriptor.
      As a result we'll get a finalized object file with all offload data.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index c66c61b0621..86f9d3050d9 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2410,6 +2410,8 @@ GOMP_offload_register_ver (unsigned version, const void *host_table,
   gomp_mutex_unlock (&register_lock);
 }
 
+/* Legacy entry point.  */
+
 void
 GOMP_offload_register (const void *host_table, int target_type,
 		       const void *target_data)
@@ -2462,6 +2464,8 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
   gomp_mutex_unlock (&register_lock);
 }
 
+/* Legacy entry point.  */
+
 void
 GOMP_offload_unregister (const void *host_table, int target_type,
 			 const void *target_data)
diff --git a/libgomp/testsuite/lib/libgomp.exp b/libgomp/testsuite/lib/libgomp.exp
index 891f90929d2..107a3c2ac9d 100644
--- a/libgomp/testsuite/lib/libgomp.exp
+++ b/libgomp/testsuite/lib/libgomp.exp
@@ -348,6 +348,11 @@ proc libgomp_check_effective_target_offload_target { target_name } {
     return 0
 }
 
+# Return 1 if compiling for any offload target.
+proc check_effective_target_offload_target_any { } {
+    return [libgomp_check_effective_target_offload_target ""]
+}
+
 # Return 1 if compiling for offload target nvptx.
 proc check_effective_target_offload_target_nvptx { } {
     return [libgomp_check_effective_target_offload_target "nvptx"]
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
index fedf9779769..ab9a8ddfcde 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do link { target offload_target_any } } */
 /* { dg-additional-sources requires-1-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
index 7091f400ef0..1c204c8a21e 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do link { target offload_target_any } } */
 /* { dg-additional-sources requires-3-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
index c94a4c10846..7473aa62e08 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -1,4 +1,4 @@
-/* { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } } */
+/* { dg-do link { target offload_target_any } } */
 /* { dg-additional-sources requires-7-aux.c } */
 
 /* Check diagnostic by device-compiler's lto1.
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
index 33741af15f1..e957b1b5918 100644
--- a/libgomp/testsuite/libgomp.fortran/requires-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -1,4 +1,4 @@
-! { dg-do link { target { offload_target_nvptx || offload_target_amdgcn } } }
+! { dg-do link { target offload_target_any } }
 ! { dg-additional-sources requires-1-aux.f90 }
 
 ! Check diagnostic by device-compiler's lto1.
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-01 21:08                         ` Tobias Burnus
  2022-07-04  8:31                           ` Jakub Jelinek
@ 2022-07-07 13:26                           ` Thomas Schwinge
  2022-07-07 13:56                             ` Tobias Burnus
  1 sibling, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-07 13:26 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 5621 bytes --]

Hi!

On 2022-07-01T23:08:16+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> Updated version attached – I hope I got everything right, but I start to
> get tired, I am not 100% sure.

..., and so the obligatory copy'n'past-o ;-) crept in:

> --- a/gcc/lto-cgraph.cc
> +++ b/gcc/lto-cgraph.cc

> @@ -1773,6 +1804,10 @@ input_offload_tables (bool do_force_output)
>    struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
>    struct lto_file_decl_data *file_data;
>    unsigned int j = 0;
> +  const char *requires_fn = NULL;
> +  tree requires_decl = NULL_TREE;
> +
> +  omp_requires_mask = (omp_requires) 0;
>
>    while ((file_data = file_data_vec[j++]))
>      {
> @@ -1784,6 +1819,7 @@ input_offload_tables (bool do_force_output)
>        if (!ib)
>       continue;
>
> +      tree tmp_decl = NULL_TREE;
>        enum LTO_symtab_tags tag
>       = streamer_read_enum (ib, LTO_symtab_tags, LTO_symtab_last_tag);
>        while (tag)
> @@ -1799,6 +1835,7 @@ input_offload_tables (bool do_force_output)
>                LTO mode.  */
>             if (do_force_output)
>               cgraph_node::get (fn_decl)->mark_force_output ();
> +           tmp_decl = fn_decl;
>           }
>         else if (tag == LTO_symtab_variable)
>           {
> @@ -1810,6 +1847,72 @@ input_offload_tables (bool do_force_output)
>                may be no refs to var_decl in offload LTO mode.  */
>             if (do_force_output)
>               varpool_node::get (var_decl)->force_output = 1;
> +           tmp_decl = var_decl;
> +         }
> +       else if (tag == LTO_symtab_edge)
> +         {
> +           static bool error_emitted = false;
> +           HOST_WIDE_INT val = streamer_read_hwi (ib);
> +
> +           if (omp_requires_mask == 0)
> +             {
> +               omp_requires_mask = (omp_requires) val;
> +               requires_decl = tmp_decl;
> +               requires_fn = file_data->file_name;
> +             }
> +           else if (omp_requires_mask != val && !error_emitted)
> +             {
> +               const char *fn1 = requires_fn;
> +               if (requires_decl != NULL_TREE)
> +                 {
> +                   while (DECL_CONTEXT (requires_decl) != NULL_TREE
> +                          && TREE_CODE (requires_decl) != TRANSLATION_UNIT_DECL)
> +                     requires_decl = DECL_CONTEXT (requires_decl);
> +                   if (requires_decl != NULL_TREE)
> +                     fn1 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
> +                 }
> +
> +               const char *fn2 = file_data->file_name;
> +               if (tmp_decl != NULL_TREE)
> +                 {
> +                   while (DECL_CONTEXT (tmp_decl) != NULL_TREE
> +                          && TREE_CODE (tmp_decl) != TRANSLATION_UNIT_DECL)
> +                     tmp_decl = DECL_CONTEXT (tmp_decl);
> +                   if (tmp_decl != NULL_TREE)
> +                     fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
> +                 }

... here: tmp_decl' not 'requires_decl'.  OK to push the attached
"Fix one issue in OpenMP 'requires' directive diagnostics"?

I'd even push that one "as obvious", but thought I'd ask whether you
maybe have a quick idea about the XFAILs that I'm adding?  (I'm otherwise
not planning on resolving that issue at this time.)

> +
> +               char buf1[sizeof ("unified_address, unified_shared_memory, "
> +                                 "reverse_offload")];
> +               char buf2[sizeof ("unified_address, unified_shared_memory, "
> +                                 "reverse_offload")];
> +               omp_requires_to_name (buf2, sizeof (buf2),
> +                                     val != OMP_REQUIRES_TARGET_USED
> +                                     ? val
> +                                     : (HOST_WIDE_INT) omp_requires_mask);
> +               if (val != OMP_REQUIRES_TARGET_USED
> +                   && omp_requires_mask != OMP_REQUIRES_TARGET_USED)
> +                 {
> +                   omp_requires_to_name (buf1, sizeof (buf1),
> +                                         omp_requires_mask);
> +                   error ("OpenMP %<requires%> directive with non-identical "
> +                          "clauses in multiple compilation units: %qs vs. "
> +                          "%qs", buf1, buf2);
> +                   inform (UNKNOWN_LOCATION, "%qs has %qs", fn1, buf1);
> +                   inform (UNKNOWN_LOCATION, "%qs has %qs", fn2, buf2);
> +                 }
> +               else
> +                 {
> +                   error ("OpenMP %<requires%> directive with %qs specified "
> +                          "only in some compilation units", buf2);
> +                   inform (UNKNOWN_LOCATION, "%qs has %qs",
> +                           val != OMP_REQUIRES_TARGET_USED ? fn2 : fn1,
> +                           buf2);
> +                   inform (UNKNOWN_LOCATION, "but %qs has not",
> +                           val != OMP_REQUIRES_TARGET_USED ? fn1 : fn2);
> +                 }
> +               error_emitted = true;
> +             }
>           }
>         else
>           fatal_error (input_location,


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-one-issue-in-OpenMP-requires-directive-diagnosti.patch --]
[-- Type: text/x-diff, Size: 6833 bytes --]

From 2271282f127366e785601242a46d4aa668bd6660 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 7 Jul 2022 15:11:03 +0200
Subject: [PATCH] Fix one issue in OpenMP 'requires' directive diagnostics

Fix-up for recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	gcc/
	* lto-cgraph.cc (input_offload_tables) <LTO_symtab_edge>: Correct
	'fn2' computation.
	libgomp/
	* testsuite/libgomp.c-c++-common/requires-1.c: Add 'dg-note's.
	* testsuite/libgomp.c-c++-common/requires-2.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-3.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
	* testsuite/libgomp.fortran/requires-1.f90: Likewise.
---
 gcc/lto-cgraph.cc                                   | 2 +-
 libgomp/testsuite/libgomp.c-c++-common/requires-1.c | 4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-2.c | 4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-3.c | 4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-7.c | 7 ++++++-
 libgomp/testsuite/libgomp.fortran/requires-1.f90    | 5 +++++
 6 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 48629651e31..6d9c36ea8b6 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -1879,7 +1879,7 @@ input_offload_tables (bool do_force_output)
 			     && TREE_CODE (tmp_decl) != TRANSLATION_UNIT_DECL)
 			tmp_decl = DECL_CONTEXT (tmp_decl);
 		      if (tmp_decl != NULL_TREE)
-			fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
+			fn2 = IDENTIFIER_POINTER (DECL_NAME (tmp_decl));
 		    }
 
 		  char buf1[sizeof ("unified_address, unified_shared_memory, "
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
index ab9a8ddfcde..31996f1ecf6 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -20,5 +20,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-1\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {requires-1-aux\.c' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
index be1830d0c46..b20e154b0c7 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -21,5 +21,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with 'unified_shared_memory' specified only in some compilation units" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with 'unified_shared_memory' specified only in some compilation units" "" { target *-*-* } 0 }
+     { dg-note {requires-2\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {but '[^']*requires-2-aux\.c' has not} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
index 1c204c8a21e..a549a19ebbb 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -20,5 +20,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-3\.c' has 'unified_address, unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {requires-3-aux\.c' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
index 7473aa62e08..370ed338e4c 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -20,5 +20,10 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-7\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     TODO There is some issue that we're not seeing the source file name here (but a temporary '*.o' instead):
+     { dg-note {requires-7-aux\.c' has 'unified_address'} {} { xfail *-*-* } 0 }
+     ..., so verify that at least the rest of the diagnostic is correct:
+     { dg-note {' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
index e957b1b5918..59211496c33 100644
--- a/libgomp/testsuite/libgomp.fortran/requires-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -23,4 +23,9 @@ program main
 end
 
 ! { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+!   { dg-note {requires-1\.f90' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+!   TODO There is some issue that we're not seeing the source file name here (but a temporary '*.o' instead):
+!   { dg-note {requires-1-aux\.f90' has 'unified_address'} {} { xfail *-*-* } 0 }
+!   ..., so verify that at least the rest of the diagnostic is correct:
+!   { dg-note {' has 'unified_address'} {} { target *-*-* } 0 }
 ! { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-07 13:26                           ` Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
@ 2022-07-07 13:56                             ` Tobias Burnus
  2022-07-08  6:59                               ` Thomas Schwinge
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2022-07-07 13:56 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

Hi Thomas,

On 07.07.22 15:26, Thomas Schwinge wrote:
> On 2022-07-01T23:08:16+0200, Tobias Burnus <tobias@codesourcery.com>
> wrote:
>> Updated version attached – I hope I got everything right, but I start to
>> get tired, I am not 100% sure.
> ..., and so the obligatory copy'n'past-o;-)  crept in:
...
>> +                  if (tmp_decl != NULL_TREE)
>> +                    fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
>> +                }
> ... here: tmp_decl' not 'requires_decl'.  OK to push the attached
> "Fix one issue in OpenMP 'requires' directive diagnostics"?
Good that you spotted it and thanks for testing + fixing it!
> I'd even push that one "as obvious", but thought I'd ask whether you
> maybe have a quick idea about the XFAILs that I'm adding?  (I'm otherwise
> not planning on resolving that issue at this time.)

(This question relates to what's printed if there is no TRANSLATION_UNIT_DECL.)

  * * *

Pre-remark - the code does:

* If there is any offload_func or offload_var DECL in the TU, it uses
   that one for diagnostic.
   This is always the case if there is a 'declare target' or 'omp target'
   but not when there is only 'omp target data'.
   → In real-world code it likely has a proper name.

* Otherwise, it takes the file name of file_data->file_name.

With -save-temps, that's based on the input files, which
gives a useful output.

When using
   gcc -c *.c
   gcc *.o
the file name is <inputfile>.o - which is also quite useful.

However, when doing
   gcc *.c
which combines compiling and linking in one step, the filename
is /tmp/cc*.o which is not that helpful.

There is no real way to avoid this, unless we explicitly store the
filename or some location_t for the 'requires'. But the used
LTO writer does not support it directly. It can be fixed, but
requires some re-organization and increased intermittent .o file
size. (Cf. https://gcc.gnu.org/pipermail/gcc-patches/2022-June/597496.html
for what it needed and why it does not work.)

However, in the real world, there should be usually a proper message as
(a) It is unlikely to have code which only does 'omp target ... data'
     transfer - and no 'omp target'
and
(b) for larger code, separating compilation and linking is common
     while for smaller code, 'requires' mismatches is less likely and
     also easier to find the file causing the issue.

Still, like always, having a nice diagnostic would not harm :-)

  * * *

> Subject: [PATCH] Fix one issue in OpenMP 'requires' directive diagnostics
>
> Fix-up for recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
> "OpenMP: Move omp requires checks to libgomp".
>
>       gcc/
>       * lto-cgraph.cc (input_offload_tables) <LTO_symtab_edge>: Correct
>       'fn2' computation.
>       libgomp/
>       * testsuite/libgomp.c-c++-common/requires-1.c: Add 'dg-note's.
>       * testsuite/libgomp.c-c++-common/requires-2.c: Likewise.
>       * testsuite/libgomp.c-c++-common/requires-3.c: Likewise.
>       * testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
>       * testsuite/libgomp.fortran/requires-1.f90: Likewise.

Regarding the patch, it adds 'dg-note' for the location data - and fixes the wrong-decl-use bug.

Those LGTM - Thanks!

Regarding the xfail part:

> --- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
...
>   -/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
> +/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
> +     { dg-note {requires-7\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
> +     TODO There is some issue that we're not seeing the source file name here (but a temporary '*.o' instead):
> +     { dg-note {requires-7-aux\.c' has 'unified_address'} {} { xfail *-*-* } 0 }
> +     ..., so verify that at least the rest of the diagnostic is correct:
> +     { dg-note {' has 'unified_address'} {} { target *-*-* } 0 } */

The requires-7-aux.c file uses, on purpose, only 'omp target enter data'
to trigger the .o name in 'inform' as no decl is written to
offload_func/offload_vars for that TU.

As the testsuite compiles+links the two requires-7*.c files in one step
and is invoked without -save-temps, the used object file names will be
/tmp/cc*.o.

* * *

Regarding the xfail: I think it is fine to have this xfail, but as it is
clear why inform points to /tmp/cc*.o, you could reword the TODO to
state why it goes wrong.

Thanks,

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-07 13:56                             ` Tobias Burnus
@ 2022-07-08  6:59                               ` Thomas Schwinge
  0 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2022-07-08  6:59 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 2272 bytes --]

Hi!

On 2022-07-07T15:56:28+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 07.07.22 15:26, Thomas Schwinge wrote:
>> On 2022-07-01T23:08:16+0200, Tobias Burnus <tobias@codesourcery.com>
>> wrote:
>>> Updated version attached – I hope I got everything right, but I start to
>>> get tired, I am not 100% sure.
>> ..., and so the obligatory copy'n'past-o;-)  crept in:
> ...
>>> +                 if (tmp_decl != NULL_TREE)
>>> +                   fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
>>> +               }
>> ... here: tmp_decl' not 'requires_decl'.  OK to push the attached
>> "Fix one issue in OpenMP 'requires' directive diagnostics"?
> Good that you spotted it and thanks for testing + fixing it!
>> I'd even push that one "as obvious", but thought I'd ask whether you
>> maybe have a quick idea about the XFAILs that I'm adding?  (I'm otherwise
>> not planning on resolving that issue at this time.)
>
> (This question relates to what's printed if there is no TRANSLATION_UNIT_DECL.)
> [...]

Thanks for the explanation, makes sense.

> Regarding the xfail: I think it is fine to have this xfail, but as it is
> clear why inform points to /tmp/cc*.o, you could reword the TODO to
> state why it goes wrong.

Done:

    TODO We're currently not streaming location information for the OpenMP
    directives used in 'requires-7-aux.c', so we're not seeing the source file
    name here (but a temporary '*.o' instead; for details, see
    <https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598011.html>):
    { dg-note {requires-7-aux\.c' has 'unified_address'} {} { xfail *-*-* } 0 }
    ..., but we may still verify that the rest of the diagnostic is correct:
    { dg-note {' has 'unified_address'} {} { target *-*-* } 0 }

With just that changed, I've pushed to master branch
commit faa0c328ee65f0d6d65d6e20181d26e336071919
"Fix one issue in OpenMP 'requires' directive diagnostics", see attached.


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-one-issue-in-OpenMP-requires-directive-diagnosti.patch --]
[-- Type: text/x-diff, Size: 7223 bytes --]

From faa0c328ee65f0d6d65d6e20181d26e336071919 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 7 Jul 2022 15:11:03 +0200
Subject: [PATCH] Fix one issue in OpenMP 'requires' directive diagnostics

Fix-up for recent commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".

	gcc/
	* lto-cgraph.cc (input_offload_tables) <LTO_symtab_edge>: Correct
	'fn2' computation.
	libgomp/
	* testsuite/libgomp.c-c++-common/requires-1.c: Add 'dg-note's.
	* testsuite/libgomp.c-c++-common/requires-2.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-3.c: Likewise.
	* testsuite/libgomp.c-c++-common/requires-7.c: Likewise.
	* testsuite/libgomp.fortran/requires-1.f90: Likewise.
---
 gcc/lto-cgraph.cc                                   |  2 +-
 libgomp/testsuite/libgomp.c-c++-common/requires-1.c |  4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-2.c |  4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-3.c |  4 +++-
 libgomp/testsuite/libgomp.c-c++-common/requires-7.c | 10 +++++++++-
 libgomp/testsuite/libgomp.fortran/requires-1.f90    |  8 ++++++++
 6 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 48629651e31..6d9c36ea8b6 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -1879,7 +1879,7 @@ input_offload_tables (bool do_force_output)
 			     && TREE_CODE (tmp_decl) != TRANSLATION_UNIT_DECL)
 			tmp_decl = DECL_CONTEXT (tmp_decl);
 		      if (tmp_decl != NULL_TREE)
-			fn2 = IDENTIFIER_POINTER (DECL_NAME (requires_decl));
+			fn2 = IDENTIFIER_POINTER (DECL_NAME (tmp_decl));
 		    }
 
 		  char buf1[sizeof ("unified_address, unified_shared_memory, "
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
index ab9a8ddfcde..31996f1ecf6 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-1.c
@@ -20,5 +20,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-1\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {requires-1-aux\.c' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
index be1830d0c46..b20e154b0c7 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-2.c
@@ -21,5 +21,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with 'unified_shared_memory' specified only in some compilation units" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with 'unified_shared_memory' specified only in some compilation units" "" { target *-*-* } 0 }
+     { dg-note {requires-2\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {but '[^']*requires-2-aux\.c' has not} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
index 1c204c8a21e..a549a19ebbb 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-3.c
@@ -20,5 +20,7 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_address, unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-3\.c' has 'unified_address, unified_shared_memory'} {} { target *-*-* } 0 }
+     { dg-note {requires-3-aux\.c' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
index 7473aa62e08..63afcc92b9a 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/requires-7.c
@@ -20,5 +20,13 @@ main (void)
   return 0;
 }
 
-/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }  */
+/* { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+     { dg-note {requires-7\.c' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+     TODO We're currently not streaming location information for the OpenMP
+     directives used in 'requires-7-aux.c', so we're not seeing the source file
+     name here (but a temporary '*.o' instead; for details, see
+     <https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598011.html>):
+     { dg-note {requires-7-aux\.c' has 'unified_address'} {} { xfail *-*-* } 0 }
+     ..., but we may still verify that the rest of the diagnostic is correct:
+     { dg-note {' has 'unified_address'} {} { target *-*-* } 0 } */
 /* { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" } */
diff --git a/libgomp/testsuite/libgomp.fortran/requires-1.f90 b/libgomp/testsuite/libgomp.fortran/requires-1.f90
index e957b1b5918..9a0c33461f8 100644
--- a/libgomp/testsuite/libgomp.fortran/requires-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/requires-1.f90
@@ -23,4 +23,12 @@ program main
 end
 
 ! { dg-error "OpenMP 'requires' directive with non-identical clauses in multiple compilation units: 'unified_shared_memory' vs. 'unified_address'" "" { target *-*-* } 0 }
+!   { dg-note {requires-1\.f90' has 'unified_shared_memory'} {} { target *-*-* } 0 }
+!   TODO We're currently not streaming location information for the OpenMP
+!   directives used in 'requires-7-aux.c', so we're not seeing the source file
+!   name here (but a temporary '*.o' instead; for details, see
+!   <https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598011.html>):
+!   { dg-note {requires-1-aux\.f90' has 'unified_address'} {} { xfail *-*-* } 0 }
+!   ..., but we may still verify that the rest of the diagnostic is correct:
+!   { dg-note {' has 'unified_address'} {} { target *-*-* } 0 }
 ! { dg-excess-errors "Ignore messages like: errors during merging of translation units|mkoffload returned 1 exit status" }
-- 
2.35.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Restore 'GOMP_offload_unregister_ver' functionality (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp)
  2022-07-06 13:59                     ` Tobias Burnus
  2022-07-06 21:08                       ` Thomas Schwinge
@ 2022-08-17 11:45                       ` Jakub Jelinek
  1 sibling, 0 replies; 42+ messages in thread
From: Jakub Jelinek @ 2022-08-17 11:45 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: Thomas Schwinge, gcc-patches

On Wed, Jul 06, 2022 at 03:59:59PM +0200, Tobias Burnus wrote:
> > @@ -2436,12 +2452,15 @@ GOMP_offload_unregister_ver (unsigned version, const void *host_table,
> >       }
> > 
> >     /* Remove image from array of pending images.  */
> > +  bool found = false;
> >     for (i = 0; i < num_offload_images; i++)
> >       if (offload_images[i].target_data == target_data)
> >         {
> >       offload_images[i] = offload_images[--num_offload_images];
> > +     found = true;
> >       break;
> >         }
> > +  assert (found);
> > 
> >     gomp_mutex_unlock (&register_lock);
> >   }
> 
> ... I don't like that libgomp crashes without any helpful message in that case.
> 
> In my opinion:
> * Either we assume that it is unlikely to occur - ignore it.
>   (Matches the current implementation: do nothing.)

I think we don't need any asserts here, nor gomp_error/gomp_fatal.  The GOMP_* APIs
aren't meant for direct user uses, they are to be only called by compiler
generated code or support object files, and in the case of
GOMP_offload_{,un}register_ver the crt file makes sure it will be registered
and unregistered in pairs.

	Jakub


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [Patch][v5] OpenMP: Move omp requires checks to libgomp
  2022-07-01 13:06                 ` [Patch][v5] " Tobias Burnus
  2022-07-01 14:34                   ` Jakub Jelinek
  2022-07-06 10:42                   ` Restore 'GOMP_offload_unregister_ver' functionality " Thomas Schwinge
@ 2023-09-15  9:41                   ` Thomas Schwinge
  2 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2023-09-15  9:41 UTC (permalink / raw)
  To: Tobias Burnus, Jakub Jelinek; +Cc: gcc-patches

Hi!

On 2022-07-01T15:06:05+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> OpenMP: Move omp requires checks to libgomp

This became commit r13-1458-g683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp".


As of this, when I need to debug an offloading-compilation ICE, for
example, and start with 'gcc -save-temps -v', I can no longer just re-run
the offloading-compilation's 'lto1' invocation, because:

    $ [...]/gcc/lto1 [...] -foffload-abi=lp64 -fopenmp [...]
    [...]
    lto1: fatal error: GCC_OFFLOAD_OMP_REQUIRES_FILE unset
    compilation terminated.

That's because I missed setting the environment variable:

    GCC_OFFLOAD_OMP_REQUIRES_FILE=./declare-variant-1.xnvptx-none.mkoffload.omp_requires

..., which appears a number of lines earlier in the '-v' log.  Couldn't
we easily overcome this issue by turning the environment variable
'GCC_OFFLOAD_OMP_REQUIRES_FILE' into some new internal-use command-line
flag, like '-foffload-abi'?  That is, communication mechanics via
'[...].mkoffload.omp_requires' files would stay the same, just how we
communicate the file name changes: command-line flag instead of
environment variable.

For reference:

> --- a/gcc/config/gcn/mkoffload.cc
> +++ b/gcc/config/gcn/mkoffload.cc

> @@ -1077,9 +1080,27 @@ main (int argc, char **argv)
>        unsetenv ("COMPILER_PATH");
>        unsetenv ("LIBRARY_PATH");
>
> +      char *omp_requires_file;
> +      if (save_temps)
> +     omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
> +      else
> +     omp_requires_file = make_temp_file (".mkoffload.omp_requires");
> +
>        /* Run the compiler pass.  */
> +      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
>        fork_execute (cc_argv[0], CONST_CAST (char **, cc_argv), true, ".gcc_args");
>        obstack_free (&cc_argv_obstack, NULL);
> +      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
> +
> +      in = fopen (omp_requires_file, "rb");
> +      if (!in)
> +     fatal_error (input_location, "cannot open omp_requires file %qs",
> +                  omp_requires_file);
> +      uint32_t omp_requires;
> +      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
> +     fatal_error (input_location, "cannot read omp_requires file %qs",
> +                  omp_requires_file);
> +      fclose (in);

> --- a/gcc/config/nvptx/mkoffload.cc
> +++ b/gcc/config/nvptx/mkoffload.cc

> @@ -583,19 +586,37 @@ main (int argc, char **argv)
>        unsetenv ("COMPILER_PATH");
>        unsetenv ("LIBRARY_PATH");
>
> +      char *omp_requires_file;
> +      if (save_temps)
> +     omp_requires_file = concat (dumppfx, ".mkoffload.omp_requires", NULL);
> +      else
> +     omp_requires_file = make_temp_file (".mkoffload.omp_requires");
> +
> +      xputenv (concat ("GCC_OFFLOAD_OMP_REQUIRES_FILE=", omp_requires_file, NULL));
>        fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true,
>                   ".gcc_args");
>        obstack_free (&argv_obstack, NULL);
> +      unsetenv("GCC_OFFLOAD_OMP_REQUIRES_FILE");
>
>        xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
>        xputenv (concat ("COMPILER_PATH=", cpath, NULL));
>        xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
>
> +      in = fopen (omp_requires_file, "rb");
> +      if (!in)
> +     fatal_error (input_location, "cannot open omp_requires file %qs",
> +                  omp_requires_file);
> +      uint32_t omp_requires;
> +      if (fread (&omp_requires, sizeof (omp_requires), 1, in) != 1)
> +     fatal_error (input_location, "cannot read omp_requires file %qs",
> +                  omp_requires_file);
> +      fclose (in);

> --- a/gcc/lto-cgraph.cc
> +++ b/gcc/lto-cgraph.cc

> @@ -1821,6 +1906,18 @@ input_offload_tables (bool do_force_output)
>        lto_destroy_simple_input_block (file_data, LTO_section_offload_table,
>                                     ib, data, len);
>      }
> +#ifdef ACCEL_COMPILER
> +  char *omp_requires_file = getenv ("GCC_OFFLOAD_OMP_REQUIRES_FILE");
> +  if (omp_requires_file == NULL || omp_requires_file[0] == '\0')
> +    fatal_error (input_location, "GCC_OFFLOAD_OMP_REQUIRES_FILE unset");
> +  FILE *f = fopen (omp_requires_file, "wb");
> +  if (!f)
> +    fatal_error (input_location, "Cannot open omp_requires file %qs",
> +              omp_requires_file);
> +  uint32_t req_mask = omp_requires_mask & ~OMP_REQUIRES_TARGET_USED;
> +  fwrite (&req_mask, sizeof (req_mask), 1, f);
> +  fclose (f);
> +#endif
>  }


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 42+ messages in thread

* nvptx: 'cuDeviceGetCount' failure is fatal (was: [Patch] OpenMP: Move omp requires checks to libgomp)
  2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
                     ` (2 preceding siblings ...)
  2022-07-06 11:04   ` Fix Intel MIC 'mkoffload' for OpenMP 'requires' " Thomas Schwinge
@ 2024-03-07 12:38   ` Thomas Schwinge
  2024-03-07 14:28     ` nvptx: 'cuDeviceGetCount' failure is fatal Tobias Burnus
  3 siblings, 1 reply; 42+ messages in thread
From: Thomas Schwinge @ 2024-03-07 12:38 UTC (permalink / raw)
  To: Tobias Burnus, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1862 bytes --]

Hi!

On 2022-06-08T05:56:02+0200, Tobias Burnus <Tobias_Burnus@mentor.com> wrote:
> [...] On the libgomp side: The devices which do not fulfill the requirements are
> now filtered out.  [...]

> --- a/libgomp/plugin/plugin-gcn.c
> +++ b/libgomp/plugin/plugin-gcn.c

>  /* Return the number of GCN devices on the system.  */
>  
>  int
> -GOMP_OFFLOAD_get_num_devices (void)
> +GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
>  {
>    if (!init_hsa_context ())
>      return 0;
> +  /* Return -1 if no omp_requires_mask cannot be fulfilled but
> +     devices were present.  */
> +  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
> +    return -1;
>    return hsa_context.agent_count;
>  }

> --- a/libgomp/plugin/plugin-nvptx.c
> +++ b/libgomp/plugin/plugin-nvptx.c

>  int
> -GOMP_OFFLOAD_get_num_devices (void)
> +GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
>  {
> -  return nvptx_get_num_devices ();
> +  int num_devices = nvptx_get_num_devices ();
> +  /* Return -1 if no omp_requires_mask cannot be fulfilled but
> +     devices were present.  */
> +  if (num_devices > 0 && omp_requires_mask != 0)
> +    return -1;
> +  return num_devices;
>  }

> --- a/libgomp/target.c
> +++ b/libgomp/target.c

> @@ -4132,8 +4183,19 @@ gomp_target_init (void)
>  
>  	if (gomp_load_plugin_for_device (&current_device, plugin_name))
>  	  {
> -	    new_num_devs = current_device.get_num_devices_func ();
> -	    if (new_num_devs >= 1)
> +	    new_num_devs = current_device.get_num_devices_func (requires_mask);
> +	    if (new_num_devs < 0)
> +	      {
> +		[...]
> +	      }
> +	    else if (new_num_devs >= 1)
>  	      {
>  		/* Augment DEVICES and NUM_DEVICES.  */

OK to push the attached "nvptx: 'cuDeviceGetCount' failure is fatal"?


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-nvptx-cuDeviceGetCount-failure-is-fatal.patch --]
[-- Type: text/x-diff, Size: 1317 bytes --]

From 8090da93cb00e4aa47a8b21b6548d739b2cebc49 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <tschwinge@baylibre.com>
Date: Thu, 7 Mar 2024 13:18:23 +0100
Subject: [PATCH] nvptx: 'cuDeviceGetCount' failure is fatal

Per commit 683f11843974f0bdf42f79cdcbb0c2b43c7b81b0
"OpenMP: Move omp requires checks to libgomp", we're now using 'return -1'
from 'GOMP_OFFLOAD_get_num_devices' for 'omp_requires_mask' purposes.  This
missed that via 'nvptx_get_num_devices', we could also 'return -1' for
'cuDeviceGetCount' failure.  Before, this meant (in 'gomp_target_init') to
silently ignore the plugin/device -- which also has been doubtful behavior.
Let's instead turn 'cuDeviceGetCount' failure into a fatal error, similar to
other errors during device initialization.

	libgomp/
	* plugin/plugin-nvptx.c (nvptx_get_num_devices):
	'cuDeviceGetCount' failure is fatal.
---
 libgomp/plugin/plugin-nvptx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index ffb1db67d20..81b4a7f499a 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -630,7 +630,7 @@ nvptx_get_num_devices (void)
 	}
     }
 
-  CUDA_CALL_ERET (-1, cuDeviceGetCount, &n);
+  CUDA_CALL_ASSERT (cuDeviceGetCount, &n);
   return n;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: nvptx: 'cuDeviceGetCount' failure is fatal
  2024-03-07 12:38   ` nvptx: 'cuDeviceGetCount' failure is fatal " Thomas Schwinge
@ 2024-03-07 14:28     ` Tobias Burnus
  2024-03-08 15:58       ` Thomas Schwinge
  0 siblings, 1 reply; 42+ messages in thread
From: Tobias Burnus @ 2024-03-07 14:28 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1443 bytes --]

Hi Thomas,

Thomas Schwinge wrote:
>> /* Return the number of GCN devices on the system. */  
>>   int
>> -GOMP_OFFLOAD_get_num_devices (void)
>> +GOMP_OFFLOAD_get_num_devices (unsigned int omp_requires_mask)
>>   {
>>     if (!init_hsa_context ())
>>       return 0;
>> +  /* Return -1 if no omp_requires_mask cannot be fulfilled but
>> +     devices were present.  */
>> +  if (hsa_context.agent_count > 0 && omp_requires_mask != 0)
>> +    return -1;
>>     return hsa_context.agent_count;
>>   }
...
> OK to push the attached "nvptx: 'cuDeviceGetCount' failure is fatal"?

I think the real question is: what does a 'cuDeviceGetCount' fail mean?

Does it mean a serious error – or could it just be a permissions issue 
such that the user has no device access but otherwise is fine?

Because if it is, e.g., a permission problem – just returning '0' (no 
devices) would seem to be the proper solution.

But if it is expected to be always something serious, well, then a fatal 
error makes more sense.

The possible exit codes are:

CUDA_SUCCESS, CUDA_ERROR_DEINITIALIZED, CUDA_ERROR_NOT_INITIALIZED, 
CUDA_ERROR_INVALID_CONTEXT, CUDA_ERROR_INVALID_VALUE

which does not really help.

My impression is that 0 is usually returned if something goes wrong 
(e.g. with permissions) such that an error is a real exception. But all 
three choices seem to make about equally sense: either host fallback 
(with 0 or -1) or a fatal error.

Tobias

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: nvptx: 'cuDeviceGetCount' failure is fatal
  2024-03-07 14:28     ` nvptx: 'cuDeviceGetCount' failure is fatal Tobias Burnus
@ 2024-03-08 15:58       ` Thomas Schwinge
  0 siblings, 0 replies; 42+ messages in thread
From: Thomas Schwinge @ 2024-03-08 15:58 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches, Jakub Jelinek

Hi Tobias!

On 2024-03-07T15:28:21+0100, Tobias Burnus <tburnus@baylibre.com> wrote:
> Thomas Schwinge wrote:
>> OK to push the attached "nvptx: 'cuDeviceGetCount' failure is fatal"?
>
> I think the real question is: what does a 'cuDeviceGetCount' fail mean?

Internally to the CUDA stack: the error codes that you've cited below.
Per the state we're in when calling 'cuDeviceGetCount', we only expect
'CUDA_SUCCESS'.  Therefore, in our actual use: anything else means a
fatal condition that we don't attempt to recover from, like for most of
all other device access failures.

> Does it mean a serious error – or could it just be a permissions issue 
> such that the user has no device access but otherwise is fine?

As you can see, we've done a 'cuInit' right before, so in case there was
any permission issue (or similar), that's already settled (in whichever
way) by the time we do the 'cuDeviceGetCount'.

> Because if it is, e.g., a permission problem – just returning '0' (no 
> devices) would seem to be the proper solution.
>
> But if it is expected to be always something serious, well, then a fatal 
> error makes more sense.

ACK; pushed in commit 37078f241a22c45db6380c5e9a79b4d08054bb3d.


Grüße
 Thomas


> The possible exit codes are:
>
> CUDA_SUCCESS, CUDA_ERROR_DEINITIALIZED, CUDA_ERROR_NOT_INITIALIZED, 
> CUDA_ERROR_INVALID_CONTEXT, CUDA_ERROR_INVALID_VALUE
>
> which does not really help.
>
> My impression is that 0 is usually returned if something goes wrong 
> (e.g. with permissions) such that an error is a real exception. But all 
> three choices seem to make about equally sense: either host fallback 
> (with 0 or -1) or a fatal error.
>
> Tobias

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2024-03-08 15:58 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-13 15:07 [PATCH, OpenMP 5.0] More implementation of the requires directive Chung-Lin Tang
2021-01-13 15:27 ` Jakub Jelinek
2021-03-25 11:18 ` Thomas Schwinge
2022-03-29 13:42 ` Andrew Stubbs
2022-06-08  3:56 ` [Patch] OpenMP: Move omp requires checks to libgomp Tobias Burnus
2022-06-09 11:40   ` Jakub Jelinek
2022-06-09 12:46     ` Tobias Burnus
2022-06-09 14:19       ` Jakub Jelinek
2022-06-29 14:33         ` [Patch][v4] " Tobias Burnus
2022-06-29 17:02           ` Jakub Jelinek
2022-06-29 18:10             ` Tobias Burnus
2022-06-29 20:18               ` Jakub Jelinek
2022-07-01 13:06                 ` [Patch][v5] " Tobias Burnus
2022-07-01 14:34                   ` Jakub Jelinek
2022-07-01 16:31                     ` Tobias Burnus
2022-07-01 16:55                       ` Jakub Jelinek
2022-07-01 21:08                         ` Tobias Burnus
2022-07-04  8:31                           ` Jakub Jelinek
2022-07-07 13:26                           ` Fix one issue in OpenMP 'requires' directive diagnostics (was: [Patch][v5] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
2022-07-07 13:56                             ` Tobias Burnus
2022-07-08  6:59                               ` Thomas Schwinge
2022-07-06 10:42                   ` Restore 'GOMP_offload_unregister_ver' functionality " Thomas Schwinge
2022-07-06 13:59                     ` Tobias Burnus
2022-07-06 21:08                       ` Thomas Schwinge
2022-08-17 11:45                       ` Jakub Jelinek
2023-09-15  9:41                   ` [Patch][v5] OpenMP: Move omp requires checks to libgomp Thomas Schwinge
2022-07-07  8:37           ` Adjust 'libgomp.c-c++-common/requires-3.c' (was: [Patch][v4] OpenMP: Move omp requires checks to libgomp) Thomas Schwinge
2022-07-07  9:02             ` Tobias Burnus
2022-07-07  8:42           ` Enhance 'libgomp.c-c++-common/requires-4.c', 'libgomp.c-c++-common/requires-5.c' testing " Thomas Schwinge
2022-07-07  9:36             ` Tobias Burnus
2022-07-07 10:42               ` Thomas Schwinge
2022-07-06 10:30   ` Define 'OMP_REQUIRES_[...]', 'GOMP_REQUIRES_[...]' in a single place (was: [Patch] " Thomas Schwinge
2022-07-06 13:40     ` Tobias Burnus
2022-07-06 11:04   ` Fix Intel MIC 'mkoffload' for OpenMP 'requires' " Thomas Schwinge
2022-07-06 11:29     ` Tobias Burnus
2022-07-06 12:38       ` Thomas Schwinge
2022-07-06 13:30         ` Tobias Burnus
2022-07-07 10:46           ` Thomas Schwinge
2022-07-06 14:19     ` Tobias Burnus
2024-03-07 12:38   ` nvptx: 'cuDeviceGetCount' failure is fatal " Thomas Schwinge
2024-03-07 14:28     ` nvptx: 'cuDeviceGetCount' failure is fatal Tobias Burnus
2024-03-08 15:58       ` Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).