"alloc",
"calloc",
"free",
+ "get_device_from_uid",
"get_interop_int",
"get_interop_ptr",
"get_mapped_ptr",
as DECL_NAME only omp_* and omp_*_8 appear. */
"display_env",
"get_ancestor_thread_num",
- "init_allocator",
+ "omp_get_uid_from_device",
"get_partition_place_nums",
"get_place_num_procs",
"get_place_proc_ids",
"get_schedule",
"get_team_size",
+ "init_allocator",
"set_default_device",
"set_dynamic",
"set_max_active_levels",
size_t WidthInBytes, Height, Depth;
} CUDA_MEMCPY3D_PEER;
+typedef struct {
+ char bytes[16];
+} CUuuid;
+
#define cuCtxCreate cuCtxCreate_v2
CUresult cuCtxCreate (CUcontext *, unsigned, CUdevice);
#define cuCtxDestroy cuCtxDestroy_v2
CUresult cuCtxSynchronize (void);
CUresult cuCtxSetLimit (CUlimit, size_t);
CUresult cuDeviceGet (CUdevice *, int);
+/* _v2 was added in CUDA 11.4 and 'will supplant' the old one in 12.0. */
+CUresult cuDeviceGetUuid (CUuuid*, CUdevice);
+CUresult cuDeviceGetUuid_v2 (CUuuid*, CUdevice);
#define cuDeviceTotalMem cuDeviceTotalMem_v2
CUresult cuDeviceTotalMem (size_t *, CUdevice);
CUresult cuDeviceGetAttribute (int *, CUdevice_attribute, CUdevice);
return rc_strings[omp_irc_no_value - ret_code];
}
+const char *
+omp_get_uid_from_device (int device_num __attribute__ ((unused)))
+{
+ return NULL;
+}
+
+int
+omp_get_device_from_uid (const char *uid __attribute__ ((unused)))
+{
+ return omp_invalid_device;
+}
+
ialias (omp_get_num_interop_properties)
ialias (omp_get_interop_int)
ialias (omp_get_interop_ptr)
ialias (omp_get_interop_name)
ialias (omp_get_interop_type_desc)
ialias (omp_get_interop_rc_desc)
+ialias (omp_get_uid_from_device)
+ialias (omp_get_device_from_uid)
return rc_strings[omp_irc_no_value - ret_code];
}
+const char *
+omp_get_uid_from_device (int device_num __attribute__ ((unused)))
+{
+ return NULL;
+}
+
+int
+omp_get_device_from_uid (const char *uid __attribute__ ((unused)))
+{
+ return omp_invalid_device;
+}
+
ialias (omp_get_num_interop_properties)
ialias (omp_get_interop_int)
ialias (omp_get_interop_ptr)
ialias (omp_get_interop_name)
ialias (omp_get_interop_type_desc)
ialias (omp_get_interop_rc_desc)
+ialias (omp_get_uid_from_device)
+ialias (omp_get_device_from_uid)
*res_len = *res ? strlen (*res) : 0;
}
+void
+omp_get_uid_from_device_ (const char **res, size_t *res_len,
+ int32_t device_num)
+{
+ *res = omp_get_uid_from_device (device_num);
+ *res_len = *res ? strlen (*res) : 0;
+}
+
+void
+omp_get_uid_from_device_8_ (const char **res, size_t *res_len,
+ int64_t device_num)
+{
+ omp_get_uid_from_device_ (res, res_len, (int32_t) device_num);
+}
+
#ifndef LIBGOMP_OFFLOADED_ONLY
void
/* Prototypes for functions implemented by libgomp plugins. */
extern const char *GOMP_OFFLOAD_get_name (void);
+extern const char *GOMP_OFFLOAD_get_uid (int);
extern unsigned int GOMP_OFFLOAD_get_caps (void);
extern int GOMP_OFFLOAD_get_type (void);
extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
/* The name of the device. */
const char *name;
+ const char *uid;
/* Capabilities of device (supports OpenACC, OpenMP). */
unsigned int capabilities;
/* Function handlers. */
__typeof (GOMP_OFFLOAD_get_name) *get_name_func;
+ __typeof (GOMP_OFFLOAD_get_uid) *get_uid_func;
__typeof (GOMP_OFFLOAD_get_caps) *get_caps_func;
__typeof (GOMP_OFFLOAD_get_type) *get_type_func;
__typeof (GOMP_OFFLOAD_get_num_devices) *get_num_devices_func;
omp_get_interop_rc_desc_;
} GOMP_5.1.2;
+GOMP_6.0 {
+ global:
+ omp_get_device_from_uid;
+ omp_get_uid_from_device;
+ omp_get_uid_from_device_;
+ omp_get_uid_from_device_8_;
+} GOMP_5.1.3;
+
OACC_2.0 {
global:
acc_get_num_devices;
of the @code{interop} construct @tab N @tab
@item Invoke virtual member functions of C++ objects created on the host device
on other devices @tab N @tab
-@item @code{iterator} and @code{mapper} as map-type modifier in @code{declare mappter}
+@item @code{iterator} and @code{mapper} as map-type modifier in @code{declare mapper}
@tab N @tab
@end multitable
@item @code{omp_is_free_agent} and @code{omp_ancestor_is_free_agent} routines
@tab N @tab
@item @code{omp_get_device_from_uid} and @code{omp_get_uid_from_device} routines
- @tab N @tab
+ @tab Y @tab
@item @code{omp_get_device_num_teams}, @code{omp_set_device_num_teams},
@code{omp_get_device_teams_thread_limit}, and
@code{omp_set_device_teams_thread_limit} routines @tab N @tab
@menu
* omp_get_num_procs:: Number of processors online
@c * omp_get_max_progress_width:: <fixme>/TR11
-@c * omp_get_device_from_uid:: <fixme>/TR13
-@c * omp_get_uid_from_device:: <fixme>/TR13
* omp_set_default_device:: Set the default device for target regions
* omp_get_default_device:: Get the default device for target regions
* omp_get_num_devices:: Number of target devices
* omp_get_device_num:: Get device that current thread is running on
+* omp_get_device_from_uid:: Obtain the device number to a unique id
+* omp_get_uid_from_device:: Obtain the unique id of a device
* omp_is_initial_device:: Whether executing on the host device
* omp_get_initial_device:: Device number of host device
@c * omp_get_device_num_teams:: <fixme>/TR13
+@node omp_get_device_from_uid
+@subsection @code{omp_get_device_from_uid} -- Obtain the device number to a unique id
+@table @asis
+@item @emph{Description}:
+This function returns the device number associated with the passed
+unique-identifier (UID) string. If no device with this UID is available, the value
+@code{omp_invalid_device} is returned. The effect of running this routine in a
+@code{target} region is unspecified.
+
+GCC treats the UID string case sensitive; for the initial device, GCC currently
+only accepts the value @code{OMP_INITIAL_DEVICE} and returns for it the value
+of @code{omp_initial_device}.
+
+@item @emph{C/C++}:
+@multitable @columnfractions .20 .80
+@item @emph{Prototype}: @tab @code{int omp_get_device_from_uid(const char *uid);}
+@end multitable
+
+@item @emph{Fortran}:
+@multitable @columnfractions .20 .80
+@item @emph{Interface}: @tab @code{integer function omp_get_device_from_uid(uid)}
+@item @tab @code{character(len=*), intent(in) :: uid}
+@end multitable
+
+@item @emph{See also}:
+@ref{omp_get_uid_from_device}, @ref{Offload-Target Specifics}
+
+@item @emph{Reference}:
+@uref{https://www.openmp.org, OpenMP specification v6.0}, Section 24.7
+@end table
+
+
+
+@node omp_get_uid_from_device
+@subsection @code{omp_get_uid_from_device} -- Obtain the unique id of a device
+@table @asis
+@item @emph{Description}:
+This function returns a pointer to a string that represents a unique identifier
+(UID) for the device specified by @var{device_num}. It returns a @code{NULL} (C/C++)
+or a disassociated pointer (Fortran) for @code{omp_invalid_device}. The effect of
+running this routine in a @code{target} region is unspecified.
+
+GCC currently returns for initial device the value @code{OMP_INITIAL_DEVICE}.
+
+@item @emph{C/C++}:
+@multitable @columnfractions .20 .80
+@item @emph{Prototype}: @tab @code{const char *omp_get_uid_from_device(int device_num);}
+@end multitable
+
+@item @emph{Fortran}:
+@multitable @columnfractions .20 .80
+@item @emph{Interface}: @tab @code{character(:) function omp_get_uid_from_device(device_num)}
+@item @emph{Interface}: @tab @code{pointer :: omp_get_uid_from_device}
+@item @tab @code{integer, intent(in) :: device_num}
+@end multitable
+
+@item @emph{See also}:
+@ref{omp_get_uid_from_device}, @ref{Offload-Target Specifics}
+
+@item @emph{Reference}:
+@uref{https://www.openmp.org, OpenMP specification v6.0}, Section 24.8
+@end table
+
+
+
@node omp_is_initial_device
@subsection @code{omp_is_initial_device} -- Whether executing on the host device
@table @asis
@item @emph{Fortran}:
@multitable @columnfractions .20 .80
-@item @emph{Interface}: @tab @code{subroutine omp_display_env(vebose)}
+@item @emph{Interface}: @tab @code{subroutine omp_display_env(verbose)}
@item @tab @code{logical, intent(in) :: verbose}
@end multitable
@item @emph{Example}:
Note that the GCC-specific ICVs, such as the shown @code{GOMP_SPINCOUNT},
-are only printed when @var{varbose} set to @code{true}.
+are only printed when @var{verbose} set to @code{true}.
@smallexample
OPENMP DISPLAY ENVIRONMENT BEGIN
@code{omp_thread_mem_alloc}, all use low-latency memory as first
preference, and fall back to main graphics memory when the low-latency
pool is exhausted.
+@item The unique identifier (UID), used with OpenMP's API UID routines, is the
+ value returned by the HSA runtime library for @code{HSA_AMD_AGENT_INFO_UUID}.
+ For GPUs, it is currently @samp{GPU-} followed by 16 lower-case hex digits,
+ yielding a string like @code{GPU-f914a2142fc3413a}. The output matches
+ the one used by @code{rocminfo}.
@end itemize
@code{omp_thread_mem_alloc}, all use low-latency memory as first
preference, and fall back to main graphics memory when the low-latency
pool is exhausted.
+@item The unique identifier (UID), used with OpenMP's API UID routines, consists
+ of the @samp{GPU-} prefix followed by the 16-bytes UUID as returned by
+ the CUDA runtime library. This UUID is output in grouped lower-case
+ hex digits; the grouping of those 32 digits is: 8 digits, hyphen,
+ 4 digits, hyphen, 4 digits, hyphen, 16 digits. This leads to a string
+ like @code{GPU-a8081c9e-f03e-18eb-1827-bf5ba95afa5d}. The output
+ matches the format used by @code{nvidia-smi}.
@end itemize
extern const char *omp_get_interop_rc_desc (const omp_interop_t,
omp_interop_rc_t) __GOMP_NOTHROW;
+extern int omp_get_device_from_uid (const char *) __GOMP_NOTHROW;
+extern const char *omp_get_uid_from_device (int) __GOMP_NOTHROW;
+
#ifdef __cplusplus
}
#endif
end function omp_get_interop_rc_desc
end interface
+ interface
+ ! Note: In gfortran, strings are \0 termined
+ integer(c_int) function omp_get_device_from_uid(uid) bind(C)
+ use iso_c_binding
+ character(c_char), intent(in) :: uid(*)
+ end function omp_get_device_from_uid
+ end interface
+
+ interface omp_get_uid_from_device
+ ! Deviation from OpenMP 6.0: VALUE added.
+ character(:) function omp_get_uid_from_device (device_num)
+ use iso_c_binding
+ pointer :: omp_get_uid_from_device
+ integer(c_int32_t), intent(in), value :: device_num
+ end function omp_get_uid_from_device
+
+ character(:) function omp_get_uid_from_device_8 (device_num)
+ use iso_c_binding
+ pointer :: omp_get_uid_from_device_8
+ integer(c_int64_t), intent(in), value :: device_num
+ end function omp_get_uid_from_device_8
+ end interface omp_get_uid_from_device
+
#if _OPENMP >= 201811
!GCC$ ATTRIBUTES DEPRECATED :: omp_get_nested, omp_set_nested
!GCC$ ATTRIBUTES DEPRECATED :: omp_lock_hint_kind, omp_lock_hint_none
integer (omp_interop_rc_kind), value :: ret_code
end function omp_get_interop_rc_desc
end interface
+
+ interface
+! Note: In gfortran, strings are \0 termined
+ integer(c_int) function omp_get_device_from_uid(uid) bind(C)
+ use iso_c_binding
+ character(c_char), intent(in) :: uid(*)
+ end function omp_get_device_from_uid
+ end interface
+
+ interface omp_get_uid_from_device
+! Deviation from OpenMP 6.0: VALUE added.
+ character(:) function omp_get_uid_from_device (device_num)
+ use iso_c_binding
+ pointer :: omp_get_uid_from_device
+ integer(c_int32_t), intent(in), value :: device_num
+ end function omp_get_uid_from_device
+
+ character(:) function omp_get_uid_from_device_8 (device_num)
+ use iso_c_binding
+ pointer :: omp_get_uid_from_device_8
+ integer(c_int64_t), intent(in), value :: device_num
+ end function omp_get_uid_from_device_8
+ end interface omp_get_uid_from_device
CUDA_ONE_CALL (cuDeviceGetAttribute)
CUDA_ONE_CALL (cuDeviceGetCount)
CUDA_ONE_CALL (cuDeviceGetName)
+CUDA_ONE_CALL_MAYBE_NULL (cuDeviceGetUuid)
+CUDA_ONE_CALL_MAYBE_NULL (cuDeviceGetUuid_v2)
CUDA_ONE_CALL (cuDeviceTotalMem)
CUDA_ONE_CALL (cuDriverGetVersion)
CUDA_ONE_CALL (cuEventCreate)
return "gcn";
}
+const char *
+GOMP_OFFLOAD_get_uid (int ord)
+{
+ char *str;
+ hsa_status_t status;
+ struct agent_info *agent = get_agent_info (ord);
+
+ /* HSA documentation states: maximally 21 characters including NUL. */
+ str = GOMP_PLUGIN_malloc (21 * sizeof (char));
+ status = hsa_fns.hsa_agent_get_info_fn (agent->id, HSA_AMD_AGENT_INFO_UUID,
+ str);
+ if (status != HSA_STATUS_SUCCESS)
+ hsa_fatal ("Could not obtain device UUID", status);
+ return str;
+}
+
/* Return the specific capabilities the HSA accelerator have. */
unsigned int
return "nvptx";
}
+const char *
+GOMP_OFFLOAD_get_uid (int ord)
+{
+ CUresult r;
+ CUuuid s;
+ struct ptx_device *dev = ptx_devices[ord];
+
+ if (CUDA_CALL_EXISTS (cuDeviceGetUuid_v2))
+ r = CUDA_CALL_NOCHECK (cuDeviceGetUuid_v2, &s, dev->dev);
+ else if (CUDA_CALL_EXISTS (cuDeviceGetUuid))
+ r = CUDA_CALL_NOCHECK (cuDeviceGetUuid, &s, dev->dev);
+ else
+ r = CUDA_ERROR_NOT_FOUND;
+ if (r != CUDA_SUCCESS)
+ GOMP_PLUGIN_fatal ("cuDeviceGetUuid error: %s", cuda_error (r));
+
+ size_t len = strlen ("GPU-12345678-9abc-defg-hijk-lmniopqrstuv");
+ char *str = (char *) GOMP_PLUGIN_malloc (len + 1);
+ sprintf (str,
+ "GPU-%02x" "%02x" "%02x" "%02x"
+ "-%02x" "%02x"
+ "-%02x" "%02x"
+ "-%02x" "%02x" "%02x" "%02x" "%02x" "%02x" "%02x" "%02x",
+ (unsigned char) s.bytes[0], (unsigned char) s.bytes[1],
+ (unsigned char) s.bytes[2], (unsigned char) s.bytes[3],
+ (unsigned char) s.bytes[4], (unsigned char) s.bytes[5],
+ (unsigned char) s.bytes[6], (unsigned char) s.bytes[7],
+ (unsigned char) s.bytes[8], (unsigned char) s.bytes[9],
+ (unsigned char) s.bytes[10], (unsigned char) s.bytes[11],
+ (unsigned char) s.bytes[12], (unsigned char) s.bytes[13],
+ (unsigned char) s.bytes[14], (unsigned char) s.bytes[15]);
+ return str;
+}
+
unsigned int
GOMP_OFFLOAD_get_caps (void)
{
#define splay_tree_c
#include "splay-tree.h"
+/* Used by omp_get_device_from_uid / omp_get_uid_from_device for the host. */
+static char *str_omp_initial_device = "OMP_INITIAL_DEVICE";
+#define STR_OMP_DEV_PREFIX "OMP_DEV_"
typedef uintptr_t *hash_entry_type;
static inline void * htab_alloc (size_t size) { return gomp_malloc (size); }
ialias (omp_get_interop_type_desc)
ialias (omp_get_interop_rc_desc)
+static const char *
+gomp_get_uid_for_device (struct gomp_device_descr *devicep, int device_num)
+{
+ if (devicep->uid)
+ return devicep->uid;
+
+ if (devicep->get_uid_func)
+ devicep->uid = devicep->get_uid_func (devicep->target_id);
+ if (!devicep->uid)
+ {
+ size_t ln = strlen (STR_OMP_DEV_PREFIX) + 10 + 1;
+ char *uid;
+ uid = gomp_malloc (ln);
+ snprintf (uid, ln, "%s%d", STR_OMP_DEV_PREFIX, device_num);
+ devicep->uid = uid;
+ }
+ return devicep->uid;
+}
+
+const char *
+omp_get_uid_from_device (int device_num)
+{
+ if (device_num < omp_initial_device || device_num > gomp_get_num_devices ())
+ return NULL;
+
+ if (device_num == omp_initial_device || device_num == gomp_get_num_devices ())
+ return str_omp_initial_device;
+
+ struct gomp_device_descr *devicep = resolve_device (device_num, false);
+ if (devicep == NULL)
+ return NULL;
+ return gomp_get_uid_for_device (devicep, device_num);
+}
+
+int
+omp_get_device_from_uid (const char *uid)
+{
+ if (uid == NULL)
+ return omp_invalid_device;
+ if (strcmp (uid, str_omp_initial_device) == 0)
+ return omp_initial_device;
+ for (int dev = 0; dev < gomp_get_num_devices (); dev++)
+ if (strcmp (uid, gomp_get_uid_for_device (&devices[dev], dev)) == 0)
+ return dev;
+ return omp_invalid_device;
+}
+
+ialias (omp_get_uid_from_device)
+ialias (omp_get_device_from_uid)
+
#ifdef PLUGIN_SUPPORT
/* This function tries to load a plugin for DEVICE. Name of plugin is passed
}
DLSYM (get_name);
+ DLSYM_OPT (get_uid, get_uid);
DLSYM (get_caps);
DLSYM (get_type);
DLSYM (get_num_devices);
}
current_device.name = current_device.get_name_func ();
+ /* Defer UID setting until needed + after gomp_init_device. */
+ current_device.uid = NULL;
/* current_device.capabilities has already been set. */
current_device.type = current_device.get_type_func ();
current_device.mem_map.root = NULL;
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <omp.h>
+
+int main()
+{
+ const char **strs = (const char **) malloc (sizeof (char*) * (omp_get_num_devices () + 1));
+ for (int i = omp_invalid_device - 1; i <= omp_get_num_devices () + 1; i++)
+ {
+ const char *str = omp_get_uid_from_device (i);
+ int dev = omp_get_device_from_uid (str);
+// __builtin_printf("%i -> %s -> %d\n", i, str, dev);
+ if (i < omp_initial_device || i > omp_get_num_devices ())
+ {
+ if (dev != omp_invalid_device || str != NULL)
+ abort ();
+ continue;
+ }
+ if (i == omp_initial_device || i == omp_get_num_devices ())
+ {
+ if ((dev != omp_initial_device && dev != omp_get_num_devices ())
+ || str == NULL
+ || strcmp (str, "OMP_INITIAL_DEVICE") != 0) /* GCC impl. choice */
+ abort ();
+ dev = omp_get_num_devices ();
+ }
+ else if (dev != i || str == NULL || str[0] == '\0')
+ abort ();
+ strs[dev] = str;
+ }
+
+ for (int i = 0; i < omp_get_num_devices (); i++)
+ for (int j = i + 1; j <= omp_get_num_devices (); j++)
+ if (strcmp (strs[i], strs[j]) == 0)
+ abort ();
+ free (strs);
+ return 0;
+}
--- /dev/null
+program main
+ use omp_lib
+ implicit none (type, external)
+ integer :: i, j, dev
+ character(:), pointer :: str
+ type t
+ character(:), pointer :: str
+ end type t
+ type(t), allocatable :: strs(:)
+
+ allocate(strs(0:omp_get_num_devices ()))
+
+ do i = omp_invalid_device - 1, omp_get_num_devices () + 1
+ str => omp_get_uid_from_device (i)
+ dev = omp_get_device_from_uid (str);
+! print *, i, str, dev
+ if (i < omp_initial_device .or. i > omp_get_num_devices ()) then
+ if (dev /= omp_invalid_device .or. associated(str)) &
+ stop 1
+ cycle
+ end if
+ if (.not. associated(str)) &
+ stop 2
+ if (i == omp_initial_device .or. i == omp_get_num_devices ()) then
+ if ((dev /= omp_initial_device .and. dev /= omp_get_num_devices ()) &
+ .or. str /= "OMP_INITIAL_DEVICE") & ! /* GCC impl. choice */
+ stop 3
+ dev = omp_get_num_devices ()
+ else if (dev /= i .or. len(str) == 0) then
+ stop 4
+ end if
+ strs(dev)%str => str
+ end do
+
+ do i = 0, omp_get_num_devices () - 1
+ do j = i + 1, omp_get_num_devices ()
+ if (strs(i)%str == strs(j)%str) &
+ stop 4
+ end do
+ end do
+ deallocate (strs)
+end