libstdc++: Add support for POWER9 DARN instruction to std::random_device
Commit Message
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
#include <cerrno>
and:
@@ -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
+
and:
@@ -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)
{
commit 5cfef2d435b5cb3b3e959e14e4b1edde8edea473
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Tue Oct 19 12:53:00 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 (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.
Comments
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
@@ -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:
@@ -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)
@@ -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();