summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qsimd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qsimd.cpp')
-rw-r--r--src/corelib/global/qsimd.cpp348
1 files changed, 175 insertions, 173 deletions
diff --git a/src/corelib/global/qsimd.cpp b/src/corelib/global/qsimd.cpp
index c2db117853..8bc5381591 100644
--- a/src/corelib/global/qsimd.cpp
+++ b/src/corelib/global/qsimd.cpp
@@ -1,76 +1,53 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// 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 <QByteArray>
+
#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_p.h"
#endif
+#define QT_FUNCTION_TARGET_BASELINE
+
#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"
+# 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)
+# 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_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_AES (1 << 0)
#define HWCAP2_CRC32 (1 << 4)
// copied from <asm/hwcap.h> (Aarch64)
+#define HWCAP_AES (1 << 3)
#define HWCAP_CRC32 (1 << 7)
// copied from <linux/auxvec.h>
@@ -78,106 +55,110 @@
#define AT_HWCAP2 26 /* extension of AT_HWCAP */
#elif defined(Q_CC_GHS)
-#include <INTEGRITY_types.h>
+# include <INTEGRITY_types.h>
+#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM)
+# include <sys/sysctl.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.
- */
+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;
+}
-// begin generated
#if defined(Q_PROCESSOR_ARM)
/* Data:
neon
crc32
+ aes
*/
static const char features_string[] =
+ "\0"
" neon\0"
" crc32\0"
- "\0";
-static const int features_indices[] = { 0, 6 };
+ " aes\0";
+static const int features_indices[] = { 0, 1, 7, 14 };
#elif defined(Q_PROCESSOR_MIPS)
/* Data:
dsp
dspr2
*/
static const char features_string[] =
+ "\0"
" dsp\0"
- " dspr2\0"
- "\0";
+ " dspr2\0";
static const int features_indices[] = {
- 0, 5
+ 0, 1, 6
};
#elif defined(Q_PROCESSOR_X86)
# include "qsimd_x86.cpp" // generated by util/x86simdgen
#else
static const char features_string[] = "";
-static const int features_indices[] = { };
+static const int features_indices[] = { 0 };
#endif
// end generated
-#if defined (Q_OS_NACL)
-static inline uint detectProcessorFeatures()
-{
- return 0;
-}
-#elif defined(Q_PROCESSOR_ARM)
+#if 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;
- }
+#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 available
+ if (auxvHwCap & HWCAP_CRC32)
+ features |= CpuFeatureCRC32;
+ if (auxvHwCap & HWCAP_AES)
+ features |= CpuFeatureAES;
+# 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;
# endif
- }
- }
-
- qt_safe_close(auxv);
return features;
}
- // fall back if /proc/self/auxv wasn't found
+ // 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);
+ if (sysctlbyname("hw.optional.neon", &feature, &len, nullptr, 0) == 0)
+ features |= feature ? CpuFeatureNEON : 0;
+ if (sysctlbyname("hw.optional.armv8_crc32", &feature, &len, nullptr, 0) == 0)
+ features |= feature ? CpuFeatureCRC32 : 0;
+ // There is currently no optional value for crypto/AES.
+#if defined(__ARM_FEATURE_CRYPTO)
+ features |= CpuFeatureAES;
#endif
-
-#if defined(__ARM_NEON__)
- features |= Q_UINT64_C(1) << CpuFeatureNEON;
+ return 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__) || defined(__ARM_NEON)
+ features |= CpuFeatureNEON;
#endif
#if defined(__ARM_FEATURE_CRC32)
- features |= Q_UINT64_C(1) << CpuFeatureCRC32;
+ features |= CpuFeatureCRC32;
+#endif
+#if defined(__ARM_FEATURE_CRYPTO)
+ features |= CpuFeatureAES;
#endif
return features;
@@ -190,9 +171,23 @@ static inline quint64 detectProcessorFeatures()
#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)
+// 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
+#endif
static bool checkRdrndWorks() noexcept;
+QT_FUNCTION_TARGET_BASELINE
static int maxBasicCpuidSupported()
{
#if defined(Q_CC_EMSCRIPTEN)
@@ -240,6 +235,7 @@ static int maxBasicCpuidSupported()
#endif
}
+QT_FUNCTION_TARGET_BASELINE
static void cpuidFeatures01(uint &ecx, uint &edx)
{
#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
@@ -269,6 +265,7 @@ static void cpuidFeatures01(uint &ecx, uint &edx)
inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int));}
#endif
+QT_FUNCTION_TARGET_BASELINE
static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx)
{
#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
@@ -302,6 +299,7 @@ static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &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) { return 0; }
@@ -323,22 +321,7 @@ static void xgetbv(uint in, uint &eax, uint &edx)
#endif
}
-// Flags from the 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
-};
-
+QT_FUNCTION_TARGET_BASELINE
static quint64 adjustedXcr0(quint64 xcr0)
{
/*
@@ -358,59 +341,54 @@ static quint64 adjustedXcr0(quint64 xcr0)
constexpr quintptr cpu_capabilities64 = commpage + 0x10;
quint64 capab = *reinterpret_cast<quint64 *>(cpu_capabilities64);
if (capab & kHasAVX512F)
- xcr0 |= AVX512State;
+ xcr0 |= XSave_Avx512State;
#endif
return xcr0;
}
+QT_FUNCTION_TARGET_BASELINE
static quint64 detectProcessorFeatures()
{
- 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);
+ assert(cpuidLevel >= 1);
#endif
uint results[X86CpuidMaxLeaf] = {};
- cpuidFeatures01(results[Leaf1ECX], results[Leaf1EDX]);
+ cpuidFeatures01(results[Leaf01ECX], results[Leaf01EDX]);
if (cpuidLevel >= 7)
- cpuidFeatures07_00(results[Leaf7_0EBX], results[Leaf7_0ECX], results[Leaf7_0EDX]);
+ cpuidFeatures07_00(results[Leaf07_00EBX], results[Leaf07_00ECX], results[Leaf07_00EDX]);
// populate our feature list
- for (uint i = 0; i < sizeof(x86_locators) / sizeof(x86_locators[0]); ++i) {
+ for (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 + 1);
+ quint64 feature = Q_UINT64_C(1) << i;
if (results[word] & bit)
features |= feature;
}
// now check the AVX state
quint64 xcr0 = 0;
- if (results[Leaf1ECX] & (1u << 27)) {
+ if (results[Leaf01ECX] & (1u << 27)) {
// XGETBV enabled
uint xgetbvA = 0, xgetbvD = 0;
xgetbv(0, xgetbvA, xgetbvD);
xcr0 = xgetbvA;
- if (sizeof(XCR0Flags) > sizeof(xgetbvA))
+ if (sizeof(XSaveBits) > sizeof(xgetbvA))
xcr0 |= quint64(xgetbvD) << 32;
xcr0 = adjustedXcr0(xcr0);
}
- if ((xcr0 & AVXState) != AVXState) {
- // support for YMM registers is disabled, disable all AVX
- features &= ~AllAVX;
- } else if ((xcr0 & AVX512State) != AVX512State) {
- // support for ZMM registers or mask registers is disabled, disable all AVX512
- features &= ~AllAVX512;
+ for (auto req : xsave_requirements) {
+ if ((xcr0 & req.xsave_state) != req.xsave_state)
+ features &= ~req.cpu_features;
}
if (features & CpuFeatureRDRND && !checkRdrndWorks())
@@ -548,18 +526,18 @@ static inline quint64 detectProcessorFeatures()
quint64 flags = 0;
#if defined __mips_dsp
- flags |= Q_UINT64_C(1) << CpuFeatureDSP;
+ flags |= CpuFeatureDSP;
# if defined __mips_dsp_rev && __mips_dsp_rev >= 2
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
# elif defined(Q_OS_LINUX)
if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
# endif
#elif defined(Q_OS_LINUX)
if (procCpuinfoContains("ASEs implemented", "dsp")) {
- flags |= Q_UINT64_C(1) << CpuFeatureDSP;
+ flags |= CpuFeatureDSP;
if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
}
#endif
@@ -573,26 +551,38 @@ static inline uint detectProcessorFeatures()
}
#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
+static constexpr auto SimdInitialized = QCpuFeatureType(1) << (sizeof(QCpuFeatureType) * 8 - 1);
+Q_ATOMIC(QCpuFeatureType) QT_MANGLE_NAMESPACE(qt_cpu_features)[1] = { 0 };
-quint64 qDetectCpuFeatures()
+QT_FUNCTION_TARGET_BASELINE
+uint64_t QT_MANGLE_NAMESPACE(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);
+ 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 >= 200112L
+ char *saveptr = nullptr;
+ auto strtok = [&saveptr](char *str, const char *delim) {
+ return ::strtok_r(str, delim, &saveptr);
+ };
+#endif
+ while (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);
+ }
}
}
@@ -601,38 +591,37 @@ quint64 qDetectCpuFeatures()
#else
bool runningOnValgrind = false;
#endif
- if (Q_UNLIKELY(!runningOnValgrind && minFeature != 0 && (f & minFeature) != minFeature)) {
- quint64 missing = minFeature & ~f;
+ if (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 (int i = 0; i < features_count; ++i) {
+ 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);
- qFatal("Aborted. Incompatible processor: missing feature 0x%llx -%s.", missing,
- features_string + features_indices[qCountTrailingZeroBits(missing)]);
+ qAbort();
}
- qt_cpu_features[0].storeRelaxed(f | quint32(QSimdInitialized));
-#ifndef Q_ATOMIC_INT64_IS_SUPPORTED
- qt_cpu_features[1].storeRelaxed(f >> 32);
-#endif
+ 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
void qDumpCPUFeatures()
{
- quint64 features = qCpuFeatures() & ~quint64(QSimdInitialized);
+ quint64 features = detectProcessorFeatures() & ~SimdInitialized;
printf("Processor features: ");
- for (int i = 0; i < features_count; ++i) {
+ 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 (int i = 0; i < features_count; ++i) {
+ for (uint i = 0; i < arraysize(features_indices); ++i) {
if (features & (Q_UINT64_C(1) << i))
printf("%s", features_string + features_indices[i]);
}
@@ -713,7 +702,8 @@ out:
return ptr;
}
-static QT_FUNCTION_TARGET(RDRND) Q_DECL_COLD_FUNCTION bool checkRdrndWorks() noexcept
+QT_FUNCTION_TARGET(BASELINE_RDRND) Q_DECL_COLD_FUNCTION
+static bool checkRdrndWorks() noexcept
{
/*
* Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a
@@ -766,8 +756,20 @@ QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) no
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)
+#elif defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_ARM)
static bool checkRdrndWorks() noexcept { return false; }
#endif // Q_PROCESSOR_X86 && RDRND
+#if QT_SUPPORTS_INIT_PRIORITY
+namespace {
+struct QSimdInitializer
+{
+ inline QSimdInitializer() { QT_MANGLE_NAMESPACE(qDetectCpuFeatures)(); }
+};
+}
+
+// This is intentionally a dynamic initialization of the variable
+Q_DECL_INIT_PRIORITY(01) static QSimdInitializer initializer;
+#endif
+
QT_END_NAMESPACE