summaryrefslogtreecommitdiff
path: root/libgomp
diff options
context:
space:
mode:
authorPaul-Antoine Arras <parras@baylibre.com>2025-03-13 17:16:41 +0100
committerPaul-Antoine Arras <parras@baylibre.com>2025-03-21 19:24:16 +0100
commit99e2906ae255fc7b8edb008d7cd47b28b078a809 (patch)
treeaa20eea780d8962d8be0da6122795911e7376390 /libgomp
parentf45d14b495d603b8d5fbdffe6fced03cb3e2fc10 (diff)
OpenMP: 'interop' construct - add ME support + target-independent libgomp
This patch partially enables use of the OpenMP interop construct by adding middle end support, mostly in the omplower pass, and in the target-independent part of the libgomp runtime. It follows up on previous patches for C, C++ and Fortran front ends support. The full interop feature requires another patch to enable foreign runtime support in libgomp plugins. gcc/ChangeLog: * builtin-types.def (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR): New. * gimple-low.cc (lower_stmt): Handle GIMPLE_OMP_INTEROP. * gimple-pretty-print.cc (dump_gimple_omp_interop): New function. (pp_gimple_stmt_1): Handle GIMPLE_OMP_INTEROP. * gimple.cc (gimple_build_omp_interop): New function. (gimple_copy): Handle GIMPLE_OMP_INTEROP. * gimple.def (GIMPLE_OMP_INTEROP): Define. * gimple.h (gimple_build_omp_interop): Declare. (gimple_omp_interop_clauses): New function. (gimple_omp_interop_clauses_ptr): Likewise. (gimple_omp_interop_set_clauses): Likewise. (gimple_return_set_retval): Handle GIMPLE_OMP_INTEROP. * gimplify.cc (gimplify_scan_omp_clauses): Handle OMP_CLAUSE_INIT, OMP_CLAUSE_USE and OMP_CLAUSE_DESTROY. (gimplify_omp_interop): New function. (gimplify_expr): Replace sorry with call to gimplify_omp_interop. * omp-builtins.def (BUILT_IN_GOMP_INTEROP): Define. * omp-low.cc (scan_sharing_clauses): Handle OMP_CLAUSE_INIT, OMP_CLAUSE_USE and OMP_CLAUSE_DESTROY. (scan_omp_1_stmt): Handle GIMPLE_OMP_INTEROP. (lower_omp_interop_action_clauses): New function. (lower_omp_interop): Likewise. (lower_omp_1): Handle GIMPLE_OMP_INTEROP. gcc/c/ChangeLog: * c-parser.cc (c_parser_omp_clause_destroy): Make addressable. (c_parser_omp_clause_init): Make addressable. gcc/cp/ChangeLog: * parser.cc (cp_parser_omp_clause_init): Make addressable. gcc/fortran/ChangeLog: * trans-openmp.cc (gfc_trans_omp_clauses): Make OMP_CLAUSE_DESTROY and OMP_CLAUSE_INIT addressable. * types.def (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR): New. include/ChangeLog: * gomp-constants.h (GOMP_DEVICE_DEFAULT_OMP_61, GOMP_INTEROP_TARGET, GOMP_INTEROP_TARGETSYNC, GOMP_INTEROP_FLAG_NOWAIT): Define. libgomp/ChangeLog: * icv-device.c (omp_set_default_device): Check GOMP_DEVICE_DEFAULT_OMP_61. * libgomp-plugin.h (struct interop_obj_t): New. (enum gomp_interop_flag): New. (GOMP_OFFLOAD_interop): Declare. (GOMP_OFFLOAD_get_interop_int): Declare. (GOMP_OFFLOAD_get_interop_ptr): Declare. (GOMP_OFFLOAD_get_interop_str): Declare. (GOMP_OFFLOAD_get_interop_type_desc): Declare. * libgomp.h (_LIBGOMP_OMP_LOCK_DEFINED): Define. (struct gomp_device_descr): Add interop_func, get_interop_int_func, get_interop_ptr_func, get_interop_str_func, get_interop_type_desc_func. * libgomp.map: Add GOMP_interop. * libgomp_g.h (GOMP_interop): Declare. * target.c (resolve_device): Handle GOMP_DEVICE_DEFAULT_OMP_61. (omp_get_interop_int): Replace stub with actual implementation. (omp_get_interop_ptr): Likewise. (omp_get_interop_str): Likewise. (omp_get_interop_type_desc): Likewise. (struct interop_data_t): Define. (gomp_interop_internal): New function. (GOMP_interop): Likewise. (gomp_load_plugin_for_device): Load symbols for get_interop_int, get_interop_ptr, get_interop_str and get_interop_type_desc. * testsuite/libgomp.c-c++-common/interop-1.c: New test. gcc/testsuite/ChangeLog: * c-c++-common/gomp/interop-1.c: Remove dg-prune-output "sorry". * c-c++-common/gomp/interop-2.c: Likewise. * c-c++-common/gomp/interop-3.c: Likewise. * c-c++-common/gomp/interop-4.c: Remove dg-message "not supported". * g++.dg/gomp/interop-5.C: Likewise. * gfortran.dg/gomp/interop-4.f90: Likewise. * c-c++-common/gomp/interop-5.c: New test. * gfortran.dg/gomp/interop-5.f90: New test. Co-authored-by: Tobias Burnus <tburnus@baylibre.com>
Diffstat (limited to 'libgomp')
-rw-r--r--libgomp/icv-device.c7
-rw-r--r--libgomp/libgomp-plugin.h44
-rw-r--r--libgomp/libgomp.h17
-rw-r--r--libgomp/libgomp.map1
-rw-r--r--libgomp/libgomp_g.h4
-rw-r--r--libgomp/target.c213
-rw-r--r--libgomp/testsuite/libgomp.c-c++-common/interop-1.c43
7 files changed, 296 insertions, 33 deletions
diff --git a/libgomp/icv-device.c b/libgomp/icv-device.c
index ba06f50a7c0..40bf7cd658a 100644
--- a/libgomp/icv-device.c
+++ b/libgomp/icv-device.c
@@ -32,8 +32,11 @@
void
omp_set_default_device (int device_num)
{
- struct gomp_task_icv *icv = gomp_icv (true);
- icv->default_device_var = device_num;
+ if (device_num != GOMP_DEVICE_DEFAULT_OMP_61)
+ {
+ struct gomp_task_icv *icv = gomp_icv (true);
+ icv->default_device_var = device_num;
+ }
}
ialias (omp_set_default_device)
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 62bf43da937..924fc1f44b1 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -33,6 +33,14 @@
#include <stddef.h>
#include <stdint.h>
+#ifdef _LIBGOMP_PLUGIN_INCLUDE
+ /* Include 'omp.h' for the interop definitions. */
+ #define _LIBGOMP_OMP_LOCK_DEFINED 1
+ typedef struct omp_lock_t omp_lock_t;
+ typedef struct omp_nest_lock_t omp_nest_lock_t;
+ #include "omp.h.in"
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -101,6 +109,25 @@ struct addr_pair
uintptr_t end;
};
+
+#ifdef _LIBGOMP_OMP_LOCK_DEFINED
+/* Only define when omp.h.in was included, as in plugin/ and in libgomp.h. */
+struct interop_obj_t
+{
+ void *stream;
+ void *device_data;
+ omp_interop_fr_t fr;
+ int device_num;
+};
+
+enum gomp_interop_flag
+{
+ gomp_interop_flag_init,
+ gomp_interop_flag_use,
+ gomp_interop_flag_destroy
+};
+#endif
+
/* This following symbol is used to name the target side variable struct that
holds the designated ICVs of the target device. The symbol needs to be
available to libgomp code and the offload plugin (which in the latter case
@@ -181,6 +208,23 @@ extern int GOMP_OFFLOAD_openacc_cuda_set_stream (struct goacc_asyncqueue *,
extern union goacc_property_value
GOMP_OFFLOAD_openacc_get_property (int, enum goacc_property);
+#ifdef _LIBGOMP_OMP_LOCK_DEFINED
+/* Only define when omp.h.in was included, as in plugin/ and in libgomp.h. */
+extern void GOMP_OFFLOAD_interop (struct interop_obj_t *, int,
+ enum gomp_interop_flag, bool, const char *);
+extern intptr_t GOMP_OFFLOAD_get_interop_int (struct interop_obj_t *,
+ omp_interop_property_t,
+ omp_interop_rc_t *);
+extern void *GOMP_OFFLOAD_get_interop_ptr (struct interop_obj_t *,
+ omp_interop_property_t,
+ omp_interop_rc_t *);
+extern const char *GOMP_OFFLOAD_get_interop_str (struct interop_obj_t *obj,
+ omp_interop_property_t,
+ omp_interop_rc_t *);
+extern const char *GOMP_OFFLOAD_get_interop_type_desc (struct interop_obj_t *,
+ omp_interop_property_t);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 44ad980eed7..d97768f5125 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -43,7 +43,14 @@
#include "config.h"
#include <stdint.h>
+
+/* Include omp.h by parts. */
+#include "omp-lock.h"
+#define _LIBGOMP_OMP_LOCK_DEFINED 1
+#include "omp.h.in"
+
#include "libgomp-plugin.h"
+
#include "gomp-constants.h"
#ifdef HAVE_PTHREAD_H
@@ -1419,6 +1426,11 @@ struct gomp_device_descr
__typeof (GOMP_OFFLOAD_can_run) *can_run_func;
__typeof (GOMP_OFFLOAD_run) *run_func;
__typeof (GOMP_OFFLOAD_async_run) *async_run_func;
+ __typeof (GOMP_OFFLOAD_interop) *interop_func;
+ __typeof (GOMP_OFFLOAD_get_interop_int) *get_interop_int_func;
+ __typeof (GOMP_OFFLOAD_get_interop_ptr) *get_interop_ptr_func;
+ __typeof (GOMP_OFFLOAD_get_interop_str) *get_interop_str_func;
+ __typeof (GOMP_OFFLOAD_get_interop_type_desc) *get_interop_type_desc_func;
/* Splay tree containing information about mapped memory regions. */
struct splay_tree_s mem_map;
@@ -1501,11 +1513,6 @@ gomp_work_share_init_done (void)
/* Now that we're back to default visibility, include the globals. */
#include "libgomp_g.h"
-/* Include omp.h by parts. */
-#include "omp-lock.h"
-#define _LIBGOMP_OMP_LOCK_DEFINED 1
-#include "omp.h.in"
-
#if !defined (HAVE_ATTRIBUTE_VISIBILITY) \
|| !defined (HAVE_ATTRIBUTE_ALIAS) \
|| !defined (HAVE_AS_SYMVER_DIRECTIVE) \
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 4530b3adc94..eae2f53bab1 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -430,6 +430,7 @@ GOMP_5.1.2 {
GOMP_5.1.3 {
global:
+ GOMP_interop;
omp_get_num_interop_properties;
omp_get_interop_int;
omp_get_interop_ptr;
diff --git a/libgomp/libgomp_g.h b/libgomp/libgomp_g.h
index eed800b5cc9..8993ec610fb 100644
--- a/libgomp/libgomp_g.h
+++ b/libgomp/libgomp_g.h
@@ -358,6 +358,10 @@ extern void GOMP_target_enter_exit_data (int, size_t, void **, size_t *,
extern void GOMP_teams (unsigned int, unsigned int);
extern bool GOMP_teams4 (unsigned int, unsigned int, unsigned int, bool);
extern void *GOMP_target_map_indirect_ptr (void *);
+struct interop_obj_t;
+extern void GOMP_interop (int, int, struct interop_obj_t ***, const int *,
+ const char **, int, struct interop_obj_t **, int,
+ struct interop_obj_t ***, unsigned, void **);
/* teams.c */
diff --git a/libgomp/target.c b/libgomp/target.c
index dbc4535b96f..36ed797b0a9 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -146,7 +146,8 @@ resolve_device (int device_id, bool remapped)
called, which must be done before using default_device_var. */
int num_devices = gomp_get_num_devices ();
- if (remapped && device_id == GOMP_DEVICE_ICV)
+ if ((remapped && device_id == GOMP_DEVICE_ICV)
+ || device_id == GOMP_DEVICE_DEFAULT_OMP_61)
{
struct gomp_task_icv *icv = gomp_icv (false);
device_id = icv->default_device_var;
@@ -5136,45 +5137,78 @@ omp_get_num_interop_properties (const omp_interop_t interop
}
omp_intptr_t
-omp_get_interop_int (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_int (const omp_interop_t interop,
omp_interop_property_t property_id,
omp_interop_rc_t *ret_code)
{
- if (ret_code == NULL)
- return 0;
+ struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+ struct gomp_device_descr *devicep;
+
if (property_id < omp_ipr_first || property_id >= 0)
- *ret_code = omp_irc_out_of_range;
- else
- *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
- return 0;
+ {
+ if (ret_code)
+ *ret_code = omp_irc_out_of_range;
+ return 0;
+ }
+ if (obj == NULL
+ || (devicep = resolve_device (obj->device_num, false)) == NULL
+ || devicep->get_interop_int_func == NULL)
+ {
+ if (ret_code)
+ *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
+ return 0;
+ }
+ return devicep->get_interop_int_func (obj, property_id, ret_code);
}
void *
-omp_get_interop_ptr (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_ptr (const omp_interop_t interop,
omp_interop_property_t property_id,
omp_interop_rc_t *ret_code)
{
- if (ret_code == NULL)
- return NULL;
+ struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+ struct gomp_device_descr *devicep;
+
if (property_id < omp_ipr_first || property_id >= 0)
- *ret_code = omp_irc_out_of_range;
- else
- *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
- return NULL;
+ {
+ if (ret_code)
+ *ret_code = omp_irc_out_of_range;
+ return 0;
+ }
+ if (obj == NULL
+ || (devicep = resolve_device (obj->device_num, false)) == NULL
+ || devicep->get_interop_int_func == NULL)
+ {
+ if (ret_code)
+ *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
+ return 0;
+ }
+ return devicep->get_interop_ptr_func (obj, property_id, ret_code);
}
const char *
-omp_get_interop_str (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_str (const omp_interop_t interop,
omp_interop_property_t property_id,
omp_interop_rc_t *ret_code)
{
- if (ret_code == NULL)
- return NULL;
+ struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+ struct gomp_device_descr *devicep;
+
if (property_id < omp_ipr_first || property_id >= 0)
- *ret_code = omp_irc_out_of_range;
- else
- *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
- return NULL;
+ {
+ if (ret_code)
+ *ret_code = omp_irc_out_of_range;
+ return 0;
+ }
+ if (obj == NULL
+ || (devicep = resolve_device (obj->device_num, false)) == NULL
+ || devicep->get_interop_int_func == NULL)
+ {
+ if (ret_code)
+ *ret_code = omp_irc_empty; /* Assume omp_interop_none. */
+ return 0;
+ }
+ return devicep->get_interop_str_func (obj, property_id, ret_code);
}
const char *
@@ -5194,18 +5228,24 @@ omp_get_interop_type_desc (const omp_interop_t interop,
omp_interop_property_t property_id)
{
static const char *desc[omp_ipr_fr_id - omp_ipr_device_num + 1]
- = {"omp_interop_t", /* fr_id */
- "const char*", /* fr_name */
+ = {"omp_interop_t", /* fr_id */
+ "const char *", /* fr_name */
"int", /* vendor */
"const char *", /* vendor_name */
"int"}; /* device_num */
+
+ struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+ struct gomp_device_descr *devicep;
+
if (property_id > omp_ipr_fr_id || property_id < omp_ipr_first)
return NULL;
- if (interop == omp_interop_none)
+ if (obj == NULL
+ || (devicep = resolve_device (obj->device_num, false)) == NULL
+ || devicep->get_interop_int_func == NULL)
return NULL;
if (property_id >= omp_ipr_device_num)
return desc[omp_ipr_fr_id - property_id];
- return NULL; /* FIXME: Call plugin. */
+ return devicep->get_interop_type_desc_func (obj, property_id);
}
const char *
@@ -5236,6 +5276,119 @@ ialias (omp_get_interop_name)
ialias (omp_get_interop_type_desc)
ialias (omp_get_interop_rc_desc)
+struct interop_data_t
+{
+ int device_num, n_init, n_use, n_destroy;
+ struct interop_obj_t ***init;
+ struct interop_obj_t **use;
+ struct interop_obj_t ***destroy;
+ const int *target_targetsync;
+ const char **prefer_type;
+};
+
+static void
+gomp_interop_internal (void *data)
+{
+ struct interop_data_t *args = (struct interop_data_t *) data;
+ struct gomp_device_descr *devicep;
+
+ /* Destroy objects to free resources. */
+ for (int i = 0; i < args->n_destroy; i++)
+ {
+ struct interop_obj_t **obj = args->destroy[i];
+ if (*obj == NULL /* omp_interop_none */)
+ continue;
+ devicep = resolve_device ((*obj)->device_num, false);
+ if (devicep != NULL && devicep->interop_func)
+ devicep->interop_func (*obj, devicep->target_id,
+ gomp_interop_flag_destroy, false, NULL);
+ free (*obj);
+ *obj = NULL;
+ }
+
+ /* Init streams next to give 'use' more time for completion. */
+ if (args->n_init)
+ {
+ devicep = resolve_device (args->device_num, false);
+ for (int i = 0; i < args->n_init; i++)
+ {
+ struct interop_obj_t **obj = args->init[i];
+ bool targetsync
+ = (args->target_targetsync[i] & GOMP_INTEROP_TARGETSYNC);
+ const char *prefer_type
+ = (args->prefer_type ? args->prefer_type[i] : NULL);
+ if (devicep == NULL || !devicep->interop_func)
+ {
+ *obj = NULL;
+ continue;
+ }
+ *obj =
+ (struct interop_obj_t *) calloc (1, sizeof (struct interop_obj_t));
+ devicep->interop_func (*obj, devicep->target_id,
+ gomp_interop_flag_init, targetsync,
+ prefer_type);
+ }
+ }
+
+ for (int i = 0; i < args->n_use; i++)
+ {
+ struct interop_obj_t *obj = args->use[i];
+ if (obj == NULL)
+ continue;
+ devicep = resolve_device (obj->device_num, false);
+ if (devicep != NULL && devicep->interop_func)
+ devicep->interop_func (obj, devicep->target_id,
+ gomp_interop_flag_use, false, NULL);
+ }
+}
+
+/* Process the OpenMP interop directive. 'init' and 'destroy' take an array
+ of 'omp_interop_t *', 'use' an array of 'omp_interop_t', where
+ 'omp_interop_t' is internally 'struct interop_obj_t *';
+ 'flags' is used for the 'nowait' clause. */
+
+void
+GOMP_interop (int device_num, int n_init, struct interop_obj_t ***init,
+ const int *target_targetsync, const char **prefer_type, int n_use,
+ struct interop_obj_t **use, int n_destroy,
+ struct interop_obj_t ***destroy, unsigned int flags,
+ void **depend)
+{
+ struct interop_data_t args;
+ args.device_num = device_num;
+ args.n_init = n_init;
+ args.n_use = n_use;
+ args.n_destroy = n_destroy;
+ args.init = init;
+ args.target_targetsync = target_targetsync;
+ args.prefer_type = prefer_type;
+ args.use = use;
+ args.destroy = destroy;
+
+ /* No need to create a task for 'init' as that should be fast. */
+ bool use_task = false;
+ if (flags & GOMP_INTEROP_FLAG_NOWAIT)
+ {
+ for (int i = 0; i < n_use && !use_task; i++)
+ if (args.use[i])
+ use_task |= args.use[i]->stream != NULL;
+ for (int i = 0; i < n_destroy && !use_task; i++)
+ if (*args.destroy[i])
+ use_task |= (*args.destroy[i])->stream != NULL;
+ }
+
+ if (use_task)
+ GOMP_task (gomp_interop_internal, &args, NULL, sizeof (args),
+ __alignof__ (args), true, depend ? GOMP_TASK_FLAG_DEPEND : 0,
+ depend, 0, NULL);
+ else
+ {
+ gomp_interop_internal (&args);
+ if (depend)
+ GOMP_taskwait_depend (depend);
+ }
+}
+
static const char *
gomp_get_uid_for_device (struct gomp_device_descr *devicep, int device_num)
{
@@ -5344,6 +5497,14 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
DLSYM (host2dev);
DLSYM_OPT (memcpy2d, memcpy2d);
DLSYM_OPT (memcpy3d, memcpy3d);
+ if (DLSYM_OPT (interop, interop))
+ {
+ DLSYM (get_interop_int);
+ DLSYM (get_interop_ptr);
+ DLSYM (get_interop_str);
+ DLSYM (get_interop_type_desc);
+ }
+
device->capabilities = device->get_caps_func ();
if (device->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400)
{
diff --git a/libgomp/testsuite/libgomp.c-c++-common/interop-1.c b/libgomp/testsuite/libgomp.c-c++-common/interop-1.c
new file mode 100644
index 00000000000..149f3877b30
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/interop-1.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+
+#include <omp.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+ int dev = omp_get_num_devices ();
+ int x[6];
+ omp_interop_t obj1 = omp_interop_none;
+#pragma omp interop init(targetsync : obj1) depend(in : x) device(dev)
+ if (obj1 != omp_interop_none)
+ abort ();
+
+#pragma omp interop use(obj1)
+#pragma omp interop destroy(obj1) depend(out : x)
+ if (obj1 != omp_interop_none)
+ abort ();
+
+ omp_set_default_device (dev);
+ omp_interop_t obj2;
+
+#pragma omp interop init( \
+ target, targetsync, \
+ prefer_type({fr("hip"), attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, \
+ {attr("ompx_gnu_nicest"), attr("ompx_something")}) : obj1, \
+ obj2) nowait
+ if (obj1 != omp_interop_none || obj2 != omp_interop_none)
+ abort ();
+#pragma omp interop use(obj1, obj2) nowait
+
+ omp_interop_t obj3 = __omp_interop_t_max__;
+
+#pragma omp interop init(target : obj3) use(obj2) destroy(obj1) nowait
+ if (obj1 != omp_interop_none || obj3 != omp_interop_none)
+ abort ();
+#pragma omp interop destroy(obj3, obj2) nowait
+ if (obj2 != omp_interop_none || obj3 != omp_interop_none)
+ abort ();
+
+ return 0;
+}