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