]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
openmp: Implement support for OMP_TARGET_OFFLOAD environment variable
authorKwok Cheung Yeung <kcy@codesourcery.com>
Tue, 20 Oct 2020 11:15:59 +0000 (04:15 -0700)
committerKwok Cheung Yeung <kcy@codesourcery.com>
Wed, 11 Nov 2020 21:32:13 +0000 (13:32 -0800)
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).

This is a backport from mainline (commits
1bfc07d150790fae93184a79a7cce897655cb37b,
35f258f4bbba7fa044f90b4f14d1bc942db58089 and
121a8812c45b3155ccbd268b000ad00a778e81e8).

2020-10-20  Kwok Cheung Yeung  <kcy@codesourcery.com>
    Jakub Jelinek  <jakub@redhat.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.
Inside of the function, use automatic
variables corresponding to num_devices, num_devices_openmp and devices
global variables and update the globals only at the end of the
function.

libgomp/ChangeLog.omp
libgomp/env.c
libgomp/libgomp.h
libgomp/libgomp.texi
libgomp/target.c

index f4963e85170aecd0a6d7e60eccd042003050a7ef..9b50729bd46a05f1c1cfef0d333d05ab5a4c1b56 100644 (file)
@@ -1,3 +1,31 @@
+2020-11-11  Kwok Cheung Yeung  <kcy@codesourcery.com>
+
+       Backport from mainline
+       2020-10-20  Kwok Cheung Yeung  <kcy@codesourcery.com>
+                   Jakub Jelinek  <jakub@redhat.com>
+
+       * 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.
+       Inside of the function, use automatic
+       variables corresponding to num_devices, num_devices_openmp and devices
+       global variables and update the globals only at the end of the
+       function.
+
 2020-11-11  Kwok Cheung Yeung  <kcy@codesourcery.com>
 
        Backport from mainline
index d730c483d7f7f33dd7523862599d1c48a8e85b61..ab22525570929d50bdba0d9c386af390b072ba20 100644 (file)
@@ -75,6 +75,8 @@ 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 +376,44 @@ 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;
+  int new_offload = -1;
+
+  env = getenv (name);
+  if (env == NULL)
+    return;
+
+  while (isspace ((unsigned char) *env))
+    ++env;
+  if (strncasecmp (env, "default", 7) == 0)
+    {
+      env += 7;
+      new_offload = GOMP_TARGET_OFFLOAD_DEFAULT;
+    }
+  else if (strncasecmp (env, "mandatory", 9) == 0)
+    {
+      env += 9;
+      new_offload = GOMP_TARGET_OFFLOAD_MANDATORY;
+    }
+  else if (strncasecmp (env, "disabled", 8) == 0)
+    {
+      env += 8;
+      new_offload = GOMP_TARGET_OFFLOAD_DISABLED;
+    }
+  while (isspace ((unsigned char) *env))
+    ++env;
+  if (new_offload != -1 && *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 +1374,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 +1421,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);
index 20b9042e3330d538fdaff8b85316d98c11d307d8..231f7e2ae7ae98859cdcf6df1369130a608c950e 100644 (file)
@@ -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;
index 5a9d4c8349eab0e32574b7ff07edae0169ec3014..92168960ae8bf2158dbda7a9506ed72a9de3f6d1 100644 (file)
@@ -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
index 6b2e82f8c624188a3b52a16dcd87acd8e4a8d23d..95c094105b36786ad74a9edfb7bd104fdf9108ac 100644 (file)
@@ -116,7 +116,14 @@ 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 +131,12 @@ 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);
@@ -2185,9 +2198,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)
@@ -2295,7 +2315,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,
@@ -2431,7 +2451,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;
     }
 
@@ -2464,9 +2484,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
@@ -2490,7 +2516,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,
@@ -2509,7 +2535,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,
@@ -2805,7 +2831,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;
        }
 
@@ -3443,10 +3469,12 @@ gomp_target_init (void)
   const char *suffix = SONAME_SUFFIX (1);
   const char *cur, *next;
   char *plugin_name;
-  int i, new_num_devices;
+  int i, new_num_devs;
+  int num_devs = 0, num_devs_openmp;
+  struct gomp_device_descr *devs = NULL;
 
-  num_devices = 0;
-  devices = NULL;
+  if (gomp_target_offload_var == GOMP_TARGET_OFFLOAD_DISABLED)
+    return;
 
   cur = OFFLOAD_PLUGINS;
   if (*cur)
@@ -3464,7 +3492,7 @@ gomp_target_init (void)
        plugin_name = (char *) malloc (prefix_len + cur_len + suffix_len + 1);
        if (!plugin_name)
          {
-           num_devices = 0;
+           num_devs = 0;
            break;
          }
 
@@ -3474,16 +3502,16 @@ gomp_target_init (void)
 
        if (gomp_load_plugin_for_device (&current_device, plugin_name))
          {
-           new_num_devices = current_device.get_num_devices_func ();
-           if (new_num_devices >= 1)
+           new_num_devs = current_device.get_num_devices_func ();
+           if (new_num_devs >= 1)
              {
                /* Augment DEVICES and NUM_DEVICES.  */
 
-               devices = realloc (devices, (num_devices + new_num_devices)
-                                  * sizeof (struct gomp_device_descr));
-               if (!devices)
+               devs = realloc (devs, (num_devs + new_num_devs)
+                                     * sizeof (struct gomp_device_descr));
+               if (!devs)
                  {
-                   num_devices = 0;
+                   num_devs = 0;
                    free (plugin_name);
                    break;
                  }
@@ -3493,12 +3521,12 @@ gomp_target_init (void)
                current_device.type = current_device.get_type_func ();
                current_device.mem_map.root = NULL;
                current_device.state = GOMP_DEVICE_UNINITIALIZED;
-               for (i = 0; i < new_num_devices; i++)
+               for (i = 0; i < new_num_devs; i++)
                  {
                    current_device.target_id = i;
-                   devices[num_devices] = current_device;
-                   gomp_mutex_init (&devices[num_devices].lock);
-                   num_devices++;
+                   devs[num_devs] = current_device;
+                   gomp_mutex_init (&devs[num_devs].lock);
+                   num_devs++;
                  }
              }
          }
@@ -3510,34 +3538,37 @@ gomp_target_init (void)
 
   /* In DEVICES, sort the GOMP_OFFLOAD_CAP_OPENMP_400 ones first, and set
      NUM_DEVICES_OPENMP.  */
-  struct gomp_device_descr *devices_s
-    = malloc (num_devices * sizeof (struct gomp_device_descr));
-  if (!devices_s)
-    {
-      num_devices = 0;
-      free (devices);
-      devices = NULL;
-    }
-  num_devices_openmp = 0;
-  for (i = 0; i < num_devices; i++)
-    if (devices[i].capabilities & GOMP_OFFLOAD_CAP_OPENMP_400)
-      devices_s[num_devices_openmp++] = devices[i];
-  int num_devices_after_openmp = num_devices_openmp;
-  for (i = 0; i < num_devices; i++)
-    if (!(devices[i].capabilities & GOMP_OFFLOAD_CAP_OPENMP_400))
-      devices_s[num_devices_after_openmp++] = devices[i];
-  free (devices);
-  devices = devices_s;
-
-  for (i = 0; i < num_devices; i++)
+  struct gomp_device_descr *devs_s
+    = malloc (num_devs * sizeof (struct gomp_device_descr));
+  if (!devs_s)
+    {
+      num_devs = 0;
+      free (devs);
+      devs = NULL;
+    }
+  num_devs_openmp = 0;
+  for (i = 0; i < num_devs; i++)
+    if (devs[i].capabilities & GOMP_OFFLOAD_CAP_OPENMP_400)
+      devs_s[num_devs_openmp++] = devs[i];
+  int num_devs_after_openmp = num_devs_openmp;
+  for (i = 0; i < num_devs; i++)
+    if (!(devs[i].capabilities & GOMP_OFFLOAD_CAP_OPENMP_400))
+      devs_s[num_devs_after_openmp++] = devs[i];
+  free (devs);
+  devs = devs_s;
+
+  for (i = 0; i < num_devs; i++)
     {
       /* The 'devices' array can be moved (by the realloc call) until we have
         found all the plugins, so registering with the OpenACC runtime (which
         takes a copy of the pointer argument) must be delayed until now.  */
-      if (devices[i].capabilities & GOMP_OFFLOAD_CAP_OPENACC_200)
-       goacc_register (&devices[i]);
+      if (devs[i].capabilities & GOMP_OFFLOAD_CAP_OPENACC_200)
+       goacc_register (&devs[i]);
     }
 
+  num_devices = num_devs;
+  num_devices_openmp = num_devs_openmp;
+  devices = devs;
   if (atexit (gomp_target_fini) != 0)
     gomp_fatal ("atexit failed");
 }