commit 77816264712923481db37a29d1638faa1d99aadc 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: * src/c++11/random.cc [__powerpc__] (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/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc index 4b64bde00ea..b0d88374d59 100644 --- a/libstdc++-v3/src/c++11/random.cc +++ b/libstdc++-v3/src/c++11/random.cc @@ -37,6 +37,8 @@ # ifdef _GLIBCXX_X86_RDSEED # define USE_RDSEED 1 # endif +#elif defined __powerpc__ && defined __BUILTIN_CPU_SUPPORTS__ +# define USE_DARN 1 #endif #include @@ -69,7 +71,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 +137,24 @@ namespace std _GLIBCXX_VISIBILITY(default) #endif #endif +#ifdef USE_DARN + unsigned int + __attribute__((target("cpu=power9"))) + __ppc_darn(void*) + { + const uint64_t failed = -1; + unsigned int retries = 100; + uint64_t val = __builtin_darn(); + while (val == failed) [[__unlikely__]] + { + if (--retries == 0) + std::__throw_runtime_error(__N("random_device: darn failed")); + val = __builtin_darn(); + } + return (uint32_t)val; + } +#endif + #ifdef _GLIBCXX_USE_CRT_RAND_S unsigned int __winxp_rand_s(void*) @@ -193,11 +213,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 +246,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 +299,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 +384,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 +546,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();