diff options
author | Lars Knoll <lars.knoll@qt.io> | 2020-02-29 20:11:39 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2020-03-26 09:15:32 +0100 |
commit | f2f32b88b873b8b18cc1b9e1f66135e6d09b9286 (patch) | |
tree | c86df678a6dd71e132f39927b39d8fae90922180 /src/corelib/global/qsimd.cpp | |
parent | c0538d5ff39b086d40fed943729b26aa3d17b602 (diff) |
Move qsimd* from corelib/tools to corelib/global
It's not used in tools at all and fits a lot better in global.
Also fix the qsimd_x86* files to have a proper copyright
header.
Change-Id: Id3d8e7cfcd7769a1ca9f3d8cf6d357a31a99ba40
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/global/qsimd.cpp')
-rw-r--r-- | src/corelib/global/qsimd.cpp | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/src/corelib/global/qsimd.cpp b/src/corelib/global/qsimd.cpp new file mode 100644 index 0000000000..75c380ee8a --- /dev/null +++ b/src/corelib/global/qsimd.cpp @@ -0,0 +1,718 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsimd_p.h" +#include "qalgorithms.h" +#include <QByteArray> +#include <stdio.h> + +#ifdef Q_OS_LINUX +# include "../testlib/3rdparty/valgrind_p.h" +#endif + +#if defined(Q_OS_WIN) +# if !defined(Q_CC_GNU) +# include <intrin.h> +# endif +#elif defined(Q_OS_LINUX) && (defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_MIPS_32)) +#include "private/qcore_unix_p.h" + +// the kernel header definitions for HWCAP_* +// (the ones we need/may need anyway) + +// copied from <asm/hwcap.h> (ARM) +#define HWCAP_CRUNCH 1024 +#define HWCAP_THUMBEE 2048 +#define HWCAP_NEON 4096 +#define HWCAP_VFPv3 8192 +#define HWCAP_VFPv3D16 16384 + +// copied from <asm/hwcap.h> (ARM): +#define HWCAP2_CRC32 (1 << 4) + +// copied from <asm/hwcap.h> (Aarch64) +#define HWCAP_CRC32 (1 << 7) + +// copied from <linux/auxvec.h> +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_HWCAP2 26 /* extension of AT_HWCAP */ + +#elif defined(Q_CC_GHS) +#include <INTEGRITY_types.h> +#endif + +QT_BEGIN_NAMESPACE + +/* + * Use kdesdk/scripts/generate_string_table.pl to update the table below. Note + * we remove the terminating -1 that the script adds. + */ + +// begin generated +#if defined(Q_PROCESSOR_ARM) +/* Data: + neon + crc32 + */ +static const char features_string[] = + " neon\0" + " crc32\0" + "\0"; +static const int features_indices[] = { 0, 6 }; +#elif defined(Q_PROCESSOR_MIPS) +/* Data: + dsp + dspr2 +*/ +static const char features_string[] = + " dsp\0" + " dspr2\0" + "\0"; + +static const int features_indices[] = { + 0, 5 +}; +#elif defined(Q_PROCESSOR_X86) +# include "qsimd_x86.cpp" // generated by util/x86simdgen +#else +static const char features_string[] = ""; +static const int features_indices[] = { }; +#endif +// end generated + +#if defined (Q_OS_NACL) +static inline uint detectProcessorFeatures() +{ + return 0; +} +#elif defined(Q_PROCESSOR_ARM) +static inline quint64 detectProcessorFeatures() +{ + quint64 features = 0; + +#if defined(Q_OS_LINUX) +# if defined(Q_PROCESSOR_ARM_V8) && defined(Q_PROCESSOR_ARM_64) + features |= Q_UINT64_C(1) << CpuFeatureNEON; // NEON is always available on ARMv8 64bit. +# endif + int auxv = qt_safe_open("/proc/self/auxv", O_RDONLY); + if (auxv != -1) { + unsigned long vector[64]; + int nread; + while (features == 0) { + nread = qt_safe_read(auxv, (char *)vector, sizeof vector); + if (nread <= 0) { + // EOF or error + break; + } + + int max = nread / (sizeof vector[0]); + for (int i = 0; i < max; i += 2) { + if (vector[i] == AT_HWCAP) { +# if defined(Q_PROCESSOR_ARM_V8) && defined(Q_PROCESSOR_ARM_64) + // For Aarch64: + if (vector[i+1] & HWCAP_CRC32) + features |= Q_UINT64_C(1) << CpuFeatureCRC32; +# endif + // Aarch32, or ARMv7 or before: + if (vector[i+1] & HWCAP_NEON) + features |= Q_UINT64_C(1) << CpuFeatureNEON; + } +# if defined(Q_PROCESSOR_ARM_32) + // For Aarch32: + if (vector[i] == AT_HWCAP2) { + if (vector[i+1] & HWCAP2_CRC32) + features |= Q_UINT64_C(1) << CpuFeatureCRC32; + } +# endif + } + } + + qt_safe_close(auxv); + return features; + } + // fall back if /proc/self/auxv wasn't found +#endif + +#if defined(__ARM_NEON__) + features |= Q_UINT64_C(1) << CpuFeatureNEON; +#endif +#if defined(__ARM_FEATURE_CRC32) + features |= Q_UINT64_C(1) << CpuFeatureCRC32; +#endif + + return features; +} + +#elif defined(Q_PROCESSOR_X86) + +#ifdef Q_PROCESSOR_X86_32 +# define PICreg "%%ebx" +#else +# define PICreg "%%rbx" +#endif + +static bool checkRdrndWorks() noexcept; + +static int maxBasicCpuidSupported() +{ +#if defined(Q_CC_EMSCRIPTEN) + return 6; // All features supported by Emscripten +#elif defined(Q_CC_GNU) + qregisterint tmp1; + +# if Q_PROCESSOR_X86 < 5 + // check if the CPUID instruction is supported + long cpuid_supported; + asm ("pushf\n" + "pop %0\n" + "mov %0, %1\n" + "xor $0x00200000, %0\n" + "push %0\n" + "popf\n" + "pushf\n" + "pop %0\n" + "xor %1, %0\n" // %eax is now 0 if CPUID is not supported + : "=a" (cpuid_supported), "=r" (tmp1) + ); + if (!cpuid_supported) + return 0; +# endif + + int result; + asm ("xchg " PICreg", %1\n" + "cpuid\n" + "xchg " PICreg", %1\n" + : "=&a" (result), "=&r" (tmp1) + : "0" (0) + : "ecx", "edx"); + return result; +#elif defined(Q_OS_WIN) + // Use the __cpuid function; if the CPUID instruction isn't supported, it will return 0 + int info[4]; + __cpuid(info, 0); + return info[0]; +#elif defined(Q_CC_GHS) + unsigned int info[4]; + __CPUID(0, info); + return info[0]; +#else + return 0; +#endif +} + +static void cpuidFeatures01(uint &ecx, uint &edx) +{ +#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN) + qregisterint tmp1; + asm ("xchg " PICreg", %2\n" + "cpuid\n" + "xchg " PICreg", %2\n" + : "=&c" (ecx), "=&d" (edx), "=&r" (tmp1) + : "a" (1)); +#elif defined(Q_OS_WIN) + int info[4]; + __cpuid(info, 1); + ecx = info[2]; + edx = info[3]; +#elif defined(Q_CC_GHS) + unsigned int info[4]; + __CPUID(1, info); + ecx = info[2]; + edx = info[3]; +#else + Q_UNUSED(ecx); + Q_UNUSED(edx); +#endif +} + +#ifdef Q_OS_WIN +inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int));} +#endif + +static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx) +{ +#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN) + qregisteruint rbx; // in case it's 64-bit + qregisteruint rcx = 0; + qregisteruint rdx = 0; + asm ("xchg " PICreg", %0\n" + "cpuid\n" + "xchg " PICreg", %0\n" + : "=&r" (rbx), "+&c" (rcx), "+&d" (rdx) + : "a" (7)); + ebx = rbx; + ecx = rcx; + edx = rdx; +#elif defined(Q_OS_WIN) + int info[4]; + __cpuidex(info, 7, 0); + ebx = info[1]; + ecx = info[2]; + edx = info[3]; +#elif defined(Q_CC_GHS) + unsigned int info[4]; + __CPUIDEX(7, 0, info); + ebx = info[1]; + ecx = info[2]; + edx = info[3]; +#else + Q_UNUSED(ebx); + Q_UNUSED(ecx); + Q_UNUSED(edx); +#endif +} + +#if defined(Q_OS_WIN) && !(defined(Q_CC_GNU) || defined(Q_CC_GHS)) +// fallback overload in case this intrinsic does not exist: unsigned __int64 _xgetbv(unsigned int); +inline quint64 _xgetbv(__int64) { return 0; } +#endif +static void xgetbv(uint in, uint &eax, uint &edx) +{ +#if (defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)) || defined(Q_CC_GHS) + asm (".byte 0x0F, 0x01, 0xD0" // xgetbv instruction + : "=a" (eax), "=d" (edx) + : "c" (in)); +#elif defined(Q_OS_WIN) + quint64 result = _xgetbv(in); + eax = result; + edx = result >> 32; +#else + Q_UNUSED(in); + Q_UNUSED(eax); + Q_UNUSED(edx); +#endif +} + +static quint64 detectProcessorFeatures() +{ + // Flags from the CR0 / XCR0 state register + enum XCR0Flags { + X87 = 1 << 0, + XMM0_15 = 1 << 1, + YMM0_15Hi128 = 1 << 2, + BNDRegs = 1 << 3, + BNDCSR = 1 << 4, + OpMask = 1 << 5, + ZMM0_15Hi256 = 1 << 6, + ZMM16_31 = 1 << 7, + + SSEState = XMM0_15, + AVXState = XMM0_15 | YMM0_15Hi128, + AVX512State = AVXState | OpMask | ZMM0_15Hi256 | ZMM16_31 + }; + static const quint64 AllAVX2 = CpuFeatureAVX2 | AllAVX512; + static const quint64 AllAVX = CpuFeatureAVX | AllAVX2; + + quint64 features = 0; + int cpuidLevel = maxBasicCpuidSupported(); +#if Q_PROCESSOR_X86 < 5 + if (cpuidLevel < 1) + return 0; +#else + Q_ASSERT(cpuidLevel >= 1); +#endif + + uint results[X86CpuidMaxLeaf] = {}; + cpuidFeatures01(results[Leaf1ECX], results[Leaf1EDX]); + if (cpuidLevel >= 7) + cpuidFeatures07_00(results[Leaf7_0EBX], results[Leaf7_0ECX], results[Leaf7_0EDX]); + + // populate our feature list + for (uint i = 0; i < sizeof(x86_locators) / sizeof(x86_locators[0]); ++i) { + uint word = x86_locators[i] / 32; + uint bit = 1U << (x86_locators[i] % 32); + quint64 feature = Q_UINT64_C(1) << (i + 1); + if (results[word] & bit) + features |= feature; + } + + // now check the AVX state + uint xgetbvA = 0, xgetbvD = 0; + if (results[Leaf1ECX] & (1u << 27)) { + // XGETBV enabled + xgetbv(0, xgetbvA, xgetbvD); + } + + if ((xgetbvA & AVXState) != AVXState) { + // support for YMM registers is disabled, disable all AVX + features &= ~AllAVX; + } else if ((xgetbvA & AVX512State) != AVX512State) { + // support for ZMM registers or mask registers is disabled, disable all AVX512 + features &= ~AllAVX512; + } + + if (features & CpuFeatureRDRND && !checkRdrndWorks()) + features &= ~(CpuFeatureRDRND | CpuFeatureRDSEED); + + return features; +} + +#elif defined(Q_PROCESSOR_MIPS_32) + +#if defined(Q_OS_LINUX) +// +// Do not use QByteArray: it could use SIMD instructions itself at +// some point, thus creating a recursive dependency. Instead, use a +// QSimpleBuffer, which has the bare minimum needed to use memory +// dynamically and read lines from /proc/cpuinfo of arbitrary sizes. +// +struct QSimpleBuffer { + static const int chunk_size = 256; + char *data; + unsigned alloc; + unsigned size; + + QSimpleBuffer(): data(0), alloc(0), size(0) {} + ~QSimpleBuffer() { ::free(data); } + + void resize(unsigned newsize) { + if (newsize > alloc) { + unsigned newalloc = chunk_size * ((newsize / chunk_size) + 1); + if (newalloc < newsize) newalloc = newsize; + if (newalloc != alloc) { + data = static_cast<char*>(::realloc(data, newalloc)); + alloc = newalloc; + } + } + size = newsize; + } + void append(const QSimpleBuffer &other, unsigned appendsize) { + unsigned oldsize = size; + resize(oldsize + appendsize); + ::memcpy(data + oldsize, other.data, appendsize); + } + void popleft(unsigned amount) { + if (amount >= size) return resize(0); + size -= amount; + ::memmove(data, data + amount, size); + } + char* cString() { + if (!alloc) resize(1); + return (data[size] = '\0', data); + } +}; + +// +// Uses a scratch "buffer" (which must be used for all reads done in the +// same file descriptor) to read chunks of data from a file, to read +// one line at a time. Lines include the trailing newline character ('\n'). +// On EOF, line.size is zero. +// +static void bufReadLine(int fd, QSimpleBuffer &line, QSimpleBuffer &buffer) +{ + for (;;) { + char *newline = static_cast<char*>(::memchr(buffer.data, '\n', buffer.size)); + if (newline) { + unsigned piece_size = newline - buffer.data + 1; + line.append(buffer, piece_size); + buffer.popleft(piece_size); + line.resize(line.size - 1); + return; + } + if (buffer.size + QSimpleBuffer::chunk_size > buffer.alloc) { + int oldsize = buffer.size; + buffer.resize(buffer.size + QSimpleBuffer::chunk_size); + buffer.size = oldsize; + } + ssize_t read_bytes = ::qt_safe_read(fd, buffer.data + buffer.size, QSimpleBuffer::chunk_size); + if (read_bytes > 0) buffer.size += read_bytes; + else return; + } +} + +// +// Checks if any line with a given prefix from /proc/cpuinfo contains +// a certain string, surrounded by spaces. +// +static bool procCpuinfoContains(const char *prefix, const char *string) +{ + int cpuinfo_fd = ::qt_safe_open("/proc/cpuinfo", O_RDONLY); + if (cpuinfo_fd == -1) + return false; + + unsigned string_len = ::strlen(string); + unsigned prefix_len = ::strlen(prefix); + QSimpleBuffer line, buffer; + bool present = false; + do { + line.resize(0); + bufReadLine(cpuinfo_fd, line, buffer); + char *colon = static_cast<char*>(::memchr(line.data, ':', line.size)); + if (colon && line.size > prefix_len + string_len) { + if (!::strncmp(prefix, line.data, prefix_len)) { + // prefix matches, next character must be ':' or space + if (line.data[prefix_len] == ':' || ::isspace(line.data[prefix_len])) { + // Does it contain the string? + char *found = ::strstr(line.cString(), string); + if (found && ::isspace(found[-1]) && + (::isspace(found[string_len]) || found[string_len] == '\0')) { + present = true; + break; + } + } + } + } + } while (line.size); + + ::qt_safe_close(cpuinfo_fd); + return present; +} +#endif + +static inline quint64 detectProcessorFeatures() +{ + // NOTE: MIPS 74K cores are the only ones supporting DSPr2. + quint64 flags = 0; + +#if defined __mips_dsp + flags |= Q_UINT64_C(1) << CpuFeatureDSP; +# if defined __mips_dsp_rev && __mips_dsp_rev >= 2 + flags |= Q_UINT64_C(1) << CpuFeatureDSPR2; +# elif defined(Q_OS_LINUX) + if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf")) + flags |= Q_UINT64_C(1) << CpuFeatureDSPR2; +# endif +#elif defined(Q_OS_LINUX) + if (procCpuinfoContains("ASEs implemented", "dsp")) { + flags |= Q_UINT64_C(1) << CpuFeatureDSP; + if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf")) + flags |= Q_UINT64_C(1) << CpuFeatureDSPR2; + } +#endif + + return flags; +} + +#else +static inline uint detectProcessorFeatures() +{ + return 0; +} +#endif + +static const int features_count = (sizeof features_indices) / (sizeof features_indices[0]); + +// record what CPU features were enabled by default in this Qt build +static const quint64 minFeature = qCompilerCpuFeatures; + +#ifdef Q_ATOMIC_INT64_IS_SUPPORTED +Q_CORE_EXPORT QBasicAtomicInteger<quint64> qt_cpu_features[1] = { Q_BASIC_ATOMIC_INITIALIZER(0) }; +#else +Q_CORE_EXPORT QBasicAtomicInteger<unsigned> qt_cpu_features[2] = { Q_BASIC_ATOMIC_INITIALIZER(0), Q_BASIC_ATOMIC_INITIALIZER(0) }; +#endif + +quint64 qDetectCpuFeatures() +{ + quint64 f = detectProcessorFeatures(); + QByteArray disable = qgetenv("QT_NO_CPU_FEATURE"); + if (!disable.isEmpty()) { + disable.prepend(' '); + for (int i = 0; i < features_count; ++i) { + if (disable.contains(features_string + features_indices[i])) + f &= ~(Q_UINT64_C(1) << i); + } + } + +#ifdef RUNNING_ON_VALGRIND + bool runningOnValgrind = RUNNING_ON_VALGRIND; +#else + bool runningOnValgrind = false; +#endif + if (Q_UNLIKELY(!runningOnValgrind && minFeature != 0 && (f & minFeature) != minFeature)) { + quint64 missing = minFeature & ~f; + fprintf(stderr, "Incompatible processor. This Qt build requires the following features:\n "); + for (int i = 0; i < features_count; ++i) { + if (missing & (Q_UINT64_C(1) << i)) + fprintf(stderr, "%s", features_string + features_indices[i]); + } + fprintf(stderr, "\n"); + fflush(stderr); + qFatal("Aborted. Incompatible processor: missing feature 0x%llx -%s.", missing, + features_string + features_indices[qCountTrailingZeroBits(missing)]); + } + + qt_cpu_features[0].storeRelaxed(f | quint32(QSimdInitialized)); +#ifndef Q_ATOMIC_INT64_IS_SUPPORTED + qt_cpu_features[1].storeRelaxed(f >> 32); +#endif + return f; +} + +void qDumpCPUFeatures() +{ + quint64 features = qCpuFeatures() & ~quint64(QSimdInitialized); + printf("Processor features: "); + for (int i = 0; i < features_count; ++i) { + if (features & (Q_UINT64_C(1) << i)) + printf("%s%s", features_string + features_indices[i], + minFeature & (Q_UINT64_C(1) << i) ? "[required]" : ""); + } + if ((features = (qCompilerCpuFeatures & ~features))) { + printf("\n!!!!!!!!!!!!!!!!!!!!\n!!! Missing required features:"); + for (int i = 0; i < features_count; ++i) { + if (features & (Q_UINT64_C(1) << i)) + printf("%s", features_string + features_indices[i]); + } + printf("\n!!! Applications will likely crash with \"Invalid Instruction\"\n!!!!!!!!!!!!!!!!!!!!"); + } + puts(""); +} + +#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND) + +# ifdef Q_PROCESSOR_X86_64 +# define _rdrandXX_step _rdrand64_step +# define _rdseedXX_step _rdseed64_step +# else +# define _rdrandXX_step _rdrand32_step +# define _rdseedXX_step _rdseed32_step +# endif + +# if QT_COMPILER_SUPPORTS_HERE(RDSEED) +static QT_FUNCTION_TARGET(RDSEED) unsigned *qt_random_rdseed(unsigned *ptr, unsigned *end) noexcept +{ + // Unlike for the RDRAND code below, the Intel whitepaper describing the + // use of the RDSEED instruction indicates we should not retry in a loop. + // If the independent bit generator used by RDSEED is out of entropy, it + // may take time to replenish. + // https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide + while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) { + if (_rdseedXX_step(reinterpret_cast<qregisteruint *>(ptr)) == 0) + goto out; + ptr += sizeof(qregisteruint)/sizeof(*ptr); + } + + if (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) { + if (_rdseed32_step(ptr) == 0) + goto out; + ++ptr; + } + +out: + return ptr; +} +# else +static unsigned *qt_random_rdseed(unsigned *ptr, unsigned *) +{ + return ptr; +} +# endif + +static QT_FUNCTION_TARGET(RDRND) unsigned *qt_random_rdrnd(unsigned *ptr, unsigned *end) noexcept +{ + int retries = 10; + while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) { + if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr))) + ptr += sizeof(qregisteruint)/sizeof(*ptr); + else if (--retries == 0) + goto out; + } + + while (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) { + bool ok = _rdrand32_step(ptr); + if (!ok && --retries) + continue; + if (ok) + ++ptr; + break; + } + +out: + return ptr; +} + +static QT_FUNCTION_TARGET(RDRND) Q_DECL_COLD_FUNCTION bool checkRdrndWorks() noexcept +{ + /* + * Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a + * failing random generation instruction, which always returns + * 0xffffffff, even when generation was "successful". + * + * This code checks if hardware random generator generates four consecutive + * equal numbers. If it does, then we probably have a failing one and + * should disable it completely. + * + * https://bugreports.qt.io/browse/QTBUG-69423 + */ + constexpr qsizetype TestBufferSize = 4; + unsigned testBuffer[TestBufferSize] = {}; + + unsigned *end = qt_random_rdrnd(testBuffer, testBuffer + TestBufferSize); + if (end < testBuffer + 3) { + // Random generation didn't produce enough data for us to make a + // determination whether it's working or not. Assume it isn't, but + // don't print a warning. + return false; + } + + // Check the results for equality + if (testBuffer[0] == testBuffer[1] + && testBuffer[0] == testBuffer[2] + && (end < testBuffer + TestBufferSize || testBuffer[0] == testBuffer[3])) { + fprintf(stderr, "WARNING: CPU random generator seem to be failing, " + "disabling hardware random number generation\n" + "WARNING: RDRND generated:"); + for (unsigned *ptr = testBuffer; ptr < end; ++ptr) + fprintf(stderr, " 0x%x", *ptr); + fprintf(stderr, "\n"); + return false; + } + + // We're good + return true; +} + +QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) noexcept +{ + unsigned *ptr = reinterpret_cast<unsigned *>(buffer); + unsigned *end = ptr + count; + + if (qCpuHasFeature(RDSEED)) + ptr = qt_random_rdseed(ptr, end); + + // fill the buffer with RDRND if RDSEED didn't + ptr = qt_random_rdrnd(ptr, end); + return ptr - reinterpret_cast<unsigned *>(buffer); +} +#elif defined(Q_PROCESSOR_X86) && !defined(Q_OS_NACL) && !defined(Q_PROCESSOR_ARM) +static bool checkRdrndWorks() noexcept { return false; } +#endif // Q_PROCESSOR_X86 && RDRND + +QT_END_NAMESPACE |