https://gcc.gnu.org/g:41b9c3b848c8cbe9cb4b9d923c4e569767284e28

commit r15-8657-g41b9c3b848c8cbe9cb4b9d923c4e569767284e28
Author: Tobias Burnus <tbur...@baylibre.com>
Date:   Fri Mar 21 21:39:42 2025 +0100

    libgomp/plugin: Add initial interop support to nvptx + gcn
    
    The interop directive operates on an opaque object that represents a
    foreign runtime. This commit adds support for
    this to the two offloading plugins.
    
    For nvptx, it supports cuda, cuda_driver and hip; the latter is AMD's
    version of CUDA which for Nvidia devices boils down to normal CUDA.
    Thus, at the end for this limited use, cuda/cuda_driver/hip are all
    the same - and for plugin-nvptx.c, the they differ only in terms of
    what gets fr_id, fr_name and get_interop_type_desc return.
    
    For gcn, it supports hip and hsa.
    
    Regarding get-mapped-ptr-1.c: That's actually a fix for the
    GOMP_interop commit r15-8654-g99e2906ae255fc that added
    GOMP_DEVICE_DEFAULT_OMP_61 alias omp_default_device, which is
    a conforming device number. But that test used -5 as check for a
    non-conforming device number.
    
    libgomp/ChangeLog:
    
            * plugin/plugin-gcn.c (_LIBGOMP_PLUGIN_INCLUDE): Define.
            (struct hsa_runtime_fn_info): Add two queue functions.
            (hipError_t, hipCtx_t, hipStream_s, hipStream_t): New types.
            (struct hip_runtime_fn_info): New.
            (hip_runtime_lib, hip_fns): New global vars.
            (init_environment_variables): Handle hip_runtime_lib.
            (init_hsa_runtime_functions): Load the two queue functions.
            (init_hip_runtime_functions, GOMP_OFFLOAD_interop,
            GOMP_OFFLOAD_get_interop_int, GOMP_OFFLOAD_get_interop_ptr,
            GOMP_OFFLOAD_get_interop_str,
            GOMP_OFFLOAD_get_interop_type_desc): New.
            * plugin/plugin-nvptx.c (_LIBGOMP_PLUGIN_INCLUDE): Define.
            (GOMP_OFFLOAD_interop, GOMP_OFFLOAD_get_interop_int,
            GOMP_OFFLOAD_get_interop_ptr, GOMP_OFFLOAD_get_interop_str,
            GOMP_OFFLOAD_get_interop_type_desc): New.
            * testsuite/libgomp.c/interop-fr-1.c: New test.
            * testsuite/libgomp.c-c++-common/get-mapped-ptr-1.c: Use -6
            not -5 as non-conforming device number.

Diff:
---
 libgomp/plugin/plugin-gcn.c                        | 464 ++++++++++++++++-
 libgomp/plugin/plugin-nvptx.c                      | 302 +++++++++++
 .../libgomp.c-c++-common/get-mapped-ptr-1.c        |   2 +-
 libgomp/testsuite/libgomp.c/interop-fr-1.c         | 577 +++++++++++++++++++++
 4 files changed, 1342 insertions(+), 3 deletions(-)

diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 5c65778191a6..4b42a597cbdd 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -41,7 +41,9 @@
 #include <hsa_ext_amd.h>
 #include <dlfcn.h>
 #include <signal.h>
+#define _LIBGOMP_PLUGIN_INCLUDE 1
 #include "libgomp-plugin.h"
+#undef _LIBGOMP_PLUGIN_INCLUDE
 #include "config/gcn/libgomp-gcn.h"  /* For struct output.  */
 #include "gomp-constants.h"
 #include <elf.h>
@@ -190,6 +192,8 @@ struct hsa_runtime_fn_info
   uint64_t (*hsa_queue_add_write_index_release_fn) (const hsa_queue_t *queue,
                                                    uint64_t value);
   uint64_t (*hsa_queue_load_read_index_acquire_fn) (const hsa_queue_t *queue);
+  uint64_t (*hsa_queue_load_read_index_relaxed_fn) (const hsa_queue_t *queue);
+  uint64_t (*hsa_queue_load_write_index_relaxed_fn) (const hsa_queue_t *queue);
   void (*hsa_signal_store_relaxed_fn) (hsa_signal_t signal,
                                       hsa_signal_value_t value);
   void (*hsa_signal_store_release_fn) (hsa_signal_t signal,
@@ -216,6 +220,25 @@ struct hsa_runtime_fn_info
      const hsa_signal_t *dep_signals, hsa_signal_t completion_signal);
 };
 
+/* As an HIP runtime is dlopened, following structure defines function
+   pointers utilized by the interop feature of this plugin.
+   Add suffient type declarations to get this work.  */
+
+typedef int hipError_t;  /* Actually an enum; 0 == success. */
+typedef void* hipCtx_t;
+struct hipStream_s;
+typedef struct hipStream_s* hipStream_t;
+
+struct hip_runtime_fn_info
+{
+  hipError_t (*hipStreamCreate_fn) (hipStream_t *);
+  hipError_t (*hipStreamDestroy_fn) (hipStream_t);
+  hipError_t (*hipStreamSynchronize_fn) (hipStream_t);
+  hipError_t (*hipCtxGetCurrent_fn) (hipCtx_t *ctx);
+  hipError_t (*hipSetDevice_fn) (int deviceId);
+  hipError_t (*hipGetDevice_fn) (int *deviceId);
+};
+
 /* Structure describing the run-time and grid properties of an HSA kernel
    lauch.  This needs to match the format passed to GOMP_OFFLOAD_run.  */
 
@@ -553,9 +576,11 @@ struct hsa_context_info
 static struct hsa_context_info hsa_context;
 
 /* HSA runtime functions that are initialized in init_hsa_context.  */
-
 static struct hsa_runtime_fn_info hsa_fns;
 
+/* HIP runtime functions that are initialized in init_hip_runtime_functions.  
*/
+static struct hip_runtime_fn_info hip_fns;
+
 /* Heap space, allocated target-side, provided for use of newlib malloc.
    Each module should have it's own heap allocated.
    Beware that heap usage increases with OpenMP teams.  See also arenas.  */
@@ -578,10 +603,11 @@ static bool debug;
 
 static bool suppress_host_fallback;
 
-/* Flag to locate HSA runtime shared library that is dlopened
+/* Flag to locate HSA and HIP runtime shared libraries that are dlopened
    by this plug-in.  */
 
 static const char *hsa_runtime_lib;
+static const char *hip_runtime_lib;
 
 /* Flag to decide if the runtime should support also CPU devices (can be
    a simulator).  */
@@ -1068,6 +1094,10 @@ init_environment_variables (void)
   if (hsa_runtime_lib == NULL)
     hsa_runtime_lib = "libhsa-runtime64.so.1";
 
+  hip_runtime_lib = secure_getenv ("HIP_RUNTIME_LIB");
+  if (hip_runtime_lib == NULL)
+    hip_runtime_lib = "libamdhip64.so";
+
   support_cpu_devices = secure_getenv ("GCN_SUPPORT_CPU_DEVICES");
 
   const char *x = secure_getenv ("GCN_NUM_TEAMS");
@@ -1418,6 +1448,8 @@ init_hsa_runtime_functions (void)
   DLSYM_FN (hsa_executable_iterate_symbols)
   DLSYM_FN (hsa_queue_add_write_index_release)
   DLSYM_FN (hsa_queue_load_read_index_acquire)
+  DLSYM_FN (hsa_queue_load_read_index_relaxed)
+  DLSYM_FN (hsa_queue_load_write_index_relaxed)
   DLSYM_FN (hsa_signal_wait_acquire)
   DLSYM_FN (hsa_signal_store_relaxed)
   DLSYM_FN (hsa_signal_store_release)
@@ -4365,6 +4397,434 @@ unlock:
   return retval;
 }
 
+
+static bool
+init_hip_runtime_functions (void)
+{
+  bool inited = false;
+  if (inited)
+    return hip_fns.hipStreamCreate_fn != NULL;
+  inited = true;
+
+  void *handle = dlopen (hip_runtime_lib, RTLD_LAZY);
+  if (handle == NULL)
+    return false;
+
+#define DLSYM_OPT_FN(function) \
+  hip_fns.function##_fn = dlsym (handle, #function)
+
+  DLSYM_OPT_FN (hipStreamCreate);
+  DLSYM_OPT_FN (hipStreamDestroy);
+  DLSYM_OPT_FN (hipStreamSynchronize);
+  DLSYM_OPT_FN (hipCtxGetCurrent);
+  DLSYM_OPT_FN (hipGetDevice);
+  DLSYM_OPT_FN (hipSetDevice);
+#undef DLSYM_OPT_FN
+
+  if (!hip_fns.hipStreamCreate_fn
+      || !hip_fns.hipStreamDestroy_fn
+      || !hip_fns.hipStreamSynchronize_fn
+      || !hip_fns.hipCtxGetCurrent_fn
+      || !hip_fns.hipGetDevice_fn
+      || !hip_fns.hipSetDevice_fn)
+    {
+      hip_fns.hipStreamCreate_fn = NULL;
+      return false;
+    }
+
+  return true;
+}
+
+
+void
+GOMP_OFFLOAD_interop (struct interop_obj_t *obj, int ord,
+                     enum gomp_interop_flag action, bool targetsync,
+                     const char *prefer_type)
+{
+  if ((action == gomp_interop_flag_destroy || action == gomp_interop_flag_use)
+      && !obj->stream)
+    return;
+  if ((action == gomp_interop_flag_destroy || action == gomp_interop_flag_use)
+      && obj->fr == omp_ifr_hsa)
+    {
+      /* Wait until the queue is is empty.   */
+      bool is_empty;
+      uint64_t read_index, write_index;
+      hsa_queue_t *queue = (hsa_queue_t *) obj->stream;
+      do
+       {
+         read_index = hsa_fns.hsa_queue_load_read_index_relaxed_fn (queue);
+         write_index = hsa_fns.hsa_queue_load_write_index_relaxed_fn (queue);
+         is_empty = (read_index == write_index);
+       }
+      while (!is_empty);
+
+      if (action == gomp_interop_flag_destroy)
+       {
+         hsa_status_t status = hsa_fns.hsa_queue_destroy_fn (queue);
+         if (status != HSA_STATUS_SUCCESS)
+           hsa_fatal ("Error destroying interop hsa_queue_t", status);
+       }
+      return;
+    }
+  if (action == gomp_interop_flag_destroy)
+    {
+      hipError_t err = hip_fns.hipStreamDestroy_fn ((hipStream_t) obj->stream);
+      if (err != 0)
+       GOMP_PLUGIN_fatal ("Error destroying interop hipStream_t: %d", err);
+      return;
+    }
+  if (action == gomp_interop_flag_use)
+    {
+      hipError_t err
+       = hip_fns.hipStreamSynchronize_fn ((hipStream_t) obj->stream);
+      if (err != 0)
+       GOMP_PLUGIN_fatal ("Error synchronizing interop hipStream_t: %d", err);
+      return;
+    }
+
+  bool fr_set = false;
+
+  /* Check for the preferred type; cf. parser in C/C++/Fortran or
+     dump_omp_init_prefer_type for the format.
+     Accept the first '{...}' block that specifies a 'fr' that we support.
+     Currently, no 'attr(...)' are supported.  */
+  if (prefer_type)
+    while (prefer_type[0] == (char) GOMP_INTEROP_IFR_SEPARATOR)
+      {
+       /* '{' item block starts.  */
+       prefer_type++;
+       /* 'fr(...)' block  */
+       while (prefer_type[0] != (char) GOMP_INTEROP_IFR_SEPARATOR)
+         {
+           omp_interop_fr_t fr = (omp_interop_fr_t) prefer_type[0];
+           if (fr == omp_ifr_hip)
+             {
+               obj->fr = omp_ifr_hip;
+               fr_set = true;
+             }
+           if (fr == omp_ifr_hsa)
+             {
+               obj->fr = omp_ifr_hsa;
+               fr_set = true;
+             }
+           prefer_type++;
+         }
+       prefer_type++;
+       /* 'attr(...)' block  */
+       while (prefer_type[0] != '\0')
+         {
+           /* const char *attr = prefer_type;  */
+           prefer_type += strlen (prefer_type) + 1;
+         }
+       prefer_type++;
+       /* end of '}'.  */
+       if (fr_set)
+         break;
+      }
+
+  /* Prefer HIP, use HSA as fallback.  The warning is only printed if GCN_DEBUG
+     is set and does not distinguishes between on prefer_type or hip 
prefer_type
+     nor whether a later/lower preference also specifies 'hsa'.
+     The assumption is that the user code handles HSA gracefully, but likely
+     just by falling back to the host version.  On the other hand, have_hip is
+     likely true if HSA is available.  */
+  if (!fr_set || obj->fr == omp_ifr_hip)
+    {
+      bool have_hip = init_hip_runtime_functions ();
+      if (have_hip)
+       obj->fr = omp_ifr_hip;
+      else
+       {
+         GCN_WARNING ("interop object requested, using HSA instead of HIP "
+                      "as %s could not be loaded", hip_runtime_lib);
+         obj->fr = omp_ifr_hsa;
+       }
+    }
+
+  _Static_assert (sizeof (uint64_t) == sizeof (hsa_agent_t),
+                 "sizeof (uint64_t) == sizeof (hsa_agent_t)");
+  struct agent_info *agent = get_agent_info (ord);
+  obj->device_data = agent;
+
+  if (targetsync && obj->fr == omp_ifr_hsa)
+    {
+      hsa_status_t status;
+      /* Queue size must be (for GPUs) a power of 2 >= 40, i.e. at least 64 and
+        maximally HSA_AGENT_INFO_QUEUE_MAX_SIZE. Arbitrary choice:  */
+      uint32_t queue_size = ASYNC_QUEUE_SIZE;
+      status = hsa_fns.hsa_queue_create_fn (agent->id, queue_size,
+                                           HSA_QUEUE_TYPE_MULTI,
+                                           NULL, NULL, UINT32_MAX, UINT32_MAX,
+                                           (hsa_queue_t **) &obj->stream);
+      if (status != HSA_STATUS_SUCCESS)
+       hsa_fatal ("Error creating interop hsa_queue_t", status);
+    }
+  else if (targetsync)
+    {
+      hipError_t err;
+      int dev_curr;
+      err = hip_fns.hipGetDevice_fn (&dev_curr);
+      if (!err && ord != dev_curr)
+       err = hip_fns.hipSetDevice_fn (ord);
+      if (!err)
+       err = hip_fns.hipStreamCreate_fn ((hipStream_t *) &obj->stream);
+      if (!err && ord != dev_curr)
+       err = hip_fns.hipSetDevice_fn (dev_curr);
+      if (err != 0)
+       GOMP_PLUGIN_fatal ("Error creating interop hipStream_t: %d", err);
+    }
+}
+
+intptr_t
+GOMP_OFFLOAD_get_interop_int (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_hip && obj->fr != omp_ifr_hsa)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->fr;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return 0;
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return 1; /* amd */
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return 0;
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->device_num;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return 0;
+    case omp_ipr_device:
+      if (obj->fr == omp_ifr_hsa)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_type_ptr;
+         return 0;
+       }
+       if (ret_code)
+         *ret_code = omp_irc_success;
+       return ((struct agent_info *) obj->device_data)->device_id;
+    case omp_ipr_device_context:
+      if (ret_code && obj->fr == omp_ifr_hsa)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return 0;
+    case omp_ipr_targetsync:
+      if (ret_code && !obj->stream)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return 0;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return 0;
+}
+
+void *
+GOMP_OFFLOAD_get_interop_ptr (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_hip && obj->fr != omp_ifr_hsa)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return NULL;
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return NULL;
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return NULL;
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return NULL;
+    case omp_ipr_device:
+      if (obj->fr == omp_ifr_hsa)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_success;
+         /* hsa_agent_t is an struct containing a single uint64_t. */
+         return &((struct agent_info *) obj->device_data)->id;
+       }
+      else
+       {
+         if (ret_code)
+           *ret_code = omp_irc_type_int;
+         return NULL;
+       }
+    case omp_ipr_device_context:
+      if (obj->fr == omp_ifr_hsa)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_no_value;
+         return NULL;
+       }
+      else
+       {
+         hipCtx_t ctx;
+         int dev_curr;
+         int dev = ((struct agent_info *) obj->device_data)->device_id;
+         hipError_t err;
+         err = hip_fns.hipGetDevice_fn (&dev_curr);
+         if (!err && dev != dev_curr)
+           err = hip_fns.hipSetDevice_fn (dev);
+         if (!err)
+           err = hip_fns.hipCtxGetCurrent_fn (&ctx);
+         if (!err && dev != dev_curr)
+           err = hip_fns.hipSetDevice_fn (dev_curr);
+         if (err)
+           GOMP_PLUGIN_fatal ("Error obtaining hipCtx_t for device %d: %d",
+                              obj->device_num, err);
+         if (ret_code)
+           *ret_code = omp_irc_success;
+         return ctx;
+       }
+    case omp_ipr_targetsync:
+      if (!obj->stream)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_no_value;
+         return NULL;
+       }
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->stream;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return NULL;
+}
+
+const char *
+GOMP_OFFLOAD_get_interop_str (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_hip && obj->fr != omp_ifr_hsa)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      if (obj->fr == omp_ifr_hip)
+       return "hip";
+      if (obj->fr == omp_ifr_hsa)
+       return "hsa";
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return "amd";
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return NULL;
+    case omp_ipr_device:
+      if (ret_code && obj->fr == omp_ifr_hsa)
+       *ret_code = omp_irc_type_ptr;
+      else if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_device_context:
+      if (ret_code && obj->fr == omp_ifr_hsa)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return NULL;
+    case omp_ipr_targetsync:
+      if (ret_code && !obj->stream)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return NULL;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return 0;
+}
+
+const char *
+GOMP_OFFLOAD_get_interop_type_desc (struct interop_obj_t *obj,
+                                   omp_interop_property_t property_id)
+{
+  _Static_assert (omp_ipr_targetsync == omp_ipr_first,
+                 "omp_ipr_targetsync == omp_ipr_first");
+  _Static_assert (omp_ipr_platform - omp_ipr_first + 1 == 4,
+                 "omp_ipr_platform - omp_ipr_first + 1 == 4");
+  static const char *desc_hip[] = {"N/A",              /* platform */
+                                  "hipDevice_t",       /* device */
+                                  "hipCtx_t",          /* device_context */
+                                  "hipStream_t"};      /* targetsync */
+  static const char *desc_hsa[] = {"N/A",              /* platform */
+                                  "hsa_agent_t *",     /* device */
+                                  "N/A",               /* device_context */
+                                  "hsa_queue_t *"};    /* targetsync */
+  if (obj->fr == omp_ifr_hip)
+    return desc_hip[omp_ipr_platform - property_id];
+  else
+    return desc_hsa[omp_ipr_platform - property_id];
+  return NULL;
+}
+
 /* }}}  */
 /* {{{ OpenMP Plugin API  */
 
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index c47461eeccd3..822c6a410e28 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -35,7 +35,9 @@
 #include "openacc.h"
 #include "config.h"
 #include "symcat.h"
+#define _LIBGOMP_PLUGIN_INCLUDE 1
 #include "libgomp-plugin.h"
+#undef _LIBGOMP_PLUGIN_INCLUDE
 #include "oacc-plugin.h"
 #include "gomp-constants.h"
 #include "oacc-int.h"
@@ -2425,6 +2427,306 @@ nvptx_stacks_acquire (struct ptx_device *ptx_dev, 
size_t size, int num)
   return (void *) ptx_dev->omp_stacks.ptr;
 }
 
+void
+GOMP_OFFLOAD_interop (struct interop_obj_t *obj, int ord,
+                     enum gomp_interop_flag action, bool targetsync,
+                     const char *prefer_type)
+{
+  obj->fr = omp_ifr_cuda;
+
+  if (action == gomp_interop_flag_destroy)
+    {
+      if (obj->stream)
+       CUDA_CALL_ASSERT (cuStreamDestroy, obj->stream);
+      return;
+    }
+  if (action == gomp_interop_flag_use)
+    {
+      if (obj->stream)
+       CUDA_CALL_ASSERT (cuStreamSynchronize, obj->stream);
+      return;
+    }
+
+  /* Check for the preferred type; cf. parser in C/C++/Fortran or
+     dump_omp_init_prefer_type for the format.
+     Accept the first '{...}' block that specifies a 'fr' that we support.
+     Currently, no 'attr(...)' are supported.  */
+  if (prefer_type)
+    while (prefer_type[0] == (char) GOMP_INTEROP_IFR_SEPARATOR)
+      {
+       bool found = false;
+       /* '{' item block starts.  */
+       prefer_type++;
+       /* 'fr(...)' block  */
+       while (prefer_type[0] != (char) GOMP_INTEROP_IFR_SEPARATOR)
+         {
+           omp_interop_fr_t fr = (omp_interop_fr_t) prefer_type[0];
+           if (fr == omp_ifr_cuda
+               || fr == omp_ifr_cuda_driver
+               || fr == omp_ifr_hip)
+             {
+               obj->fr = fr;
+               found = true;
+             }
+           prefer_type++;
+         }
+       prefer_type++;
+       /* 'attr(...)' block  */
+       while (prefer_type[0] != '\0')
+         {
+           /* const char *attr = prefer_type;  */
+           prefer_type += strlen (prefer_type) + 1;
+         }
+       prefer_type++;
+       /* end of '}'.  */
+       if (found)
+         break;
+      }
+
+  obj->device_data = ptx_devices[ord];
+
+  if (targetsync)
+    {
+      CUstream stream = NULL;
+      CUDA_CALL_ASSERT (cuStreamCreate, &stream, CU_STREAM_DEFAULT);
+      obj->stream = stream;
+    }
+}
+
+
+intptr_t
+GOMP_OFFLOAD_get_interop_int (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_cuda
+      && obj->fr != omp_ifr_cuda_driver
+      && obj->fr != omp_ifr_hip)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->fr;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return 0;
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return 11; /* nvidia */
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return 0;
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->device_num;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return 0;
+    case omp_ipr_device:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return ((struct ptx_device *) obj->device_data)->dev;
+    case omp_ipr_device_context:
+      if (ret_code && obj->fr == omp_ifr_cuda)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return 0;
+    case omp_ipr_targetsync:
+      if (!obj->stream)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_no_value;
+         return 0;
+       }
+      /* ptr fits into (u)intptr_t */
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return (uintptr_t) obj->stream;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return 0;
+}
+
+void *
+GOMP_OFFLOAD_get_interop_ptr (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_cuda
+      && obj->fr != omp_ifr_cuda_driver
+      && obj->fr != omp_ifr_hip)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return NULL;
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_type_str;
+      return NULL;
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return NULL;
+    case omp_ipr_device:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_device_context:
+      if (obj->fr == omp_ifr_cuda)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_no_value;
+         return NULL;
+       }
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return ((struct ptx_device *) obj->device_data)->ctx;
+    case omp_ipr_targetsync:
+      if (!obj->stream)
+       {
+         if (ret_code)
+           *ret_code = omp_irc_no_value;
+         return NULL;
+       }
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return obj->stream;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return NULL;
+}
+
+const char *
+GOMP_OFFLOAD_get_interop_str (struct interop_obj_t *obj,
+                             omp_interop_property_t property_id,
+                             omp_interop_rc_t *ret_code)
+{
+  if (obj->fr != omp_ifr_cuda
+      && obj->fr != omp_ifr_cuda_driver
+      && obj->fr != omp_ifr_hip)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_no_value;  /* Hmm. */
+      return 0;
+    }
+  switch (property_id)
+    {
+    case omp_ipr_fr_id:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_fr_name:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      if (obj->fr == omp_ifr_cuda)
+       return "cuda";
+      if (obj->fr == omp_ifr_cuda_driver)
+       return "cuda_driver";
+      if (obj->fr == omp_ifr_hip)
+       return "hip";
+      break;
+    case omp_ipr_vendor:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_vendor_name:
+      if (ret_code)
+       *ret_code = omp_irc_success;
+      return "nvidia";
+    case omp_ipr_device_num:
+      if (ret_code)
+       *ret_code = omp_irc_type_int;
+      return NULL;
+    case omp_ipr_platform:
+      if (ret_code)
+       *ret_code = omp_irc_no_value;
+      return NULL;
+    case omp_ipr_device:
+      if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return NULL;
+    case omp_ipr_device_context:
+      if (ret_code && obj->fr == omp_ifr_cuda)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return NULL;
+    case omp_ipr_targetsync:
+      if (ret_code && !obj->stream)
+       *ret_code = omp_irc_no_value;
+      else if (ret_code)
+       *ret_code = omp_irc_type_ptr;
+      return NULL;
+    default:
+      break;
+    }
+  __builtin_unreachable ();
+  return NULL;
+}
+
+const char *
+GOMP_OFFLOAD_get_interop_type_desc (struct interop_obj_t *obj,
+                                   omp_interop_property_t property_id)
+{
+  _Static_assert (omp_ipr_targetsync == omp_ipr_first,
+                 "omp_ipr_targetsync == omp_ipr_first");
+  _Static_assert (omp_ipr_platform - omp_ipr_first + 1 == 4,
+                 "omp_ipr_platform - omp_ipr_first + 1 == 4");
+  static const char *desc_cuda[] = {"N/A",             /* platform */
+                                   "int",              /* device */
+                                   "N/A",              /* device_context */
+                                   "cudaStream_t"};    /* targetsync */
+  static const char *desc_cuda_driver[] = {"N/A",      /* platform */
+                                          "CUdevice",  /* device */
+                                          "CUcontext", /* device_context */
+                                          "CUstream"}; /* targetsync */
+  static const char *desc_hip[] = {"N/A",              /* platform */
+                                  "hipDevice_t",       /* device */
+                                  "hipCtx_t",          /* device_context */
+                                  "hipStream_t"};      /* targetsync */
+  if (obj->fr == omp_ifr_cuda)
+    return desc_cuda[omp_ipr_platform - property_id];
+  if (obj->fr == omp_ifr_cuda_driver)
+    return desc_cuda_driver[omp_ipr_platform - property_id];
+  else
+    return desc_hip[omp_ipr_platform - property_id];
+  return NULL;
+}
 
 void
 GOMP_OFFLOAD_run (int ord, void *tgt_fn, void *tgt_vars, void **args)
diff --git a/libgomp/testsuite/libgomp.c-c++-common/get-mapped-ptr-1.c 
b/libgomp/testsuite/libgomp.c-c++-common/get-mapped-ptr-1.c
index 4708ae86bfe4..90d1a7245d2a 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/get-mapped-ptr-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/get-mapped-ptr-1.c
@@ -21,7 +21,7 @@ main ()
   if (omp_target_associate_ptr (q, p, sizeof (int), 0, d) != 0)
     return 0;
 
-  if (omp_get_mapped_ptr (q, -5) != NULL)
+  if (omp_get_mapped_ptr (q, -6) != NULL)
     abort ();
 
   if (omp_get_mapped_ptr (q, omp_get_num_devices () + 1) != NULL)
diff --git a/libgomp/testsuite/libgomp.c/interop-fr-1.c 
b/libgomp/testsuite/libgomp.c/interop-fr-1.c
new file mode 100644
index 000000000000..9310c95644ef
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/interop-fr-1.c
@@ -0,0 +1,577 @@
+/* { dg-do run } */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <omp.h>
+#include "../libgomp.c-c++-common/on_device_arch.h"
+
+#define DEFAULT_DEVICE -99
+
+/* The following assumes that when a nvptx device is available,
+   cuda/cuda_driver/hip are supported.
+   And that likewise when a gcn device is available that the
+   plugin also can not only the HSA but also the HIP library
+   such that hsa/hip are supported.
+   For the host, omp_interop_none is expected.
+
+   Otherwise, it only does some basic tests without checking
+   that the returned result really makes sense.  */
+
+void check_host (int);
+void check_nvptx (int);
+void check_gcn (int);
+
+void check_type (omp_interop_t obj)
+{
+  const char *type;
+
+  type = omp_get_interop_type_desc (obj, omp_ipr_fr_id);
+  if (obj != omp_interop_none)
+    assert (strcmp (type, "omp_interop_t") == 0);
+  else
+    assert (type == NULL);
+
+  type = omp_get_interop_type_desc (obj, omp_ipr_fr_name);
+  if (obj != omp_interop_none)
+    assert (strcmp (type, "const char *") == 0);
+  else
+    assert (type == NULL);
+
+  type = omp_get_interop_type_desc (obj, omp_ipr_vendor);
+  if (obj != omp_interop_none)
+    assert (strcmp (type, "int") == 0);
+  else
+    assert (type == NULL);
+
+  type = omp_get_interop_type_desc (obj, omp_ipr_vendor_name);
+  if (obj != omp_interop_none)
+    assert (strcmp (type, "const char *") == 0);
+  else
+    assert (type == NULL);
+
+  type = omp_get_interop_type_desc (obj, omp_ipr_device_num);
+  if (obj != omp_interop_none)
+    assert (strcmp (type, "int") == 0);
+  else
+    assert (type == NULL);
+
+  if (obj != omp_interop_none)
+    return;
+  assert (omp_get_interop_type_desc (obj, omp_ipr_platform) == NULL);
+  assert (omp_get_interop_type_desc (obj, omp_ipr_device) == NULL);
+  assert (omp_get_interop_type_desc (obj, omp_ipr_device_context) == NULL);
+  assert (omp_get_interop_type_desc (obj, omp_ipr_targetsync) == NULL);
+}
+
+void
+do_check (int dev)
+{
+  int num_dev = omp_get_num_devices ();
+  const char *dev_type;
+  if (dev != DEFAULT_DEVICE)
+    omp_set_default_device (dev);
+  int is_nvptx = on_device_arch_nvptx ();
+  int is_gcn = on_device_arch_gcn ();
+  int is_host;
+ 
+  if (dev != DEFAULT_DEVICE)
+    is_host = dev == -1 || dev == num_dev;
+  else
+    {
+      int def_dev = omp_get_default_device ();
+      is_host = def_dev == -1 || def_dev == num_dev;
+    }
+
+  assert (is_nvptx + is_gcn + is_host == 1);
+
+  if (num_dev > 0 && dev != DEFAULT_DEVICE)
+    {
+      if (is_host)
+       omp_set_default_device (0);
+      else
+       omp_set_default_device (-1);
+    }
+
+  if (is_host)
+    dev_type = "host";
+  else if (is_nvptx)
+    dev_type = "nvptx";
+  else if (is_gcn)
+    dev_type = "gcn";
+
+  printf ("Running on the %s device (%d)\n", dev_type, dev);
+  if (is_host)
+    check_host (dev);
+  else if (is_nvptx)
+    check_nvptx (dev);
+  else if (is_gcn)
+    check_gcn (dev);
+}
+
+
+void
+check_host (int dev)
+{
+  omp_interop_t obj = (omp_interop_t) -1L;
+  if (dev == DEFAULT_DEVICE) {
+    #pragma omp interop init(target : obj)
+  } else {
+    #pragma omp interop init(target : obj) device(dev)
+  }
+  assert (obj == omp_interop_none);
+  check_type (obj);
+
+  obj = (omp_interop_t) -1L;
+  if (dev == DEFAULT_DEVICE) {
+    #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{attr("ompx_bar"), fr("cuda"), attr("ompx_foobar")},{fr("cuda_driver")}, 
{fr("hip")}, {fr("hsa")}) : obj)
+  } else {
+    #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{attr("ompx_bar"), fr("cuda"), attr("ompx_foobar")},{fr("cuda_driver")}, 
{fr("hip")}, {fr("hsa")}) : obj) device(dev)
+  }
+  assert (obj == omp_interop_none);
+  check_type (obj);
+
+  obj = (omp_interop_t) -1L;
+  if (dev == DEFAULT_DEVICE) {
+    #pragma omp interop init(targetsync : obj)
+  } else {
+    #pragma omp interop init(targetsync : obj) device(dev)
+  }
+  assert (obj == omp_interop_none);
+  check_type (obj);
+
+  obj = (omp_interop_t) -1L;
+  if (dev == DEFAULT_DEVICE) {
+    #pragma omp interop init(targetsync, prefer_type("cuda","cuda_driver", 
"hip", "hsa") : obj)
+  } else {
+    #pragma omp interop init(targetsync, prefer_type("cuda","cuda_driver", 
"hip", "hsa") : obj) device(dev)
+  }
+  assert (obj == omp_interop_none);
+  check_type (obj);
+}
+
+
+void
+check_nvptx (int dev)
+{
+  for (int variant = 0; variant <= 7; variant++)
+    {
+      omp_interop_t obj = (omp_interop_t) -1L;
+      switch (variant)
+       {
+       /* Expect 'cuda'.  */
+       case 0:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target : obj)
+         } else {
+           #pragma omp interop init(target : obj) device(dev)
+         }
+         break;
+         }
+       case 1:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync : obj)
+         } else {
+           #pragma omp interop init(targetsync : obj) device(dev)
+         }
+         break;
+         }
+       case 2:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{fr("hsa")}, {attr("ompx_bar"), fr("cuda"), 
attr("ompx_foobar")},{fr("cuda_driver")}, {fr("hip")}) : obj)
+         } else {
+           #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{fr("hsa")}, {attr("ompx_bar"), fr("cuda"), 
attr("ompx_foobar")},{fr("cuda_driver")}, {fr("hip")}) : obj) device(dev)
+         }
+         break;
+         }
+       case 3:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync, prefer_type("hsa", "cuda", 
"cuda_driver", "hip") : obj)
+         } else {
+           #pragma omp interop init(targetsync, prefer_type("hsa", "cuda", 
"cuda_driver", "hip") : obj) device(dev)
+         }
+         break;
+         }
+
+       /* Expect 'cuda_driver'.  */
+       case 4:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target, prefer_type("hsa", "cuda_driver", 
"hip", "cuda") : obj)
+         } else {
+           #pragma omp interop init(target, prefer_type("hsa", "cuda_driver", 
"hip", "cuda") : obj) device(dev)
+         }
+         break;
+         }
+       case 5:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync, prefer_type("hsa", 
"cuda_driver", "hip", "cuda") : obj)
+         } else {
+           #pragma omp interop init(targetsync, prefer_type("hsa", 
"cuda_driver", "hip", "cuda") : obj) device(dev)
+         }
+         break;
+         }
+
+       /* Expect 'hip'.  */
+       case 6:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target, prefer_type("hsa", "hip", "cuda", 
"cuda_driver") : obj)
+         } else {
+           #pragma omp interop init(target, prefer_type("hsa", "hip", "cuda", 
"cuda_driver") : obj) device(dev)
+         }
+         break;
+         }
+       case 7:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync, prefer_type("hsa", "hip", 
"cuda", "cuda_driver") : obj)
+         } else {
+           #pragma omp interop init(targetsync, prefer_type("hsa", "hip", 
"cuda", "cuda_driver") : obj) device(dev)
+         }
+         break;
+         }
+       default:
+         abort ();
+       }
+      assert (obj != omp_interop_none && obj != (omp_interop_t) -1L);
+
+      omp_interop_rc_t ret_code = omp_irc_no_value;
+      omp_interop_fr_t fr = (omp_interop_fr_t) omp_get_interop_int (obj, 
omp_ipr_fr_id, &ret_code);
+
+      assert (ret_code == omp_irc_success);
+      if (variant >= 0 && variant <= 3)
+       assert (fr == omp_ifr_cuda);
+      else if (variant <= 5)
+       assert (fr == omp_ifr_cuda_driver);
+      else if (variant <= 7)
+       assert (fr == omp_ifr_hip);
+      else
+       assert (0);
+
+      ret_code = omp_irc_no_value;
+      const char *fr_name = omp_get_interop_str (obj, omp_ipr_fr_name, 
&ret_code);
+
+      assert (ret_code == omp_irc_success);
+      if (fr == omp_ifr_cuda)
+       assert (strcmp (fr_name, "cuda") == 0);
+      else if (fr == omp_ifr_cuda_driver)
+       assert (strcmp (fr_name, "cuda_driver") == 0);
+      else if (fr == omp_ifr_hip)
+       assert (strcmp (fr_name, "hip") == 0);
+      else
+       assert (0);
+
+      ret_code = omp_irc_no_value;
+      int vendor = (int) omp_get_interop_int (obj, omp_ipr_vendor, &ret_code);
+      assert (ret_code == omp_irc_success);
+      assert (vendor == 11);  /* Nvidia */
+
+      ret_code = omp_irc_no_value;
+      const char *vendor_name = omp_get_interop_str (obj, omp_ipr_vendor_name, 
&ret_code);
+      assert (ret_code == omp_irc_success);
+      assert (strcmp (vendor_name, "nvidia") == 0);
+
+      ret_code = omp_irc_no_value;
+      int dev_num = (int) omp_get_interop_int (obj, omp_ipr_device_num, 
&ret_code);
+      assert (ret_code == omp_irc_success);
+      if (dev == DEFAULT_DEVICE)
+       assert (dev_num == omp_get_default_device ());
+      else
+       assert (dev_num == dev);
+
+      /* Platform: N/A.  */
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_int (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_ptr (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_str (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+
+      /* Device: int / CUdevice / hipDevice_t -- all internally an 'int'.  */
+      ret_code = omp_irc_no_value;
+      int fr_device = (int) omp_get_interop_int (obj, omp_ipr_device, 
&ret_code);
+
+      /* CUDA also starts from 0 and goes to < n with cudaGetDeviceCount(&cn). 
 */
+      assert (ret_code == omp_irc_success);
+      assert (fr_device >= 0 && fr_device < omp_get_num_devices ());
+
+      /* Device context: N/A / CUcontext / hipCtx_t -- a pointer.  */
+      ret_code = omp_irc_out_of_range;
+      void *ctx = omp_get_interop_ptr (obj, omp_ipr_device_context, &ret_code);
+
+      if (fr == omp_ifr_cuda)
+       {
+         assert (ret_code == omp_irc_no_value);
+         assert (ctx == NULL);
+       }
+      else
+       {
+         assert (ret_code == omp_irc_success);
+         assert (ctx != NULL);
+       }
+
+      /* Stream/targetsync: cudaStream_t / CUstream / hipStream_t -- a 
pointer.  */
+      ret_code = omp_irc_out_of_range;
+      void *stream = omp_get_interop_ptr (obj, omp_ipr_targetsync, &ret_code);
+
+      if (variant % 2 == 0)  /* no targetsync */
+       {
+         assert (ret_code == omp_irc_no_value);
+         assert (stream == NULL);
+       }
+      else
+       {
+         assert (ret_code == omp_irc_success);
+         assert (stream != NULL);
+       }
+
+      check_type (obj);
+      if (fr == omp_ifr_cuda)
+       {
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_platform), 
"N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_device), 
"int") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, 
omp_ipr_device_context), "N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_targetsync), 
"cudaStream_t") == 0);
+       }
+      else if (fr == omp_ifr_cuda_driver)
+       {
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_platform), 
"N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_device), 
"CUdevice") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, 
omp_ipr_device_context), "CUcontext") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_targetsync), 
"CUstream") == 0);
+       }
+      else
+       {
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_platform), 
"N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_device), 
"hipDevice_t") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, 
omp_ipr_device_context), "hipCtx_t") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_targetsync), 
"hipStream_t") == 0);
+       }
+
+      if (dev == DEFAULT_DEVICE) {
+       #pragma omp interop use(obj)
+       #pragma omp interop destroy(obj)
+      } else {
+       #pragma omp interop use(obj) device(dev)
+       #pragma omp interop destroy(obj) device(dev)
+      }
+    }
+}
+
+
+void
+check_gcn (int dev)
+{
+  for (int variant = 0; variant <= 5; variant++)
+    {
+      omp_interop_t obj = (omp_interop_t) -1L;
+      switch (variant)
+       {
+       /* Expect 'hip'.  */
+       case 0:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target : obj)
+         } else {
+           #pragma omp interop init(target : obj) device(dev)
+         }
+         break;
+         }
+       case 1:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync : obj)
+         } else {
+           #pragma omp interop init(targetsync : obj) device(dev)
+         }
+         break;
+         }
+       case 2:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{fr("cuda")}, {fr("cuda_driver")}, {attr("ompx_bar"), fr("hip"), 
attr("ompx_foobar")},{fr("hsa")}) : obj)
+         } else {
+           #pragma omp interop init(target, prefer_type({attr("ompx_foo")}, 
{fr("cuda")}, {fr("cuda_driver")}, {attr("ompx_bar"), fr("hip"), 
attr("ompx_foobar")},{fr("hsa")}) : obj) device(dev)
+         }
+         break;
+         }
+       case 3:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync, prefer_type("cuda", 
"cuda_driver", "hip", "hsa") : obj)
+         } else {
+           #pragma omp interop init(targetsync, prefer_type("cuda", 
"cuda_driver", "hip", "hsa") : obj) device(dev)
+         }
+         break;
+         }
+
+       /* Expect 'hsa'.  */
+       case 4:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(target, prefer_type("cuda", "cuda_driver", 
"hsa", "hip") : obj)
+         } else {
+           #pragma omp interop init(target, prefer_type("cuda", "cuda_driver", 
"hsa", "hip") : obj) device(dev)
+         }
+         break;
+         }
+       case 5:
+         {
+         if (dev == DEFAULT_DEVICE) {
+           #pragma omp interop init(targetsync, prefer_type("cuda", 
"cuda_driver", "hsa", "hip") : obj)
+         } else {
+           #pragma omp interop init(targetsync, prefer_type("cuda", 
"cuda_driver", "hsa", "hip") : obj) device(dev)
+         }
+         break;
+         }
+       default:
+         abort ();
+       }
+      assert (obj != omp_interop_none && obj != (omp_interop_t) -1L);
+
+      omp_interop_rc_t ret_code = omp_irc_no_value;
+      omp_interop_fr_t fr = (omp_interop_fr_t) omp_get_interop_int (obj, 
omp_ipr_fr_id, &ret_code);
+
+      assert (ret_code == omp_irc_success);
+      if (variant >= 0 && variant <= 3)
+       assert (fr == omp_ifr_hip);
+      else if (variant <= 5)
+       assert (fr == omp_ifr_hsa);
+      else
+       assert (0);
+
+      ret_code = omp_irc_no_value;
+      const char *fr_name = omp_get_interop_str (obj, omp_ipr_fr_name, 
&ret_code);
+
+      assert (ret_code == omp_irc_success);
+      if (fr == omp_ifr_hip)
+       assert (strcmp (fr_name, "hip") == 0);
+      else if (fr == omp_ifr_hsa)
+       assert (strcmp (fr_name, "hsa") == 0);
+      else
+       assert (0);
+
+      ret_code = omp_irc_no_value;
+      int vendor = (int) omp_get_interop_int (obj, omp_ipr_vendor, &ret_code);
+      assert (ret_code == omp_irc_success);
+      assert (vendor == 1);  /* Amd */
+
+      ret_code = omp_irc_no_value;
+      const char *vendor_name = omp_get_interop_str (obj, omp_ipr_vendor_name, 
&ret_code);
+      assert (ret_code == omp_irc_success);
+      assert (strcmp (vendor_name, "amd") == 0);
+
+      ret_code = omp_irc_no_value;
+      int dev_num = (int) omp_get_interop_int (obj, omp_ipr_device_num, 
&ret_code);
+      assert (ret_code == omp_irc_success);
+      if (dev == DEFAULT_DEVICE)
+       assert (dev_num == omp_get_default_device ());
+      else
+       assert (dev_num == dev);
+
+      /* Platform: N/A.  */
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_int (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_ptr (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+      ret_code = omp_irc_success;
+      (void) omp_get_interop_str (obj, omp_ipr_platform, &ret_code);
+      assert (ret_code == omp_irc_no_value);
+
+      /* Device: hipDevice_t / hsa_agent_t* -- hip is internally an 'int'.  */
+      ret_code = omp_irc_no_value;
+      if (fr == omp_ifr_hip)
+       {
+         /* HIP also starts from 0 and goes to < n as with 
cudaGetDeviceCount(&cn).  */
+         int fr_device = (int) omp_get_interop_int (obj, omp_ipr_device, 
&ret_code);
+         assert (ret_code == omp_irc_success);
+         assert (fr_device >= 0 && fr_device < omp_get_num_devices ());
+       }
+      else
+       {
+         void *agent = omp_get_interop_ptr (obj, omp_ipr_device, &ret_code);
+         assert (ret_code == omp_irc_success);
+         assert (agent != NULL);
+       }
+
+      /* Device context: hipCtx_t / N/A -- a pointer.  */
+      ret_code = omp_irc_out_of_range;
+      void *ctx = omp_get_interop_ptr (obj, omp_ipr_device_context, &ret_code);
+      if (fr == omp_ifr_hip)
+       {
+         assert (ret_code == omp_irc_success);
+         assert (ctx != NULL);
+       }
+      else
+       {
+         assert (ret_code == omp_irc_no_value);
+         assert (ctx == NULL);
+       }
+
+      /* Stream/targetsync: cudaStream_t / CUstream / hipStream_t -- a 
pointer.  */
+      ret_code = omp_irc_out_of_range;
+      void *stream = omp_get_interop_ptr (obj, omp_ipr_targetsync, &ret_code);
+
+      if (variant % 2 == 0)  /* no targetsync */
+       {
+         assert (ret_code == omp_irc_no_value);
+         assert (stream == NULL);
+       }
+      else
+       {
+         assert (ret_code == omp_irc_success);
+         assert (stream != NULL);
+       }
+
+      check_type (obj);
+      if (fr == omp_ifr_hip)
+       {
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_platform), 
"N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_device), 
"hipDevice_t") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, 
omp_ipr_device_context), "hipCtx_t") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_targetsync), 
"hipStream_t") == 0);
+       }
+      else
+       {
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_platform), 
"N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_device), 
"hsa_agent_t *") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, 
omp_ipr_device_context), "N/A") == 0);
+         assert (strcmp (omp_get_interop_type_desc (obj, omp_ipr_targetsync), 
"hsa_queue_t *") == 0);
+       }
+
+      if (dev == DEFAULT_DEVICE) {
+       #pragma omp interop use(obj)
+       #pragma omp interop destroy(obj)
+      } else {
+       #pragma omp interop use(obj) device(dev)
+       #pragma omp interop destroy(obj) device(dev)
+      }
+    }
+}
+
+
+int
+main ()
+{
+  do_check (DEFAULT_DEVICE);
+  int ndev = omp_get_num_devices ();
+  for (int dev = -1; dev < ndev; dev++)
+    do_check (dev);
+  for (int dev = -1; dev < ndev; dev++)
+    {
+      omp_set_default_device (dev);
+      do_check (DEFAULT_DEVICE);
+    }
+}

Reply via email to