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 similarly 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.
This commit is contained in:
Jonathan Wakely 2021-10-20 09:25:24 +01:00
parent bdb9d47218
commit 5997e6a6ec
3 changed files with 57 additions and 6 deletions

View File

@ -37,6 +37,8 @@
# ifdef _GLIBCXX_X86_RDSEED # ifdef _GLIBCXX_X86_RDSEED
# define USE_RDSEED 1 # define USE_RDSEED 1
# endif # endif
#elif defined __powerpc__ && defined __BUILTIN_CPU_SUPPORTS__
# define USE_DARN 1
#endif #endif
#include <cerrno> #include <cerrno>
@ -69,7 +71,7 @@
#if defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM #if defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM
// The OS provides a source of randomness we can use. // The OS provides a source of randomness we can use.
# pragma GCC poison _M_mt # 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. // Hardware instructions might be available, but use cpuid checks at runtime.
# pragma GCC poison _M_mt # pragma GCC poison _M_mt
// If the runtime cpuid checks fail we'll use a linear congruential engine. // If the runtime cpuid checks fail we'll use a linear congruential engine.
@ -135,6 +137,24 @@ namespace std _GLIBCXX_VISIBILITY(default)
#endif #endif
#endif #endif
#ifdef USE_DARN
unsigned int
__attribute__((target("cpu=power9")))
__ppc_darn(void*)
{
const uint64_t failed = -1;
unsigned int retries = 10;
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 #ifdef _GLIBCXX_USE_CRT_RAND_S
unsigned int unsigned int
__winxp_rand_s(void*) __winxp_rand_s(void*)
@ -193,11 +213,16 @@ namespace std _GLIBCXX_VISIBILITY(default)
} }
#endif #endif
enum Which { enum Which : unsigned {
rand_s = 1, rdseed = 2, rdrand = 4, device_file = 8, prng = 16, device_file = 1, prng = 2, rand_s = 4,
rdseed = 64, rdrand = 128, darn = 256,
any = 0xffff any = 0xffff
}; };
constexpr Which
operator|(Which l, Which r) noexcept
{ return Which(unsigned(l) | unsigned(r)); }
inline Which inline Which
which_source(random_device::result_type (*func [[maybe_unused]])(void*), which_source(random_device::result_type (*func [[maybe_unused]])(void*),
void* file [[maybe_unused]]) void* file [[maybe_unused]])
@ -221,6 +246,11 @@ namespace std _GLIBCXX_VISIBILITY(default)
return rdrand; return rdrand;
#endif #endif
#ifdef USE_DARN
if (func == &__ppc_darn)
return darn;
#endif
#ifdef _GLIBCXX_USE_DEV_RANDOM #ifdef _GLIBCXX_USE_DEV_RANDOM
if (file != nullptr) if (file != nullptr)
return device_file; return device_file;
@ -269,6 +299,14 @@ namespace std _GLIBCXX_VISIBILITY(default)
else if (token == "rdrand" || token == "rdrnd") else if (token == "rdrand" || token == "rdrnd")
which = rdrand; which = rdrand;
#endif // USE_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 #ifdef _GLIBCXX_USE_CRT_RAND_S
else if (token == "rand_s") else if (token == "rand_s")
which = rand_s; which = rand_s;
@ -346,6 +384,17 @@ namespace std _GLIBCXX_VISIBILITY(default)
} }
#endif // USE_RDRAND #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 #ifdef _GLIBCXX_USE_DEV_RANDOM
if (which & device_file) if (which & device_file)
{ {
@ -497,6 +546,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
{ {
case rdrand: case rdrand:
case rdseed: case rdseed:
case darn:
return (double) max; return (double) max;
case rand_s: case rand_s:
case prng: case prng:

View File

@ -51,8 +51,9 @@ test03()
{ {
// At least one of these tokens should be valid. // At least one of these tokens should be valid.
const std::string tokens[] = { const std::string tokens[] = {
"rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19937", "rdseed", "rdrand", "darn",
"prng" "rand_s", "/dev/urandom", "/dev/random",
"mt19937", "prng"
}; };
int count = 0; int count = 0;
for (const std::string& token : tokens) for (const std::string& token : tokens)

View File

@ -22,7 +22,7 @@ test01()
VERIFY( entropy <= max ); VERIFY( entropy <= max );
} }
for (auto token : { "rdrand", "rdseed" }) for (auto token : { "rdrand", "rdseed", "darn", "hw" })
if (__gnu_test::random_device_available(token)) if (__gnu_test::random_device_available(token))
{ {
const double entropy = std::random_device(token).entropy(); const double entropy = std::random_device(token).entropy();