Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsimd.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5// we need ICC to define the prototype for _rdseed64_step
6// Qt-Security score:significant reason:default
7#define __INTEL_COMPILER_USE_INTRINSIC_PROTOTYPES
8#undef _FORTIFY_SOURCE // otherwise, the always_inline from stdio.h fail to inline
9
10#include "qsimd_p.h"
11#include "qalgorithms.h"
12
13#include <stdio.h>
14#include <string.h>
15
16#if defined(QT_NO_DEBUG) && !defined(NDEBUG)
17# define NDEBUG
18#endif
19#include <assert.h>
20
21#ifdef Q_OS_LINUX
22# include "../testlib/3rdparty/valgrind/valgrind_p.h"
23#endif
24
25#define QT_FUNCTION_TARGET_BASELINE
26
27#if defined(Q_OS_WIN)
28# if !defined(Q_CC_GNU)
29# include <intrin.h>
30# endif
31# if defined(Q_PROCESSOR_ARM_64)
32# include <qt_windows.h>
33# include <processthreadsapi.h>
34# endif
35#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_MIPS_32)
36# include "private/qcore_unix_p.h"
37#elif QT_CONFIG(getauxval) && (defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_LOONGARCH))
38# include <sys/auxv.h>
39
40// the kernel header definitions for HWCAP_*
41// (the ones we need/may need anyway)
42
43// copied from <asm/hwcap.h> (ARM)
44#define HWCAP_NEON 4096
45
46// copied from <asm/hwcap.h> (ARM):
47#define HWCAP2_AES (1 << 0)
48#define HWCAP2_CRC32 (1 << 4)
49
50// copied from <asm/hwcap.h> (Aarch64)
51#define HWCAP_AES (1 << 3)
52#define HWCAP_CRC32 (1 << 7)
53#define HWCAP_SVE (1 << 22)
54
55// copied from <linux/auxvec.h>
56#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
57#define AT_HWCAP2 26 /* extension of AT_HWCAP */
58
59#elif defined(Q_CC_GHS)
60# include <INTEGRITY_types.h>
61#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM)
62# include <sys/sysctl.h>
63#endif
64
65QT_BEGIN_NAMESPACE
66
67template <typename T, uint N> QT_FUNCTION_TARGET_BASELINE
68uint arraysize(T (&)[N])
69{
70 // Same as std::size, but with QT_FUNCTION_TARGET_BASELIE,
71 // otherwise some versions of GCC fail to compile.
72 return N;
73}
74
75#if defined(Q_PROCESSOR_ARM)
76/* Data:
77 neon
78 crc32
79 aes
80 sve
81 */
82static const char features_string[] =
83 "\0"
84 " neon\0"
85 " crc32\0"
86 " aes\0"
87 " sve\0";
88static const int features_indices[] = { 0, 1, 7, 14, 19 };
89#elif defined(Q_PROCESSOR_MIPS)
90/* Data:
91 dsp
92 dspr2
93*/
94static const char features_string[] =
95 "\0"
96 " dsp\0"
97 " dspr2\0";
98
99static const int features_indices[] = {
100 0, 1, 6
101};
102#elif defined(Q_PROCESSOR_LOONGARCH)
103/* Data:
104 lsx
105 lasx
106*/
107static const char features_string[] =
108 "\0"
109 " lsx\0"
110 " lasx\0";
111
112static const int features_indices[] = {
113 0, 1, 6
114};
115#elif defined(Q_PROCESSOR_X86)
116# include "qsimd_x86.cpp" // generated by util/x86simdgen
117#else
118static const char features_string[] = "";
119static const int features_indices[] = { 0 };
120#endif
121// end generated
122
123#if defined(Q_PROCESSOR_ARM)
124static inline quint64 detectProcessorFeatures()
125{
126 quint64 features = 0;
127
128#if QT_CONFIG(getauxval)
129 unsigned long auxvHwCap = getauxval(AT_HWCAP);
130 if (auxvHwCap != 0) {
131# if defined(Q_PROCESSOR_ARM_64)
132 // For Aarch64:
133 features |= CpuFeatureNEON; // NEON is always available
134 if (auxvHwCap & HWCAP_CRC32)
135 features |= CpuFeatureCRC32;
136 if (auxvHwCap & HWCAP_AES)
137 features |= CpuFeatureAES;
138 if (auxvHwCap & HWCAP_SVE)
139 features |= CpuFeatureSVE;
140# else
141 // For ARM32:
142 if (auxvHwCap & HWCAP_NEON)
143 features |= CpuFeatureNEON;
144 auxvHwCap = getauxval(AT_HWCAP2);
145 if (auxvHwCap & HWCAP2_CRC32)
146 features |= CpuFeatureCRC32;
147 if (auxvHwCap & HWCAP2_AES)
148 features |= CpuFeatureAES;
149# endif
150 return features;
151 }
152 // fall back to compile-time flags if getauxval failed
153#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM)
154 unsigned feature;
155 size_t len = sizeof(feature);
156 Q_UNUSED(len);
157#if defined(__ARM_NEON)
158 features |= CpuFeatureNEON;
159#else
160 #error "Misconfiguration, NEON should always be enabled on Apple hardware"
161#endif
162#if defined(__ARM_FEATURE_CRC32)
163 features |= CpuFeatureCRC32;
164#elif defined(Q_OS_MACOS)
165 #error "Misconfiguration, CRC32 should always be enabled on Apple desktop hardware"
166#else
167 if (sysctlbyname("hw.optional.armv8_crc32", &feature, &len, nullptr, 0) == 0)
168 features |= feature ? CpuFeatureCRC32 : 0;
169#endif
170#if defined(__ARM_FEATURE_CRYPTO)
171 features |= CpuFeatureAES;
172#elif defined(Q_OS_MACOS)
173 #error "Misconfiguration, CRYPTO/AES should always be enabled on Apple desktop hardware"
174#else
175 if (sysctlbyname("hw.optional.arm.FEAT_AES", &feature, &len, nullptr, 0) == 0)
176 features |= feature ? CpuFeatureAES : 0;
177#endif
178#if defined(__ARM_FEATURE_SVE)
179 features |= CpuFeatureSVE;
180#else
181 if (sysctlbyname("hw.optional.arm.FEAT_SVE", &feature, &len, nullptr, 0) == 0)
182 features |= feature ? CpuFeatureSVE : 0;
183#endif
184 return features;
185#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM_64)
186 features |= CpuFeatureNEON;
187 if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != 0)
188 features |= CpuFeatureCRC32;
189 if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != 0)
190 features |= CpuFeatureAES;
191 return features;
192#endif
193#if defined(__ARM_NEON__)
194 features |= CpuFeatureNEON;
195#endif
196#if defined(__ARM_FEATURE_CRC32)
197 features |= CpuFeatureCRC32;
198#endif
199#if defined(__ARM_FEATURE_CRYPTO)
200 features |= CpuFeatureAES;
201#endif
202#if defined(__ARM_FEATURE_SVE)
203 features |= CpuFeatureSVE;
204#endif
205
206 return features;
207}
208
209#elif defined(Q_PROCESSOR_LOONGARCH)
210static inline quint64 detectProcessorFeatures()
211{
212 quint64 features = 0;
213# if QT_CONFIG(getauxval)
214 quint64 hwcap = getauxval(AT_HWCAP);
215
216 if (hwcap & HWCAP_LOONGARCH_LSX)
217 features |= CpuFeatureLSX;
218 if (hwcap & HWCAP_LOONGARCH_LASX)
219 features |= CpuFeatureLASX;
220# else
221 enum LoongArchFeatures {
222 LOONGARCH_CFG2 = 0x2,
223 LOONGARCH_CFG2_LSX = (1 << 6),
224 LOONGARCH_CFG2_LASX = (1 << 7)
225 };
226
227 quint64 reg = 0;
228
229 __asm__ volatile(
230 "cpucfg %0, %1 \n\t"
231 : "+&r"(reg)
232 : "r"(LOONGARCH_CFG2)
233 );
234
235 if (reg & LOONGARCH_CFG2_LSX)
236 features |= CpuFeatureLSX;
237 if (reg & LOONGARCH_CFG2_LASX)
238 features |= CpuFeatureLASX;
239# endif
240 return features;
241}
242
243#elif defined(Q_PROCESSOR_X86)
244
245#ifdef Q_PROCESSOR_X86_32
246# define PICreg "%%ebx"
247#else
248# define PICreg "%%rbx"
249#endif
250#ifdef __SSE2_MATH__
251# define X86_BASELINE "no-sse3"
252#else
253# define X86_BASELINE "no-sse"
254#endif
255
256#if defined(Q_CC_GNU) || defined(Q_CC_CLANG)
257// lower the target for functions in this file
258# undef QT_FUNCTION_TARGET_BASELINE
259# define QT_FUNCTION_TARGET_BASELINE __attribute__((target(X86_BASELINE)))
260#endif
261
262QT_FUNCTION_TARGET_BASELINE
263static int maxBasicCpuidSupported()
264{
265#if defined(Q_CC_EMSCRIPTEN)
266 return 6; // All features supported by Emscripten
267#elif defined(Q_CC_GNU)
268 qregisterint tmp1;
269
270# if Q_PROCESSOR_X86 < 5
271 // check if the CPUID instruction is supported
272 long cpuid_supported;
273 asm ("pushf\n"
274 "pop %0\n"
275 "mov %0, %1\n"
276 "xor $0x00200000, %0\n"
277 "push %0\n"
278 "popf\n"
279 "pushf\n"
280 "pop %0\n"
281 "xor %1, %0\n" // %eax is now 0 if CPUID is not supported
282 : "=a" (cpuid_supported), "=r" (tmp1)
283 );
284 if (!cpuid_supported)
285 return 0;
286# endif
287
288 int result;
289 asm ("xchg " PICreg", %1\n"
290 "cpuid\n"
291 "xchg " PICreg", %1\n"
292 : "=&a" (result), "=&r" (tmp1)
293 : "0" (0)
294 : "ecx", "edx");
295 return result;
296#elif defined(Q_OS_WIN)
297 // Use the __cpuid function; if the CPUID instruction isn't supported, it will return 0
298 int info[4];
299 __cpuid(info, 0);
300 return info[0];
301#elif defined(Q_CC_GHS)
302 unsigned int info[4];
303 __CPUID(0, info);
304 return info[0];
305#else
306 return 0;
307#endif
308}
309
310QT_FUNCTION_TARGET_BASELINE
311static void cpuidFeatures01(uint &ecx, uint &edx)
312{
313#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
314 qregisterint tmp1;
315 asm ("xchg " PICreg", %2\n"
316 "cpuid\n"
317 "xchg " PICreg", %2\n"
318 : "=&c" (ecx), "=&d" (edx), "=&r" (tmp1)
319 : "a" (1));
320#elif defined(Q_OS_WIN)
321 int info[4];
322 __cpuid(info, 1);
323 ecx = info[2];
324 edx = info[3];
325#elif defined(Q_CC_GHS)
326 unsigned int info[4];
327 __CPUID(1, info);
328 ecx = info[2];
329 edx = info[3];
330#else
331 Q_UNUSED(ecx);
332 Q_UNUSED(edx);
333#endif
334}
335
336#ifdef Q_OS_WIN
337inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int));}
338#endif
339
340QT_FUNCTION_TARGET_BASELINE
341static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx)
342{
343#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
344 qregisteruint rbx; // in case it's 64-bit
345 qregisteruint rcx = 0;
346 qregisteruint rdx = 0;
347 asm ("xchg " PICreg", %0\n"
348 "cpuid\n"
349 "xchg " PICreg", %0\n"
350 : "=&r" (rbx), "+&c" (rcx), "+&d" (rdx)
351 : "a" (7));
352 ebx = rbx;
353 ecx = rcx;
354 edx = rdx;
355#elif defined(Q_OS_WIN)
356 int info[4];
357 __cpuidex(info, 7, 0);
358 ebx = info[1];
359 ecx = info[2];
360 edx = info[3];
361#elif defined(Q_CC_GHS)
362 unsigned int info[4];
363 __CPUIDEX(7, 0, info);
364 ebx = info[1];
365 ecx = info[2];
366 edx = info[3];
367#else
368 Q_UNUSED(ebx);
369 Q_UNUSED(ecx);
370 Q_UNUSED(edx);
371#endif
372}
373
374QT_FUNCTION_TARGET_BASELINE
375#if defined(Q_OS_WIN) && !(defined(Q_CC_GNU) || defined(Q_CC_GHS))
376// fallback overload in case this intrinsic does not exist: unsigned __int64 _xgetbv(unsigned int);
377inline quint64 _xgetbv(__int64) { return 0; }
378#endif
379static void xgetbv(uint in, uint &eax, uint &edx)
380{
381#if (defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)) || defined(Q_CC_GHS)
382 asm (".byte 0x0F, 0x01, 0xD0" // xgetbv instruction
383 : "=a" (eax), "=d" (edx)
384 : "c" (in));
385#elif defined(Q_OS_WIN)
386 quint64 result = _xgetbv(in);
387 eax = result;
388 edx = result >> 32;
389#else
390 Q_UNUSED(in);
391 Q_UNUSED(eax);
392 Q_UNUSED(edx);
393#endif
394}
395
396QT_FUNCTION_TARGET_BASELINE
397static quint64 adjustedXcr0(quint64 xcr0)
398{
399 /*
400 * Some OSes hide their capability of context-switching the AVX512 state in
401 * the XCR0 register. They do that so the first time we execute an
402 * instruction that may access the AVX512 state (requiring the EVEX prefix)
403 * they allocate the necessary context switch space.
404 *
405 * This behavior is deprecated with the XFD (Extended Feature Disable)
406 * register, but we can't change existing OSes.
407 */
408#ifdef Q_OS_DARWIN
409 // from <machine/cpu_capabilities.h> in xnu
410 // <https://github.com/apple/darwin-xnu/blob/xnu-4903.221.2/osfmk/i386/cpu_capabilities.h>
411 constexpr quint64 kHasAVX512F = Q_UINT64_C(0x0000004000000000);
412 constexpr quintptr commpage = sizeof(void *) > 4 ? Q_UINT64_C(0x00007fffffe00000) : 0xffff0000;
413 constexpr quintptr cpu_capabilities64 = commpage + 0x10;
414 quint64 capab = *reinterpret_cast<quint64 *>(cpu_capabilities64);
415 if (capab & kHasAVX512F)
416 xcr0 |= XSave_Avx512State;
417#endif
418
419 return xcr0;
420}
421
422QT_FUNCTION_TARGET_BASELINE
423static quint64 detectProcessorFeatures()
424{
425 quint64 features = 0;
426 int cpuidLevel = maxBasicCpuidSupported();
427#if Q_PROCESSOR_X86 < 5
428 if (cpuidLevel < 1)
429 return 0;
430#else
431 assert(cpuidLevel >= 1);
432#endif
433
434 uint results[X86CpuidMaxLeaf] = {};
435 cpuidFeatures01(results[Leaf01ECX], results[Leaf01EDX]);
436 if (cpuidLevel >= 7)
437 cpuidFeatures07_00(results[Leaf07_00EBX], results[Leaf07_00ECX], results[Leaf07_00EDX]);
438
439 // populate our feature list
440 for (uint i = 0; i < arraysize(x86_locators); ++i) {
441 uint word = x86_locators[i] / 32;
442 uint bit = 1U << (x86_locators[i] % 32);
443 quint64 feature = Q_UINT64_C(1) << i;
444 if (results[word] & bit)
445 features |= feature;
446 }
447
448 // now check the AVX state
449 quint64 xcr0 = 0;
450 if (results[Leaf01ECX] & (1u << 27)) {
451 // XGETBV enabled
452 uint xgetbvA = 0, xgetbvD = 0;
453 xgetbv(0, xgetbvA, xgetbvD);
454
455 xcr0 = xgetbvA;
456 if (sizeof(XSaveBits) > sizeof(xgetbvA))
457 xcr0 |= quint64(xgetbvD) << 32;
458 xcr0 = adjustedXcr0(xcr0);
459 }
460
461 for (auto req : xsave_requirements) {
462 if ((xcr0 & req.xsave_state) != req.xsave_state)
463 features &= ~req.cpu_features;
464 }
465
466 return features;
467}
468
469#elif defined(Q_PROCESSOR_MIPS_32)
470
471#if defined(Q_OS_LINUX)
472//
473// Do not use QByteArray: it could use SIMD instructions itself at
474// some point, thus creating a recursive dependency. Instead, use a
475// QSimpleBuffer, which has the bare minimum needed to use memory
476// dynamically and read lines from /proc/cpuinfo of arbitrary sizes.
477//
478struct QSimpleBuffer
479{
480 static const int chunk_size = 256;
481 char *data;
482 unsigned alloc;
483 unsigned size;
484
485 QSimpleBuffer() : data(nullptr), alloc(0), size(0) { }
486 ~QSimpleBuffer() { ::free(data); }
487
488 void resize(unsigned newsize)
489 {
490 if (newsize > alloc) {
491 unsigned newalloc = chunk_size * ((newsize / chunk_size) + 1);
492 if (newalloc < newsize)
493 newalloc = newsize;
494 if (newalloc != alloc) {
495 data = static_cast<char *>(::realloc(data, newalloc));
496 alloc = newalloc;
497 }
498 }
499 size = newsize;
500 }
501 void append(const QSimpleBuffer &other, unsigned appendsize)
502 {
503 unsigned oldsize = size;
504 resize(oldsize + appendsize);
505 ::memcpy(data + oldsize, other.data, appendsize);
506 }
507 void popleft(unsigned amount)
508 {
509 if (amount >= size)
510 return resize(0);
511 size -= amount;
512 ::memmove(data, data + amount, size);
513 }
514 char *cString()
515 {
516 if (!alloc)
517 resize(1);
518 return (data[size] = '\0', data);
519 }
520};
521
522//
523// Uses a scratch "buffer" (which must be used for all reads done in the
524// same file descriptor) to read chunks of data from a file, to read
525// one line at a time. Lines include the trailing newline character ('\n').
526// On EOF, line.size is zero.
527//
528static void bufReadLine(int fd, QSimpleBuffer &line, QSimpleBuffer &buffer)
529{
530 for (;;) {
531 char *newline = static_cast<char *>(::memchr(buffer.data, '\n', buffer.size));
532 if (newline) {
533 unsigned piece_size = newline - buffer.data + 1;
534 line.append(buffer, piece_size);
535 buffer.popleft(piece_size);
536 line.resize(line.size - 1);
537 return;
538 }
539 if (buffer.size + QSimpleBuffer::chunk_size > buffer.alloc) {
540 int oldsize = buffer.size;
541 buffer.resize(buffer.size + QSimpleBuffer::chunk_size);
542 buffer.size = oldsize;
543 }
544 ssize_t read_bytes =
545 ::qt_safe_read(fd, buffer.data + buffer.size, QSimpleBuffer::chunk_size);
546 if (read_bytes > 0)
547 buffer.size += read_bytes;
548 else
549 return;
550 }
551}
552
553//
554// Checks if any line with a given prefix from /proc/cpuinfo contains
555// a certain string, surrounded by spaces.
556//
557static bool procCpuinfoContains(const char *prefix, const char *string)
558{
559 int cpuinfo_fd = ::qt_safe_open("/proc/cpuinfo", O_RDONLY);
560 if (cpuinfo_fd == -1)
561 return false;
562
563 unsigned string_len = ::strlen(string);
564 unsigned prefix_len = ::strlen(prefix);
565 QSimpleBuffer line, buffer;
566 bool present = false;
567 do {
568 line.resize(0);
569 bufReadLine(cpuinfo_fd, line, buffer);
570 char *colon = static_cast<char *>(::memchr(line.data, ':', line.size));
571 if (colon && line.size > prefix_len + string_len) {
572 if (!::strncmp(prefix, line.data, prefix_len)) {
573 // prefix matches, next character must be ':' or space
574 if (line.data[prefix_len] == ':' || ::isspace(line.data[prefix_len])) {
575 // Does it contain the string?
576 char *found = ::strstr(line.cString(), string);
577 if (found && ::isspace(found[-1]) &&
578 (::isspace(found[string_len]) || found[string_len] == '\0')) {
579 present = true;
580 break;
581 }
582 }
583 }
584 }
585 } while (line.size);
586
587 ::qt_safe_close(cpuinfo_fd);
588 return present;
589}
590#endif
591
592static inline quint64 detectProcessorFeatures()
593{
594 // NOTE: MIPS 74K cores are the only ones supporting DSPr2.
595 quint64 flags = 0;
596
597#if defined __mips_dsp
598 flags |= CpuFeatureDSP;
599# if defined __mips_dsp_rev && __mips_dsp_rev >= 2
600 flags |= CpuFeatureDSPR2;
601# elif defined(Q_OS_LINUX)
602 if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
603 flags |= CpuFeatureDSPR2;
604# endif
605#elif defined(Q_OS_LINUX)
606 if (procCpuinfoContains("ASEs implemented", "dsp")) {
607 flags |= CpuFeatureDSP;
608 if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
609 flags |= CpuFeatureDSPR2;
610 }
611#endif
612
613 return flags;
614}
615
616#else
618{
619 return 0;
620}
621#endif
622
623// record what CPU features were enabled by default in this Qt build
624static const quint64 minFeature = qCompilerCpuFeatures;
625
626static constexpr auto SimdInitialized = QCpuFeatureType(1) << (sizeof(QCpuFeatureType) * 8 - 1);
627Q_ATOMIC(QCpuFeatureType) QT_MANGLE_NAMESPACE(qt_cpu_features)[1] = { 0 };
628
631{
632 auto minFeatureTest = minFeature;
633#if defined(Q_PROCESSOR_X86_64) && defined(cpu_feature_shstk)
634 // Controlflow Enforcement Technology (CET) is an OS-assisted
635 // hardware-feature, meaning the CPUID bit may be disabled if the OS
636 // doesn't support it, but that's ok.
637 minFeatureTest &= ~CpuFeatureSHSTK;
638#endif
639 QCpuFeatureType f = detectProcessorFeatures();
640
641 // Intentionally NOT qgetenv (this code runs too early)
642 if (char *disable = getenv("QT_NO_CPU_FEATURE"); disable && *disable) {
643#if _POSIX_C_SOURCE >= 200112L
644 char *saveptr = nullptr;
645 auto strtok = [&saveptr](char *str, const char *delim) {
646 return ::strtok_r(str, delim, &saveptr);
647 };
648#endif
649 while (char *token = strtok(disable, " ")) {
650 disable = nullptr;
651 for (uint i = 0; i < arraysize(features_indices); ++i) {
652 if (strcmp(token, features_string + features_indices[i]) == 0)
653 f &= ~(Q_UINT64_C(1) << i);
654 }
655 }
656 }
657
658#ifdef RUNNING_ON_VALGRIND
659 bool runningOnValgrind = RUNNING_ON_VALGRIND;
660#else
661 bool runningOnValgrind = false;
662#endif
663 if (Q_UNLIKELY(!runningOnValgrind && minFeatureTest != 0 && (f & minFeatureTest) != minFeatureTest)) {
664 quint64 missing = minFeatureTest & ~quint64(f);
665 fprintf(stderr, "Incompatible processor. This Qt build requires the following features:\n ");
666 for (uint i = 0; i < arraysize(features_indices); ++i) {
667 if (missing & (Q_UINT64_C(1) << i))
668 fprintf(stderr, "%s", features_string + features_indices[i]);
669 }
670 fprintf(stderr, "\n");
671 fflush(stderr);
672 qAbort();
673 }
674
675 assert((f & SimdInitialized) == 0);
676 f |= SimdInitialized;
677 std::atomic_store_explicit(QT_MANGLE_NAMESPACE(qt_cpu_features), f, std::memory_order_relaxed);
678 return f;
679}
680
683{
684 quint64 features = detectProcessorFeatures() & ~SimdInitialized;
685 printf("Processor features: ");
686 for (uint i = 0; i < arraysize(features_indices); ++i) {
687 if (features & (Q_UINT64_C(1) << i))
688 printf("%s%s", features_string + features_indices[i],
689 minFeature & (Q_UINT64_C(1) << i) ? "[required]" : "");
690 }
691 if ((features = (qCompilerCpuFeatures & ~features))) {
692 printf("\n!!!!!!!!!!!!!!!!!!!!\n!!! Missing required features:");
693 for (uint i = 0; i < arraysize(features_indices); ++i) {
694 if (features & (Q_UINT64_C(1) << i))
695 printf("%s", features_string + features_indices[i]);
696 }
697 printf("\n!!! Applications will likely crash with \"Invalid Instruction\"\n!!!!!!!!!!!!!!!!!!!!");
698 }
699 puts("");
700}
701
702#if QT_SUPPORTS_INIT_PRIORITY
703namespace {
704struct QSimdInitializer
705{
706 inline QSimdInitializer() { QT_MANGLE_NAMESPACE(qDetectCpuFeatures)(); }
707};
708}
709
710// This is intentionally a dynamic initialization of the variable
711Q_DECL_INIT_PRIORITY(01) static QSimdInitializer initializer;
712#endif
713
714QT_END_NAMESPACE
#define assert
void qDumpCPUFeatures()
Definition qsimd.cpp:682
#define QT_FUNCTION_TARGET_BASELINE
Definition qsimd.cpp:25
QT_FUNCTION_TARGET_BASELINE uint64_t QT_MANGLE_NAMESPACE qDetectCpuFeatures()
Definition qsimd.cpp:630
static constexpr auto SimdInitialized
Definition qsimd.cpp:626
static const int features_indices[]
Definition qsimd.cpp:119
static uint detectProcessorFeatures()
Definition qsimd.cpp:617
static const char features_string[]
Definition qsimd.cpp:118
static const quint64 minFeature
Definition qsimd.cpp:624
static const uint64_t qCompilerCpuFeatures
Definition qsimd_p.h:399
unsigned QCpuFeatureType
Definition qsimd_p.h:452
#define Q_ATOMIC(T)
Definition qsimd_p.h:443