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;
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. */
}
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);
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);
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;
#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;
* 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
+@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
}
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)
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);
/* 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)
/* 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,
tgt_align, tgt_size);
}
}
- gomp_target_fallback (fn, hostaddrs);
+ gomp_target_fallback (fn, hostaddrs, devicep);
return;
}
/* 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
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,
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,
|| (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;
}
num_devices = 0;
devices = NULL;
+ if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED)
+ return;
+
cur = OFFLOAD_PLUGINS;
if (*cur)
do