Message ID | YW72mu89f24vKPrl@redhat.com |
---|---|
State | New |
Headers | show |
Series | libstdc++: Add support for POWER9 DARN instruction to std::random_device | expand |
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. >+ __ppc_darn(void*) >+ { >+ return __builtin_darn_32(); >+ } >+#endif
diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc index 4b64bde00ea..96d59799c2c 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 <cerrno> @@ -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,15 @@ namespace std _GLIBCXX_VISIBILITY(default) #endif #endif +#ifdef USE_DARN + unsigned int + __attribute__((target("power9"))) + __ppc_darn(void*) + { + return __builtin_darn_32(); + } +#endif + #ifdef _GLIBCXX_USE_CRT_RAND_S unsigned int __winxp_rand_s(void*) @@ -193,11 +204,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 +237,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 +290,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 +375,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 +537,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();