On Wed, 3 Nov 2021 at 15:01, Jonathan Wakely wrote: > Any feedback from POWER maintainers about this? I'd like to push it soon > if there's nothing wrong with it. >
With the updated patch attached again this time ... > On Wed, 20 Oct 2021 at 14:00, Jonathan Wakely via Libstdc++ < > libstd...@gcc.gnu.org> wrote: > >> On 20/10/21 10:12 +0100, Jonathan Wakely wrote: >> >On 19/10/21 17:47 +0100, Jonathan Wakely wrote: >> >>The ISA-3.0 instruction set includes DARN ("deliver a random number") >> >>which can be used similar to the existing support for RDRAND and RDSEED. >> >> >> >>libstdc++-v3/ChangeLog: >> >> >> >> * src/c++11/random.cc (USE_DARN): Define. >> >> (__ppc_darn): New function to use POWER9 DARN instruction. >> >> (Which): Add 'darn' enumerator. >> >> (which_source): Check for __ppc_darn. >> >> (random_device::_M_init): Support "darn" and "hw" tokens. >> >> (random_device::_M_getentropy): Add darn to switch. >> >> * testsuite/26_numerics/random/random_device/cons/token.cc: >> >> Check "darn" token. >> >> * testsuite/26_numerics/random/random_device/entropy.cc: >> >> Likewise. >> >> >> >>Tested powerpc64le-linux (power8 and power9) and x86_64-linux. >> >> >> >>The new "darn" (power-specific) and "hw" (x86 and power) >> >>strings should be documented, but I'll do that if this gets committed. >> >> >> >>Most of this patch is just "more of the same", similar to the existing >> >>code for RDRAND and RDSEED on x86, but the parts of the patch I'd like >> >>more eyes on are: >> >> >> >> >> >>+#elif defined __powerpc__ && defined __BUILTIN_CPU_SUPPORTS__ >> >>+# define USE_DARN 1 >> >>#endif >> > >> >This means DARN can only be used when __builtin_cpu_supports is >> >available, which means glibc 2.23 ... is that acceptable? It means >> >RHEL 7 wouldn't be able to use DARN, but RHEL 8 would. >> > >> >There certainly are POWER9 machines running RHEL 7 and similar >> >vintages (the GCC compile farm has one) so if there's another way to >> >check for ISA 3.0 then I could use that. >> > >> >If __POWER9_VECTOR__ is defined when building libstdc++, presumably >> >that means the whole library can only be run on POWER9 hardware. So >> >would that mean we don't need to check __builtin_cpu_supports("darn") >> >when __POWER9_VECTOR__ is defined? Or is it possible to build with >> >-mcpu=power8 -mpower9-vector and run it on h/w without the DARN >> >instruction? >> > >> >Also, I forgot to add a configure check that the assembler supports >> >darn, which is another prerequisite for using it here. >> > >> >>@@ -135,6 +137,15 @@ namespace std _GLIBCXX_VISIBILITY(default) >> >>#endif >> >>#endif >> >> >> >>+#ifdef USE_DARN >> >>+ unsigned int >> >>+ __attribute__((target("power9"))) >> > >> >Oops, that should be "cpu=power9". >> > >> >With that change it works on a POWER9 machine (9009-42A) with glibc >> >2.34 and binutils 2.35. >> > >> >> Here's the updated patch with a configure check for assembler support, >> and the target attribute fixed. >> >> This still requires Glibc 2.23 for __builtin_cpu_supports, which I'm >> assuming is acceptable. >> >> >>
commit a6f925407dd05c593b230da1627435adc53584f8 Author: Jonathan Wakely <jwak...@redhat.com> Date: Wed Oct 20 09:25:24 2021 libstdc++: Add support for POWER9 DARN instruction to std::random_device The ISA-3.0 instruction set includes DARN ("deliver a random number") which can be used similar to the existing support for RDRAND and RDSEED. libstdc++-v3/ChangeLog: * acinclude.m4 (GLIBCXX_CHECK_PPC_DARN): Check assembler. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac: Use GLIBCXX_CHECK_PPC_DARN. * src/c++11/random.cc [_GLIBCXX_PPC_DARN] (USE_DARN): Define. (__ppc_darn): New function to use POWER9 DARN instruction. (Which): Add 'darn' enumerator. (which_source): Check for __ppc_darn. (random_device::_M_init): Support "darn" and "hw" tokens. (random_device::_M_getentropy): Add darn to switch. * testsuite/26_numerics/random/random_device/cons/token.cc: Check "darn" token. * testsuite/26_numerics/random/random_device/entropy.cc: Likewise. diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 90ecc4a87a2..9ff9ceb20ac 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -4100,6 +4100,27 @@ AC_DEFUN([GLIBCXX_CHECK_X86_RDSEED], [ AC_MSG_RESULT($ac_cv_x86_rdseed) ]) +dnl +dnl Check whether darn is supported in the assembler. +AC_DEFUN([GLIBCXX_CHECK_PPC_DARN], [ + AC_MSG_CHECKING([for darn support in assembler]) + AC_CACHE_VAL(ac_cv_ppc_darn, [ + ac_cv_ppc_darn=no + case "$target" in + powerpc*-*-*) + AC_TRY_COMPILE(, [ + signed int x; + __asm__ __volatile__ (".machine power9; darn %0,0;": "=r" (x)); + ], [ac_cv_ppc_darn=yes], [ac_cv_ppc_darn=no]) + esac + ]) + if test $ac_cv_ppc_darn = yes; then + AC_DEFINE(_GLIBCXX_PPC_DARN, 1, + [ Defined if as can handle darn. ]) + fi + AC_MSG_RESULT($ac_cv_ppc_darn) +]) + dnl dnl Check whether get_nprocs is available in <sys/sysinfo.h>, and define _GLIBCXX_USE_GET_NPROCS. dnl diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac index 2d68b3672b9..1189c68c380 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -467,6 +467,8 @@ GCC_CHECK_ASSEMBLER_HWCAP GLIBCXX_CHECK_X86_RDRAND # Check if assembler supports rdseed opcode. GLIBCXX_CHECK_X86_RDSEED +# Check if assembler supports darn opcode. +GLIBCXX_CHECK_PPC_DARN # This depends on GLIBCXX_ENABLE_SYMVERS and GLIBCXX_IS_NATIVE. GLIBCXX_CONFIGURE_TESTSUITE diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc index 4b64bde00ea..213ad691837 100644 --- a/libstdc++-v3/src/c++11/random.cc +++ b/libstdc++-v3/src/c++11/random.cc @@ -37,6 +37,9 @@ # ifdef _GLIBCXX_X86_RDSEED # define USE_RDSEED 1 # endif +#elif defined __powerpc__ && defined __BUILTIN_CPU_SUPPORTS__ \ + && defined _GLIBCXX_PPC_DARN +# define USE_DARN 1 #endif #include <cerrno> @@ -69,7 +72,7 @@ #if defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM // The OS provides a source of randomness we can use. # pragma GCC poison _M_mt -#elif defined USE_RDRAND || defined USE_RDSEED +#elif defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN // Hardware instructions might be available, but use cpuid checks at runtime. # pragma GCC poison _M_mt // If the runtime cpuid checks fail we'll use a linear congruential engine. @@ -135,6 +138,15 @@ namespace std _GLIBCXX_VISIBILITY(default) #endif #endif +#ifdef USE_DARN + unsigned int + __attribute__((target("cpu=power9"))) + __ppc_darn(void*) + { + return __builtin_darn_32(); + } +#endif + #ifdef _GLIBCXX_USE_CRT_RAND_S unsigned int __winxp_rand_s(void*) @@ -193,11 +205,16 @@ namespace std _GLIBCXX_VISIBILITY(default) } #endif - enum Which { - rand_s = 1, rdseed = 2, rdrand = 4, device_file = 8, prng = 16, + enum Which : unsigned { + device_file = 1, prng = 2, rand_s = 4, + rdseed = 64, rdrand = 128, darn = 256, any = 0xffff }; + constexpr Which + operator|(Which l, Which r) noexcept + { return Which(unsigned(l) | unsigned(r)); } + inline Which which_source(random_device::result_type (*func [[maybe_unused]])(void*), void* file [[maybe_unused]]) @@ -221,6 +238,11 @@ namespace std _GLIBCXX_VISIBILITY(default) return rdrand; #endif +#ifdef USE_DARN + if (func == &__ppc_darn) + return darn; +#endif + #ifdef _GLIBCXX_USE_DEV_RANDOM if (file != nullptr) return device_file; @@ -269,6 +291,14 @@ namespace std _GLIBCXX_VISIBILITY(default) else if (token == "rdrand" || token == "rdrnd") which = rdrand; #endif // USE_RDRAND +#ifdef USE_DARN + else if (token == "darn") + which = darn; +#endif +#if defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN + else if (token == "hw" || token == "hardware") + which = rdrand | rdseed | darn; +#endif #ifdef _GLIBCXX_USE_CRT_RAND_S else if (token == "rand_s") which = rand_s; @@ -346,6 +376,17 @@ namespace std _GLIBCXX_VISIBILITY(default) } #endif // USE_RDRAND +#ifdef USE_DARN + if (which & darn) + { + if (__builtin_cpu_supports("darn")) + { + _M_func = &__ppc_darn; + return; + } + } +#endif // USE_DARN + #ifdef _GLIBCXX_USE_DEV_RANDOM if (which & device_file) { @@ -497,6 +538,7 @@ namespace std _GLIBCXX_VISIBILITY(default) { case rdrand: case rdseed: + case darn: return (double) max; case rand_s: case prng: diff --git a/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc b/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc index aeb7403e830..d6ac3a37c64 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc @@ -51,8 +51,9 @@ test03() { // At least one of these tokens should be valid. const std::string tokens[] = { - "rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19937", - "prng" + "rdseed", "rdrand", "darn", + "rand_s", "/dev/urandom", "/dev/random", + "mt19937", "prng" }; int count = 0; for (const std::string& token : tokens) diff --git a/libstdc++-v3/testsuite/26_numerics/random/random_device/entropy.cc b/libstdc++-v3/testsuite/26_numerics/random/random_device/entropy.cc index 9ef1538d2bb..6f3ebb1b38e 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/random_device/entropy.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/entropy.cc @@ -22,7 +22,7 @@ test01() VERIFY( entropy <= max ); } - for (auto token : { "rdrand", "rdseed" }) + for (auto token : { "rdrand", "rdseed", "darn", "hw" }) if (__gnu_test::random_device_available(token)) { const double entropy = std::random_device(token).entropy();