https://gcc.gnu.org/g:387209938d2c476a67966c6ddbdbf817626f24a2

commit r16-1262-g387209938d2c476a67966c6ddbdbf817626f24a2
Author: Tobias Burnus <tbur...@baylibre.com>
Date:   Fri Jun 6 16:22:06 2025 +0200

    OpenMP: Add omp_get_initial_device/omp_get_num_devices builtins
    
    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 <sloosem...@baylibre.com>

Diff:
---
 gcc/fortran/f95-lang.cc                            |  3 +-
 gcc/fortran/gfortran.h                             |  6 ++--
 gcc/fortran/options.cc                             |  4 +++
 gcc/fortran/trans-expr.cc                          | 10 ++++++
 gcc/gimple-fold.cc                                 | 40 ++++++++++++++++++++++
 gcc/omp-builtins.def                               |  9 +++--
 .../gomp/omp_get_num_devices_initial_device-2.c    | 29 ++++++++++++++++
 .../gomp/omp_get_num_devices_initial_device.c      | 32 +++++++++++++++++
 .../gomp/omp_get_num_devices_initial_device-2.f90  | 21 ++++++++++++
 .../gomp/omp_get_num_devices_initial_device.f90    | 24 +++++++++++++
 libgomp/libgomp.texi                               | 14 ++++++++
 11 files changed, 186 insertions(+), 6 deletions(-)

diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 1f09553142d6..bb4ce6d82882 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -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,
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index e461aa68470d..f73b5f9c23f4 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -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;
diff --git a/gcc/fortran/options.cc b/gcc/fortran/options.cc
index ddddc1c2833f..d3c9066630b7 100644
--- a/gcc/fortran/options.cc
+++ b/gcc/fortran/options.cc
@@ -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
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 74d4265f27d8..c8a207609e4b 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -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"))
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 6e32063f29e2..729080ad6e5b 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -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);
 
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index f73fb7b9dd88..db1ec9638419 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -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
index 000000000000..891f5cf23d20
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c
@@ -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
index 000000000000..4b17143c2829
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c
@@ -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
index 000000000000..18613d458b1b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device-2.f90
@@ -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
index 000000000000..5409f12f4648
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/omp_get_num_devices_initial_device.f90
@@ -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 } } 
}
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 7116fcda13fe..8374595bc823 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -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);}

Reply via email to