/
SDL_cpuinfo.c
561 lines (519 loc) · 16.9 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2012 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
20
21
Sam Lantinga
slouken@libsdl.org
*/
22
#include "SDL_config.h"
23
24
25
/* CPU feature detection for SDL */
26
27
28
#include "SDL.h"
#include "SDL_cpuinfo.h"
29
#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
30
#include <sys/sysctl.h> /* For AltiVec check */
31
32
33
34
#elif defined(__OpenBSD__) && defined(__powerpc__)
#include <sys/param.h>
#include <sys/sysctl.h> /* For AltiVec check */
#include <machine/cpu.h>
35
36
37
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
#include <signal.h>
#include <setjmp.h>
38
39
#endif
40
41
#define CPU_HAS_RDTSC 0x00000001
#define CPU_HAS_MMX 0x00000002
42
43
44
45
46
47
#define CPU_HAS_MMXEXT 0x00000004
#define CPU_HAS_3DNOW 0x00000010
#define CPU_HAS_3DNOWEXT 0x00000020
#define CPU_HAS_SSE 0x00000040
#define CPU_HAS_SSE2 0x00000080
#define CPU_HAS_ALTIVEC 0x00000100
48
#define CPU_HAS_ARM_SIMD 0x00000200
49
50
#if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
51
52
53
54
55
56
57
58
/* This is the brute force way of detecting instruction sets...
the idea is borrowed from the libmpeg2 library - thanks!
*/
static jmp_buf jmpbuf;
static void illegal_instruction(int sig)
{
longjmp(jmpbuf, 1);
}
59
#endif /* HAVE_SETJMP */
60
61
static __inline__ int CPU_haveCPUID(void)
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{
int has_CPUID = 0;
#if defined(__GNUC__) && defined(i386)
__asm__ (
" pushfl # Get original EFLAGS \n"
" popl %%eax \n"
" movl %%eax,%%ecx \n"
" xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
" pushl %%eax # Save new EFLAGS value on stack \n"
" popfl # Replace current EFLAGS value \n"
" pushfl # Get new EFLAGS \n"
" popl %%eax # Store new EFLAGS in EAX \n"
" xorl %%ecx,%%eax # Can not toggle ID bit, \n"
" jz 1f # Processor=80486 \n"
" movl $1,%0 # We have CPUID support \n"
"1: \n"
78
: "=m" (has_CPUID)
79
80
81
:
: "%eax", "%ecx"
);
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#elif defined(__GNUC__) && defined(__x86_64__)
/* Technically, if this is being compiled under __x86_64__ then it has
CPUid by definition. But it's nice to be able to prove it. :) */
__asm__ (
" pushfq # Get original EFLAGS \n"
" popq %%rax \n"
" movq %%rax,%%rcx \n"
" xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
" pushq %%rax # Save new EFLAGS value on stack \n"
" popfq # Replace current EFLAGS value \n"
" pushfq # Get new EFLAGS \n"
" popq %%rax # Store new EFLAGS in EAX \n"
" xorl %%ecx,%%eax # Can not toggle ID bit, \n"
" jz 1f # Processor=80486 \n"
" movl $1,%0 # We have CPUID support \n"
"1: \n"
: "=m" (has_CPUID)
:
: "%rax", "%rcx"
);
102
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
103
__asm {
104
105
106
107
108
109
110
111
112
113
114
115
116
pushfd ; Get original EFLAGS
pop eax
mov ecx, eax
xor eax, 200000h ; Flip ID bit in EFLAGS
push eax ; Save new EFLAGS value on stack
popfd ; Replace current EFLAGS value
pushfd ; Get new EFLAGS
pop eax ; Store new EFLAGS in EAX
xor eax, ecx ; Can not toggle ID bit,
jz done ; Processor=80486
mov has_CPUID,1 ; We have CPUID support
done:
}
117
#elif defined(__sun) && defined(__i386)
118
__asm (
119
" pushfl \n"
120
121
122
123
124
125
126
127
128
129
" popl %eax \n"
" movl %eax,%ecx \n"
" xorl $0x200000,%eax \n"
" pushl %eax \n"
" popfl \n"
" pushfl \n"
" popl %eax \n"
" xorl %ecx,%eax \n"
" jz 1f \n"
" movl $1,-8(%ebp) \n"
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"1: \n"
);
#elif defined(__sun) && defined(__amd64)
__asm (
" pushfq \n"
" popq %rax \n"
" movq %rax,%rcx \n"
" xorl $0x200000,%eax \n"
" pushq %rax \n"
" popfq \n"
" pushfq \n"
" popq %rax \n"
" xorl %ecx,%eax \n"
" jz 1f \n"
" movl $1,-8(%rbp) \n"
"1: \n"
);
147
148
149
150
#endif
return has_CPUID;
}
151
static __inline__ int CPU_getCPUIDFeatures(void)
152
153
{
int features = 0;
154
#if defined(__GNUC__) && defined(i386)
155
156
__asm__ (
" xorl %%eax,%%eax # Set up for CPUID instruction \n"
157
" pushl %%ebx \n"
158
" cpuid # Get and save vendor ID \n"
159
" popl %%ebx \n"
160
161
162
163
" cmpl $1,%%eax # Make sure 1 is valid input for CPUID\n"
" jl 1f # We dont have the CPUID instruction\n"
" xorl %%eax,%%eax \n"
" incl %%eax \n"
164
" pushl %%ebx \n"
165
" cpuid # Get family/model/stepping/features\n"
166
" popl %%ebx \n"
167
168
" movl %%edx,%0 \n"
"1: \n"
169
: "=m" (features)
170
:
171
: "%eax", "%ecx", "%edx"
172
);
173
174
175
#elif defined(__GNUC__) && defined(__x86_64__)
__asm__ (
" xorl %%eax,%%eax # Set up for CPUID instruction \n"
176
" pushq %%rbx \n"
177
" cpuid # Get and save vendor ID \n"
178
" popq %%rbx \n"
179
180
181
182
" cmpl $1,%%eax # Make sure 1 is valid input for CPUID\n"
" jl 1f # We dont have the CPUID instruction\n"
" xorl %%eax,%%eax \n"
" incl %%eax \n"
183
" pushq %%rbx \n"
184
" cpuid # Get family/model/stepping/features\n"
185
" popq %%rbx \n"
186
187
188
189
" movl %%edx,%0 \n"
"1: \n"
: "=m" (features)
:
190
: "%rax", "%rcx", "%rdx"
191
);
192
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
193
__asm {
194
xor eax, eax ; Set up for CPUID instruction
195
push ebx
196
cpuid ; Get and save vendor ID
197
pop ebx
198
199
200
201
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl done ; We dont have the CPUID instruction
xor eax, eax
inc eax
202
push ebx
203
cpuid ; Get family/model/stepping/features
204
pop ebx
205
206
207
mov features, edx
done:
}
208
#elif defined(__sun) && (defined(__i386) || defined(__amd64))
209
210
__asm(
" xorl %eax,%eax \n"
211
" pushl %ebx \n"
212
" cpuid \n"
213
" popl %ebx \n"
214
215
216
217
" cmpl $1,%eax \n"
" jl 1f \n"
" xorl %eax,%eax \n"
" incl %eax \n"
218
" pushl %ebx \n"
219
" cpuid \n"
220
" popl %ebx \n"
221
222
223
224
225
226
#ifdef __i386
" movl %edx,-8(%ebp) \n"
#else
" movl %edx,-8(%rbp) \n"
#endif
"1: \n"
227
);
228
229
230
231
#endif
return features;
}
232
static __inline__ int CPU_getCPUIDFeaturesExt(void)
233
{
234
int features = 0;
235
#if defined(__GNUC__) && defined(i386)
236
237
__asm__ (
" movl $0x80000000,%%eax # Query for extended functions \n"
238
" pushl %%ebx \n"
239
" cpuid # Get extended function limit \n"
240
" popl %%ebx \n"
241
" cmpl $0x80000001,%%eax \n"
242
" jl 1f # Nope, we dont have function 800000001h\n"
243
" movl $0x80000001,%%eax # Setup extended function 800000001h\n"
244
" pushl %%ebx \n"
245
" cpuid # and get the information \n"
246
" popl %%ebx \n"
247
" movl %%edx,%0 \n"
248
"1: \n"
249
: "=m" (features)
250
:
251
: "%eax", "%ecx", "%edx"
252
);
253
254
255
#elif defined(__GNUC__) && defined (__x86_64__)
__asm__ (
" movl $0x80000000,%%eax # Query for extended functions \n"
256
" pushq %%rbx \n"
257
" cpuid # Get extended function limit \n"
258
" popq %%rbx \n"
259
260
261
" cmpl $0x80000001,%%eax \n"
" jl 1f # Nope, we dont have function 800000001h\n"
" movl $0x80000001,%%eax # Setup extended function 800000001h\n"
262
" pushq %%rbx \n"
263
" cpuid # and get the information \n"
264
" popq %%rbx \n"
265
266
267
268
" movl %%edx,%0 \n"
"1: \n"
: "=m" (features)
:
269
: "%rax", "%rcx", "%rdx"
270
);
271
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
272
__asm {
273
mov eax,80000000h ; Query for extended functions
274
push ebx
275
cpuid ; Get extended function limit
276
pop ebx
277
cmp eax,80000001h
278
jl done ; Nope, we dont have function 800000001h
279
mov eax,80000001h ; Setup extended function 800000001h
280
push ebx
281
cpuid ; and get the information
282
pop ebx
283
mov features,edx
284
285
done:
}
286
287
288
#elif defined(__sun) && ( defined(__i386) || defined(__amd64) )
__asm (
" movl $0x80000000,%eax \n"
289
" pushl %ebx \n"
290
" cpuid \n"
291
" popl %ebx \n"
292
293
294
" cmpl $0x80000001,%eax \n"
" jl 1f \n"
" movl $0x80000001,%eax \n"
295
" pushl %ebx \n"
296
" cpuid \n"
297
" popl %ebx \n"
298
299
300
301
302
303
304
#ifdef __i386
" movl %edx,-8(%ebp) \n"
#else
" movl %edx,-8(%rbp) \n"
#endif
"1: \n"
);
305
#endif
306
307
308
return features;
}
309
static __inline__ int CPU_haveRDTSC(void)
310
311
312
313
314
315
316
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x00000010);
}
return 0;
}
317
static __inline__ int CPU_haveMMX(void)
318
319
320
321
322
323
324
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x00800000);
}
return 0;
}
325
static __inline__ int CPU_haveMMXExt(void)
326
327
328
329
330
331
332
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x00400000);
}
return 0;
}
333
static __inline__ int CPU_have3DNow(void)
334
335
336
337
338
339
340
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x80000000);
}
return 0;
}
341
static __inline__ int CPU_have3DNowExt(void)
342
343
344
345
346
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x40000000);
}
return 0;
347
348
}
349
static __inline__ int CPU_haveSSE(void)
350
351
352
353
354
355
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x02000000);
}
return 0;
}
356
357
static __inline__ int CPU_haveSSE2(void)
358
359
360
361
362
363
364
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x04000000);
}
return 0;
}
365
static __inline__ int CPU_haveAltiVec(void)
366
{
367
volatile int altivec = 0;
368
369
370
371
372
373
#if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
# ifdef __OpenBSD__
int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
# else
int selectors[2] = { CTL_HW, HW_VECTORUNIT };
# endif
374
375
376
377
int hasVectorUnit = 0;
size_t length = sizeof(hasVectorUnit);
int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
if( 0 == error )
378
altivec = (hasVectorUnit != 0);
379
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
380
381
382
383
384
385
386
387
388
389
void (*handler)(int sig);
handler = signal(SIGILL, illegal_instruction);
if ( setjmp(jmpbuf) == 0 ) {
asm volatile ("mtspr 256, %0\n\t"
"vand %%v0, %%v0, %%v0"
:
: "r" (-1));
altivec = 1;
}
signal(SIGILL, handler);
390
#endif
391
return altivec;
392
393
}
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
#ifdef __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <elf.h>
static __inline__ int CPU_haveARMSIMD(void)
{
int arm_simd = 0;
int fd;
fd = open("/proc/self/auxv", O_RDONLY);
if (fd >= 0)
{
Elf32_auxv_t aux;
while (read(fd, &aux, sizeof aux) == sizeof aux)
{
if (aux.a_type == AT_PLATFORM)
{
const char *plat = (const char *) aux.a_un.a_val;
arm_simd = strncmp(plat, "v6l", 3) == 0 ||
strncmp(plat, "v7l", 3) == 0;
}
}
close(fd);
}
return arm_simd;
}
#else
static __inline__ int CPU_haveARMSIMD(void)
{
return 0;
}
#endif
434
435
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
436
static Uint32 SDL_GetCPUFeatures(void)
437
438
439
{
if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
SDL_CPUFeatures = 0;
440
441
442
if ( CPU_haveRDTSC() ) {
SDL_CPUFeatures |= CPU_HAS_RDTSC;
}
443
444
445
if ( CPU_haveMMX() ) {
SDL_CPUFeatures |= CPU_HAS_MMX;
}
446
447
448
if ( CPU_haveMMXExt() ) {
SDL_CPUFeatures |= CPU_HAS_MMXEXT;
}
449
450
451
if ( CPU_have3DNow() ) {
SDL_CPUFeatures |= CPU_HAS_3DNOW;
}
452
453
454
if ( CPU_have3DNowExt() ) {
SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
}
455
456
457
if ( CPU_haveSSE() ) {
SDL_CPUFeatures |= CPU_HAS_SSE;
}
458
459
460
if ( CPU_haveSSE2() ) {
SDL_CPUFeatures |= CPU_HAS_SSE2;
}
461
462
463
if ( CPU_haveAltiVec() ) {
SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
}
464
465
466
if ( CPU_haveARMSIMD() ) {
SDL_CPUFeatures |= CPU_HAS_ARM_SIMD;
}
467
468
469
470
}
return SDL_CPUFeatures;
}
471
SDL_bool SDL_HasRDTSC(void)
472
473
474
475
476
477
478
{
if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
return SDL_TRUE;
}
return SDL_FALSE;
}
479
SDL_bool SDL_HasMMX(void)
480
481
482
483
484
485
486
{
if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
return SDL_TRUE;
}
return SDL_FALSE;
}
487
SDL_bool SDL_HasMMXExt(void)
488
{
489
if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
490
491
492
493
494
return SDL_TRUE;
}
return SDL_FALSE;
}
495
SDL_bool SDL_Has3DNow(void)
496
{
497
if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
498
499
500
501
502
return SDL_TRUE;
}
return SDL_FALSE;
}
503
SDL_bool SDL_Has3DNowExt(void)
504
{
505
if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
506
507
508
509
510
return SDL_TRUE;
}
return SDL_FALSE;
}
511
SDL_bool SDL_HasSSE(void)
512
{
513
514
515
516
if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
return SDL_TRUE;
}
return SDL_FALSE;
517
518
}
519
SDL_bool SDL_HasSSE2(void)
520
{
521
522
523
524
if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
return SDL_TRUE;
}
return SDL_FALSE;
525
526
}
527
SDL_bool SDL_HasAltiVec(void)
528
{
529
530
531
532
if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
return SDL_TRUE;
}
return SDL_FALSE;
533
534
}
535
536
537
538
539
540
541
542
SDL_bool SDL_HasARMSIMD(void)
{
if ( SDL_GetCPUFeatures() & CPU_HAS_ARM_SIMD ) {
return SDL_TRUE;
}
return SDL_FALSE;
}
543
544
545
546
547
548
#ifdef TEST_MAIN
#include <stdio.h>
int main()
{
549
printf("RDTSC: %d\n", SDL_HasRDTSC());
550
printf("MMX: %d\n", SDL_HasMMX());
551
printf("MMXExt: %d\n", SDL_HasMMXExt());
552
printf("3DNow: %d\n", SDL_Has3DNow());
553
printf("3DNowExt: %d\n", SDL_Has3DNowExt());
554
printf("SSE: %d\n", SDL_HasSSE());
555
printf("SSE2: %d\n", SDL_HasSSE2());
556
printf("AltiVec: %d\n", SDL_HasAltiVec());
557
printf("ARM SIMD: %d\n", SDL_HasARMSIMD());
558
return 0;
559
560
561
}
#endif /* TEST_MAIN */