123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861 | // Copyright (C) 2021 The Qt Company Ltd.// Copyright (C) 2022 Intel Corporation.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only// we need ICC to define the prototype for _rdseed64_step#define __INTEL_COMPILER_USE_INTRINSIC_PROTOTYPES#undef _FORTIFY_SOURCE// otherwise, the always_inline from stdio.h fail to inline#include"qsimd_p.h"#include"qalgorithms.h"#include <stdio.h>#include <string.h>#if defined(QT_NO_DEBUG) && !defined(NDEBUG)# define NDEBUG#endif#include <assert.h>#ifdef Q_OS_LINUX# include"../testlib/3rdparty/valgrind/valgrind_p.h"#endif#define QT_FUNCTION_TARGET_BASELINE#if defined(Q_OS_WIN)# if !defined(Q_CC_GNU)# include <intrin.h># endif# if defined(Q_PROCESSOR_ARM_64)# include <qt_windows.h># include <processthreadsapi.h># endif#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_MIPS_32)# include"private/qcore_unix_p.h"#elif QT_CONFIG(getauxval) && (defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_LOONGARCH))# include <sys/auxv.h>// the kernel header definitions for HWCAP_*// (the ones we need/may need anyway)// copied from <asm/hwcap.h> (ARM)#define HWCAP_NEON 4096// copied from <asm/hwcap.h> (ARM):#define HWCAP2_AES (1 << 0)#define HWCAP2_CRC32 (1 << 4)// copied from <asm/hwcap.h> (Aarch64)#define HWCAP_AES (1 << 3)#define HWCAP_CRC32 (1 << 7)#define HWCAP_SVE (1 << 22)// 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>#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM)# include <sys/sysctl.h>#endif QT_BEGIN_NAMESPACE template<typename T, uint N> QT_FUNCTION_TARGET_BASELINE uint arraysize(T(&)[N]){// Same as std::size, but with QT_FUNCTION_TARGET_BASELIE,// otherwise some versions of GCC fail to compile.return N;}#if defined(Q_PROCESSOR_ARM)/* Data: neon crc32 aes sve */static const char features_string[] ="\0"" neon\0"" crc32\0"" aes\0"" sve\0";static const int features_indices[] = {0,1,7,14,19};#elif defined(Q_PROCESSOR_MIPS)/* Data: dsp dspr2*/static const char features_string[] ="\0"" dsp\0"" dspr2\0";static const int features_indices[] = {0,1,6};#elif defined(Q_PROCESSOR_LOONGARCH)/* Data: lsx lasx*/static const char features_string[] ="\0"" lsx\0"" lasx\0";static const int features_indices[] = {0,1,6};#elif defined(Q_PROCESSOR_X86)# include"qsimd_x86.cpp"// generated by util/x86simdgen#elsestatic const char features_string[] ="";static const int features_indices[] = {0};#endif// end generated#if defined(Q_PROCESSOR_ARM)staticinline quint64 detectProcessorFeatures(){ quint64 features =0;#if QT_CONFIG(getauxval)unsigned long auxvHwCap =getauxval(AT_HWCAP);if(auxvHwCap !=0) {# if defined(Q_PROCESSOR_ARM_64)// For Aarch64: features |= CpuFeatureNEON;// NEON is always availableif(auxvHwCap & HWCAP_CRC32) features |= CpuFeatureCRC32;if(auxvHwCap & HWCAP_AES) features |= CpuFeatureAES;if(auxvHwCap & HWCAP_SVE) features |= CpuFeatureSVE;# else// For ARM32:if(auxvHwCap & HWCAP_NEON) features |= CpuFeatureNEON; auxvHwCap =getauxval(AT_HWCAP2);if(auxvHwCap & HWCAP2_CRC32) features |= CpuFeatureCRC32;if(auxvHwCap & HWCAP2_AES) features |= CpuFeatureAES;# endifreturn features;}// fall back to compile-time flags if getauxval failed#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM)unsigned feature;size_t len =sizeof(feature);Q_UNUSED(len);#if defined(__ARM_NEON) features |= CpuFeatureNEON;#else#error"Misconfiguration, NEON should always be enabled on Apple hardware"#endif#if defined(__ARM_FEATURE_CRC32) features |= CpuFeatureCRC32;#elif defined(Q_OS_MACOS)#error"Misconfiguration, CRC32 should always be enabled on Apple desktop hardware"#elseif(sysctlbyname("hw.optional.armv8_crc32", &feature, &len,nullptr,0) ==0) features |= feature ? CpuFeatureCRC32 :0;#endif#if defined(__ARM_FEATURE_CRYPTO) features |= CpuFeatureAES;#elif defined(Q_OS_MACOS)#error"Misconfiguration, CRYPTO/AES should always be enabled on Apple desktop hardware"#elseif(sysctlbyname("hw.optional.arm.FEAT_AES", &feature, &len,nullptr,0) ==0) features |= feature ? CpuFeatureAES :0;#endif#if defined(__ARM_FEATURE_SVE) features |= CpuFeatureSVE;#elseif(sysctlbyname("hw.optional.arm.FEAT_SVE", &feature, &len,nullptr,0) ==0) features |= feature ? CpuFeatureSVE :0;#endifreturn features;#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM_64) features |= CpuFeatureNEON;if(IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) !=0) features |= CpuFeatureCRC32;if(IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) !=0) features |= CpuFeatureAES;return features;#endif#if defined(__ARM_NEON__) features |= CpuFeatureNEON;#endif#if defined(__ARM_FEATURE_CRC32) features |= CpuFeatureCRC32;#endif#if defined(__ARM_FEATURE_CRYPTO) features |= CpuFeatureAES;#endif#if defined(__ARM_FEATURE_SVE) features |= CpuFeatureSVE;#endifreturn features;}#elif defined(Q_PROCESSOR_LOONGARCH)staticinline quint64 detectProcessorFeatures(){ quint64 features =0;# if QT_CONFIG(getauxval) quint64 hwcap =getauxval(AT_HWCAP);if(hwcap & HWCAP_LOONGARCH_LSX) features |= CpuFeatureLSX;if(hwcap & HWCAP_LOONGARCH_LASX) features |= CpuFeatureLASX;# elseenum LoongArchFeatures { LOONGARCH_CFG2 =0x2, LOONGARCH_CFG2_LSX = (1<<6), LOONGARCH_CFG2_LASX = (1<<7)}; quint64 reg =0; __asm__ volatile("cpucfg %0, %1\n\t":"+&r"(reg):"r"(LOONGARCH_CFG2));if(reg & LOONGARCH_CFG2_LSX) features |= CpuFeatureLSX;if(reg & LOONGARCH_CFG2_LASX) features |= CpuFeatureLASX;# endifreturn features;}#elif defined(Q_PROCESSOR_X86)#ifdef Q_PROCESSOR_X86_32# define PICreg"%%ebx"#else# define PICreg"%%rbx"#endif#ifdef __SSE2_MATH__# define X86_BASELINE"no-sse3"#else# define X86_BASELINE"no-sse"#endif#if defined(Q_CC_GNU) || defined(Q_CC_CLANG)// lower the target for functions in this file# undef QT_FUNCTION_TARGET_BASELINE# define QT_FUNCTION_TARGET_BASELINE __attribute__((target(X86_BASELINE)))# define QT_FUNCTION_TARGET_STRING_BASELINE_RDRND \ X86_BASELINE"," QT_FUNCTION_TARGET_STRING_RDRND#endifstatic boolcheckRdrndWorks(quint64) noexcept; QT_FUNCTION_TARGET_BASELINE static intmaxBasicCpuidSupported(){#if defined(Q_CC_EMSCRIPTEN)return6;// All features supported by Emscripten#elif defined(Q_CC_GNU) qregisterint tmp1;# if Q_PROCESSOR_X86 < 5// check if the CPUID instruction is supportedlong 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)return0;# endifint 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 0int info[4];__cpuid(info,0);return info[0];#elif defined(Q_CC_GHS)unsigned int info[4];__CPUID(0, info);return info[0];#elsereturn0;#endif} QT_FUNCTION_TARGET_BASELINE static voidcpuidFeatures01(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];#elseQ_UNUSED(ecx);Q_UNUSED(edx);#endif}#ifdef Q_OS_WINinlinevoid__cpuidex(int info[4],int, __int64) {memset(info,0,4*sizeof(int));}#endif QT_FUNCTION_TARGET_BASELINE static voidcpuidFeatures07_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];#elseQ_UNUSED(ebx);Q_UNUSED(ecx);Q_UNUSED(edx);#endif} QT_FUNCTION_TARGET_BASELINE #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) {return0; }#endifstatic voidxgetbv(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;#elseQ_UNUSED(in);Q_UNUSED(eax);Q_UNUSED(edx);#endif} QT_FUNCTION_TARGET_BASELINE static quint64 adjustedXcr0(quint64 xcr0){/* * Some OSes hide their capability of context-switching the AVX512 state in * the XCR0 register. They do that so the first time we execute an * instruction that may access the AVX512 state (requiring the EVEX prefix) * they allocate the necessary context switch space. * * This behavior is deprecated with the XFD (Extended Feature Disable) * register, but we can't change existing OSes. */#ifdef Q_OS_DARWIN// from <machine/cpu_capabilities.h> in xnu// <https://github.com/apple/darwin-xnu/blob/xnu-4903.221.2/osfmk/i386/cpu_capabilities.h>constexpr quint64 kHasAVX512F =Q_UINT64_C(0x0000004000000000);constexpr quintptr commpage =sizeof(void*) >4?Q_UINT64_C(0x00007fffffe00000) :0xffff0000;constexpr quintptr cpu_capabilities64 = commpage +0x10; quint64 capab = *reinterpret_cast<quint64 *>(cpu_capabilities64);if(capab & kHasAVX512F) xcr0 |= XSave_Avx512State;#endifreturn xcr0;} QT_FUNCTION_TARGET_BASELINE static quint64 detectProcessorFeatures(){ quint64 features =0;int cpuidLevel =maxBasicCpuidSupported();#if Q_PROCESSOR_X86 < 5if(cpuidLevel <1)return0;#elseassert(cpuidLevel >=1);#endif uint results[X86CpuidMaxLeaf] = {};cpuidFeatures01(results[Leaf01ECX], results[Leaf01EDX]);if(cpuidLevel >=7)cpuidFeatures07_00(results[Leaf07_00EBX], results[Leaf07_00ECX], results[Leaf07_00EDX]);// populate our feature listfor(uint i =0; i <arraysize(x86_locators); ++i) { uint word = x86_locators[i] /32; uint bit =1U<< (x86_locators[i] %32); quint64 feature =Q_UINT64_C(1) << i;if(results[word] & bit) features |= feature;}// now check the AVX state quint64 xcr0 =0;if(results[Leaf01ECX] & (1u<<27)) {// XGETBV enabled uint xgetbvA =0, xgetbvD =0;xgetbv(0, xgetbvA, xgetbvD); xcr0 = xgetbvA;if(sizeof(XSaveBits) >sizeof(xgetbvA)) xcr0 |=quint64(xgetbvD) <<32; xcr0 =adjustedXcr0(xcr0);}for(auto req : xsave_requirements) {if((xcr0 & req.xsave_state) != req.xsave_state) features &= ~req.cpu_features;}if(features & CpuFeatureRDRND && !checkRdrndWorks(features)) 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(nullptr),alloc(0),size(0) { }~QSimpleBuffer() { ::free(data); }voidresize(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;}voidappend(const QSimpleBuffer &other,unsigned appendsize){unsigned oldsize = size;resize(oldsize + appendsize);::memcpy(data + oldsize, other.data, appendsize);}voidpopleft(unsigned amount){if(amount >= size)returnresize(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 voidbufReadLine(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;elsereturn;}}//// Checks if any line with a given prefix from /proc/cpuinfo contains// a certain string, surrounded by spaces.//static boolprocCpuinfoContains(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 spaceif(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;}#endifstaticinline quint64 detectProcessorFeatures(){// NOTE: MIPS 74K cores are the only ones supporting DSPr2. quint64 flags =0;#if defined __mips_dsp flags |= CpuFeatureDSP;# if defined __mips_dsp_rev && __mips_dsp_rev >= 2 flags |= CpuFeatureDSPR2;# elif defined(Q_OS_LINUX)if(procCpuinfoContains("cpu model","MIPS 74Kc") ||procCpuinfoContains("cpu model","MIPS 74Kf")) flags |= CpuFeatureDSPR2;# endif#elif defined(Q_OS_LINUX)if(procCpuinfoContains("ASEs implemented","dsp")) { flags |= CpuFeatureDSP;if(procCpuinfoContains("cpu model","MIPS 74Kc") ||procCpuinfoContains("cpu model","MIPS 74Kf")) flags |= CpuFeatureDSPR2;}#endifreturn flags;}#elsestaticinline uint detectProcessorFeatures(){return0;}#endif// record what CPU features were enabled by default in this Qt buildstatic const quint64 minFeature = qCompilerCpuFeatures;staticconstexpr auto SimdInitialized =QCpuFeatureType(1) << (sizeof(QCpuFeatureType) *8-1);Q_ATOMIC(QCpuFeatureType)QT_MANGLE_NAMESPACE(qt_cpu_features)[1] = {0}; QT_FUNCTION_TARGET_BASELINE uint64_tQT_MANGLE_NAMESPACE(qDetectCpuFeatures)(){auto minFeatureTest = minFeature;#if defined(Q_PROCESSOR_X86_64) && defined(cpu_feature_shstk)// Controlflow Enforcement Technology (CET) is an OS-assisted// hardware-feature, meaning the CPUID bit may be disabled if the OS// doesn't support it, but that's ok. minFeatureTest &= ~CpuFeatureSHSTK;#endif QCpuFeatureType f =detectProcessorFeatures();// Intentionally NOT qgetenv (this code runs too early)if(char*disable =getenv("QT_NO_CPU_FEATURE"); disable && *disable) {#if _POSIX_C_SOURCE >= 200112Lchar*saveptr =nullptr;auto strtok = [&saveptr](char*str,const char*delim) {return::strtok_r(str, delim, &saveptr);};#endifwhile(char*token =strtok(disable," ")) { disable =nullptr;for(uint i =0; i <arraysize(features_indices); ++i) {if(strcmp(token, features_string + features_indices[i]) ==0) f &= ~(Q_UINT64_C(1) << i);}}}#ifdef RUNNING_ON_VALGRINDbool runningOnValgrind = RUNNING_ON_VALGRIND;#elsebool runningOnValgrind =false;#endifif(Q_UNLIKELY(!runningOnValgrind && minFeatureTest !=0&& (f & minFeatureTest) != minFeatureTest)) { quint64 missing = minFeatureTest & ~quint64(f);fprintf(stderr,"Incompatible processor. This Qt build requires the following features:\n");for(uint i =0; i <arraysize(features_indices); ++i) {if(missing & (Q_UINT64_C(1) << i))fprintf(stderr,"%s", features_string + features_indices[i]);}fprintf(stderr,"\n");fflush(stderr);qAbort();}assert((f & SimdInitialized) ==0); f |= SimdInitialized;std::atomic_store_explicit(QT_MANGLE_NAMESPACE(qt_cpu_features), f,std::memory_order_relaxed);return f;} QT_FUNCTION_TARGET_BASELINE voidqDumpCPUFeatures(){ quint64 features =detectProcessorFeatures() & ~SimdInitialized;printf("Processor features: ");for(uint i =0; i <arraysize(features_indices); ++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(uint i =0; i <arraysize(features_indices); ++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// The parameter to _rdrand64_step & _rdseed64_step is unsigned long long for// Clang and GCC but unsigned __int64 for MSVC and ICC, which is unsigned long// long on Windows, but unsigned long on Linux.namespace{template<typename F>struct ExtractParameter;template<typename T>struct ExtractParameter<int(T *)> {using Type = T; };using randuint = ExtractParameter<decltype(_rdrandXX_step)>::Type;}# if QT_COMPILER_SUPPORTS_HERE(RDSEED)staticQT_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-guidewhile(ptr +sizeof(randuint) /sizeof(*ptr) <= end) {if(_rdseedXX_step(reinterpret_cast<randuint *>(ptr)) ==0)goto out; ptr +=sizeof(randuint) /sizeof(*ptr);}if(sizeof(*ptr) !=sizeof(randuint) && ptr != end) {if(_rdseed32_step(ptr) ==0)goto out;++ptr;} out:return ptr;}# elsestatic unsigned*qt_random_rdseed(unsigned*ptr,unsigned*){return ptr;}# endifstaticQT_FUNCTION_TARGET(RDRND)unsigned*qt_random_rdrnd(unsigned*ptr,unsigned*end) noexcept {int retries =10;while(ptr +sizeof(randuint)/sizeof(*ptr) <= end) {if(_rdrandXX_step(reinterpret_cast<randuint *>(ptr))) ptr +=sizeof(randuint)/sizeof(*ptr);else if(--retries ==0)goto out;}while(sizeof(*ptr) !=sizeof(randuint) && ptr != end) {bool ok =_rdrand32_step(ptr);if(!ok && --retries)continue;if(ok)++ptr;break;} out:return ptr;}QT_FUNCTION_TARGET(BASELINE_RDRND) Q_DECL_COLD_FUNCTION static boolcheckRdrndWorks(quint64 features) 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] = {};// But if the RDRND feature was statically enabled by the compiler, we// assume that the RNG works. That's because the calls to qRandomCpu() will// be guarded by qCpuHasFeature(RDRND) and that will be a constant true.if(_compilerCpuFeatures & CpuFeatureRDRND)return true;unsigned*end;if(features & CpuFeatureRDSEED) end =qt_random_rdseed(testBuffer, testBuffer + TestBufferSize);else 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 equalityif(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 goodreturn 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_PROCESSOR_ARM)static boolcheckRdrndWorks(quint64) noexcept {return false; }#endif// Q_PROCESSOR_X86 && RDRND#if QT_SUPPORTS_INIT_PRIORITYnamespace{struct QSimdInitializer {inlineQSimdInitializer() {QT_MANGLE_NAMESPACE(qDetectCpuFeatures)(); }};}// This is intentionally a dynamic initialization of the variableQ_DECL_INIT_PRIORITY(01)static QSimdInitializer initializer;#endif QT_END_NAMESPACE
|