v3:

Changes:

(A) The 'ret_code' arguments of omp_get_interop_{int,ptr,str} are actually 'optional'.

That's something that got lost in at some point between OpenMP 5.2 and TR13 (I filed OpenMP spec Issue #4165 for it). When adding it, I noticed that two '…_async' function lacked the '= NULL' for C++, permitting to omit the argument. — For my C and Fortran testcases, I added a test with NULL for C and omitted the argument for Fortran. I also changed the C code such that it also compiles with C++ and added a check that the omitted argument is handled correctly.

(B) Fixed a few libgomp/target.c issues, which sneaked in due to the wip patch for the libgomp plugin patch, posted at https://gcc.gnu.org/pipermail/gcc-patches/2024-August/661207.html (among others, it also contained some spurious spaces).

Build and regtested on x86-64-gnu-linux (w/o offloading configured).

Any additional comments, suggestions, remarks?

Andre Vehreschild wrote:
[…]
First, Thanks for your comments. However, regarding:
+omp_intptr_t
Do I get this correct, that omp_intptr_t is a pointer to an integer?

No 'intptr_t' is a (signed) integer type which is has (at least) the size of a pointer; in Fortran, that's 'integer(c_intptr_t)'. And 'omp_intptr_t' is just a typedef for 'intptr_t'. [BTW: I don't know why 'intptr_t' was used and not, e.g., int64_t or just 'int'.]

Tobias
libgomp: Add interop types and routines to OpenMP's headers and module

This commit adds OpenMP 5.1+'s interop enumeration, type and routine
declarations to the C/C++ header file and, new in OpenMP TR13, also to
the Fortran module and omp_lib.h header file.

While a stub implementation is provided, only with foreign runtime
support by the libgomp GPU plugins and with the 'interop' directive,
this becomes really useful.

libgomp/ChangeLog:

	* fortran.c (omp_get_interop_str_, omp_get_interop_name_,
	omp_get_interop_type_desc_, omp_get_interop_rc_desc_): Add.
	* libgomp.map (GOMP_5.1.3): New; add interop routines.
	* omp.h.in: Add interop typedefs, enum and prototypes.
	(__GOMP_DEFAULT_NULL): Define.
	(omp_target_memcpy_async, omp_target_memcpy_rect_async):
	Use it for the optional depend argument.
 	* omp_lib.f90.in: Add paramters and interfaces for interop.
	* omp_lib.h.in: Likewise; move F90 '&' to column 81 for
	-ffree-length-80.
	* target.c (omp_get_num_interop_properties, omp_get_interop_int,
	omp_get_interop_ptr, omp_get_interop_str, omp_get_interop_name,
	omp_get_interop_type_desc, omp_get_interop_rc_desc): Add.
	* config/gcn/target.c (omp_get_num_interop_properties,
	omp_get_interop_int, omp_get_interop_ptr, omp_get_interop_str,
	omp_get_interop_name, omp_get_interop_type_desc,
	omp_get_interop_rc_desc): Add.
	* config/nvptx/target.c (omp_get_num_interop_properties,
	omp_get_interop_int, omp_get_interop_ptr, omp_get_interop_str,
	omp_get_interop_name, omp_get_interop_type_desc,
	omp_get_interop_rc_desc): Add.
	* testsuite/libgomp.c-c++-common/interop-routines-1.c: New test.
	* testsuite/libgomp.c-c++-common/interop-routines-2.c: New test.
	* testsuite/libgomp.fortran/interop-routines-1.F90: New test.
	* testsuite/libgomp.fortran/interop-routines-2.F90: New test.
	* testsuite/libgomp.fortran/interop-routines-3.F: New test.
	* testsuite/libgomp.fortran/interop-routines-4.F: New test.
	* testsuite/libgomp.fortran/interop-routines-5.F: New test.
	* testsuite/libgomp.fortran/interop-routines-6.F: New test.
	* testsuite/libgomp.fortran/interop-routines-7.F90: New test.

 libgomp/config/gcn/target.c                        | 105 ++++++
 libgomp/config/nvptx/target.c                      | 105 ++++++
 libgomp/fortran.c                                  |  41 +++
 libgomp/libgomp.map                                |  15 +
 libgomp/omp.h.in                                   |  78 ++++-
 libgomp/omp_lib.f90.in                             |  99 ++++++
 libgomp/omp_lib.h.in                               | 170 ++++++++--
 libgomp/target.c                                   | 110 +++++++
 .../libgomp.c-c++-common/interop-routines-1.c      | 287 +++++++++++++++++
 .../libgomp.c-c++-common/interop-routines-2.c      | 354 +++++++++++++++++++++
 .../libgomp.fortran/interop-routines-1.F90         | 236 ++++++++++++++
 .../libgomp.fortran/interop-routines-2.F90         |   3 +
 .../testsuite/libgomp.fortran/interop-routines-3.F |   2 +
 .../testsuite/libgomp.fortran/interop-routines-4.F |   4 +
 .../testsuite/libgomp.fortran/interop-routines-5.F |   4 +
 .../testsuite/libgomp.fortran/interop-routines-6.F |   4 +
 .../libgomp.fortran/interop-routines-7.F90         | 290 +++++++++++++++++
 17 files changed, 1883 insertions(+), 24 deletions(-)

diff --git a/libgomp/config/gcn/target.c b/libgomp/config/gcn/target.c
index 9cafea4e2cc..f7fa6aa6396 100644
--- a/libgomp/config/gcn/target.c
+++ b/libgomp/config/gcn/target.c
@@ -185,3 +185,108 @@ GOMP_target_enter_exit_data (int device, size_t mapnum, void **hostaddrs,
   (void) depend;
   __builtin_unreachable ();
 }
+
+int
+omp_get_num_interop_properties (const omp_interop_t interop
+				__attribute__ ((unused)))
+{
+  return 0;
+}
+
+omp_intptr_t
+omp_get_interop_int (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return 0;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return 0;
+}
+
+void *
+omp_get_interop_ptr (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return NULL;
+}
+
+const char *
+omp_get_interop_str (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return NULL;
+}
+
+const char *
+omp_get_interop_name (const omp_interop_t interop __attribute__ ((unused)),
+		      omp_interop_property_t property_id)
+{
+  static const char *prop_string[0 - omp_ipr_first]
+    = {"fr_id", "fr_name", "vendor", "vendor_name", "device_num", "platform",
+       "device", "device_context", "targetsync"};
+  if (property_id < omp_ipr_first || property_id >= 0)
+    return NULL;
+  return prop_string[omp_ipr_fr_id - property_id];
+}
+
+const char *
+omp_get_interop_type_desc (const omp_interop_t interop __attribute__ ((unused)),
+			   omp_interop_property_t property_id
+			   __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+const char *
+omp_get_interop_rc_desc (const omp_interop_t interop __attribute__ ((unused)),
+			 omp_interop_rc_t ret_code)
+{
+  static const char *rc_strings[omp_irc_no_value - omp_irc_other + 1]
+    = {"no meaningful value available",
+       "successful",
+       "provided interoperability object is equal to omp_interop_none",
+       "property ID is out of range",
+       "property type is integer; use omp_get_interop_int",
+       "property type is pointer; use omp_get_interop_ptr",
+       "property type is string; use omp_get_interop_str",
+       "obtaining properties is only supported on the initial device"};
+  /* omp_irc_other is returned by device-side omp_get_interop_{int,ptr,str};
+     the host returns for omp_irc_other NULL as it is  not used. Besides the
+     three omp_interop_rc_t values used on the device side, handle host values
+     leaked to the device side.  */
+  if (ret_code > omp_irc_no_value || ret_code < omp_irc_other)
+    return NULL;
+  return rc_strings[omp_irc_no_value - ret_code];
+}
+
+ialias (omp_get_num_interop_properties)
+ialias (omp_get_interop_int)
+ialias (omp_get_interop_ptr)
+ialias (omp_get_interop_str)
+ialias (omp_get_interop_name)
+ialias (omp_get_interop_type_desc)
+ialias (omp_get_interop_rc_desc)
diff --git a/libgomp/config/nvptx/target.c b/libgomp/config/nvptx/target.c
index 8d4dc5f661a..69666578c29 100644
--- a/libgomp/config/nvptx/target.c
+++ b/libgomp/config/nvptx/target.c
@@ -197,3 +197,108 @@ GOMP_target_enter_exit_data (int device, size_t mapnum, void **hostaddrs,
   (void) depend;
   __builtin_unreachable ();
 }
+
+int
+omp_get_num_interop_properties (const omp_interop_t interop
+				__attribute__ ((unused)))
+{
+  return 0;
+}
+
+omp_intptr_t
+omp_get_interop_int (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return 0;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return 0;
+}
+
+void *
+omp_get_interop_ptr (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return NULL;
+}
+
+const char *
+omp_get_interop_str (const omp_interop_t interop,
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else if (interop == omp_interop_none)
+    *ret_code = omp_irc_empty;
+  else
+    *ret_code = omp_irc_other;
+  return NULL;
+}
+
+const char *
+omp_get_interop_name (const omp_interop_t interop __attribute__ ((unused)),
+		      omp_interop_property_t property_id)
+{
+  static const char *prop_string[0 - omp_ipr_first]
+    = {"fr_id", "fr_name", "vendor", "vendor_name", "device_num", "platform",
+       "device", "device_context", "targetsync"};
+  if (property_id < omp_ipr_first || property_id >= 0)
+    return NULL;
+  return prop_string[omp_ipr_fr_id - property_id];
+}
+
+const char *
+omp_get_interop_type_desc (const omp_interop_t interop __attribute__ ((unused)),
+			   omp_interop_property_t property_id
+			   __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+const char *
+omp_get_interop_rc_desc (const omp_interop_t interop __attribute__ ((unused)),
+			 omp_interop_rc_t ret_code)
+{
+  static const char *rc_strings[omp_irc_no_value - omp_irc_other + 1]
+    = {"no meaningful value available",
+       "successful",
+       "provided interoperability object is equal to omp_interop_none",
+       "property ID is out of range",
+       "property type is integer; use omp_get_interop_int",
+       "property type is pointer; use omp_get_interop_ptr",
+       "property type is string; use omp_get_interop_str",
+       "obtaining properties is only supported on the initial device"};
+  /* omp_irc_other is returned by device-side omp_get_interop_{int,ptr,str};
+     the host returns for omp_irc_other NULL as it is  not used. Besides the
+     three omp_interop_rc_t values used on the device side, handle host values
+     leaked to the device side.  */
+  if (ret_code > omp_irc_no_value || ret_code < omp_irc_other)
+    return NULL;
+  return rc_strings[omp_irc_no_value - ret_code];
+}
+
+ialias (omp_get_num_interop_properties)
+ialias (omp_get_interop_int)
+ialias (omp_get_interop_ptr)
+ialias (omp_get_interop_str)
+ialias (omp_get_interop_name)
+ialias (omp_get_interop_type_desc)
+ialias (omp_get_interop_rc_desc)
diff --git a/libgomp/fortran.c b/libgomp/fortran.c
index cfbea32b022..a76c33cee52 100644
--- a/libgomp/fortran.c
+++ b/libgomp/fortran.c
@@ -102,6 +102,10 @@ ialias_redirect (omp_set_default_allocator)
 ialias_redirect (omp_get_default_allocator)
 ialias_redirect (omp_display_env)
 ialias_redirect (omp_fulfill_event)
+ialias_redirect (omp_get_interop_str)
+ialias_redirect (omp_get_interop_name)
+ialias_redirect (omp_get_interop_type_desc)
+ialias_redirect (omp_get_interop_rc_desc)
 #endif
 
 #ifndef LIBGOMP_GNU_SYMBOL_VERSIONING
@@ -793,6 +797,43 @@ omp_get_default_allocator_ ()
   return (intptr_t) omp_get_default_allocator ();
 }
 
+void
+omp_get_interop_str_ (const char **res, size_t *res_len,
+		      const omp_interop_t interop,
+		      omp_interop_property_t property_id,
+		      omp_interop_rc_t *ret_code)
+{
+  *res = omp_get_interop_str (interop, property_id, ret_code);
+  *res_len = *res ? strlen (*res) : 0;
+}
+
+void
+omp_get_interop_name_ (const char **res, size_t *res_len,
+		       const omp_interop_t interop,
+		       omp_interop_property_t property_id)
+{
+  *res = omp_get_interop_name (interop, property_id);
+  *res_len = *res ? strlen (*res) : 0;
+}
+
+void
+omp_get_interop_type_desc_ (const char **res, size_t *res_len,
+			    const omp_interop_t interop,
+			    omp_interop_property_t property_id)
+{
+  *res = omp_get_interop_type_desc (interop, property_id);
+  *res_len = *res ? strlen (*res) : 0;
+}
+
+void
+omp_get_interop_rc_desc_ (const char **res, size_t *res_len,
+			  const omp_interop_t interop,
+			  omp_interop_rc_t ret_code)
+{
+  *res = omp_get_interop_rc_desc (interop, ret_code);
+  *res_len = *res ? strlen (*res) : 0;
+}
+
 #ifndef LIBGOMP_OFFLOADED_ONLY
 
 void
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 65901dff235..7c2345eb29b 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -428,6 +428,21 @@ GOMP_5.1.2 {
 	GOMP_target_map_indirect_ptr;
 } GOMP_5.1.1;
 
+GOMP_5.1.3 {
+  global:
+	omp_get_num_interop_properties;
+	omp_get_interop_int;
+	omp_get_interop_ptr;
+	omp_get_interop_str;
+	omp_get_interop_name;
+	omp_get_interop_type_desc;
+	omp_get_interop_rc_desc;
+	omp_get_interop_str_;
+	omp_get_interop_name_;
+	omp_get_interop_type_desc_;
+	omp_get_interop_rc_desc_;
+} GOMP_5.1.2;
+
 OACC_2.0 {
   global:
 	acc_get_num_devices;
diff --git a/libgomp/omp.h.in b/libgomp/omp.h.in
index 4438d341160..4ce790833ed 100644
--- a/libgomp/omp.h.in
+++ b/libgomp/omp.h.in
@@ -105,6 +105,7 @@ typedef enum omp_pause_resource_t
   omp_pause_hard = 2
 } omp_pause_resource_t;
 
+typedef __INTPTR_TYPE__ omp_intptr_t;
 typedef __UINTPTR_TYPE__ omp_uintptr_t;
 
 #if __cplusplus >= 201103L
@@ -191,13 +192,60 @@ enum
   omp_invalid_device = -4
 };
 
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+typedef enum omp_interop_fr_t
+{
+  omp_ifr_cuda = 1,
+  omp_ifr_cuda_driver = 2,
+  omp_ifr_opencl = 3,
+  omp_ifr_sycl = 4,
+  omp_ifr_hip = 5,
+  omp_ifr_level_zero = 6,
+  omp_ifr_hsa = 7,
+  omp_ifr_last = omp_ifr_hsa
+} omp_interop_fr_t;
+
+typedef enum omp_interop_property_t
+{
+  omp_ipr_fr_id = -1,
+  omp_ipr_fr_name = -2,
+  omp_ipr_vendor = -3,
+  omp_ipr_vendor_name = -4,
+  omp_ipr_device_num = -5,
+  omp_ipr_platform = -6,
+  omp_ipr_device = -7,
+  omp_ipr_device_context = -8,
+  omp_ipr_targetsync = -9,
+  omp_ipr_first = -9
+} omp_interop_property_t;
+
+
+typedef enum omp_interop_rc_t
+{
+  omp_irc_no_value = 1,
+  omp_irc_success = 0,
+  omp_irc_empty = -1,
+  omp_irc_out_of_range = -2,
+  omp_irc_type_int = -3,
+  omp_irc_type_ptr = -4,
+  omp_irc_type_str = -5,
+  omp_irc_other = -6
+} omp_interop_rc_t;
+
 #ifdef __cplusplus
 extern "C" {
 # define __GOMP_NOTHROW throw ()
 # define __GOMP_DEFAULT_NULL_ALLOCATOR = omp_null_allocator
+# define __GOMP_DEFAULT_NULL = __null
 #else
 # define __GOMP_NOTHROW __attribute__((__nothrow__))
 # define __GOMP_DEFAULT_NULL_ALLOCATOR
+# define __GOMP_DEFAULT_NULL
 #endif
 
 extern void omp_set_num_threads (int) __GOMP_NOTHROW;
@@ -282,7 +330,7 @@ extern int omp_target_memcpy (void *, const void *, __SIZE_TYPE__,
   __GOMP_NOTHROW;
 extern int omp_target_memcpy_async (void *, const void *, __SIZE_TYPE__,
 				    __SIZE_TYPE__, __SIZE_TYPE__, int, int,
-				    int, omp_depend_t *)
+				    int, omp_depend_t * __GOMP_DEFAULT_NULL)
   __GOMP_NOTHROW;
 extern int omp_target_memcpy_rect (void *, const void *, __SIZE_TYPE__, int,
 				   const __SIZE_TYPE__ *,
@@ -297,7 +345,7 @@ extern int omp_target_memcpy_rect_async (void *, const void *, __SIZE_TYPE__,
 					 const __SIZE_TYPE__ *,
 					 const __SIZE_TYPE__ *,
 					 const __SIZE_TYPE__ *, int, int, int,
-					 omp_depend_t *)
+					 omp_depend_t * __GOMP_DEFAULT_NULL)
   __GOMP_NOTHROW;
 extern int omp_target_associate_ptr (const void *, const void *, __SIZE_TYPE__,
 				     __SIZE_TYPE__, int) __GOMP_NOTHROW;
@@ -351,6 +399,32 @@ extern void *omp_realloc (void *, __SIZE_TYPE__,
 
 extern void omp_display_env (int) __GOMP_NOTHROW;
 
+extern int omp_get_num_interop_properties (const omp_interop_t) __GOMP_NOTHROW;
+
+extern omp_intptr_t omp_get_interop_int (const omp_interop_t,
+					 omp_interop_property_t,
+					 omp_interop_rc_t * __GOMP_DEFAULT_NULL)
+  __GOMP_NOTHROW;
+
+extern void *omp_get_interop_ptr (const omp_interop_t, omp_interop_property_t,
+				  omp_interop_rc_t * __GOMP_DEFAULT_NULL)
+  __GOMP_NOTHROW;
+
+extern const char *omp_get_interop_str (const omp_interop_t,
+					omp_interop_property_t,
+					omp_interop_rc_t * __GOMP_DEFAULT_NULL)
+  __GOMP_NOTHROW;
+
+extern const char *omp_get_interop_name (const omp_interop_t,
+					 omp_interop_property_t) __GOMP_NOTHROW;
+
+extern const char *omp_get_interop_type_desc (const omp_interop_t,
+					      omp_interop_property_t)
+  __GOMP_NOTHROW;
+
+extern const char *omp_get_interop_rc_desc (const omp_interop_t,
+					    omp_interop_rc_t) __GOMP_NOTHROW;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libgomp/omp_lib.f90.in b/libgomp/omp_lib.f90.in
index 545a79fcec9..1861c40266b 100644
--- a/libgomp/omp_lib.f90.in
+++ b/libgomp/omp_lib.f90.in
@@ -40,6 +40,10 @@
         integer, parameter :: omp_memspace_handle_kind = c_intptr_t
         integer, parameter :: omp_depend_kind = @OMP_DEPEND_KIND@
         integer, parameter :: omp_event_handle_kind = c_intptr_t
+        integer, parameter :: omp_interop_kind = c_intptr_t
+        integer, parameter :: omp_interop_fr_kind = c_int
+        integer, parameter :: omp_interop_property_kind = c_int
+        integer, parameter :: omp_interop_rc_kind = c_int
         integer (omp_sched_kind), parameter :: omp_sched_static = 1
         integer (omp_sched_kind), parameter :: omp_sched_dynamic = 2
         integer (omp_sched_kind), parameter :: omp_sched_guided = 3
@@ -172,6 +176,40 @@
                  parameter :: omp_low_lat_mem_space = 4
         integer, parameter :: omp_initial_device = -1
         integer, parameter :: omp_invalid_device = -4
+        integer (omp_interop_kind), &
+                 parameter :: omp_interop_none = 0_omp_interop_kind
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_cuda = 1
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_cuda_driver = 2
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_opencl = 3
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_sycl = 4
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_hip = 5
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_level_zero = 6
+        integer (omp_interop_fr_kind), parameter :: omp_ifr_hsa = 7
+        integer (omp_interop_fr_kind), &
+                 parameter :: omp_ifr_last = omp_ifr_hsa
+        integer (omp_interop_property_kind), parameter :: omp_ipr_fr_id = -1
+        integer (omp_interop_property_kind), parameter :: omp_ipr_fr_name = -2
+        integer (omp_interop_property_kind), parameter :: omp_ipr_vendor = -3
+        integer (omp_interop_property_kind), &
+                 parameter :: omp_ipr_vendor_name = -4
+        integer (omp_interop_property_kind), &
+                 parameter :: omp_ipr_device_num = -5
+        integer (omp_interop_property_kind), parameter :: omp_ipr_platform = -6
+        integer (omp_interop_property_kind), parameter :: omp_ipr_device = -7
+        integer (omp_interop_property_kind), &
+                 parameter :: omp_ipr_device_context = -8
+        integer (omp_interop_property_kind), &
+                 parameter :: omp_ipr_targetsync = -9
+        integer (omp_interop_property_kind), &
+                 parameter :: omp_ipr_first = omp_ipr_targetsync
+        integer (omp_interop_rc_kind), parameter :: omp_irc_no_value = 1
+        integer (omp_interop_rc_kind), parameter :: omp_irc_success = 0
+        integer (omp_interop_rc_kind), parameter :: omp_irc_empty = -1
+        integer (omp_interop_rc_kind), parameter :: omp_irc_out_of_range = -2
+        integer (omp_interop_rc_kind), parameter :: omp_irc_type_int = -3
+        integer (omp_interop_rc_kind), parameter :: omp_irc_type_ptr = -4
+        integer (omp_interop_rc_kind), parameter :: omp_irc_type_str = -5
+        integer (omp_interop_rc_kind), parameter :: omp_irc_other = -6
 
         type omp_alloctrait
           integer (kind=omp_alloctrait_key_kind) key
@@ -904,6 +942,67 @@
           end function omp_target_is_accessible
         end interface
 
+        ! Interop functions: Note that the interface is not identical to the
+        ! OpenMP specification (c_int + VALUE + BIND(C) added) but usage
+        ! compatible; the following declarations permit to directly call the C
+        ! library function, except for the four string-returning functions.
+        interface
+          integer (c_int) function omp_get_num_interop_properties (interop) &
+              bind(C)
+            use, intrinsic :: iso_c_binding, only: c_int
+            use omp_lib_kinds
+            integer (omp_interop_kind), intent(in), value :: interop
+          end function omp_get_num_interop_properties
+
+          integer (c_intptr_t) function omp_get_interop_int (interop, &
+              property_id, ret_code) bind(C)
+            use, intrinsic :: iso_c_binding, only : c_intptr_t
+            use omp_lib_kinds
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_property_kind), value :: property_id
+            integer (omp_interop_rc_kind), optional, intent(out) :: ret_code
+          end function omp_get_interop_int
+
+          type (c_ptr) function omp_get_interop_ptr (interop, property_id, &
+              ret_code) bind(C)
+            use, intrinsic :: iso_c_binding, only : c_ptr
+            use omp_lib_kinds
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_property_kind), value :: property_id
+            integer (omp_interop_rc_kind), optional, intent(out) :: ret_code
+          end function omp_get_interop_ptr
+
+          character(:) function omp_get_interop_str (interop, property_id, &
+              ret_code)
+            use omp_lib_kinds
+            pointer :: omp_get_interop_str
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_property_kind), value :: property_id
+            integer (omp_interop_rc_kind), optional, intent(out) :: ret_code
+          end function omp_get_interop_str
+
+          character(:) function omp_get_interop_name (interop, property_id)
+            use omp_lib_kinds
+            pointer :: omp_get_interop_name
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_property_kind), value :: property_id
+          end function omp_get_interop_name
+
+          character(:) function omp_get_interop_type_desc (interop, property_id)
+            use omp_lib_kinds
+            pointer :: omp_get_interop_type_desc
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_property_kind), value :: property_id
+          end function omp_get_interop_type_desc
+
+          character(:) function omp_get_interop_rc_desc (interop, ret_code)
+            use omp_lib_kinds
+            pointer :: omp_get_interop_rc_desc
+            integer (omp_interop_kind), intent(in), value :: interop
+            integer (omp_interop_rc_kind), value :: ret_code
+          end function omp_get_interop_rc_desc
+        end interface
+
 #if _OPENMP >= 201811
 !GCC$ ATTRIBUTES DEPRECATED :: omp_get_nested, omp_set_nested
 !GCC$ ATTRIBUTES DEPRECATED :: omp_lock_hint_kind, omp_lock_hint_none
diff --git a/libgomp/omp_lib.h.in b/libgomp/omp_lib.h.in
index b5144bb4144..6959f1e96c7 100644
--- a/libgomp/omp_lib.h.in
+++ b/libgomp/omp_lib.h.in
@@ -85,11 +85,17 @@
       integer omp_allocator_handle_kind, omp_alloctrait_key_kind
       integer omp_alloctrait_val_kind, omp_memspace_handle_kind
       integer omp_event_handle_kind
+      integer omp_interop_kind, omp_interop_fr_kind
+      integer omp_interop_property_kind, omp_interop_rc_kind
       parameter (omp_allocator_handle_kind = @INTPTR_T_KIND@)
       parameter (omp_alloctrait_key_kind = 4)
       parameter (omp_alloctrait_val_kind = @INTPTR_T_KIND@)
       parameter (omp_memspace_handle_kind = @INTPTR_T_KIND@)
       parameter (omp_event_handle_kind = @INTPTR_T_KIND@)
+      parameter (omp_interop_kind = @INTPTR_T_KIND@)
+      parameter (omp_interop_fr_kind = 4)
+      parameter (omp_interop_property_kind = 4)
+      parameter (omp_interop_rc_kind = 4)
       integer (omp_alloctrait_key_kind) omp_atk_sync_hint
       integer (omp_alloctrait_key_kind) omp_atk_alignment
       integer (omp_alloctrait_key_kind) omp_atk_access
@@ -179,6 +185,60 @@
       integer omp_initial_device, omp_invalid_device
       parameter (omp_initial_device = -1)
       parameter (omp_invalid_device = -4)
+      integer (omp_interop_kind) omp_interop_none
+      parameter (omp_interop_none = 0_omp_interop_kind)
+      integer (omp_interop_fr_kind) omp_ifr_cuda
+      integer (omp_interop_fr_kind) omp_ifr_cuda_driver
+      integer (omp_interop_fr_kind) omp_ifr_opencl
+      integer (omp_interop_fr_kind) omp_ifr_sycl
+      integer (omp_interop_fr_kind) omp_ifr_hip
+      integer (omp_interop_fr_kind) omp_ifr_level_zero
+      integer (omp_interop_fr_kind) omp_ifr_hsa
+      integer (omp_interop_fr_kind) omp_ifr_last
+      parameter (omp_ifr_cuda = 1)
+      parameter (omp_ifr_cuda_driver = 2)
+      parameter (omp_ifr_opencl = 3)
+      parameter (omp_ifr_sycl = 4)
+      parameter (omp_ifr_hip = 5)
+      parameter (omp_ifr_level_zero = 6)
+      parameter (omp_ifr_hsa = 7)
+      parameter (omp_ifr_last = omp_ifr_hsa)
+      integer (omp_interop_property_kind) omp_ipr_fr_id
+      integer (omp_interop_property_kind) omp_ipr_fr_name
+      integer (omp_interop_property_kind) omp_ipr_vendor
+      integer (omp_interop_property_kind) omp_ipr_vendor_name
+      integer (omp_interop_property_kind) omp_ipr_device_num
+      integer (omp_interop_property_kind) omp_ipr_platform
+      integer (omp_interop_property_kind) omp_ipr_device
+      integer (omp_interop_property_kind) omp_ipr_device_context
+      integer (omp_interop_property_kind) omp_ipr_targetsync
+      integer (omp_interop_property_kind) omp_ipr_first
+      parameter (omp_ipr_fr_id = -1)
+      parameter (omp_ipr_fr_name = -2)
+      parameter (omp_ipr_vendor = -3)
+      parameter (omp_ipr_vendor_name = -4)
+      parameter (omp_ipr_device_num = -5)
+      parameter (omp_ipr_platform = -6)
+      parameter (omp_ipr_device = -7)
+      parameter (omp_ipr_device_context = -8)
+      parameter (omp_ipr_targetsync = -9)
+      parameter (omp_ipr_first = omp_ipr_targetsync)
+      integer (omp_interop_rc_kind) omp_irc_no_value
+      integer (omp_interop_rc_kind) omp_irc_success
+      integer (omp_interop_rc_kind) omp_irc_empty
+      integer (omp_interop_rc_kind) omp_irc_out_of_range
+      integer (omp_interop_rc_kind) omp_irc_type_int
+      integer (omp_interop_rc_kind) omp_irc_type_ptr
+      integer (omp_interop_rc_kind) omp_irc_type_str
+      integer (omp_interop_rc_kind) omp_irc_other
+      parameter (omp_irc_no_value = 1)
+      parameter (omp_irc_success = 0)
+      parameter (omp_irc_empty = -1)
+      parameter (omp_irc_out_of_range = -2)
+      parameter (omp_irc_type_int = -3)
+      parameter (omp_irc_type_ptr = -4)
+      parameter (omp_irc_type_str = -5)
+      parameter (omp_irc_other = -6)
 
       type omp_alloctrait
         integer (omp_alloctrait_key_kind) key
@@ -323,7 +383,7 @@
       end interface
 
       interface
-        function omp_aligned_calloc (alignment, nmemb, size, allocator)   &
+        function omp_aligned_calloc (alignment, nmemb, size, allocator)         &
      &      bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_size_t
           use, intrinsic :: omp_lib_kinds
@@ -334,7 +394,7 @@
       end interface
 
       interface
-        function omp_realloc (ptr, size, allocator, free_allocator)      &
+        function omp_realloc (ptr, size, allocator, free_allocator)             &
      &      bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_size_t
           use, intrinsic :: omp_lib_kinds
@@ -373,8 +433,8 @@
       end interface
 
       interface
-        function omp_target_memcpy (dst, src, length, dst_offset,          &
-     &                              src_offset, dst_device_num,            &
+        function omp_target_memcpy (dst, src, length, dst_offset,               &
+     &                              src_offset, dst_device_num,                 &
      &                              src_device_num) bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_size_t
           integer(c_int) :: omp_target_memcpy
@@ -385,9 +445,9 @@
       end interface
 
       interface
-        function omp_target_memcpy_async (dst, src, length, dst_offset,    &
-     &                              src_offset, dst_device_num,            &
-     &                              src_device_num, depobj_count,          &
+        function omp_target_memcpy_async (dst, src, length, dst_offset,         &
+     &                              src_offset, dst_device_num,                 &
+     &                              src_device_num, depobj_count,               &
      &                              depobj_list) bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_size_t
           import :: omp_depend_kind
@@ -401,10 +461,10 @@
       end interface
 
       interface
-        function omp_target_memcpy_rect (dst,src,element_size, num_dims,   &
-     &                                   volume, dst_offsets,              &
-     &                                   src_offsets, dst_dimensions,      &
-     &                                   src_dimensions, dst_device_num,   &
+        function omp_target_memcpy_rect (dst,src,element_size, num_dims,        &
+     &                                   volume, dst_offsets,                   &
+     &                                   src_offsets, dst_dimensions,           &
+     &                                   src_dimensions, dst_device_num,        &
      &                                   src_device_num) bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_size_t
           integer(c_int) :: omp_target_memcpy_rect
@@ -420,14 +480,14 @@
       end interface
 
       interface
-        function omp_target_memcpy_rect_async (dst,src,element_size,        &
-     &                                         num_dims, volume,            &
-     &                                         dst_offsets, src_offsets,    &
-     &                                         dst_dimensions,              &
-     &                                         src_dimensions,              &
-     &                                         dst_device_num,              &
-     &                                         src_device_num,              &
-     &                                         depobj_count,                &
+        function omp_target_memcpy_rect_async (dst,src,element_size,            &
+     &                                         num_dims, volume,                &
+     &                                         dst_offsets, src_offsets,        &
+     &                                         dst_dimensions,                  &
+     &                                         src_dimensions,                  &
+     &                                         dst_device_num,                  &
+     &                                         src_device_num,                  &
+     &                                         depobj_count,                    &
      &                                         depobj_list) bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_size_t
           import :: omp_depend_kind
@@ -445,8 +505,8 @@
       end interface
 
       interface
-        function omp_target_associate_ptr (host_ptr, device_ptr, size,     &
-     &                                     device_offset, device_num)      &
+        function omp_target_associate_ptr (host_ptr, device_ptr, size,          &
+     &                                     device_offset, device_num)           &
      &      bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_size_t, c_int
           integer(c_int) :: omp_target_associate_ptr
@@ -475,7 +535,7 @@
       end interface
 
       interface
-        function omp_target_is_accessible (ptr, size, device_num)          &
+        function omp_target_is_accessible (ptr, size, device_num)               &
      &      bind(c)
           use, intrinsic :: iso_c_binding, only : c_ptr, c_size_t, c_int
           integer(c_int) :: omp_target_is_accessible
@@ -484,3 +544,69 @@
           integer(c_int), value :: device_num
         end function omp_target_is_accessible
       end interface
+
+! Interop functions: Note that the interface is not identical to the
+! OpenMP specification (c_int + VALUE + BIND(C) added) but usage
+! compatible; the following declarations permit to directly call the C
+! library function, except for the four string-returning functions.
+      interface
+        integer (c_int) function omp_get_num_interop_properties(interop)        &
+     &      bind(C)
+          use, intrinsic :: iso_c_binding, only: c_int
+          use, intrinsic :: omp_lib_kinds
+          integer (omp_interop_kind), intent(in), value :: interop
+        end function omp_get_num_interop_properties
+
+        integer (c_intptr_t) function omp_get_interop_int (interop,             &
+     &      property_id, ret_code) bind(C)
+          use, intrinsic :: iso_c_binding, only : c_intptr_t
+          use, intrinsic :: omp_lib_kinds
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_property_kind), value :: property_id
+          integer (omp_interop_rc_kind), optional,                              &
+     &      intent(out) :: ret_code
+        end function omp_get_interop_int
+
+        type (c_ptr) function omp_get_interop_ptr (interop, property_id,        &
+     &      ret_code) bind(C)
+          use, intrinsic :: iso_c_binding, only : c_ptr
+          use, intrinsic :: omp_lib_kinds
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_property_kind), value :: property_id
+          integer (omp_interop_rc_kind), optional,                              &
+     &      intent(out) :: ret_code
+        end function omp_get_interop_ptr
+
+        character(:) function omp_get_interop_str (interop, property_id,        &
+     &      ret_code)
+          use, intrinsic :: omp_lib_kinds
+          pointer :: omp_get_interop_str
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_property_kind), value :: property_id
+          integer (omp_interop_rc_kind), optional,                              &
+     &      intent(out) :: ret_code
+        end function omp_get_interop_str
+
+        character(:) function omp_get_interop_name(interop, property_id)
+          use, intrinsic :: omp_lib_kinds
+          pointer :: omp_get_interop_name
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_property_kind), value :: property_id
+        end function omp_get_interop_name
+
+        character(:) function omp_get_interop_type_desc (interop,               &
+     &                                                   property_id)
+          use, intrinsic :: omp_lib_kinds
+          pointer :: omp_get_interop_type_desc
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_property_kind), value :: property_id
+        end function omp_get_interop_type_desc
+
+        character(:) function omp_get_interop_rc_desc (interop,                 &
+     &                                                 ret_code)
+          use, intrinsic :: omp_lib_kinds
+          pointer :: omp_get_interop_rc_desc
+          integer (omp_interop_kind), intent(in), value :: interop
+          integer (omp_interop_rc_kind), value :: ret_code
+        end function omp_get_interop_rc_desc
+      end interface
diff --git a/libgomp/target.c b/libgomp/target.c
index fb9a6fb5c79..45fc6036cca 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -5113,6 +5113,116 @@ omp_pause_resource_all (omp_pause_resource_t kind)
 ialias (omp_pause_resource)
 ialias (omp_pause_resource_all)
 
+int
+omp_get_num_interop_properties (const omp_interop_t interop
+				__attribute__ ((unused)))
+{
+  /* Zero implementation defined. In total:
+     omp_get_num_interop_properties () - omp_ipr_first.  */
+  return 0;
+}
+
+omp_intptr_t
+omp_get_interop_int (const omp_interop_t interop __attribute__ ((unused)),
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return 0;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else
+    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+  return 0;
+}
+
+void *
+omp_get_interop_ptr (const omp_interop_t interop __attribute__ ((unused)),
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else
+    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+  return NULL;
+}
+
+const char *
+omp_get_interop_str (const omp_interop_t interop __attribute__ ((unused)),
+		     omp_interop_property_t property_id,
+		     omp_interop_rc_t *ret_code)
+{
+  if (ret_code == NULL)
+    return NULL;
+  if (property_id < omp_ipr_first || property_id >= 0)
+    *ret_code = omp_irc_out_of_range;
+  else
+    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+  return NULL;
+}
+
+const char *
+omp_get_interop_name (const omp_interop_t interop __attribute__ ((unused)),
+		      omp_interop_property_t property_id)
+{
+  static const char *prop_string[0 - omp_ipr_first]
+    = {"fr_id", "fr_name", "vendor", "vendor_name", "device_num", "platform",
+       "device", "device_context", "targetsync"};
+  if (property_id < omp_ipr_first || property_id >= 0)
+    return NULL;
+  return prop_string[omp_ipr_fr_id - property_id];
+}
+
+const char *
+omp_get_interop_type_desc (const omp_interop_t interop,
+			   omp_interop_property_t property_id)
+{
+  static const char *desc[omp_ipr_fr_id - omp_ipr_device_num + 1]
+    = {"int",             /* fr_id */
+       "const char*",     /* fr_name */
+       "int",             /* vendor */
+       "const char *",    /* vendor_name */
+       "int"};            /* device_num */
+  if (property_id > omp_ipr_fr_id || property_id < omp_ipr_first)
+    return NULL;
+  if (interop == omp_interop_none)
+    return NULL;
+  if (property_id >= omp_ipr_device_num)
+    return desc[omp_ipr_fr_id - property_id];
+  return NULL; /* Fixme: Call plugin.  */
+}
+
+const char *
+omp_get_interop_rc_desc (const omp_interop_t interop __attribute__ ((unused)),
+			 omp_interop_rc_t ret_code)
+{
+  static const char *rc_strings[omp_irc_no_value - omp_irc_other]
+    = {"no meaningful value available",
+       "successful",
+       "provided interoperability object is equal to omp_interop_none",
+       "property ID is out of range",
+       "property type is integer; use omp_get_interop_int",
+       "property type is pointer; use omp_get_interop_ptr",
+       "property type is string; use omp_get_interop_str"};
+
+  /* omp_irc_other is returned by device-side omp_get_interop_{int,ptr,str};
+     on the host it is not used, hence, return NULL here.   */
+  if (ret_code > omp_irc_no_value || ret_code <= omp_irc_other)
+    return NULL;
+  return rc_strings[omp_irc_no_value - ret_code];
+}
+
+ialias (omp_get_num_interop_properties)
+ialias (omp_get_interop_int)
+ialias (omp_get_interop_ptr)
+ialias (omp_get_interop_str)
+ialias (omp_get_interop_name)
+ialias (omp_get_interop_type_desc)
+ialias (omp_get_interop_rc_desc)
+
 #ifdef PLUGIN_SUPPORT
 
 /* This function tries to load a plugin for DEVICE.  Name of plugin is passed
diff --git a/libgomp/testsuite/libgomp.c-c++-common/interop-routines-1.c b/libgomp/testsuite/libgomp.c-c++-common/interop-routines-1.c
new file mode 100644
index 00000000000..6e3b3ebd689
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/interop-routines-1.c
@@ -0,0 +1,287 @@
+/* { dg-do run } */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <omp.h>
+
+int
+main ()
+{
+  omp_interop_t interop = omp_interop_none;
+
+  assert (omp_irc_no_value == 1);
+  assert (omp_irc_success == 0);
+  assert (omp_irc_empty == -1);
+  assert (omp_irc_out_of_range == -2);
+  assert (omp_irc_type_int == -3);
+  assert (omp_irc_type_ptr == -4);
+  assert (omp_irc_type_str == -5);
+  assert (omp_irc_other == -6);
+
+  /* Check values, including invalid values.  */
+  for (omp_interop_rc_t ret_code3 = (omp_interop_rc_t)((int)omp_irc_other-1);
+       ret_code3 <= omp_irc_no_value + 1;
+       ret_code3 = (omp_interop_rc_t)((int)ret_code3 + 1))
+    {
+      const char *msg = omp_get_interop_rc_desc (interop, ret_code3);
+      if (ret_code3 < omp_irc_other || ret_code3 > omp_irc_no_value)
+	/* Assume NULL for an invalid value.  */
+	assert (msg == NULL);
+      else if (ret_code3 == omp_irc_other)
+	/* Likely not to exist in an implementation; esp. not for
+	   omp_interop_none. Thus, expect NULL.  */
+	/* In GCC, it is used on the device side, only, to complain about
+	   omp_get_interop_{int,ptr,str} usage.  */
+	assert (msg == NULL);
+      else
+	/* Assume that omp_get_interop_rc_desc handles all of those and
+	   not only omp_irc_empty (and possibly omp_irc_out_of_range),
+	   which do occur for omp_interop_none. */
+	assert (msg != NULL && strlen (msg) > 5);  /* Some sensible message.  */
+    }
+
+  assert (omp_ifr_last >= omp_ifr_hsa);
+
+  for (omp_interop_fr_t fr = omp_ifr_cuda;
+       fr <= omp_ifr_last;
+       fr = (omp_interop_fr_t)((int)fr +1))
+    {
+      switch (fr)
+	{
+	/* Expect the id values from the additional-definition document.  */
+	case omp_ifr_cuda:
+	  if (fr != 1)
+	    abort ();
+	  break;
+	case omp_ifr_cuda_driver:
+	  if (fr != 2)
+	    abort ();
+	  break;
+	case omp_ifr_opencl:
+	  if (fr != 3)
+	    abort ();
+	  break;
+	case omp_ifr_sycl:
+	  if (fr != 4)
+	    abort ();
+	  break;
+	case omp_ifr_hip:
+	  if (fr != 5)
+	    abort ();
+	  break;
+	case omp_ifr_level_zero:
+	  if (fr != 6)
+	    abort ();
+	  break;
+	case omp_ifr_hsa:
+	  if (fr != 7)
+	    abort ();
+	  break;
+	default:
+	  /* Valid, but unexpected to have more interop types.  */
+	  abort ();
+	}
+    }
+
+  assert (omp_ipr_first <= omp_ipr_targetsync
+	  && omp_get_num_interop_properties (interop) > omp_ipr_fr_id);
+
+  for (omp_interop_property_t ipr = omp_ipr_first;
+       ipr < omp_get_num_interop_properties (interop);
+       ipr = (omp_interop_property_t) ((int) ipr + 1))
+    {
+      /* As interop == omp_interop_none, NULL is permissible;
+	 nonetheless, require != NULL for the GCC implementation.  */
+      const char *name = omp_get_interop_name (interop, ipr);
+      if (name == NULL)
+	abort ();
+      switch (ipr)
+	{
+	case omp_ipr_fr_id:
+	  if (ipr != -1 || !!strcmp (name, "fr_id"))
+	    abort ();
+	  break;
+	case omp_ipr_fr_name:
+	  if (ipr != -2 || !!strcmp (name, "fr_name"))
+	    abort ();
+	  break;
+	case omp_ipr_vendor:
+	  if (ipr != -3 || !!strcmp (name, "vendor"))
+	    abort ();
+	  break;
+	case omp_ipr_vendor_name:
+	  if (ipr != -4 || !!strcmp (name, "vendor_name"))
+	    abort ();
+	  break;
+	case omp_ipr_device_num:
+	  if (ipr != -5 || !!strcmp (name, "device_num"))
+	    abort ();
+	  break;
+	case omp_ipr_platform:
+	  if (ipr != -6 || !!strcmp (name, "platform"))
+	    abort ();
+	  break;
+	case omp_ipr_device:
+	  if (ipr != -7 || !!strcmp (name, "device"))
+	    abort ();
+	  break;
+	case omp_ipr_device_context:
+	  if (ipr != -8 || !!strcmp (name, "device_context"))
+	    abort ();
+	  break;
+	case omp_ipr_targetsync:
+	  if (ipr != -9 || !!strcmp (name, "targetsync"))
+	    abort ();
+	  break;
+	default:
+	  /* Valid, but unexpected to have more interop types,
+	     especially not for interop == omp_interop_none.  */
+	  abort ();
+	}
+
+      /* As interop == omp_interop_none, expect NULL.  */
+      if (omp_get_interop_type_desc (interop, ipr) != NULL)
+	abort ();
+
+      omp_interop_rc_t ret_code;
+      const char *err;
+
+      ret_code = omp_irc_success;
+      omp_intptr_t ival = omp_get_interop_int (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (ival == 0);  /* Implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+			    "omp_interop_none"));  /* GCC implementation choice.  */
+      omp_intptr_t ival4 = omp_get_interop_int (interop, ipr, NULL);
+      assert(ival4 == ival);
+
+      ret_code = omp_irc_success;
+      void *ptr = omp_get_interop_ptr (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (ptr == NULL);  /* Obvious implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+			    "omp_interop_none"));  /* GCC implementation choice.  */
+      void *ptr4 = omp_get_interop_ptr (interop, ipr, NULL);
+      assert (ptr4 == ptr);
+
+      ret_code = omp_irc_success;
+      const char *str = omp_get_interop_str (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (str == NULL);  /* Obvious implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+			    "omp_interop_none"));  /* GCC implementation choice.  */
+      const char *str4 = omp_get_interop_str (interop, ipr, NULL);
+      assert (str4 == str);
+    }
+
+  /* Invalid ipr.  */
+  /* Valid are either omp_irc_empty (due to omp_interop_none) or
+     omp_irc_out_of_range; assume omp_irc_out_of_range with GCC.  */
+
+  omp_interop_rc_t ret_code2;
+  const char *err2;
+  omp_intptr_t ival2;
+  void *ptr2;
+  const char *str2;
+
+  /* omp_ipr_targetsync-1, i.e < lower bound.  */
+
+  ret_code2 = omp_irc_success;
+  ival2  = omp_get_interop_int (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ival2 == 0);  /* Implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  omp_intptr_t ival5  = omp_get_interop_int (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (ival5 == ival2);
+#ifdef __cplusplus
+  ival5  = omp_get_interop_int (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (ival5 == ival2);
+#endif
+
+  ret_code2 = omp_irc_success;
+  ptr2 = omp_get_interop_ptr (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  void *ptr5 = omp_get_interop_ptr (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (ptr5 == ptr2);
+#ifdef __cplusplus
+  ptr5 = omp_get_interop_ptr (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (ptr5 == ptr2);
+#endif
+
+  ret_code2 = omp_irc_success;
+  str2 = omp_get_interop_str (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  const char *str5 = omp_get_interop_str (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (str2 == str5);
+#ifdef __cplusplus
+  str5 = omp_get_interop_str (interop,
+	     (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (str2 == str5);
+#endif
+
+  /* omp_get_num_interop_properties (), i.e > upper bound.  */
+
+  ret_code2 = omp_irc_success;
+  ival2 = omp_get_interop_int (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ival2 == 0);  /* Implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+
+  ret_code2 = omp_irc_success;
+  ptr2 = omp_get_interop_ptr (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+
+  ret_code2 = omp_irc_success;
+  str2 = omp_get_interop_str (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message. */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/interop-routines-2.c b/libgomp/testsuite/libgomp.c-c++-common/interop-routines-2.c
new file mode 100644
index 00000000000..2af2e421048
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/interop-routines-2.c
@@ -0,0 +1,354 @@
+/* { dg-do run { target { offload_device } } } */
+
+/* OpenMP permits using the interop functions on the device,
+   but it is not really supported. Hence, check that the stubs
+   are working.  */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <omp.h>
+
+/* Assert is not available with newlib on the device side, hence... */
+#define assert(cond) \
+  do { \
+    if (!(cond)) \
+      { \
+	__builtin_printf ("assert failed: " #cond "\n"); \
+	abort (); \
+      } \
+    } \
+  while (0)
+
+#pragma omp begin declare target
+void
+target_test ()
+{
+  if (omp_is_initial_device ())
+    return;  /* Already tested in interop-routines-1.c.  */
+
+  omp_interop_t interop = omp_interop_none;
+
+  assert (omp_irc_no_value == 1);
+  assert (omp_irc_success == 0);
+  assert (omp_irc_empty == -1);
+  assert (omp_irc_out_of_range == -2);
+  assert (omp_irc_type_int == -3);
+  assert (omp_irc_type_ptr == -4);
+  assert (omp_irc_type_str == -5);
+  assert (omp_irc_other == -6);
+
+  /* Check values, including invalid values.  */
+  for (omp_interop_rc_t ret_code3 = (omp_interop_rc_t) ((int) omp_irc_other - 1);
+       ret_code3 <= omp_irc_no_value + 1;
+       ret_code3 = (omp_interop_rc_t) ((int) ret_code3 + 1))
+    {
+      const char *msg = omp_get_interop_rc_desc (interop, ret_code3);
+      if (ret_code3 < omp_irc_other || ret_code3 > omp_irc_no_value)
+	/* Assume NULL for an invalid value.  */
+	assert (msg == NULL);
+      else if (ret_code3 == omp_irc_other)
+	/* In GCC, this is used on the device side, only, if the
+	   omp_get_interop_{int,ptr,str} were invoked on the host.
+	   see below for a check. */
+	assert (msg != NULL && strlen (msg) > 5);  /* Some sensible message.  */
+      else
+	/* Assume that omp_get_interop_rc_desc handles all of those and
+	   not only omp_irc_empty (and possibly omp_irc_out_of_range),
+	   which do occur for omp_interop_none. */
+	assert (msg != NULL && strlen (msg) > 5);  /* Some sensible message.  */
+    }
+
+  assert (omp_ifr_last >= omp_ifr_hsa);
+
+  for (omp_interop_fr_t fr = omp_ifr_cuda; fr <= omp_ifr_last;
+       fr = (omp_interop_fr_t) ((int) fr + 1))
+    {
+      switch (fr)
+	{
+	/* Expect the id values from the additional-definition document.  */
+	case omp_ifr_cuda:
+	  if (fr != 1)
+	    abort ();
+	  break;
+	case omp_ifr_cuda_driver:
+	  if (fr != 2)
+	    abort ();
+	  break;
+	case omp_ifr_opencl:
+	  if (fr != 3)
+	    abort ();
+	  break;
+	case omp_ifr_sycl:
+	  if (fr != 4)
+	    abort ();
+	  break;
+	case omp_ifr_hip:
+	  if (fr != 5)
+	    abort ();
+	  break;
+	case omp_ifr_level_zero:
+	  if (fr != 6)
+	    abort ();
+	  break;
+	case omp_ifr_hsa:
+	  if (fr != 7)
+	    abort ();
+	  break;
+	default:
+	  /* Valid, but unexpected to have more interop types.  */
+	  abort ();
+	}
+    }
+
+  assert (omp_ipr_first <= omp_ipr_targetsync
+	  && omp_get_num_interop_properties (interop) > omp_ipr_fr_id);
+
+  for (omp_interop_property_t ipr = omp_ipr_first;
+       ipr < omp_get_num_interop_properties (interop);
+       ipr = (omp_interop_property_t) ((int) ipr + 1))
+    {
+      /* As interop == omp_interop_none, NULL is permissible;
+	 nonetheless, require != NULL for the GCC implementation.  */
+      const char *name = omp_get_interop_name (interop, ipr);
+      if (name == NULL)
+	abort ();
+      switch (ipr)
+	{
+	case omp_ipr_fr_id:
+	  if (ipr != -1 || !!strcmp (name, "fr_id"))
+	    abort ();
+	  break;
+	case omp_ipr_fr_name:
+	  if (ipr != -2 || !!strcmp (name, "fr_name"))
+	    abort ();
+	  break;
+	case omp_ipr_vendor:
+	  if (ipr != -3 || !!strcmp (name, "vendor"))
+	    abort ();
+	  break;
+	case omp_ipr_vendor_name:
+	  if (ipr != -4 || !!strcmp (name, "vendor_name"))
+	    abort ();
+	  break;
+	case omp_ipr_device_num:
+	  if (ipr != -5 || !!strcmp (name, "device_num"))
+	    abort ();
+	  break;
+	case omp_ipr_platform:
+	  if (ipr != -6 || !!strcmp (name, "platform"))
+	    abort ();
+	  break;
+	case omp_ipr_device:
+	  if (ipr != -7 || !!strcmp (name, "device"))
+	    abort ();
+	  break;
+	case omp_ipr_device_context:
+	  if (ipr != -8 || !!strcmp (name, "device_context"))
+	    abort ();
+	  break;
+	case omp_ipr_targetsync:
+	  if (ipr != -9 || !!strcmp (name, "targetsync"))
+	    abort ();
+	  break;
+	default:
+	  /* Valid, but unexpected to have more interop types,
+	     especially not for interop == omp_interop_none.  */
+	  abort ();
+	}
+
+      /* As interop == omp_interop_none, expect NULL.  */
+      if (omp_get_interop_type_desc (interop, ipr) != NULL)
+	abort ();
+
+      omp_interop_rc_t ret_code;
+      const char *err;
+
+      ret_code = omp_irc_success;
+      omp_intptr_t ival = omp_get_interop_int (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (ival == 0);  /* Implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+			    "omp_interop_none"));  /* GCC implementation choice.  */
+      ival = omp_get_interop_int (interop, ipr, NULL);
+      assert (ival == 0);  /* Implementation choice.  */
+#ifdef __cplusplus
+      ival = omp_get_interop_int (interop, ipr);
+      assert (ival == 0);  /* Implementation choice.  */
+#endif
+
+      ret_code = omp_irc_success;
+      void *ptr = omp_get_interop_ptr (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (ptr == NULL);  /* Obvious implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+			    "omp_interop_none"));  /* GCC implementation choice.  */
+      ptr = omp_get_interop_ptr (interop, ipr, NULL);
+      assert (ptr == NULL);  /* Obvious implementation choice.  */
+#ifdef __cplusplus
+      ptr = omp_get_interop_ptr (interop, ipr);
+      assert (ptr == NULL);  /* Obvious implementation choice.  */
+#endif
+
+      ret_code = omp_irc_success;
+      const char *str = omp_get_interop_str (interop, ipr, &ret_code);
+      assert (ret_code == omp_irc_empty); /* As interop == omp_interop_none.  */
+      assert (str == NULL);  /* Obvious implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "provided interoperability object is equal to "
+  			    "omp_interop_none"));  /* GCC implementation choice.  */
+      str = omp_get_interop_str (interop, ipr, NULL);
+      assert (str == NULL);  /* Obvious implementation choice.  */
+#ifdef __cplusplus
+      str = omp_get_interop_str (interop, ipr);
+      assert (str == NULL);  /* Obvious implementation choice.  */
+#endif
+
+      /* Special case of GCC: For any non-'omp_interop_none' valued interop,
+	 a device-side call to omp_get_interop_{int,ptr,src} will yield
+	 omp_irc_other - with the error message as checked below.  */
+
+      omp_interop_t interop_not_none_invalid = (omp_interop_t) 0xDEADBEEF;
+
+      ret_code = omp_irc_success;
+      ival = omp_get_interop_int (interop_not_none_invalid, ipr, &ret_code);
+      assert (ret_code == omp_irc_other);
+      assert (ival == 0);  /* Implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+
+      ret_code = omp_irc_success;
+      ptr = omp_get_interop_ptr (interop_not_none_invalid, ipr, &ret_code);
+      assert (ret_code == omp_irc_other);
+      assert (ptr == NULL);  /* Implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (err != NULL && strlen (err) > 5);  /* Some sensible message.  */
+      assert (!strcmp (err, "obtaining properties is only supported on the "
+			    "initial device"));  /* GCC special case.  */
+
+      ret_code = omp_irc_success;
+      str = omp_get_interop_str (interop_not_none_invalid, ipr, &ret_code);
+      assert (ret_code == omp_irc_other);
+      assert (str == NULL);  /* Implementation choice.  */
+      err = omp_get_interop_rc_desc (interop, ret_code);
+      assert (!strcmp (err, "obtaining properties is only supported on the "
+			    "initial device"));  /* GCC special case.  */
+    }
+
+  /* Invalid ipr.  */
+  /* Valid are either omp_irc_empty (due to omp_interop_none) or
+     omp_irc_out_of_range; assume omp_irc_out_of_range with GCC.  */
+
+  omp_interop_rc_t ret_code2;
+  const char *err2;
+  omp_intptr_t ival2;
+  void *ptr2;
+  const char *str2;
+
+  /* omp_ipr_targetsync-1, i.e < lower bound.  */
+
+  ret_code2 = omp_irc_success;
+  ival2  = omp_get_interop_int (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ival2 == 0);  /* Implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  ival2  = omp_get_interop_int (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (ival2 == 0);  /* Implementation choice.  */
+#ifdef __cplusplus
+  ival2  = omp_get_interop_int (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (ival2 == 0);  /* Implementation choice.  */
+#endif
+
+  ret_code2 = omp_irc_success;
+  ptr2 = omp_get_interop_ptr (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  ptr2 = omp_get_interop_ptr (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+#ifdef __cplusplus
+  ptr2 = omp_get_interop_ptr (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+#endif
+
+  ret_code2 = omp_irc_success;
+  str2 = omp_get_interop_str (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+  str2 = omp_get_interop_str (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1), NULL);
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+#ifdef __cplusplus
+  str2 = omp_get_interop_str (interop,
+	    (omp_interop_property_t) ((int) omp_ipr_targetsync-1));
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+#endif
+
+  /* omp_get_num_interop_properties (), i.e > upper bound.  */
+
+  ret_code2 = omp_irc_success;
+  ival2 = omp_get_interop_int (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ival2 == 0);  /* Implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+
+  ret_code2 = omp_irc_success;
+  ptr2 = omp_get_interop_ptr (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (ptr2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message.  */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+
+  ret_code2 = omp_irc_success;
+  str2 = omp_get_interop_str (interop,
+	    (omp_interop_property_t) omp_get_num_interop_properties (interop),
+	    &ret_code2);
+  assert (ret_code2 == omp_irc_out_of_range);
+  assert (str2 == NULL);  /* Obvious implementation choice.  */
+  err2 = omp_get_interop_rc_desc (interop, ret_code2);
+  assert (err2 != NULL && strlen (err2) > 5);  /* Some sensible message. */
+  /* GCC implementation choice.  */
+  assert (!strcmp (err2, "property ID is out of range"));
+}
+#pragma omp end declare target
+
+int
+main ()
+{
+  for (int dev = 0; dev < omp_get_num_devices (); dev++)
+    #pragma omp target device(device_num : dev)
+      target_test ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-1.F90 b/libgomp/testsuite/libgomp.fortran/interop-routines-1.F90
new file mode 100644
index 00000000000..bca27f697e7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-1.F90
@@ -0,0 +1,236 @@
+      program main
+        use iso_c_binding, only: c_intptr_t, c_ptr, c_associated
+#ifndef USE_OMP_HEADER
+        use omp_lib
+#endif
+        implicit none (type, external)
+
+#ifdef USE_OMP_HEADER
+        include "omp_lib.h"
+#endif
+
+        integer(omp_interop_kind) :: interop = omp_interop_none
+        integer(omp_interop_rc_kind) :: ret_code
+        integer(omp_interop_fr_kind) :: fr
+        integer(omp_interop_property_kind) :: ipr
+
+        integer(c_intptr_t) :: ival
+        type(c_ptr) :: ptr
+        character(len=:), pointer :: str
+
+        if (omp_irc_no_value /= 1) stop 1
+        if (omp_irc_success /= 0) stop 2
+        if (omp_irc_empty /= -1) stop 3
+        if (omp_irc_out_of_range /= -2) stop 4
+        if (omp_irc_type_int /= -3) stop 5
+        if (omp_irc_type_ptr /= -4) stop 6
+        if (omp_irc_type_str /= -5) stop 7
+        if (omp_irc_other /= -6) stop 8
+
+        ! Check values, including invalid values.
+        do ret_code = omp_irc_other - 1, omp_irc_no_value + 1
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (ret_code < omp_irc_other                                          &
+     &        .or. ret_code > omp_irc_no_value) then
+            ! Assume disassociated for an invalid value.
+            if (associated (str)) stop 9
+          else if (ret_code == omp_irc_other) then
+            ! Likely not to exist in an implementation; esp. not for
+            ! omp_interop_none. Thus, assume disassociated.
+            ! In GCC, omp_irc_other is used on the device side, only, to
+            ! complain about omp_get_interop_{int,ptr,str} usage.
+            if (associated (str)) stop 10
+          else
+            ! Assume that omp_get_interop_rc_desc handles all of those and
+            ! not only omp_irc_empty (and possibly omp_irc_out_of_range),
+            ! which do occur for omp_interop_none.
+            ! Assume some sensible message, i.e. at least 5 characters.
+            if (len_trim (str) <= 5) stop 11
+          end if
+        end do
+
+        if (omp_ifr_last < omp_ifr_hsa) stop 12
+
+        do fr = omp_ifr_cuda, omp_ifr_last
+          select case (fr)
+            ! Expect the id values from the additional-definition document.
+            case (omp_ifr_cuda)
+              if (fr /= 1) stop 13
+            case (omp_ifr_cuda_driver)
+              if (fr /= 2) stop 14
+            case (omp_ifr_opencl)
+              if (fr /= 3) stop 15
+            case (omp_ifr_sycl)
+              if (fr /= 4) stop 16
+            case (omp_ifr_hip)
+              if (fr /= 5) stop 17
+            case (omp_ifr_level_zero)
+              if (fr /= 6) stop 18
+            case (omp_ifr_hsa)
+              if (fr /= 7) stop 19
+            case default
+              ! Valid, but unexpected to have more interop types.
+              stop 20
+          end select
+        end do
+
+        if (omp_ipr_first > omp_ipr_targetsync                                  &
+     &      .or. (omp_ipr_fr_id                                                 &
+     &            >= omp_get_num_interop_properties (interop)))                 &
+     &    stop 21
+
+        do ipr = omp_ipr_first,                                                 &
+     &           omp_get_num_interop_properties (interop) - 1
+          ! As interop == omp_interop_none, NULL is permissible;
+          ! nonetheless, require != NULL for the GCC implementation.
+          str => omp_get_interop_name (interop, ipr)
+          select case (ipr)
+            case (omp_ipr_fr_id)
+              if (ipr /= -1 .or. str /= "fr_id")                                &
+     &          stop 21
+            case (omp_ipr_fr_name)
+              if (ipr /= -2 .or. str /= "fr_name")                              &
+     &          stop 22
+            case (omp_ipr_vendor)
+              if (ipr /= -3 .or. str /= "vendor")                               &
+     &          stop 23
+            case (omp_ipr_vendor_name)
+              if (ipr /= -4 .or. str /= "vendor_name")                          &
+     &          stop 24
+            case (omp_ipr_device_num)
+              if (ipr /= -5 .or. str /= "device_num")                           &
+     &          stop 25
+            case (omp_ipr_platform)
+              if (ipr /= -6 .or. str /= "platform")                             &
+     &          stop 26
+            case (omp_ipr_device)
+              if (ipr /= -7 .or. str /= "device")                               &
+     &          stop 27
+            case (omp_ipr_device_context)
+              if (ipr /= -8 .or. str /= "device_context")                       &
+     &          stop 28
+            case (omp_ipr_targetsync)
+              if (ipr /= -9 .or. str /= "targetsync")                           &
+     &          stop 29
+            case default
+              ! Valid, but unexpected to have more interop types,
+              ! especially not for interop == omp_interop_none.
+              stop 30
+          end select
+
+          ! As interop == omp_interop_none, expect NULL.
+          if (associated (omp_get_interop_type_desc (interop, ipr)))            &
+     &      stop 31
+
+          ret_code = omp_irc_success
+          ival = omp_get_interop_int (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 32
+          if (ival /= 0) stop 33  ! Implementation choice
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 34
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 35  ! GCC implementation choice.
+          ival = omp_get_interop_int (interop, ipr)
+          if (ival /= 0) stop 33  ! Implementation choice
+
+          ret_code = omp_irc_success
+          ptr = omp_get_interop_ptr (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 36
+          if (c_associated (ptr)) stop 37  ! Obvious implementation choice.
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 38
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 39  ! GCC implementation choice.
+          ptr = omp_get_interop_ptr (interop, ipr)
+          if (c_associated (ptr)) stop 37  ! Obvious implementation choice.
+
+          ret_code = omp_irc_success
+          str => omp_get_interop_str (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 40
+          if (associated (str)) stop 41  ! Obvious mplementation choice
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 42
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 43  ! GCC implementation choice.
+          str => omp_get_interop_str (interop, ipr)
+          if (associated (str)) stop 41  ! Obvious mplementation choice
+        end do
+
+        ! Invalid ipr.
+        ! Valid are either omp_irc_empty (due to omp_interop_none) or
+        ! omp_irc_out_of_range; assume omp_irc_out_of_range with GCC.
+
+        ! omp_ipr_targetsync-1, i.e < lower bound.
+
+        ret_code = omp_irc_success
+        ival = omp_get_interop_int (interop, omp_ipr_targetsync-1,              &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 44
+        if (ival /= 0) stop 45  ! Implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 46
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 47
+        ival = omp_get_interop_int (interop, omp_ipr_targetsync-1)
+        if (ival /= 0) stop 45  ! Implementation choice.
+
+        ret_code = omp_irc_success
+        ptr = omp_get_interop_ptr (interop, omp_ipr_targetsync-1,               &
+     &                             ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 48
+        if (c_associated (ptr)) stop 49 ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 50
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 51
+        ptr = omp_get_interop_ptr (interop, omp_ipr_targetsync-1)
+        if (c_associated (ptr)) stop 49 ! Obvious implementation choice.
+
+        ret_code = omp_irc_success
+        str => omp_get_interop_str (interop, omp_ipr_targetsync-1,              &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 52
+        if (associated (str)) stop 53  ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 54
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 55
+        str => omp_get_interop_str (interop, omp_ipr_targetsync-1)
+        if (associated (str)) stop 53  ! Obvious implementation choice.
+
+        ! omp_get_num_interop_properties (), i.e > upper bound.
+
+        ret_code = omp_irc_success
+        ival = omp_get_interop_int (interop,                                    &
+     &            omp_get_num_interop_properties (interop),                     &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 56
+        if (ival /= 0) stop 57  ! Implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 58
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 59
+
+        ret_code = omp_irc_success
+        ptr = omp_get_interop_ptr (interop,                                     &
+     &          omp_get_num_interop_properties (interop), ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 60
+        if (c_associated (ptr)) stop 61 ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 62
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 63
+
+        ret_code = omp_irc_success
+        str => omp_get_interop_str (interop,                                    &
+     &           omp_get_num_interop_properties (interop), ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 64
+        if (associated (str)) stop 65  ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 66
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 67
+      end
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-2.F90 b/libgomp/testsuite/libgomp.fortran/interop-routines-2.F90
new file mode 100644
index 00000000000..d0a6752ff8e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-2.F90
@@ -0,0 +1,3 @@
+! { dg-additional-options "-DUSE_OMP_HEADER=1" }
+
+#include "interop-routines-1.F90"
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-3.F b/libgomp/testsuite/libgomp.fortran/interop-routines-3.F
new file mode 100644
index 00000000000..a8d23d7b2d6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-3.F
@@ -0,0 +1,2 @@
+! Use fixed form
+#include "interop-routines-1.F90"
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-4.F b/libgomp/testsuite/libgomp.fortran/interop-routines-4.F
new file mode 100644
index 00000000000..bbc245f8e8f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-4.F
@@ -0,0 +1,4 @@
+! { dg-additional-options "-DUSE_OMP_HEADER=1" }
+! Use fixed form
+
+#include "interop-routines-1.F90"
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-5.F b/libgomp/testsuite/libgomp.fortran/interop-routines-5.F
new file mode 100644
index 00000000000..b97bfcf30d1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-5.F
@@ -0,0 +1,4 @@
+! { dg-additional-options "-ffixed-line-length-80" }
+! Use fixed form - but with 80 columns instead of 72
+
+#include "interop-routines-1.F90"
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-6.F b/libgomp/testsuite/libgomp.fortran/interop-routines-6.F
new file mode 100644
index 00000000000..faea4853a85
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-6.F
@@ -0,0 +1,4 @@
+! { dg-additional-options "-DUSE_OMP_HEADER=1 -ffixed-line-length-80" }
+! Use fixed form - but with 80 columns instead of 72
+
+#include "interop-routines-1.F90"
diff --git a/libgomp/testsuite/libgomp.fortran/interop-routines-7.F90 b/libgomp/testsuite/libgomp.fortran/interop-routines-7.F90
new file mode 100644
index 00000000000..a615d4b03ca
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/interop-routines-7.F90
@@ -0,0 +1,290 @@
+! { dg-do run { target { offload_device } } }
+
+! OpenMP permits using the interop functions on the device,
+! but it is not really supported. Hence, check that the stubs
+! are working.
+
+      module m
+      contains
+      subroutine target_test
+        use iso_c_binding, only: c_intptr_t, c_ptr, c_associated
+#ifndef USE_OMP_HEADER
+        use omp_lib
+#endif
+        implicit none (type, external)
+
+#ifdef USE_OMP_HEADER
+        include "omp_lib.h"
+#endif
+
+        integer(omp_interop_kind) :: interop = omp_interop_none
+        integer(omp_interop_rc_kind) :: ret_code
+        integer(omp_interop_fr_kind) :: fr
+        integer(omp_interop_property_kind) :: ipr
+
+        integer(c_intptr_t) :: ival
+        type(c_ptr) :: ptr
+        character(len=:), pointer :: str
+
+        if (omp_is_initial_device())                                            &
+          ! Already checked in interop-routines-1.F90
+          ! And some assumptions below are only fulfilled for nonhost
+     &    return
+
+        if (omp_irc_no_value /= 1) stop 1
+        if (omp_irc_success /= 0) stop 2
+        if (omp_irc_empty /= -1) stop 3
+        if (omp_irc_out_of_range /= -2) stop 4
+        if (omp_irc_type_int /= -3) stop 5
+        if (omp_irc_type_ptr /= -4) stop 6
+        if (omp_irc_type_str /= -5) stop 7
+        if (omp_irc_other /= -6) stop 8
+
+        ! Check values, including invalid values.
+        do ret_code = omp_irc_other - 1, omp_irc_no_value + 1
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (ret_code < omp_irc_other                                          &
+     &        .or. ret_code > omp_irc_no_value) then
+            ! Assume disassociated for an invalid value.
+            if (associated (str)) stop 9
+          else if (ret_code == omp_irc_other) then
+            ! Likely not to exist in an implementation; esp. not for
+            ! omp_interop_none. Thus, assume disassociated.
+            ! In GCC, omp_irc_other is used on the device side, only, to
+            ! complain about omp_get_interop_{int,ptr,str} usage.
+            ! See below for a check for the device side.
+            if (len_trim (str) <= 5) stop 11
+          else
+            ! Assume that omp_get_interop_rc_desc handles all of those and
+            ! not only omp_irc_empty (and possibly omp_irc_out_of_range),
+            ! which do occur for omp_interop_none.
+            ! Assume some sensible message, i.e. at least 5 characters.
+            if (len_trim (str) <= 5) stop 11
+          end if
+        end do
+
+        if (omp_ifr_last < omp_ifr_hsa) stop 12
+
+        do fr = omp_ifr_cuda, omp_ifr_last
+          select case (fr)
+            ! Expect the id values from the additional-definition document.
+            case (omp_ifr_cuda)
+              if (fr /= 1) stop 13
+            case (omp_ifr_cuda_driver)
+              if (fr /= 2) stop 14
+            case (omp_ifr_opencl)
+              if (fr /= 3) stop 15
+            case (omp_ifr_sycl)
+              if (fr /= 4) stop 16
+            case (omp_ifr_hip)
+              if (fr /= 5) stop 17
+            case (omp_ifr_level_zero)
+              if (fr /= 6) stop 18
+            case (omp_ifr_hsa)
+              if (fr /= 7) stop 19
+            case default
+              ! Valid, but unexpected to have more interop types.
+              stop 20
+          end select
+        end do
+
+        if (omp_ipr_first > omp_ipr_targetsync                                  &
+     &      .or. (omp_ipr_fr_id                                                 &
+     &            >= omp_get_num_interop_properties (interop)))                 &
+     &    stop 21
+
+        do ipr = omp_ipr_first,                                                 &
+     &           omp_get_num_interop_properties (interop) - 1
+          ! As interop == omp_interop_none, NULL is permissible;
+          ! nonetheless, require != NULL for the GCC implementation.
+          str => omp_get_interop_name (interop, ipr)
+          select case (ipr)
+            case (omp_ipr_fr_id)
+              if (ipr /= -1 .or. str /= "fr_id")                                &
+     &          stop 21
+            case (omp_ipr_fr_name)
+              if (ipr /= -2 .or. str /= "fr_name")                              &
+     &          stop 22
+            case (omp_ipr_vendor)
+              if (ipr /= -3 .or. str /= "vendor")                               &
+     &          stop 23
+            case (omp_ipr_vendor_name)
+              if (ipr /= -4 .or. str /= "vendor_name")                          &
+     &          stop 24
+            case (omp_ipr_device_num)
+              if (ipr /= -5 .or. str /= "device_num")                           &
+     &          stop 25
+            case (omp_ipr_platform)
+              if (ipr /= -6 .or. str /= "platform")                             &
+     &          stop 26
+            case (omp_ipr_device)
+              if (ipr /= -7 .or. str /= "device")                               &
+     &          stop 27
+            case (omp_ipr_device_context)
+              if (ipr /= -8 .or. str /= "device_context")                       &
+     &          stop 28
+            case (omp_ipr_targetsync)
+              if (ipr /= -9 .or. str /= "targetsync")                           &
+     &          stop 29
+            case default
+              ! Valid, but unexpected to have more interop types,
+              ! especially not for interop == omp_interop_none.
+              stop 30
+          end select
+
+          ! As interop == omp_interop_none, expect NULL.
+          if (associated (omp_get_interop_type_desc (interop, ipr)))            &
+     &      stop 31
+
+          ret_code = omp_irc_success
+          ival = omp_get_interop_int (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 32
+          if (ival /= 0) stop 33  ! Implementation choice
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 34
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 35  ! GCC implementation choice.
+
+          ret_code = omp_irc_success
+          ptr = omp_get_interop_ptr (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 36
+          if (c_associated (ptr)) stop 37  ! Obvious implementation choice.
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 38
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 39  ! GCC implementation choice.
+
+          ret_code = omp_irc_success
+          str => omp_get_interop_str (interop, ipr, ret_code)
+          if (ret_code /= omp_irc_empty) stop 40
+          if (associated (str)) stop 41  ! Obvious mplementation choice
+          str => omp_get_interop_rc_desc (interop, ret_code)
+          if (len_trim (str) <= 5) stop 42
+          if (str /= "provided interoperability object is equal to "            &
+     &               // "omp_interop_none")                                     &
+     &      stop 43  ! GCC implementation choice.
+
+          ! Special case of GCC: For any non-'omp_interop_none' valued interop,
+          ! a device-side call to omp_get_interop_{int,ptr,src} will yield
+          ! omp_irc_other - with the error message as checked below.
+
+          block
+          integer(omp_interop_kind) :: interop_not_none_invalid                 &
+     &      = int(z'DEADBEEF', omp_interop_kind)
+
+          ret_code = omp_irc_success
+          ival = omp_get_interop_int (interop_not_none_invalid, ipr, ret_code)
+          if (ret_code /= omp_irc_other) stop 101
+          if (ival /= 0) stop 102  ! Implementation choice
+          str => omp_get_interop_rc_desc (interop_not_none_invalid, ret_code)
+          if (len_trim (str) <= 5) stop 103
+          if (str /= "obtaining properties is only supported on the "           &
+     &               // "initial device")                                       &
+     &      stop 104  ! GCC implementation choice.
+
+          ret_code = omp_irc_success
+          ptr = omp_get_interop_ptr (interop_not_none_invalid, ipr, ret_code)
+          if (ret_code /= omp_irc_other) stop 105
+          if (c_associated (ptr)) stop 106  ! Obvious implementation choice.
+          str => omp_get_interop_rc_desc (interop_not_none_invalid, ret_code)
+          if (len_trim (str) <= 5) stop 107
+          if (str /= "obtaining properties is only supported on the "           &
+     &               // "initial device")                                       &
+     &      stop 108  ! GCC implementation choice.
+
+          ret_code = omp_irc_success
+          str => omp_get_interop_str (interop_not_none_invalid, ipr, ret_code)
+          if (ret_code /= omp_irc_other) stop 109
+          if (associated (str)) stop 110  ! Obvious mplementation choice
+          str => omp_get_interop_rc_desc (interop_not_none_invalid, ret_code)
+          if (len_trim (str) <= 5) stop 111
+          if (str /= "obtaining properties is only supported on the "           &
+     &               // "initial device")                                       &
+     &      stop 112  ! GCC implementation choice.
+          end block
+        end do
+
+        ! Invalid ipr.
+        ! Valid are either omp_irc_empty (due to omp_interop_none) or
+        ! omp_irc_out_of_range; assume omp_irc_out_of_range with GCC.
+
+        ! omp_ipr_targetsync-1, i.e < lower bound.
+
+        ret_code = omp_irc_success
+        ival = omp_get_interop_int (interop, omp_ipr_targetsync-1,              &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 44
+        if (ival /= 0) stop 45  ! Implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 46
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 47
+
+        ret_code = omp_irc_success
+        ptr = omp_get_interop_ptr (interop, omp_ipr_targetsync-1,               &
+     &                             ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 48
+        if (c_associated (ptr)) stop 49 ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 50
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 51
+
+        ret_code = omp_irc_success
+        str => omp_get_interop_str (interop, omp_ipr_targetsync-1,              &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 52
+        if (associated (str)) stop 53  ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 54
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 55
+
+        ! omp_get_num_interop_properties (), i.e > upper bound.
+
+        ret_code = omp_irc_success
+        ival = omp_get_interop_int (interop,                                    &
+     &            omp_get_num_interop_properties (interop),                     &
+     &                              ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 56
+        if (ival /= 0) stop 57  ! Implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 58
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 59
+
+        ret_code = omp_irc_success
+        ptr = omp_get_interop_ptr (interop,                                     &
+     &          omp_get_num_interop_properties (interop), ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 60
+        if (c_associated (ptr)) stop 61 ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 62
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 63
+
+        ret_code = omp_irc_success
+        str => omp_get_interop_str (interop,                                    &
+     &           omp_get_num_interop_properties (interop), ret_code)
+        if (ret_code /= omp_irc_out_of_range) stop 64
+        if (associated (str)) stop 65  ! Obvious implementation choice.
+        str => omp_get_interop_rc_desc (interop, ret_code)
+        if (len_trim (str) <= 5) stop 66
+        ! GCC implementation choice.
+        if (str /= "property ID is out of range") stop 67
+      end
+      end module
+
+      program main
+        use omp_lib, only: omp_get_num_devices
+        use m
+        implicit none (type, external)
+        integer :: dev
+        do dev = 0, omp_get_num_devices () - 1
+!$omp target device(device_num : dev)
+          call target_test
+!$omp end target
+        end do
+      end

Reply via email to