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);}