commit a6f925407dd05c593b230da1627435adc53584f8 Author: Jonathan Wakely 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 , 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 @@ -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();