On 15/10/2020 12:02 pm, Jakub Jelinek wrote:
On Thu, Oct 15, 2020 at 12:50:18PM +0200, Jakub Jelinek via Gcc-patches wrote: Ok, the first response indicates that both if (false) and omp_get_initial_device () are valid. Therefore, I think until omp_get_initial_device () value is changed, we want in resolve_device: if (device_id < 0 || device_id >= gomp_get_num_devices ()) { if (device_id != GOMP_DEVICE_HOST_FALLBACK) gomp_fatal (...); return NULL; } and do gomp_fatal also for further return NULLs in the function. And then in if (devicep == NULL || !(devicep->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400) /* All shared memory devices should use the GOMP_target_ext function. */ || devicep->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM || !(fn_addr = gomp_get_target_fn_addr (devicep, fn))) return gomp_target_fallback (fn, hostaddrs); and similar do gomp_fatal before the gomp_target_fallback call if target-offload-var ICV is mandatory and devicep != NULL.
Instead of doing a gomp_fatal before every call to gomp_target_fallback/gomp_target_data_fallback, I think it would be tidier to pass devicep into the fallback instead and keep the gomp_fatals in the fallback functions? Although it is kind of odd to pass the device to the fallback function to be used if the device in question does not work, but at least the fallback functions are static to target.c.
Is this version okay for trunk? Thanks Kwok
commit 82555f50d2930f973ab20782ebcb836b719bce96 Author: Kwok Cheung Yeung <k...@codesourcery.com> Date: Mon Oct 19 10:47:42 2020 -0700 openmp: Implement support for OMP_TARGET_OFFLOAD environment variable This implements support for the OMP_TARGET_OFFLOAD environment variable introduced in the OpenMP 5.0 standard, which controls how offloading is handled. It may be set to MANDATORY (abort if offloading cannot be performed), DISABLED (no offloading to devices) or DEFAULT (offload to device if possible, fall back to host if not). 2020-10-19 Kwok Cheung Yeung <k...@codesourcery.com> libgomp/ * env.c (gomp_target_offload_var): New. (parse_target_offload): New. (handle_omp_display_env): Print value of OMP_TARGET_OFFLOAD. (initialize_env): Parse OMP_TARGET_OFFLOAD. * libgomp.h (gomp_target_offload_t): New. (gomp_target_offload_var): New. * libgomp.texi (OMP_TARGET_OFFLOAD): New section. * target.c (resolve_device): Generate error if device not found and offloading is mandatory. (gomp_target_fallback): Generate error if offloading is mandatory. (GOMP_target): Add argument in call to gomp_target_fallback. (GOMP_target_ext): Likewise. (gomp_target_data_fallback): Generate error if offloading is mandatory. (GOMP_target_data): Add argument in call to gomp_target_data_fallback. (GOMP_target_data_ext): Likewise. (gomp_target_task_fn): Add argument in call to gomp_target_fallback. (gomp_target_init): Return early if offloading is disabled. diff --git a/libgomp/env.c b/libgomp/env.c index d730c48..d0eae8d 100644 --- a/libgomp/env.c +++ b/libgomp/env.c @@ -75,6 +75,7 @@ struct gomp_task_icv gomp_global_icv = { unsigned long gomp_max_active_levels_var = gomp_supported_active_levels; bool gomp_cancel_var = false; +enum gomp_target_offload_t gomp_target_offload_var = GOMP_TARGET_OFFLOAD_DEFAULT; int gomp_max_task_priority_var = 0; #ifndef HAVE_SYNC_BUILTINS gomp_mutex_t gomp_managed_threads_lock; @@ -374,6 +375,48 @@ parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, return false; } +static void +parse_target_offload (const char *name, enum gomp_target_offload_t *offload) +{ + const char *env; + bool found = false; + enum gomp_target_offload_t new_offload; + + env = getenv (name); + if (env == NULL) + return; + + while (isspace ((unsigned char) *env)) + ++env; + if (strncasecmp (env, "default", 7) == 0) + { + env += 7; + found = true; + new_offload = GOMP_TARGET_OFFLOAD_DEFAULT; + } + else if (strncasecmp (env, "mandatory", 9) == 0) + { + env += 9; + found = true; + new_offload = GOMP_TARGET_OFFLOAD_MANDATORY; + } + else if (strncasecmp (env, "disabled", 8) == 0) + { + env += 8; + found = true; + new_offload = GOMP_TARGET_OFFLOAD_DISABLED; + } + while (isspace ((unsigned char) *env)) + ++env; + if (found && *env == '\0') + { + *offload = new_offload; + return; + } + + gomp_error ("Invalid value for environment variable OMP_TARGET_OFFLOAD"); +} + /* Parse environment variable set to a boolean or list of omp_proc_bind_t enum values. Return true if one was present and it was successfully parsed. */ @@ -1334,6 +1377,21 @@ handle_omp_display_env (unsigned long stacksize, int wait_policy) } fputs ("'\n", stderr); + fputs (" OMP_TARGET_OFFLOAD = '", stderr); + switch (gomp_target_offload_var) + { + case GOMP_TARGET_OFFLOAD_DEFAULT: + fputs ("DEFAULT", stderr); + break; + case GOMP_TARGET_OFFLOAD_MANDATORY: + fputs ("MANDATORY", stderr); + break; + case GOMP_TARGET_OFFLOAD_DISABLED: + fputs ("DISABLED", stderr); + break; + } + fputs ("'\n", stderr); + if (verbose) { fputs (" GOMP_CPU_AFFINITY = ''\n", stderr); @@ -1366,6 +1424,7 @@ initialize_env (void) parse_boolean ("OMP_CANCELLATION", &gomp_cancel_var); parse_boolean ("OMP_DISPLAY_AFFINITY", &gomp_display_affinity_var); parse_int ("OMP_DEFAULT_DEVICE", &gomp_global_icv.default_device_var, true); + parse_target_offload ("OMP_TARGET_OFFLOAD", &gomp_target_offload_var); parse_int ("OMP_MAX_TASK_PRIORITY", &gomp_max_task_priority_var, true); parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var, true); diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h index 9d26de2..da7ac03 100644 --- a/libgomp/libgomp.h +++ b/libgomp/libgomp.h @@ -434,6 +434,13 @@ struct gomp_task_icv struct target_mem_desc *target_data; }; +enum gomp_target_offload_t +{ + GOMP_TARGET_OFFLOAD_DEFAULT, + GOMP_TARGET_OFFLOAD_MANDATORY, + GOMP_TARGET_OFFLOAD_DISABLED +}; + #define gomp_supported_active_levels INT_MAX extern struct gomp_task_icv gomp_global_icv; @@ -442,6 +449,7 @@ extern gomp_mutex_t gomp_managed_threads_lock; #endif extern unsigned long gomp_max_active_levels_var; extern bool gomp_cancel_var; +extern enum gomp_target_offload_t gomp_target_offload_var; extern int gomp_max_task_priority_var; extern unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var; extern unsigned long gomp_available_cpus, gomp_managed_threads; diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi index a888613..7c6d5fd 100644 --- a/libgomp/libgomp.texi +++ b/libgomp/libgomp.texi @@ -1381,6 +1381,7 @@ beginning with @env{GOMP_} are GNU extensions. * OMP_PLACES:: Specifies on which CPUs the theads should be placed * OMP_STACKSIZE:: Set default thread stack size * OMP_SCHEDULE:: How threads are scheduled +* OMP_TARGET_OFFLOAD:: Controls offloading behaviour * OMP_THREAD_LIMIT:: Set the maximum number of threads * OMP_WAIT_POLICY:: How waiting threads are handled * GOMP_CPU_AFFINITY:: Bind threads to specific CPUs @@ -1654,6 +1655,30 @@ dynamic scheduling and a chunk size of 1 is used. +@node OMP_TARGET_OFFLOAD +@section @env{OMP_TARGET_OFFLOAD} -- Controls offloading behaviour +@cindex Environment Variable +@cindex Implementation specific setting +@table @asis +@item @emph{Description}: +Specifies the behaviour with regard to offloading code to a device. This +variable can be set to one of three values - @code{MANDATORY}, @code{DISABLED} +or @code{DEFAULT}. + +If set to @code{MANDATORY}, the program will terminate with an error if +the offload device is not present or is not supported. If set to +@code{DISABLED}, then offloading is disabled and all code will run on the +host. If set to @code{DEFAULT}, the program will try offloading to the +device first, then fall back to running code on the host if it cannot. + +If undefined, then the program will behave as if @code{DEFAULT} was set. + +@item @emph{Reference}: +@uref{https://www.openmp.org, OpenMP specification v5.0}, Section 6.17 +@end table + + + @node OMP_THREAD_LIMIT @section @env{OMP_THREAD_LIMIT} -- Set the maximum number of threads @cindex Environment Variable diff --git a/libgomp/target.c b/libgomp/target.c index ab7ac9b..c568670 100644 --- a/libgomp/target.c +++ b/libgomp/target.c @@ -116,7 +116,13 @@ resolve_device (int device_id) } if (device_id < 0 || device_id >= gomp_get_num_devices ()) - return NULL; + { + if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_MANDATORY + && device_id != GOMP_DEVICE_HOST_FALLBACK) + gomp_fatal ("OMP_TARGET_OFFLOAD is set to MANDATORY, but device not found."); + + return NULL; + } gomp_mutex_lock (&devices[device_id].lock); if (devices[device_id].state == GOMP_DEVICE_UNINITIALIZED) @@ -124,6 +130,11 @@ resolve_device (int device_id) else if (devices[device_id].state == GOMP_DEVICE_FINALIZED) { gomp_mutex_unlock (&devices[device_id].lock); + + if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_MANDATORY + && device_id != GOMP_DEVICE_HOST_FALLBACK) + gomp_fatal ("OMP_TARGET_OFFLOAD is set to MANDATORY, but device is finalized."); + return NULL; } gomp_mutex_unlock (&devices[device_id].lock); @@ -1997,9 +2008,16 @@ gomp_unload_device (struct gomp_device_descr *devicep) /* Host fallback for GOMP_target{,_ext} routines. */ static void -gomp_target_fallback (void (*fn) (void *), void **hostaddrs) +gomp_target_fallback (void (*fn) (void *), void **hostaddrs, + struct gomp_device_descr *devicep) { struct gomp_thread old_thr, *thr = gomp_thread (); + + if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_MANDATORY + && devicep != NULL) + gomp_fatal ("OMP_TARGET_OFFLOAD is set to MANDATORY, but device cannot " + "be used for offloading."); + old_thr = *thr; memset (thr, '\0', sizeof (*thr)); if (gomp_places_list) @@ -2107,7 +2125,7 @@ GOMP_target (int device, void (*fn) (void *), const void *unused, /* All shared memory devices should use the GOMP_target_ext function. */ || devicep->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM || !(fn_addr = gomp_get_target_fn_addr (devicep, fn))) - return gomp_target_fallback (fn, hostaddrs); + return gomp_target_fallback (fn, hostaddrs, devicep); struct target_mem_desc *tgt_vars = gomp_map_vars (devicep, mapnum, hostaddrs, NULL, sizes, kinds, false, @@ -2243,7 +2261,7 @@ GOMP_target_ext (int device, void (*fn) (void *), size_t mapnum, tgt_align, tgt_size); } } - gomp_target_fallback (fn, hostaddrs); + gomp_target_fallback (fn, hostaddrs, devicep); return; } @@ -2276,9 +2294,15 @@ GOMP_target_ext (int device, void (*fn) (void *), size_t mapnum, /* Host fallback for GOMP_target_data{,_ext} routines. */ static void -gomp_target_data_fallback (void) +gomp_target_data_fallback (struct gomp_device_descr *devicep) { struct gomp_task_icv *icv = gomp_icv (false); + + if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_MANDATORY + && devicep != NULL) + gomp_fatal ("OMP_TARGET_OFFLOAD is set to MANDATORY, but device cannot " + "be used for offloading."); + if (icv->target_data) { /* Even when doing a host fallback, if there are any active @@ -2302,7 +2326,7 @@ GOMP_target_data (int device, const void *unused, size_t mapnum, if (devicep == NULL || !(devicep->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400) || (devicep->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)) - return gomp_target_data_fallback (); + return gomp_target_data_fallback (devicep); struct target_mem_desc *tgt = gomp_map_vars (devicep, mapnum, hostaddrs, NULL, sizes, kinds, false, @@ -2321,7 +2345,7 @@ GOMP_target_data_ext (int device, size_t mapnum, void **hostaddrs, if (devicep == NULL || !(devicep->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400) || devicep->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM) - return gomp_target_data_fallback (); + return gomp_target_data_fallback (devicep); struct target_mem_desc *tgt = gomp_map_vars (devicep, mapnum, hostaddrs, NULL, sizes, kinds, true, @@ -2617,7 +2641,7 @@ gomp_target_task_fn (void *data) || (devicep->can_run_func && !devicep->can_run_func (fn_addr))) { ttask->state = GOMP_TARGET_TASK_FALLBACK; - gomp_target_fallback (ttask->fn, ttask->hostaddrs); + gomp_target_fallback (ttask->fn, ttask->hostaddrs, devicep); return false; } @@ -3258,6 +3282,9 @@ gomp_target_init (void) num_devices = 0; devices = NULL; + if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED) + return; + cur = OFFLOAD_PLUGINS; if (*cur) do