From 6c84c8bf9b29ce89f5d263c1fa3630896fcb23f4 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 22 Dec 2019 19:54:09 +0000 Subject: [PATCH] Add OpenACC 2.6 `acc_get_property' support Add generic support for the OpenACC 2.6 `acc_get_property' and `acc_get_property_string' routines, as well as full handlers for the host and the NVPTX offload targets and minimal handlers for the HSA, Intel MIC, and AMD GCN offload targets. Included are C/C++ and Fortran tests that, in particular, print the property values for acc_property_vendor, acc_property_memory, acc_property_free_memory, acc_property_name, and acc_property_driver. The output looks as follows: Vendor: GNU Name: GOMP Total memory: 0 Free memory: 0 Driver: 1.0 with the host driver (where the memory related properties are not supported for the host device and yield 0, conforming to the standard) and output like: Vendor: Nvidia Total memory: 12651462656 Free memory: 12202737664 Name: TITAN V Driver: CUDA Driver 9.1 with the NVPTX driver. 2019-12-22 Maciej W. Rozycki Frederik Harwath Thomas Schwinge include/ * gomp-constants.h (gomp_device_property): New enum. libgomp/ * libgomp.h (gomp_device_descr): Add `get_property_func' member. * libgomp-plugin.h (gomp_device_property_value): New union. (gomp_device_property_value): New prototype. * openacc.h (acc_device_t): Add `acc_device_current' enumeration constant. (acc_device_property_t): New enum. (acc_get_property, acc_get_property_string): New prototypes. * oacc-init.c (acc_get_device_type): Also assert that result is not `acc_device_current'. (get_property_any, acc_get_property, acc_get_property_string): New functions. * openacc.f90 (openacc_kinds): Add `acc_device_current' and `acc_property_memory', `acc_property_free_memory', `acc_property_name', `acc_property_vendor' and `acc_property_driver' constants. Add `acc_device_property' data type. (openacc_internal): Add `acc_get_property' and `acc_get_property_string' interfaces. Add `acc_get_property_h', `acc_get_property_string_h', `acc_get_property_l' and `acc_get_property_string_l'. * oacc-host.c (host_get_property): New function. (host_dispatch): Wire it. * target.c (gomp_load_plugin_for_device): Handle `get_property'. * libgomp.map (OACC_2.6): Add `acc_get_property', `acc_get_property_h_', `acc_get_property_string' and `acc_get_property_string_h_' symbols. * libgomp.texi (OpenACC Runtime Library Routines): Add `acc_get_property'. (acc_get_property): New node. * plugin/plugin-gcn.c (GOMP_OFFLOAD_get_property): New function (stub). * plugin/plugin-hsa.c (GOMP_OFFLOAD_get_property): New function. * plugin/plugin-nvptx.c (CUDA_CALLS): Add `cuDeviceGetName', `cuDeviceTotalMem', `cuDriverGetVersion' and `cuMemGetInfo' calls. (GOMP_OFFLOAD_get_property): New function. (struct ptx_device): Add new field "name". (cuda_driver_version_s): Add new static variable ... (nvptx_init): ... and init from here. * testsuite/libgomp.oacc-c-c++-common/acc_get_property.c: New test. * testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c: New test. * testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c: New test. * testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c: New file with test helper functions. * testsuite/libgomp.oacc-fortran/acc_get_property.f90: New test. liboffloadmic/ * plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_property): New function. Reviewed-by: Thomas Schwinge Co-Authored-By: Frederik Harwath Co-Authored-By: Thomas Schwinge From-SVN: r279710 --- include/ChangeLog | 6 + include/gomp-constants.h | 15 ++ libgomp/ChangeLog | 51 +++++++ libgomp/libgomp-plugin.h | 8 ++ libgomp/libgomp.h | 1 + libgomp/libgomp.map | 4 + libgomp/libgomp.texi | 39 ++++++ libgomp/oacc-host.c | 22 +++ libgomp/oacc-init.c | 63 ++++++++- libgomp/openacc.f90 | 129 +++++++++++++++++- libgomp/openacc.h | 15 ++ libgomp/plugin/cuda-lib.def | 4 + libgomp/plugin/plugin-gcn.c | 11 ++ libgomp/plugin/plugin-hsa.c | 26 ++++ libgomp/plugin/plugin-nvptx.c | 87 +++++++++++- libgomp/target.c | 1 + .../acc_get_property-2.c | 68 +++++++++ .../acc_get_property-3.c | 19 +++ .../acc_get_property-aux.c | 80 +++++++++++ .../acc_get_property.c | 76 +++++++++++ .../libgomp.oacc-fortran/acc_get_property.f90 | 93 +++++++++++++ liboffloadmic/ChangeLog | 8 ++ .../plugin/libgomp-plugin-intelmic.cpp | 21 +++ 23 files changed, 841 insertions(+), 6 deletions(-) create mode 100644 libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c create mode 100644 libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c create mode 100644 libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c create mode 100644 libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c create mode 100644 libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90 diff --git a/include/ChangeLog b/include/ChangeLog index 3d44a9446809..60d9f207cbd3 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,9 @@ +2019-12-22 Maciej W. Rozycki + Frederik Harwath + Thomas Schwinge + + * gomp-constants.h (gomp_device_property): New enum. + 2019-12-19 Julian Brown * gomp-constants.h (gomp_map_kind): Add GOMP_MAP_ATTACH_DETACH. diff --git a/include/gomp-constants.h b/include/gomp-constants.h index fdae6ccc8701..d14e8b0394a9 100644 --- a/include/gomp-constants.h +++ b/include/gomp-constants.h @@ -195,6 +195,21 @@ enum gomp_map_kind #define GOMP_DEVICE_ICV -1 #define GOMP_DEVICE_HOST_FALLBACK -2 +/* Device property codes. Keep in sync with + libgomp/{openacc.h,openacc.f90}:acc_device_property_t */ +/* Start from 1 to catch uninitialized use. */ +enum gomp_device_property + { + GOMP_DEVICE_PROPERTY_MEMORY = 1, + GOMP_DEVICE_PROPERTY_FREE_MEMORY = 2, + GOMP_DEVICE_PROPERTY_NAME = 0x10001, + GOMP_DEVICE_PROPERTY_VENDOR = 0x10002, + GOMP_DEVICE_PROPERTY_DRIVER = 0x10003 + }; + +/* Internal property mask to tell numeric and string values apart. */ +#define GOMP_DEVICE_PROPERTY_STRING_MASK 0x10000 + /* GOMP_task/GOMP_taskloop* flags argument. */ #define GOMP_TASK_FLAG_UNTIED (1 << 0) #define GOMP_TASK_FLAG_FINAL (1 << 1) diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog index c557aa436f29..deaff15c4e4f 100644 --- a/libgomp/ChangeLog +++ b/libgomp/ChangeLog @@ -1,3 +1,54 @@ +2019-12-22 Maciej W. Rozycki + Frederik Harwath + Thomas Schwinge + + * libgomp.h (gomp_device_descr): Add `get_property_func' member. + * libgomp-plugin.h (gomp_device_property_value): New union. + (gomp_device_property_value): New prototype. + * openacc.h (acc_device_t): Add `acc_device_current' enumeration + constant. + (acc_device_property_t): New enum. + (acc_get_property, acc_get_property_string): New prototypes. + * oacc-init.c (acc_get_device_type): Also assert that result + is not `acc_device_current'. + (get_property_any, acc_get_property, acc_get_property_string): + New functions. + * openacc.f90 (openacc_kinds): Add `acc_device_current' and + `acc_property_memory', `acc_property_free_memory', + `acc_property_name', `acc_property_vendor' and + `acc_property_driver' constants. Add `acc_device_property' data + type. + (openacc_internal): Add `acc_get_property' and + `acc_get_property_string' interfaces. Add `acc_get_property_h', + `acc_get_property_string_h', `acc_get_property_l' and + `acc_get_property_string_l'. + * oacc-host.c (host_get_property): New function. + (host_dispatch): Wire it. + * target.c (gomp_load_plugin_for_device): Handle `get_property'. + * libgomp.map (OACC_2.6): Add `acc_get_property', `acc_get_property_h_', + `acc_get_property_string' and `acc_get_property_string_h_' symbols. + * libgomp.texi (OpenACC Runtime Library Routines): Add + `acc_get_property'. + (acc_get_property): New node. + * plugin/plugin-gcn.c (GOMP_OFFLOAD_get_property): New + function (stub). + * plugin/plugin-hsa.c (GOMP_OFFLOAD_get_property): New function. + * plugin/plugin-nvptx.c (CUDA_CALLS): Add `cuDeviceGetName', + `cuDeviceTotalMem', `cuDriverGetVersion' and `cuMemGetInfo' + calls. + (GOMP_OFFLOAD_get_property): New function. + (struct ptx_device): Add new field "name". + (cuda_driver_version_s): Add new static variable ... + (nvptx_init): ... and init from here. + + * testsuite/libgomp.oacc-c-c++-common/acc_get_property.c: New test. + * testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c: New test. + * testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c: New test. + * testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c: New file + with test helper functions. + + * testsuite/libgomp.oacc-fortran/acc_get_property.f90: New test. + 2019-12-22 Maciej W. Rozycki * testsuite/libgomp-test-support.exp.in (GCC_UNDER_TEST): New diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h index 037558c43f56..d3c6dc362765 100644 --- a/libgomp/libgomp-plugin.h +++ b/libgomp/libgomp-plugin.h @@ -54,6 +54,13 @@ enum offload_target_type OFFLOAD_TARGET_TYPE_GCN = 8 }; +/* Container type for passing device properties. */ +union gomp_device_property_value +{ + const char *ptr; + size_t val; +}; + /* Opaque type to represent plugin-dependent implementation of an OpenACC asynchronous queue. */ struct goacc_asyncqueue; @@ -94,6 +101,7 @@ extern const char *GOMP_OFFLOAD_get_name (void); extern unsigned int GOMP_OFFLOAD_get_caps (void); extern int GOMP_OFFLOAD_get_type (void); extern int GOMP_OFFLOAD_get_num_devices (void); +extern union gomp_device_property_value GOMP_OFFLOAD_get_property (int, int); extern bool GOMP_OFFLOAD_init_device (int); extern bool GOMP_OFFLOAD_fini_device (int); extern unsigned GOMP_OFFLOAD_version (void); diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h index c96535752088..24c76698c4e1 100644 --- a/libgomp/libgomp.h +++ b/libgomp/libgomp.h @@ -1113,6 +1113,7 @@ struct gomp_device_descr __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; + __typeof (GOMP_OFFLOAD_get_property) *get_property_func; __typeof (GOMP_OFFLOAD_init_device) *init_device_func; __typeof (GOMP_OFFLOAD_fini_device) *fini_device_func; __typeof (GOMP_OFFLOAD_version) *version_func; diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map index 63276f7d29be..c7268bfc8e76 100644 --- a/libgomp/libgomp.map +++ b/libgomp/libgomp.map @@ -492,6 +492,10 @@ OACC_2.6 { acc_detach_async; acc_detach_finalize; acc_detach_finalize_async; + acc_get_property; + acc_get_property_h_; + acc_get_property_string; + acc_get_property_string_h_; } OACC_2.5.1; GOACC_2.0 { diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi index ac9d38e01d75..5f8f1beedaff 100644 --- a/libgomp/libgomp.texi +++ b/libgomp/libgomp.texi @@ -1849,6 +1849,7 @@ acceleration device. * acc_get_device_type:: Get type of device accelerator to be used. * acc_set_device_num:: Set device number to use. * acc_get_device_num:: Get device number to be used. +* acc_get_property:: Get device property. * acc_async_test:: Tests for completion of a specific asynchronous operation. * acc_async_test_all:: Tests for completion of all asychronous @@ -2038,6 +2039,44 @@ region. +@node acc_get_property +@section @code{acc_get_property} -- Get device property. +@cindex acc_get_property +@cindex acc_get_property_string +@table @asis +@item @emph{Description} +These routines return the value of the specified @var{property} for the +device being queried according to @var{devicenum} and @var{devicetype}. +Integer-valued and string-valued properties are returned by +@code{acc_get_property} and @code{acc_get_property_string} respectively. +The Fortran @code{acc_get_property_string} subroutine returns the string +retrieved in its fourth argument while the remaining entry points are +functions, which pass the return value as their result. + +@item @emph{C/C++}: +@multitable @columnfractions .20 .80 +@item @emph{Prototype}: @tab @code{size_t acc_get_property(int devicenum, acc_device_t devicetype, acc_device_property_t property);} +@item @emph{Prototype}: @tab @code{const char *acc_get_property_string(int devicenum, acc_device_t devicetype, acc_device_property_t property);} +@end multitable + +@item @emph{Fortran}: +@multitable @columnfractions .20 .80 +@item @emph{Interface}: @tab @code{function acc_get_property(devicenum, devicetype, property)} +@item @emph{Interface}: @tab @code{subroutine acc_get_property_string(devicenum, devicetype, property, string)} +@item @tab @code{integer devicenum} +@item @tab @code{integer(kind=acc_device_kind) devicetype} +@item @tab @code{integer(kind=acc_device_property) property} +@item @tab @code{integer(kind=acc_device_property) acc_get_property} +@item @tab @code{character(*) string} +@end multitable + +@item @emph{Reference}: +@uref{https://www.openacc.org, OpenACC specification v2.6}, section +3.2.6. +@end table + + + @node acc_async_test @section @code{acc_async_test} -- Test for completion of a specific asynchronous operation. @table @asis diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c index 845140f04f52..ec9e3247a1af 100644 --- a/libgomp/oacc-host.c +++ b/libgomp/oacc-host.c @@ -59,6 +59,27 @@ host_get_num_devices (void) return 1; } +static union gomp_device_property_value +host_get_property (int n, int prop) +{ + union gomp_device_property_value nullval = { .val = 0 }; + + if (n >= host_get_num_devices ()) + return nullval; + + switch (prop) + { + case GOMP_DEVICE_PROPERTY_NAME: + return (union gomp_device_property_value) { .ptr = "GOMP" }; + case GOMP_DEVICE_PROPERTY_VENDOR: + return (union gomp_device_property_value) { .ptr = "GNU" }; + case GOMP_DEVICE_PROPERTY_DRIVER: + return (union gomp_device_property_value) { .ptr = VERSION }; + default: + return nullval; + } +} + static bool host_init_device (int n __attribute__ ((unused))) { @@ -248,6 +269,7 @@ static struct gomp_device_descr host_dispatch = .get_caps_func = host_get_caps, .get_type_func = host_get_type, .get_num_devices_func = host_get_num_devices, + .get_property_func = host_get_property, .init_device_func = host_init_device, .fini_device_func = host_fini_device, .version_func = host_version, diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c index dd88b58a379c..487a2cca61f8 100644 --- a/libgomp/oacc-init.c +++ b/libgomp/oacc-init.c @@ -670,7 +670,8 @@ acc_get_device_type (void) } assert (res != acc_device_default - && res != acc_device_not_host); + && res != acc_device_not_host + && res != acc_device_current); return res; } @@ -759,6 +760,66 @@ acc_set_device_num (int ord, acc_device_t d) ialias (acc_set_device_num) +static union gomp_device_property_value +get_property_any (int ord, acc_device_t d, acc_device_property_t prop) +{ + goacc_lazy_initialize (); + struct goacc_thread *thr = goacc_thread (); + + if (d == acc_device_current && thr && thr->dev) + return thr->dev->get_property_func (thr->dev->target_id, prop); + + gomp_mutex_lock (&acc_device_lock); + + struct gomp_device_descr *dev = resolve_device (d, true); + + int num_devices = dev->get_num_devices_func (); + + if (num_devices <= 0 || ord >= num_devices) + acc_dev_num_out_of_range (d, ord, num_devices); + + dev += ord; + + gomp_mutex_lock (&dev->lock); + if (dev->state == GOMP_DEVICE_UNINITIALIZED) + gomp_init_device (dev); + gomp_mutex_unlock (&dev->lock); + + gomp_mutex_unlock (&acc_device_lock); + + assert (dev); + + return dev->get_property_func (dev->target_id, prop); +} + +size_t +acc_get_property (int ord, acc_device_t d, acc_device_property_t prop) +{ + if (!known_device_type_p (d)) + unknown_device_type_error(d); + + if (prop & GOMP_DEVICE_PROPERTY_STRING_MASK) + return 0; + else + return get_property_any (ord, d, prop).val; +} + +ialias (acc_get_property) + +const char * +acc_get_property_string (int ord, acc_device_t d, acc_device_property_t prop) +{ + if (!known_device_type_p (d)) + unknown_device_type_error(d); + + if (prop & GOMP_DEVICE_PROPERTY_STRING_MASK) + return get_property_any (ord, d, prop).ptr; + else + return NULL; +} + +ialias (acc_get_property_string) + /* For -O and higher, the compiler always attempts to expand acc_on_device, but if the user disables the builtin, or calls it via a pointer, we'll need this version. diff --git a/libgomp/openacc.f90 b/libgomp/openacc.f90 index fb7fc6e6d77e..e5b4b40c3cce 100644 --- a/libgomp/openacc.f90 +++ b/libgomp/openacc.f90 @@ -31,16 +31,18 @@ module openacc_kinds use iso_fortran_env, only: int32 + use iso_c_binding, only: c_size_t implicit none public - private :: int32 + private :: int32, c_size_t ! When adding items, also update 'public' setting in 'module openacc' below. integer, parameter :: acc_device_kind = int32 ! Keep in sync with include/gomp-constants.h. + integer (acc_device_kind), parameter :: acc_device_current = -3 integer (acc_device_kind), parameter :: acc_device_none = 0 integer (acc_device_kind), parameter :: acc_device_default = 1 integer (acc_device_kind), parameter :: acc_device_host = 2 @@ -49,6 +51,15 @@ module openacc_kinds integer (acc_device_kind), parameter :: acc_device_nvidia = 5 integer (acc_device_kind), parameter :: acc_device_gcn = 8 + integer, parameter :: acc_device_property = c_size_t + + ! Keep in sync with include/gomp-constants.h. + integer (acc_device_property), parameter :: acc_property_memory = 1 + integer (acc_device_property), parameter :: acc_property_free_memory = 2 + integer (acc_device_property), parameter :: acc_property_name = int(Z'10001') + integer (acc_device_property), parameter :: acc_property_vendor = int(Z'10002') + integer (acc_device_property), parameter :: acc_property_driver = int(Z'10003') + integer, parameter :: acc_handle_kind = int32 ! Keep in sync with include/gomp-constants.h. @@ -89,6 +100,24 @@ module openacc_internal integer (acc_device_kind) d end function + function acc_get_property_h (n, d, p) + import + implicit none (type, external) + integer (acc_device_property) :: acc_get_property_h + integer, value :: n + integer (acc_device_kind), value :: d + integer (acc_device_property), value :: p + end function + + subroutine acc_get_property_string_h (n, d, p, s) + import + implicit none (type, external) + integer, value :: n + integer (acc_device_kind), value :: d + integer (acc_device_property), value :: p + character (*) :: s + end subroutine + function acc_async_test_h (a) logical acc_async_test_h integer a @@ -508,6 +537,26 @@ module openacc_internal integer (c_int), value :: d end function + function acc_get_property_l (n, d, p) & + bind (C, name = "acc_get_property") + use iso_c_binding, only: c_int, c_size_t + implicit none (type, external) + integer (c_size_t) :: acc_get_property_l + integer (c_int), value :: n + integer (c_int), value :: d + integer (c_int), value :: p + end function + + function acc_get_property_string_l (n, d, p) & + bind (C, name = "acc_get_property_string") + use iso_c_binding, only: c_int, c_ptr + implicit none (type, external) + type (c_ptr) :: acc_get_property_string_l + integer (c_int), value :: n + integer (c_int), value :: d + integer (c_int), value :: p + end function + function acc_async_test_l (a) & bind (C, name = "acc_async_test") use iso_c_binding, only: c_int @@ -716,16 +765,23 @@ module openacc private ! From openacc_kinds - public :: acc_device_kind, acc_handle_kind + public :: acc_device_kind public :: acc_device_none, acc_device_default, acc_device_host public :: acc_device_not_host, acc_device_nvidia, acc_device_gcn + + public :: acc_device_property + public :: acc_property_memory, acc_property_free_memory + public :: acc_property_name, acc_property_vendor, acc_property_driver + + public :: acc_handle_kind public :: acc_async_noval, acc_async_sync public :: openacc_version public :: acc_get_num_devices, acc_set_device_type, acc_get_device_type - public :: acc_set_device_num, acc_get_device_num, acc_async_test - public :: acc_async_test_all + public :: acc_set_device_num, acc_get_device_num + public :: acc_get_property, acc_get_property_string + public :: acc_async_test, acc_async_test_all public :: acc_wait, acc_async_wait, acc_wait_async public :: acc_wait_all, acc_async_wait_all, acc_wait_all_async public :: acc_init, acc_shutdown, acc_on_device @@ -758,6 +814,14 @@ module openacc procedure :: acc_get_device_num_h end interface + interface acc_get_property + procedure :: acc_get_property_h + end interface + + interface acc_get_property_string + procedure :: acc_get_property_string_h + end interface + interface acc_async_test procedure :: acc_async_test_h end interface @@ -976,6 +1040,63 @@ function acc_get_device_num_h (d) acc_get_device_num_h = acc_get_device_num_l (d) end function +function acc_get_property_h (n, d, p) + use iso_c_binding, only: c_int, c_size_t + use openacc_internal, only: acc_get_property_l + use openacc_kinds + implicit none (type, external) + integer (acc_device_property) :: acc_get_property_h + integer, value :: n + integer (acc_device_kind), value :: d + integer (acc_device_property), value :: p + + integer (c_int) :: pint + + pint = int (p, c_int) + acc_get_property_h = acc_get_property_l (n, d, pint) +end function + +subroutine acc_get_property_string_h (n, d, p, s) + use iso_c_binding, only: c_char, c_int, c_ptr, c_f_pointer, c_associated + use openacc_internal, only: acc_get_property_string_l + use openacc_kinds + implicit none (type, external) + integer, value :: n + integer (acc_device_kind), value :: d + integer (acc_device_property), value :: p + character (*) :: s + + integer (c_int) :: pint + type (c_ptr) :: cptr + integer :: clen + character (kind=c_char, len=1), pointer, contiguous :: sptr (:) + integer :: slen + integer :: i + + interface + function strlen (s) bind (C, name = "strlen") + use iso_c_binding, only: c_ptr, c_size_t + type (c_ptr), intent(in), value :: s + integer (c_size_t) :: strlen + end function strlen + end interface + + pint = int (p, c_int) + cptr = acc_get_property_string_l (n, d, pint) + s = "" + if (.not. c_associated (cptr)) then + return + end if + + clen = int (strlen (cptr)) + call c_f_pointer (cptr, sptr, [clen]) + + slen = min (clen, len (s)) + do i = 1, slen + s (i:i) = sptr (i) + end do +end subroutine + function acc_async_test_h (a) use openacc_internal, only: acc_async_test_l logical acc_async_test_h diff --git a/libgomp/openacc.h b/libgomp/openacc.h index d2e5c101f7f6..9b143064b7d4 100644 --- a/libgomp/openacc.h +++ b/libgomp/openacc.h @@ -49,6 +49,7 @@ extern "C" { /* Types */ typedef enum acc_device_t { /* Keep in sync with include/gomp-constants.h. */ + acc_device_current = -3, acc_device_none = 0, acc_device_default = 1, acc_device_host = 2, @@ -62,6 +63,16 @@ typedef enum acc_device_t { _ACC_neg = -1 } acc_device_t; +typedef enum acc_device_property_t { + /* Keep in sync with include/gomp-constants.h. */ + /* Start from 1 to catch uninitialized use. */ + acc_property_memory = 1, + acc_property_free_memory = 2, + acc_property_name = 0x10001, + acc_property_vendor = 0x10002, + acc_property_driver = 0x10003 +} acc_device_property_t; + typedef enum acc_async_t { /* Keep in sync with include/gomp-constants.h. */ acc_async_noval = -1, @@ -73,6 +84,10 @@ void acc_set_device_type (acc_device_t) __GOACC_NOTHROW; acc_device_t acc_get_device_type (void) __GOACC_NOTHROW; void acc_set_device_num (int, acc_device_t) __GOACC_NOTHROW; int acc_get_device_num (acc_device_t) __GOACC_NOTHROW; +size_t acc_get_property + (int, acc_device_t, acc_device_property_t) __GOACC_NOTHROW; +const char *acc_get_property_string + (int, acc_device_t, acc_device_property_t) __GOACC_NOTHROW; int acc_async_test (int) __GOACC_NOTHROW; int acc_async_test_all (void) __GOACC_NOTHROW; void acc_wait (int) __GOACC_NOTHROW; diff --git a/libgomp/plugin/cuda-lib.def b/libgomp/plugin/cuda-lib.def index a16badcfa9de..cd91b39b1d27 100644 --- a/libgomp/plugin/cuda-lib.def +++ b/libgomp/plugin/cuda-lib.def @@ -8,6 +8,9 @@ CUDA_ONE_CALL (cuCtxSynchronize) CUDA_ONE_CALL (cuDeviceGet) CUDA_ONE_CALL (cuDeviceGetAttribute) CUDA_ONE_CALL (cuDeviceGetCount) +CUDA_ONE_CALL (cuDeviceGetName) +CUDA_ONE_CALL (cuDeviceTotalMem) +CUDA_ONE_CALL (cuDriverGetVersion) CUDA_ONE_CALL (cuEventCreate) CUDA_ONE_CALL (cuEventDestroy) CUDA_ONE_CALL (cuEventElapsedTime) @@ -35,6 +38,7 @@ CUDA_ONE_CALL (cuMemcpyHtoDAsync) CUDA_ONE_CALL (cuMemFree) CUDA_ONE_CALL (cuMemFreeHost) CUDA_ONE_CALL (cuMemGetAddressRange) +CUDA_ONE_CALL (cuMemGetInfo) CUDA_ONE_CALL (cuMemHostGetDevicePointer) CUDA_ONE_CALL (cuModuleGetFunction) CUDA_ONE_CALL (cuModuleGetGlobal) diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c index 04fe472a70dc..32239c71c61b 100644 --- a/libgomp/plugin/plugin-gcn.c +++ b/libgomp/plugin/plugin-gcn.c @@ -3236,6 +3236,17 @@ GOMP_OFFLOAD_get_num_devices (void) return hsa_context.agent_count; } +union gomp_device_property_value +GOMP_OFFLOAD_get_property (int device, int prop) +{ + /* Stub. Check device and return default value for unsupported properties. */ + /* TODO: Implement this function. */ + get_agent_info (device); + + union gomp_device_property_value nullval = { .val = 0 }; + return nullval; +} + /* Initialize device (agent) number N so that it can be used for computation. Return TRUE on success. */ diff --git a/libgomp/plugin/plugin-hsa.c b/libgomp/plugin/plugin-hsa.c index 409e138aaca3..259f704b2e95 100644 --- a/libgomp/plugin/plugin-hsa.c +++ b/libgomp/plugin/plugin-hsa.c @@ -699,6 +699,32 @@ GOMP_OFFLOAD_get_num_devices (void) return hsa_context.agent_count; } +/* Part of the libgomp plugin interface. Return the value of property + PROP of agent number N. */ + +union gomp_device_property_value +GOMP_OFFLOAD_get_property (int n, int prop) +{ + union gomp_device_property_value nullval = { .val = 0 }; + + if (!init_hsa_context ()) + return nullval; + if (n >= hsa_context.agent_count) + { + GOMP_PLUGIN_error + ("Request for a property of a non-existing HSA device %i", n); + return nullval; + } + + switch (prop) + { + case GOMP_DEVICE_PROPERTY_VENDOR: + return (union gomp_device_property_value) { .ptr = "HSA" }; + default: + return nullval; + } +} + /* Part of the libgomp plugin interface. Initialize agent number N so that it can be used for computation. Return TRUE on success. */ diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c index 911d0f66a6e4..80e547541e33 100644 --- a/libgomp/plugin/plugin-nvptx.c +++ b/libgomp/plugin/plugin-nvptx.c @@ -189,6 +189,10 @@ cuda_error (CUresult r) return fallback; } +/* Version of the CUDA Toolkit in the same MAJOR.MINOR format that is used by + Nvidia, such as in the 'deviceQuery' program (Nvidia's CUDA samples). */ +static char cuda_driver_version_s[30]; + static unsigned int instantiated_devices = 0; static pthread_mutex_t ptx_dev_lock = PTHREAD_MUTEX_INITIALIZER; @@ -284,7 +288,7 @@ struct ptx_device bool map; bool concur; bool mkern; - int mode; + int mode; int clock_khz; int num_sms; int regs_per_block; @@ -294,6 +298,9 @@ struct ptx_device int max_threads_per_multiprocessor; int default_dims[GOMP_DIM_MAX]; + /* Length as used by the CUDA Runtime API ('struct cudaDeviceProp'). */ + char name[256]; + struct ptx_image_data *images; /* Images loaded on device. */ pthread_mutex_t image_lock; /* Lock for above list. */ @@ -327,9 +334,16 @@ nvptx_init (void) CUDA_CALL (cuInit, 0); + int cuda_driver_version; + CUDA_CALL_ERET (NULL, cuDriverGetVersion, &cuda_driver_version); + snprintf (cuda_driver_version_s, sizeof cuda_driver_version_s, + "CUDA Driver %u.%u", + cuda_driver_version / 1000, cuda_driver_version % 1000 / 10); + CUDA_CALL (cuDeviceGetCount, &ndevs); ptx_devices = GOMP_PLUGIN_malloc_cleared (sizeof (struct ptx_device *) * ndevs); + return true; } @@ -491,6 +505,9 @@ nvptx_open_device (int n) for (int i = 0; i != GOMP_DIM_MAX; i++) ptx_dev->default_dims[i] = 0; + CUDA_CALL_ERET (NULL, cuDeviceGetName, ptx_dev->name, sizeof ptx_dev->name, + dev); + ptx_dev->images = NULL; pthread_mutex_init (&ptx_dev->image_lock, NULL); @@ -1104,6 +1121,74 @@ GOMP_OFFLOAD_get_num_devices (void) return nvptx_get_num_devices (); } +union gomp_device_property_value +GOMP_OFFLOAD_get_property (int n, int prop) +{ + union gomp_device_property_value propval = { .val = 0 }; + + pthread_mutex_lock (&ptx_dev_lock); + + if (n >= nvptx_get_num_devices () || n < 0 || ptx_devices[n] == NULL) + { + pthread_mutex_unlock (&ptx_dev_lock); + return propval; + } + + struct ptx_device *ptx_dev = ptx_devices[n]; + switch (prop) + { + case GOMP_DEVICE_PROPERTY_MEMORY: + { + size_t total_mem; + + CUDA_CALL_ERET (propval, cuDeviceTotalMem, &total_mem, ptx_dev->dev); + propval.val = total_mem; + } + break; + case GOMP_DEVICE_PROPERTY_FREE_MEMORY: + { + size_t total_mem; + size_t free_mem; + CUdevice ctxdev; + + CUDA_CALL_ERET (propval, cuCtxGetDevice, &ctxdev); + if (ptx_dev->dev == ctxdev) + CUDA_CALL_ERET (propval, cuMemGetInfo, &free_mem, &total_mem); + else if (ptx_dev->ctx) + { + CUcontext old_ctx; + + CUDA_CALL_ERET (propval, cuCtxPushCurrent, ptx_dev->ctx); + CUDA_CALL_ERET (propval, cuMemGetInfo, &free_mem, &total_mem); + CUDA_CALL_ASSERT (cuCtxPopCurrent, &old_ctx); + } + else + { + CUcontext new_ctx; + + CUDA_CALL_ERET (propval, cuCtxCreate, &new_ctx, CU_CTX_SCHED_AUTO, + ptx_dev->dev); + CUDA_CALL_ERET (propval, cuMemGetInfo, &free_mem, &total_mem); + CUDA_CALL_ASSERT (cuCtxDestroy, new_ctx); + } + propval.val = free_mem; + } + break; + case GOMP_DEVICE_PROPERTY_NAME: + propval.ptr = ptx_dev->name; + break; + case GOMP_DEVICE_PROPERTY_VENDOR: + propval.ptr = "Nvidia"; + break; + case GOMP_DEVICE_PROPERTY_DRIVER: + propval.ptr = cuda_driver_version_s; + break; + } + + pthread_mutex_unlock (&ptx_dev_lock); + return propval; +} + bool GOMP_OFFLOAD_init_device (int n) { diff --git a/libgomp/target.c b/libgomp/target.c index bf30716cd855..3562c3790f10 100644 --- a/libgomp/target.c +++ b/libgomp/target.c @@ -3001,6 +3001,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device, DLSYM (get_caps); DLSYM (get_type); DLSYM (get_num_devices); + DLSYM (get_property); DLSYM (init_device); DLSYM (fini_device); DLSYM (load_image); diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c new file mode 100644 index 000000000000..4dd13c401d36 --- /dev/null +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-2.c @@ -0,0 +1,68 @@ +/* Test the `acc_get_property' and '`acc_get_property_string' library + functions on Nvidia devices by comparing property values with + those obtained through the CUDA API. */ +/* { dg-additional-sources acc_get_property-aux.c } */ +/* { dg-additional-options "-lcuda -lcudart" } */ +/* { dg-do run { target openacc_nvidia_accel_selected } } */ + +#include +#include +#include +#include +#include + +void expect_device_properties +(acc_device_t dev_type, int dev_num, + int expected_total_mem, int expected_free_mem, + const char* expected_vendor, const char* expected_name, + const char* expected_driver); + +int main () +{ + int dev_count; + cudaGetDeviceCount (&dev_count); + + for (int dev_num = 0; dev_num < dev_count; ++dev_num) + { + if (cudaSetDevice (dev_num) != cudaSuccess) + { + fprintf (stderr, "cudaSetDevice failed.\n"); + abort (); + } + + printf("Checking device %d\n", dev_num); + + const char *vendor = "Nvidia"; + size_t free_mem; + size_t total_mem; + if (cudaMemGetInfo(&free_mem, &total_mem) != cudaSuccess) + { + fprintf (stderr, "cudaMemGetInfo failed.\n"); + abort (); + } + + struct cudaDeviceProp p; + if (cudaGetDeviceProperties(&p, dev_num) != cudaSuccess) + { + fprintf (stderr, "cudaGetDeviceProperties failed.\n"); + abort (); + } + + int driver_version; + if (cudaDriverGetVersion(&driver_version) != cudaSuccess) + { + fprintf (stderr, "cudaDriverGetVersion failed.\n"); + abort (); + } + /* The version string should contain the version of the CUDA Toolkit + in the same MAJOR.MINOR format that is used by Nvidia. + The format string below is the same that is used by the deviceQuery + program, which belongs to Nvidia's CUDA samples, to print the version. */ + char driver[30]; + snprintf (driver, sizeof driver, "CUDA Driver %u.%u", + driver_version / 1000, driver_version % 1000 / 10); + + expect_device_properties(acc_device_nvidia, dev_num, + total_mem, free_mem, vendor, p.name, driver); + } +} diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c new file mode 100644 index 000000000000..92565000e499 --- /dev/null +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-3.c @@ -0,0 +1,19 @@ +/* Test the `acc_get_property' and '`acc_get_property_string' library + functions for the host device. */ +/* { dg-additional-sources acc_get_property-aux.c } */ +/* { dg-do run } */ + +#include +#include + +void expect_device_properties +(acc_device_t dev_type, int dev_num, + int expected_total_mem, int expected_free_mem, + const char* expected_vendor, const char* expected_name, + const char* expected_driver); + +int main() +{ + printf ("Checking acc_device_host device properties\n"); + expect_device_properties (acc_device_host, 0, 0, 0, "GNU", "GOMP", "1.0"); +} diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c new file mode 100644 index 000000000000..952bdbf6aeab --- /dev/null +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c @@ -0,0 +1,80 @@ +/* Auxiliary functions for acc_get_property tests */ +/* { dg-do compile { target skip-all-targets } } */ + +#include +#include +#include +#include + +void expect_device_properties +(acc_device_t dev_type, int dev_num, + int expected_total_mem, int expected_free_mem, + const char* expected_vendor, const char* expected_name, + const char* expected_driver) +{ + const char *vendor = acc_get_property_string (dev_num, dev_type, + acc_property_vendor); + if (strcmp (vendor, expected_vendor)) + { + fprintf (stderr, "Expected acc_property_vendor to equal \"%s\", " + "but was \"%s\".\n", expected_vendor, vendor); + abort (); + } + + int total_mem = acc_get_property (dev_num, dev_type, + acc_property_memory); + if (total_mem != expected_total_mem) + { + fprintf (stderr, "Expected acc_property_memory to equal %d, " + "but was %d.\n", expected_total_mem, total_mem); + abort (); + + } + + int free_mem = acc_get_property (dev_num, dev_type, + acc_property_free_memory); + if (free_mem != expected_free_mem) + { + fprintf (stderr, "Expected acc_property_free_memory to equal %d, " + "but was %d.\n", expected_free_mem, free_mem); + abort (); + } + + const char *name = acc_get_property_string (dev_num, dev_type, + acc_property_name); + if (strcmp (name, expected_name)) + { + fprintf(stderr, "Expected acc_property_name to equal \"%s\", " + "but was \"%s\".\n", expected_name, name); + abort (); + } + + const char *driver = acc_get_property_string (dev_num, dev_type, + acc_property_driver); + if (strcmp (expected_driver, driver)) + { + fprintf (stderr, "Expected acc_property_driver to equal %s, " + "but was %s.\n", expected_driver, driver); + abort (); + } + + int unknown_property = 16058; + int v = acc_get_property (dev_num, dev_type, (acc_device_property_t)unknown_property); + if (v != 0) + { + fprintf (stderr, "Expected value of unknown numeric property to equal 0, " + "but was %d.\n", v); + abort (); + } + + int unknown_property2 = -16058; + const char *s = acc_get_property_string (dev_num, dev_type, (acc_device_property_t)unknown_property2); + if (s != NULL) + { + fprintf (stderr, "Expected value of unknown string property to be NULL, " + "but was %d.\n", s); + abort (); + } + + +} diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c new file mode 100644 index 000000000000..289d1bab7f81 --- /dev/null +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c @@ -0,0 +1,76 @@ +/* Test the `acc_get_property' and '`acc_get_property_string' library + functions by printing the results of those functions for all devices + of all device types mentioned in the OpenACC standard. + + See also acc_get_property.f90. */ +/* { dg-do run { target { { ! { openacc_host_selected } } && { ! { openacc_amdgcn_accel_selected } } } } } */ +/* FIXME: This test does not work with the GCN implementation stub yet. */ + +#include +#include +#include +#include + +/* Print the values of the properties of all devices of the given type + and do basic device independent validation. */ + +void +print_device_properties(acc_device_t type) +{ + const char *s; + size_t v; + + int dev_count = acc_get_num_devices(type); + + for (int i = 0; i < dev_count; ++i) + { + printf(" Device %d:\n", i+1); + + s = acc_get_property_string (i, type, acc_property_vendor); + printf (" Vendor: %s\n", s); + if (s == NULL || *s == 0) + { + fprintf (stderr, "acc_property_vendor should not be null or empty.\n"); + abort (); + } + + v = acc_get_property (i, type, acc_property_memory); + printf (" Total memory: %zd\n", v); + + v = acc_get_property (i, type, acc_property_free_memory); + printf (" Free memory: %zd\n", v); + + s = acc_get_property_string (i, type, acc_property_name); + printf (" Name: %s\n", s); + if (s == NULL || *s == 0) + { + fprintf (stderr, "acc_property_name should not be null or empty.\n"); + abort (); + } + + s = acc_get_property_string (i, type, acc_property_driver); + printf (" Driver: %s\n", s); + if (s == NULL || *s == 0) + { + fprintf (stderr, "acc_property_string should not be null or empty.\n"); + abort (); + } + } +} + +int main () +{ + printf("acc_device_none:\n"); + /* For completness; not expected to print anything since there + should be no devices of this type. */ + print_device_properties(acc_device_none); + + printf("acc_device_default:\n"); + print_device_properties(acc_device_default); + + printf("acc_device_host:\n"); + print_device_properties(acc_device_host); + + printf("acc_device_not_host:\n"); + print_device_properties(acc_device_not_host); +} diff --git a/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90 b/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90 new file mode 100644 index 000000000000..ce695475ae4e --- /dev/null +++ b/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90 @@ -0,0 +1,93 @@ +! Test the `acc_get_property' and '`acc_get_property_string' library +! functions by printing the results of those functions for all devices +! of all device types mentioned in the OpenACC standard. +! +! See also acc_get_property.c +! { dg-do run { target { { ! { openacc_host_selected } } && { ! { openacc_amdgcn_accel_selected } } } } } +! FIXME: This test does not work with the GCN implementation stub yet. + +program test + use openacc + implicit none + + print *, "acc_device_none:" + ! For completeness; not expected to print anything + call print_device_properties (acc_device_none) + + print *, "acc_device_default:" + call print_device_properties (acc_device_default) + + print *, "acc_device_host:" + call print_device_properties (acc_device_host) + + print *, "acc_device_not_host:" + call print_device_properties (acc_device_not_host) +end program test + +! Print the values of the properties of all devices of the given type +! and do basic device independent validation. +subroutine print_device_properties (device_type) + use openacc + implicit none + + integer, intent(in) :: device_type + + integer :: device_count + integer :: device + integer(acc_device_property) :: v + character*256 :: s + + device_count = acc_get_num_devices(device_type) + + do device = 0, device_count - 1 + print "(a, i0)", " Device ", device + + call acc_get_property_string (device, device_type, acc_property_vendor, s) + print "(a, a)", " Vendor: ", trim (s) + if (s == "") then + print *, "acc_property_vendor should not be empty." + stop 1 + end if + + v = acc_get_property (device, device_type, acc_property_memory) + print "(a, i0)", " Total memory: ", v + if (v < 0) then + print *, "acc_property_memory should not be negative." + stop 1 + end if + + v = acc_get_property (device, device_type, acc_property_free_memory) + print "(a, i0)", " Free memory: ", v + if (v < 0) then + print *, "acc_property_free_memory should not to be negative." + stop 1 + end if + + v = acc_get_property (device, device_type, int(2360, kind = acc_device_property)) + if (v /= 0) then + print *, "Value of unknown numeric property should be 0." + stop 1 + end if + + call acc_get_property_string (device, device_type, acc_property_name, s) + print "(a, a)", " Name: ", trim (s) + if (s == "") then + print *, "acc_property_name should not be empty." + stop 1 + end if + + call acc_get_property_string (device, device_type, acc_property_driver, s) + print "(a, a)", " Driver: ", trim (s) + if (s == "") then + print *, "acc_property_driver should not be empty." + stop 1 + end if + + call acc_get_property_string (device, device_type, int(4060, kind = acc_device_property), s) + if (s /= "") then + print *, "Value of unknown string property should be empty string." + stop 1 + end if + + end do +end subroutine print_device_properties diff --git a/liboffloadmic/ChangeLog b/liboffloadmic/ChangeLog index 5a5161585270..b5fae878e9d5 100644 --- a/liboffloadmic/ChangeLog +++ b/liboffloadmic/ChangeLog @@ -1,3 +1,11 @@ +2019-12-22 Maciej W. Rozycki + Frederik Harwath + Thomas Schwinge + + liboffloadmic/ + * plugin/libgomp-plugin-intelmic.cpp (GOMP_OFFLOAD_get_property): + New function. + 2019-10-01 Maciej W. Rozycki * plugin/configure: Regenerate. diff --git a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp index d1678d0514e9..40d97702b87e 100644 --- a/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp +++ b/liboffloadmic/plugin/libgomp-plugin-intelmic.cpp @@ -174,6 +174,27 @@ GOMP_OFFLOAD_get_num_devices (void) return num_devices; } +extern "C" union gomp_device_property_value +GOMP_OFFLOAD_get_property (int n, int prop) +{ + union gomp_device_property_value nullval = { .val = 0 }; + + if (n >= num_devices) + { + GOMP_PLUGIN_error + ("Request for a property of a non-existing Intel MIC device %i", n); + return nullval; + } + + switch (prop) + { + case GOMP_DEVICE_PROPERTY_VENDOR: + return (union gomp_device_property_value) { .ptr = "Intel" }; + default: + return nullval; + } +} + static bool offload (const char *file, uint64_t line, int device, const char *name, int num_vars, VarDesc *vars, const void **async_data) -- 2.39.2