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