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); + } +}