]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
OpenMP: Add omp_get_initial_device/omp_get_num_devices builtins
authorTobias Burnus <tburnus@baylibre.com>
Fri, 6 Jun 2025 14:22:06 +0000 (16:22 +0200)
committerTobias Burnus <tburnus@baylibre.com>
Fri, 6 Jun 2025 14:22:06 +0000 (16:22 +0200)
By adding omp_get_initial_device and omp_get_num_devices builtins for
C, C++, and Fortran, the following can be achieved:
* By making them pure, multiple calls can be avoiding in some cases.
* Some comparisons can be optimized at compile time.

omp_get_initial_device will be converted to omp_get_num_devices for
consistency; note that OpenMP 6 also permits omp_initial_device (== -1)
as value.

If GCC has not been configure for offloading, either intrinsic will
leads to 0 - and on the offload side, -1 (= omp_initial_device) is
returned for omp_initial_device.

gcc/fortran/ChangeLog:

* f95-lang.cc (ATTR_PURE_NOTHROW_LIST): Define.
* trans-expr.cc (get_builtin_fn): Handle omp_get_num_devices
and omp_get_intrinsic_device.
* gfortran.h (gfc_option_t): Add disable_omp_... for them.
* options.cc (gfc_handle_option): Handle them with
-fno-builtin-.

gcc/ChangeLog:

* gimple-fold.cc (gimple_fold_builtin_omp_get_initial_device,
gimple_fold_builtin_omp_get_num_devices): New.
(gimple_fold_builtin): Call them.
* omp-builtins.def (BUILT_IN_OMP_GET_INITIAL_DEVICE): Add
(BUILT_IN_OMP_GET_NUM_DEVICES): Make uservisible + pure.

libgomp/ChangeLog:

* libgomp.texi (omp_get_num_devices, omp_get_intrinsic_device):
Document builtin handling.

gcc/testsuite/ChangeLog:

* c-c++-common/gomp/omp_get_num_devices_initial_device-2.c: New test.
* c-c++-common/gomp/omp_get_num_devices_initial_device.c: New test.
* gfortran.dg/gomp/omp_get_num_devices_initial_device-2.f90: New test.
* gfortran.dg/gomp/omp_get_num_devices_initial_device.f90: New test.

Co-authored-by: Sandra Loosemore <sloosemore@baylibre.com>
gcc/fortran/f95-lang.cc
gcc/fortran/gfortran.h
gcc/fortran/options.cc
gcc/fortran/trans-expr.cc
gcc/gimple-fold.cc
gcc/omp-builtins.def
gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c [new file with mode: 0644]
gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device-2.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device.f90 [new file with mode: 0644]
libgomp/libgomp.texi

index 1f09553142d68bbc70a12766263aa18924bf7e21..bb4ce6d82882bd07ad4d3d051ea4ad9576ec7cfe 100644 (file)
@@ -564,7 +564,7 @@ gfc_builtin_function (tree decl)
   return decl;
 }
 
-/* So far we need just these 10 attribute types.  */
+/* So far we need just these 12 attribute types.  */
 #define ATTR_NULL                      0
 #define ATTR_LEAF_LIST                 (ECF_LEAF)
 #define ATTR_NOTHROW_LEAF_LIST         (ECF_NOTHROW | ECF_LEAF)
@@ -580,6 +580,7 @@ gfc_builtin_function (tree decl)
 #define ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST \
                                        (ECF_COLD | ECF_NORETURN | \
                                         ECF_NOTHROW | ECF_LEAF)
+#define ATTR_PURE_NOTHROW_LIST (ECF_PURE | ECF_NOTHROW)
 
 static void
 gfc_define_builtin (const char *name, tree type, enum built_in_function code,
index e461aa68470d2948c441b2d81c9f050f92786f7e..f73b5f9c23f43db70e8205c17fdb48aaa366a7af 100644 (file)
@@ -3302,8 +3302,10 @@ typedef struct
   int flag_init_logical;
   int flag_init_character;
   char flag_init_character_value;
-  bool disable_omp_is_initial_device;
-  bool disable_acc_on_device;
+  bool disable_omp_is_initial_device:1;
+  bool disable_omp_get_initial_device:1;
+  bool disable_omp_get_num_devices:1;
+  bool disable_acc_on_device:1;
 
   int fpe;
   int fpe_summary;
index ddddc1c2833ff96b410e4b7f793ca38aa5dac031..d3c9066630b72ff7362eecbe1661296a8e3c8365 100644 (file)
@@ -883,6 +883,10 @@ gfc_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
        return false;  /* Not supported. */
       if (!strcmp ("omp_is_initial_device", arg))
        gfc_option.disable_omp_is_initial_device = true;
+      else if (!strcmp ("omp_get_initial_device", arg))
+       gfc_option.disable_omp_get_initial_device = true;
+      else if (!strcmp ("omp_get_num_devices", arg))
+       gfc_option.disable_omp_get_num_devices = true;
       else if (!strcmp ("acc_on_device", arg))
        gfc_option.disable_acc_on_device = true;
       else
index 74d4265f27d81270752eda736047838f0dc725dc..c8a207609e4b532cdfb43a4286239992af09a4d7 100644 (file)
@@ -4635,6 +4635,16 @@ get_builtin_fn (gfc_symbol * sym)
       && !strcmp (sym->name, "omp_is_initial_device"))
     return builtin_decl_explicit (BUILT_IN_OMP_IS_INITIAL_DEVICE);
 
+  if (!gfc_option.disable_omp_get_initial_device
+      && flag_openmp && sym->attr.function && sym->ts.type == BT_INTEGER
+      && !strcmp (sym->name, "omp_get_initial_device"))
+    return builtin_decl_explicit (BUILT_IN_OMP_GET_INITIAL_DEVICE);
+
+  if (!gfc_option.disable_omp_get_num_devices
+      && flag_openmp && sym->attr.function && sym->ts.type == BT_INTEGER
+      && !strcmp (sym->name, "omp_get_num_devices"))
+    return builtin_decl_explicit (BUILT_IN_OMP_GET_NUM_DEVICES);
+
   if (!gfc_option.disable_acc_on_device
       && flag_openacc && sym->attr.function && sym->ts.type == BT_LOGICAL
       && !strcmp (sym->name, "acc_on_device_h"))
index 6e32063f29e2632c885c34a2d99cb55aa6868810..729080ad6e5b4e53131b897edb20ec40ea092a71 100644 (file)
@@ -4085,6 +4085,40 @@ gimple_fold_builtin_omp_is_initial_device (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* omp_get_initial_device was in OpenMP 5.0/5.1 explicitly and in
+   5.0 implicitly the same as omp_get_num_devices; since 6.0 it is
+   unspecified whether -1 or omp_get_num_devices() is returned.  For
+   better backward compatibility, use omp_get_num_devices() on the
+   host - and -1 on the device (where the result is unspecified).  */
+
+static bool
+gimple_fold_builtin_omp_get_initial_device (gimple_stmt_iterator *gsi)
+{
+#if ACCEL_COMPILER
+  replace_call_with_value (gsi, build_int_cst (integer_type_node, -1));
+#else
+  if (!ENABLE_OFFLOADING)
+    replace_call_with_value (gsi, integer_zero_node);
+  else
+    {
+      tree fn = builtin_decl_explicit (BUILT_IN_OMP_GET_NUM_DEVICES);
+      gcall *repl = gimple_build_call (fn, 0);
+      replace_call_with_call_and_fold (gsi, repl);
+    }
+#endif
+  return true;
+}
+
+static bool
+gimple_fold_builtin_omp_get_num_devices (gimple_stmt_iterator *gsi)
+{
+  if (!ENABLE_OFFLOADING)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+  return false;
+}
 
 /* Fold a call to __builtin_acc_on_device.  */
 
@@ -5329,6 +5363,12 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_OMP_IS_INITIAL_DEVICE:
       return gimple_fold_builtin_omp_is_initial_device (gsi);
 
+    case BUILT_IN_OMP_GET_INITIAL_DEVICE:
+      return gimple_fold_builtin_omp_get_initial_device (gsi);
+
+    case BUILT_IN_OMP_GET_NUM_DEVICES:
+      return gimple_fold_builtin_omp_get_num_devices (gsi);
+
     case BUILT_IN_REALLOC:
       return gimple_fold_builtin_realloc (gsi);
 
index f73fb7b9dd88782961e45727e1399a9e9546955a..db1ec9638419fef269ac532ee80a431232eea58e 100644 (file)
@@ -71,7 +71,12 @@ DEF_GOACC_BUILTIN_ONLY (BUILT_IN_GOACC_SINGLE_COPY_END, "GOACC_single_copy_end",
 
 DEF_GOMP_BUILTIN_COMPILER (BUILT_IN_OMP_IS_INITIAL_DEVICE,
                           "omp_is_initial_device", BT_FN_INT,
-                          ATTR_CONST_NOTHROW_LEAF_LIST)
+                          ATTR_CONST_NOTHROW_LIST)
+DEF_GOMP_BUILTIN_COMPILER (BUILT_IN_OMP_GET_INITIAL_DEVICE,
+                          "omp_get_initial_device", BT_FN_INT,
+                          ATTR_PURE_NOTHROW_LIST)
+DEF_GOMP_BUILTIN_COMPILER (BUILT_IN_OMP_GET_NUM_DEVICES, "omp_get_num_devices",
+                          BT_FN_INT, ATTR_PURE_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_THREAD_NUM, "omp_get_thread_num",
                  BT_FN_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_NUM_THREADS, "omp_get_num_threads",
@@ -88,8 +93,6 @@ DEF_GOMP_BUILTIN (BUILT_IN_OMP_SET_DEFAULT_DEVICE, "omp_set_default_device",
                  BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_INTEROP_INT, "omp_get_interop_int",
                  BT_FN_PTRMODE_PTR_INT_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_NUM_DEVICES, "omp_get_num_devices",
-                 BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ATOMIC_START, "GOMP_atomic_start",
                  BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c
new file mode 100644 (file)
index 0000000..891f5cf
--- /dev/null
@@ -0,0 +1,29 @@
+/* { dg-do compile }  */
+/* { dg-additional-options "-O1 -fdump-tree-optimized -fno-builtin-omp_get_num_devices -fno-builtin-omp_get_initial_device" }  */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int omp_get_initial_device ();
+extern int omp_get_num_devices ();
+#ifdef __cplusplus
+}
+#endif
+
+int f()
+{
+/* The following assumes that omp_get_initial_device () will not return
+   omp_initial_device (== -1), which is also permitted since OpenMP 6.0.  */
+  if (omp_get_initial_device () != omp_get_num_devices ()) __builtin_abort ();
+
+  if (omp_get_num_devices () != omp_get_num_devices ()) __builtin_abort ();
+
+  if (omp_get_initial_device () != omp_get_initial_device ()) __builtin_abort ();
+
+  return omp_get_num_devices ();
+}
+
+/* { dg-final { scan-tree-dump-times "abort" 3 "optimized" } }  */
+
+/* { dg-final { scan-tree-dump-times "omp_get_num_devices" 4 "optimized" } }  */
+/* { dg-final { scan-tree-dump-times "omp_get_initial_device" 3 "optimized" } }  */
diff --git a/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c
new file mode 100644 (file)
index 0000000..4b17143
--- /dev/null
@@ -0,0 +1,32 @@
+/* { dg-do compile }  */
+/* { dg-additional-options "-O1 -fdump-tree-optimized" }  */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int omp_get_initial_device ();
+extern int omp_get_num_devices ();
+#ifdef __cplusplus
+}
+#endif
+
+int f()
+{
+/* The following assumes that omp_get_initial_device () will not return
+   omp_initial_device (== -1), which is also permitted since OpenMP 6.0.  */
+  if (omp_get_initial_device () != omp_get_num_devices ()) __builtin_abort ();
+
+  if (omp_get_num_devices () != omp_get_num_devices ()) __builtin_abort ();
+
+  if (omp_get_initial_device () != omp_get_initial_device ()) __builtin_abort ();
+
+  return omp_get_num_devices ();
+}
+
+/* { dg-final { scan-tree-dump-not "abort" "optimized" } }  */
+
+/* { dg-final { scan-tree-dump-not "omp_get_num_devices;" "optimized" { target { ! offloading_enabled } } } }  */
+/* { dg-final { scan-tree-dump "return 0;" "optimized" { target { ! offloading_enabled } } } }  */
+
+/* { dg-final { scan-tree-dump-times "omp_get_num_devices;" 1 "optimized" { target offloading_enabled } } }  */
+/* { dg-final { scan-tree-dump "_1 = __builtin_omp_get_num_devices \\(\\);\[\\r\\n\]+\[ \]+return _1;" "optimized" { target offloading_enabled } } }  */
diff --git a/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device-2.f90 b/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device-2.f90
new file mode 100644 (file)
index 0000000..18613d4
--- /dev/null
@@ -0,0 +1,21 @@
+! { dg-do compile }
+! { dg-additional-options "-O1 -fdump-tree-optimized -fno-builtin-omp_get_num_devices -fno-builtin-omp_get_initial_device" }
+integer function f() result(ret)
+  interface
+    integer function omp_get_initial_device (); end
+    integer function omp_get_num_devices (); end
+  end interface
+
+  if (omp_get_initial_device () /= omp_get_num_devices ()) error stop
+
+  if (omp_get_num_devices () /= omp_get_num_devices ()) error stop
+
+  if (omp_get_initial_device () /= omp_get_initial_device ()) error stop
+
+  ret = omp_get_num_devices ()
+end
+
+! { dg-final { scan-tree-dump-times "error_stop" 3 "optimized" } }
+
+! { dg-final { scan-tree-dump-times "omp_get_num_devices" 4 "optimized" } }
+! { dg-final { scan-tree-dump-times "omp_get_initial_device" 3 "optimized" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device.f90 b/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device.f90
new file mode 100644 (file)
index 0000000..5409f12
--- /dev/null
@@ -0,0 +1,24 @@
+! { dg-do compile }
+! { dg-additional-options "-O1 -fdump-tree-optimized" }
+integer function f() result(ret)
+  interface
+    integer function omp_get_initial_device (); end
+    integer function omp_get_num_devices (); end
+  end interface
+
+  if (omp_get_initial_device () /= omp_get_num_devices ()) error stop
+
+  if (omp_get_num_devices () /= omp_get_num_devices ()) error stop
+
+  if (omp_get_initial_device () /= omp_get_initial_device ()) error stop
+
+  ret = omp_get_num_devices ()
+end
+
+! { dg-final { scan-tree-dump-not "error_stop" "optimized" } }
+
+! { dg-final { scan-tree-dump-not "omp_get_num_devices;" "optimized" { target { ! offloading_enabled } } } }
+! { dg-final { scan-tree-dump "return 0;" "optimized" { target { ! offloading_enabled } } } }
+
+! { dg-final { scan-tree-dump-times "omp_get_num_devices;" 1 "optimized" { target offloading_enabled } } }
+! { dg-final { scan-tree-dump "_1 = __builtin_omp_get_num_devices \\(\\);\[\\r\\n\]+\[ \]+return _1;" "optimized" { target offloading_enabled } } }
index 7116fcda13fec0ae79f62d78018954ebdbee58b3..8374595bc8237c751448ed631acf686082702c76 100644 (file)
@@ -1802,6 +1802,11 @@ Returns the number of available non-host devices.
 
 The effect of running this routine in a @code{target} region is unspecified.
 
+Note that in GCC the function is marked pure, i.e. as returning always the
+same number.  When GCC was not configured to support offloading, it is replaced
+by zero; compile with @option{-fno-builtin-omp_get_num_devices} if a run-time
+function is desired.
+
 @item @emph{C/C++}:
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{int omp_get_num_devices(void);}
@@ -1812,6 +1817,9 @@ The effect of running this routine in a @code{target} region is unspecified.
 @item @emph{Interface}: @tab @code{integer function omp_get_num_devices()}
 @end multitable
 
+@item @emph{See also}:
+@ref{omp_get_initial_device}
+
 @item @emph{Reference}:
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 3.2.31.
 @end table
@@ -1950,6 +1958,12 @@ the value of @code{omp_initial_device}.
 
 The effect of running this routine in a @code{target} region is unspecified.
 
+Note that GCC inlines this function unless you compile with
+@option{-fno-builtin-omp_get_initial_device}.  If GCC was not configured to
+support offloading, it expands to constant zero; in non-host code it expands
+to @code{omp_initial_device}; and otherwise it is replaced with a call to
+@code{omp_get_num_devices}.
+
 @item @emph{C/C++}
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{int omp_get_initial_device(void);}