821de8d3ee99519cdbc6c7ca219c931bcb18c507
[profile/ivi/qtbase.git] / src / corelib / tools / qsimd.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Copyright (C) 2012 Intel Corporation.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsimd_p.h"
44 #include <QByteArray>
45 #include <stdio.h>
46
47 #if defined(Q_OS_WIN)
48 #  if defined(Q_OS_WINCE)
49 #    include <qt_windows.h>
50 #    include <cmnintrin.h>
51 #  endif
52 #  if !defined(Q_CC_GNU)
53 #    ifndef Q_OS_WINCE
54 #      include <intrin.h>
55 #    endif
56 #  endif
57 #elif defined(Q_OS_LINUX) && defined(__arm__)
58 #include "private/qcore_unix_p.h"
59
60 // the kernel header definitions for HWCAP_*
61 // (the ones we need/may need anyway)
62
63 // copied from <asm/hwcap.h> (ARM)
64 #define HWCAP_IWMMXT    512
65 #define HWCAP_CRUNCH    1024
66 #define HWCAP_THUMBEE   2048
67 #define HWCAP_NEON      4096
68 #define HWCAP_VFPv3     8192
69 #define HWCAP_VFPv3D16  16384
70
71 // copied from <linux/auxvec.h>
72 #define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
73
74 #endif
75
76 QT_BEGIN_NAMESPACE
77
78 #if defined (Q_OS_NACL)
79 static inline uint detectProcessorFeatures()
80 {
81     return 0;
82 }
83 #elif defined (Q_OS_WINCE)
84 static inline uint detectProcessorFeatures()
85 {
86     uint features = 0;
87
88 #if defined (ARM)
89     if (IsProcessorFeaturePresent(PF_ARM_INTEL_WMMX)) {
90         features = IWMMXT;
91         return features;
92     }
93 #elif defined(_X86_)
94     features = 0;
95     if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
96         features |= SSE2;
97     if (IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE))
98         features |= SSE3;
99     return features;
100 #endif
101     features = 0;
102     return features;
103 }
104
105 #elif defined(Q_PROCESSOR_ARM) || defined(QT_HAVE_IWMMXT) || defined(QT_HAVE_NEON)
106 static inline uint detectProcessorFeatures()
107 {
108     uint features = 0;
109
110 #if defined(Q_OS_LINUX)
111     int auxv = qt_safe_open("/proc/self/auxv", O_RDONLY);
112     if (auxv != -1) {
113         unsigned long vector[64];
114         int nread;
115         while (features == 0) {
116             nread = qt_safe_read(auxv, (char *)vector, sizeof vector);
117             if (nread <= 0) {
118                 // EOF or error
119                 break;
120             }
121
122             int max = nread / (sizeof vector[0]);
123             for (int i = 0; i < max; i += 2)
124                 if (vector[i] == AT_HWCAP) {
125                     if (vector[i+1] & HWCAP_IWMMXT)
126                         features |= IWMMXT;
127                     if (vector[i+1] & HWCAP_NEON)
128                         features |= NEON;
129                     break;
130                 }
131         }
132
133         qt_safe_close(auxv);
134         return features;
135     }
136     // fall back if /proc/self/auxv wasn't found
137 #endif
138
139 #if defined(QT_HAVE_IWMMXT)
140     // runtime detection only available when running as a previlegied process
141     features = IWMMXT;
142 #elif defined(QT_ALWAYS_HAVE_NEON)
143     features = NEON;
144 #endif
145
146     return features;
147 }
148
149 #elif defined(Q_PROCESSOR_X86)
150
151 #ifdef Q_PROCESSOR_X86_32
152 # define PICreg "%%ebx"
153 #else
154 # define PICreg "%%rbx"
155 #endif
156
157 static int maxBasicCpuidSupported()
158 {
159 #if defined(Q_CC_GNU)
160     qintptr tmp1;
161
162 # ifdef Q_PROCESSOR_X86_32
163     // check if the CPUID instruction is supported
164     long cpuid_supported;
165     asm ("pushf\n"
166          "pop %0\n"
167          "mov %0, %1\n"
168          "xor $0x00200000, %0\n"
169          "push %0\n"
170          "popf\n"
171          "pushf\n"
172          "pop %0\n"
173          "xor %1, %0\n" // %eax is now 0 if CPUID is not supported
174          : "=a" (cpuid_supported), "=r" (tmp1)
175          );
176     if (!cpuid_supported)
177         return 0;
178 # endif
179
180     int result;
181     asm ("xchg " PICreg", %1\n"
182          "cpuid\n"
183          "xchg " PICreg", %1\n"
184         : "=&a" (result), "=&r" (tmp1)
185         : "0" (0)
186         : "ecx", "edx");
187     return result;
188 #elif defined(Q_OS_WIN)
189     // Use the __cpuid function; if the CPUID instruction isn't supported, it will return 0
190     int info[4];
191     __cpuid(info, 0);
192     return info[0];
193 #else
194     return 0;
195 #endif
196 }
197
198 static void cpuidFeatures01(uint &ecx, uint &edx)
199 {
200 #if defined(Q_CC_GNU)
201     qintptr tmp1;
202     asm ("xchg " PICreg", %2\n"
203          "cpuid\n"
204          "xchg " PICreg", %2\n"
205         : "=&c" (ecx), "=&d" (edx), "=&r" (tmp1)
206         : "a" (1));
207 #elif defined(Q_OS_WIN)
208     int info[4];
209     __cpuid(info, 1);
210     ecx = info[2];
211     edx = info[3];
212 #endif
213 }
214
215 #ifdef Q_OS_WIN
216 inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int));}
217 #endif
218
219 static void cpuidFeatures07_00(uint &ebx)
220 {
221 #if defined(Q_CC_GNU)
222     quintptr rbx; // in case it's 64-bit
223     asm ("xchg " PICreg", %0\n"
224          "cpuid\n"
225          "xchg " PICreg", %0\n"
226         : "=&r" (rbx)
227         : "a" (7), "c" (0)
228         : "%edx");
229     ebx = rbx;
230 #elif defined(Q_OS_WIN)
231     int info[4];
232     __cpuidex(info, 7, 0);
233     ebx = info[1];
234 #endif
235 }
236
237 #ifdef Q_OS_WIN
238 inline quint64 _xgetbv(__int64) { return 0; }
239 #endif
240
241 static void xgetbv(int in, uint &eax, uint &edx)
242 {
243 #ifdef Q_OS_WIN
244     quint64 result = _xgetbv(in);
245     eax = result;
246     edx = result >> 32;
247 #elif defined(Q_CC_GNU)
248     asm (".byte 0x0F, 0x01, 0xD0" // xgetbv instruction
249         : "=a" (eax), "=d" (edx)
250         : "c" (in));
251 #endif
252 }
253
254 static inline uint detectProcessorFeatures()
255 {
256     uint features = 0;
257     int cpuidLevel = maxBasicCpuidSupported();
258     if (cpuidLevel < 1)
259         return 0;
260
261     uint cpuid01ECX = 0, cpuid01EDX = 0;
262     cpuidFeatures01(cpuid01ECX, cpuid01EDX);
263 #if defined(Q_PROCESSOR_X86_32)
264     // x86 might not have SSE2 support
265     if (cpuid01EDX & (1u << 26))
266         features |= SSE2;
267 #else
268     // x86-64 or x32
269     features = SSE2;
270 #endif
271
272     // common part between 32- and 64-bit
273     if (cpuid01ECX & (1u))
274         features |= SSE3;
275     if (cpuid01ECX & (1u << 9))
276         features |= SSSE3;
277     if (cpuid01ECX & (1u << 19))
278         features |= SSE4_1;
279     if (cpuid01ECX & (1u << 20))
280         features |= SSE4_2;
281     if (cpuid01ECX & (1u << 25))
282         features |= 0; // AES, enable if needed
283
284     uint xgetbvA = 0, xgetbvD = 0;
285     if (cpuid01ECX & (1u << 27)) {
286         // XGETBV enabled
287         xgetbv(0, xgetbvA, xgetbvD);
288     }
289
290     uint cpuid0700EBX = 0;
291     if (cpuidLevel >= 7)
292         cpuidFeatures07_00(cpuid0700EBX);
293
294     if ((xgetbvA & 6) == 6) {
295         // support for YMM and XMM registers is enabled
296         if (cpuid01ECX & (1u << 28))
297             features |= AVX;
298
299         if (cpuid0700EBX & (1u << 5))
300             features |= AVX2;
301     }
302
303     if (cpuid0700EBX & (1u << 4))
304         features |= HLE; // Hardware Lock Ellision
305     if (cpuid0700EBX & (1u << 11))
306         features |= RTM; // Restricted Transactional Memory
307
308     return features;
309 }
310
311
312 #else
313 static inline uint detectProcessorFeatures()
314 {
315     return 0;
316 }
317 #endif
318
319 /*
320  * Use kdesdk/scripts/generate_string_table.pl to update the table below.
321  * Here's the data (don't forget the ONE leading space):
322  iwmmxt
323  neon
324  sse2
325  sse3
326  ssse3
327  sse4.1
328  sse4.2
329  avx
330  avx2
331  hle
332  rtm
333   */
334
335 // begin generated
336 static const char features_string[] =
337     " iwmmxt\0"
338     " neon\0"
339     " sse2\0"
340     " sse3\0"
341     " ssse3\0"
342     " sse4.1\0"
343     " sse4.2\0"
344     " avx\0"
345     " avx2\0"
346     " hle\0"
347     " rtm\0"
348     "\0";
349
350 static const int features_indices[] = {
351     0,    8,   14,   20,   26,   33,   41,   49,
352    54,   60,   65,   -1
353 };
354 // end generated
355
356 static const int features_count = (sizeof features_indices - 1) / (sizeof features_indices[0]);
357
358 static const uint minFeature = None
359 #if defined __RTM__
360                                | RTM
361 #endif
362 // don't define for HLE, since the HLE prefix can be run on older CPUs
363 #if defined __AVX2__
364                                | AVX2
365 #endif
366 #if defined __AVX__
367                                | AVX
368 #endif
369 #if defined __SSE4_2__
370                                | SSE4_2
371 #endif
372 #if defined __SSE4_1__
373                                | SSE4_1
374 #endif
375 #if defined __SSSE3__
376                                | SSSE3
377 #endif
378 #if defined __SSE3__
379                                | SSE3
380 #endif
381 #if defined __SSE2__
382                                | SSE2
383 #endif
384 #if defined __ARM_NEON__
385                                | NEON
386 #endif
387 #if defined __IWMMXT__
388                                | IWMMXT
389 #endif
390                                ;
391
392 #ifdef Q_OS_WIN
393 #if defined(Q_CC_GNU)
394 #  define ffs __builtin_ffs
395 #else
396 int ffs(int i)
397 {
398 #ifndef Q_OS_WINCE
399     unsigned long result;
400     return _BitScanForward(&result, i) ? result : 0;
401 #else
402     return 0;
403 #endif
404 }
405 #endif
406 #endif // Q_OS_WIN
407
408 uint qDetectCPUFeatures()
409 {
410     static QBasicAtomicInt features = Q_BASIC_ATOMIC_INITIALIZER(-1);
411     if (features.load() != -1)
412         return features.load();
413
414     uint f = detectProcessorFeatures();
415     QByteArray disable = qgetenv("QT_NO_CPU_FEATURE");
416     if (!disable.isEmpty()) {
417         disable.prepend(' ');
418         for (int i = 0; i < features_count; ++i) {
419             if (disable.contains(features_string + features_indices[i]))
420                 f &= ~(1 << i);
421         }
422     }
423
424     if (minFeature != 0 && (f & minFeature) != minFeature) {
425         uint missing = minFeature & ~f;
426         fprintf(stderr, "Incompatible processor. This Qt build requires the following features:\n   ");
427         for (int i = 0; i < features_count; ++i) {
428             if (missing & (1 << i))
429                 fprintf(stderr, "%s", features_string + features_indices[i]);
430         }
431         fprintf(stderr, "\n");
432         fflush(stderr);
433         qFatal("Aborted. Incompatible processor: missing feature 0x%x -%s.", missing,
434                features_string + features_indices[ffs(missing) - 1]);
435     }
436
437     features.store(f);
438     return f;
439 }
440
441 void qDumpCPUFeatures()
442 {
443     uint features = qDetectCPUFeatures();
444     printf("Processor features: ");
445     for (int i = 0; i < features_count; ++i) {
446         if (features & (1 << i))
447             printf("%s%s", features_string + features_indices[i],
448                    minFeature & (1 << i) ? "[required]" : "");
449     }
450     puts("");
451 }
452
453 QT_END_NAMESPACE