This came up when looking at some context selectors that use 'target_device',
but is largely unrelated to it. (target_device has its own special casing).

Namely, it makes omp_get_initial_device and omp_get_num_devices PURE,
which attributes don't permit for Fortran - based on the state that
the number of devices cannot change after startup. I am not sure whether
"LEAF" is true here (and elsewhere) as the check whether the devices
setup has been to run. [Q:] Is that's an issue?

Caveat: Using -std=c++XX will disable all OpenMP builtins, unless
-fbuiltin-... or -fnonansi-builtins. I wonder whether
[RFC 1] we should do something about this ...

[RFC 1]
- Does this patch make sense or not?
- Do the simplifications make sense or not?
- Is the documentation fine or overly complex?

Tobias
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/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    | 33 ++++++++++++++++++
 .../gomp/omp_get_num_devices_initial_device.c      | 36 +++++++++++++++++++
 .../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, 194 insertions(+), 6 deletions(-)

diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 1f09553142d..bb4ce6d8288 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 e461aa68470..f73b5f9c23f 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 ddddc1c2833..d3c9066630b 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 74d4265f27d..c8a207609e4 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 185f9db69d8..41e32677c9f 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -4097,6 +4097,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.  */
 
@@ -5341,6 +5375,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 f73fb7b9dd8..db1ec963841 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 00000000000..29e0da2b6c2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c
@@ -0,0 +1,33 @@
+/* { dg-do compile }  */
+
+/* The test suite cycles through -std=c++* which do not include
+   non-ansi builtins, contrary to -std=gnu++*.  */
+/* { dg-additional-options "-fnonansi-builtins" { target c++ } }  */
+/* { 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 00000000000..3b5dd639721
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c
@@ -0,0 +1,36 @@
+/* { dg-do compile }  */
+
+/* The test suite cycles through -std=c++* which do not include
+   non-ansi builtins, contrary to -std=gnu++*.  */
+/* { dg-additional-options "-fnonansi-builtins" { target c++ } }  */
+/* { 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 00000000000..18613d458b1
--- /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 00000000000..5409f12f464
--- /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 7116fcda13f..bd7cf7cb811 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 in GCC this function call is already folded to at compile time:
+To the value @code{omp_initial_device} in non-host code, to zero if GCC
+was not configured to support offloading, or otherwise to a call to
+@code{omp_get_num_devices}; compile with
+@option{-fno-builtin-omp_get_initial_device} if a run-time function is desired.
+
 @item @emph{C/C++}
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{int omp_get_initial_device(void);}
 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    | 33 ++++++++++++++++++
 .../gomp/omp_get_num_devices_initial_device.c      | 36 +++++++++++++++++++
 .../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, 194 insertions(+), 6 deletions(-)

diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 1f09553142d..bb4ce6d8288 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 e461aa68470..f73b5f9c23f 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 ddddc1c2833..d3c9066630b 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 74d4265f27d..c8a207609e4 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 185f9db69d8..41e32677c9f 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -4097,6 +4097,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.  */
 
@@ -5341,6 +5375,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 f73fb7b9dd8..db1ec963841 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 00000000000..29e0da2b6c2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device-2.c
@@ -0,0 +1,33 @@
+/* { dg-do compile }  */
+
+/* The test suite cycles through -std=c++* which do not include
+   non-ansi builtins, contrary to -std=gnu++*.  */
+/* { dg-additional-options "-fnonansi-builtins" { target c++ } }  */
+/* { 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 00000000000..3b5dd639721
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/omp_get_num_devices_initial_device.c
@@ -0,0 +1,36 @@
+/* { dg-do compile }  */
+
+/* The test suite cycles through -std=c++* which do not include
+   non-ansi builtins, contrary to -std=gnu++*.  */
+/* { dg-additional-options "-fnonansi-builtins" { target c++ } }  */
+/* { 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 00000000000..18613d458b1
--- /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 00000000000..5409f12f464
--- /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 7116fcda13f..bd7cf7cb811 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 in GCC this function call is already folded to at compile time:
+To the value @code{omp_initial_device} in non-host code, to zero if GCC
+was not configured to support offloading, or otherwise to a call to
+@code{omp_get_num_devices}; compile with
+@option{-fno-builtin-omp_get_initial_device} if a run-time function is desired.
+
 @item @emph{C/C++}
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{int omp_get_initial_device(void);}

Reply via email to