This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_cpuinfo.c
625 lines (571 loc) · 17.9 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2011 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
#include "SDL_cpuinfo.h"
28
29
30
#ifdef HAVE_SYSCONF
#include <unistd.h>
#endif
31
32
33
34
#ifdef HAVE_SYSCTLBYNAME
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
35
36
37
38
39
40
#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
#include <sys/sysctl.h> /* For AltiVec check */
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
#include <signal.h>
#include <setjmp.h>
#endif
41
#ifdef __WIN32__
42
#include "../core/windows/SDL_windows.h"
43
#endif
44
45
#define CPU_HAS_RDTSC 0x00000001
46
47
48
#define CPU_HAS_ALTIVEC 0x00000002
#define CPU_HAS_MMX 0x00000004
#define CPU_HAS_3DNOW 0x00000008
49
50
51
#define CPU_HAS_SSE 0x00000010
#define CPU_HAS_SSE2 0x00000020
#define CPU_HAS_SSE3 0x00000040
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#define CPU_HAS_SSE41 0x00000100
#define CPU_HAS_SSE42 0x00000200
#if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__
/* 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);
}
#endif /* HAVE_SETJMP */
66
67
68
static __inline__ int
CPU_haveCPUID(void)
69
{
70
71
int has_CPUID = 0;
/* *INDENT-OFF* */
72
#if defined(__GNUC__) && defined(i386)
73
__asm__ (
74
75
76
77
78
79
80
81
82
83
84
85
" 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"
86
87
88
89
: "=m" (has_CPUID)
:
: "%eax", "%ecx"
);
90
91
92
#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. :) */
93
__asm__ (
94
95
96
97
98
99
100
101
102
103
104
105
" 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"
106
107
108
109
: "=m" (has_CPUID)
:
: "%rax", "%rcx"
);
110
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
111
__asm {
112
113
114
115
116
117
118
119
120
121
122
123
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:
124
}
125
#elif defined(__sun) && defined(__i386)
126
__asm (
127
" pushfl \n"
128
129
130
131
132
133
134
135
136
137
" 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"
138
"1: \n"
139
);
140
#elif defined(__sun) && defined(__amd64)
141
__asm (
142
143
144
145
146
147
148
149
150
151
152
153
" 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"
154
);
155
#endif
156
157
/* *INDENT-ON* */
return has_CPUID;
158
159
}
160
#if defined(__GNUC__) && defined(i386)
161
#define cpuid(func, a, b, c, d) \
162
163
164
165
166
167
__asm__ __volatile__ ( \
" pushl %%ebx \n" \
" cpuid \n" \
" movl %%ebx, %%esi \n" \
" popl %%ebx \n" : \
"=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
168
169
170
171
172
173
174
175
#elif defined(__GNUC__) && defined(__x86_64__)
#define cpuid(func, a, b, c, d) \
__asm__ __volatile__ ( \
" pushq %%rbx \n" \
" cpuid \n" \
" movq %%rbx, %%rsi \n" \
" popq %%rbx \n" : \
"=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
176
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
177
178
179
180
181
182
183
184
#define cpuid(func, a, b, c, d) \
__asm { \
__asm mov eax, func \
__asm cpuid \
__asm mov a, eax \
__asm mov b, ebx \
__asm mov c, ecx \
__asm mov d, edx \
185
186
}
#else
187
188
#define cpuid(func, a, b, c, d) \
a = b = c = d = 0
189
190
#endif
191
192
static __inline__ int
CPU_getCPUIDFeatures(void)
193
{
194
int features = 0;
195
int a, b, c, d;
196
197
198
199
200
cpuid(0, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
features = d;
201
}
202
return features;
203
204
}
205
206
static __inline__ int
CPU_haveRDTSC(void)
207
{
208
209
210
211
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00000010);
}
return 0;
212
213
}
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
static __inline__ int
CPU_haveAltiVec(void)
{
volatile int altivec = 0;
#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
int selectors[2] = { CTL_HW, HW_VECTORUNIT };
int hasVectorUnit = 0;
size_t length = sizeof(hasVectorUnit);
int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
if (0 == error)
altivec = (hasVectorUnit != 0);
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
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);
#endif
return altivec;
}
237
238
static __inline__ int
CPU_haveMMX(void)
239
{
240
241
242
243
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00800000);
}
return 0;
244
245
}
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
static __inline__ int
CPU_have3DNow(void)
{
if (CPU_haveCPUID()) {
int a, b, c, d;
cpuid(0x80000000, a, b, c, d);
if (a >= 0x80000001) {
cpuid(0x80000001, a, b, c, d);
return (d & 0x80000000);
}
}
return 0;
}
261
static __inline__ int
262
CPU_haveSSE(void)
263
{
264
if (CPU_haveCPUID()) {
265
return (CPU_getCPUIDFeatures() & 0x02000000);
266
267
}
return 0;
268
269
}
270
static __inline__ int
271
CPU_haveSSE2(void)
272
{
273
if (CPU_haveCPUID()) {
274
return (CPU_getCPUIDFeatures() & 0x04000000);
275
276
}
return 0;
277
278
}
279
static __inline__ int
280
CPU_haveSSE3(void)
281
{
282
if (CPU_haveCPUID()) {
283
int a, b, c, d;
284
285
286
287
288
289
cpuid(0, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
return (c & 0x00000001);
}
290
291
}
return 0;
292
}
293
294
static __inline__ int
295
CPU_haveSSE41(void)
296
{
297
if (CPU_haveCPUID()) {
298
int a, b, c, d;
299
300
cpuid(1, a, b, c, d);
301
302
if (a >= 1) {
cpuid(1, a, b, c, d);
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
return (c & 0x00080000);
}
}
return 0;
}
static __inline__ int
CPU_haveSSE42(void)
{
if (CPU_haveCPUID()) {
int a, b, c, d;
cpuid(1, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
return (c & 0x00100000);
319
}
320
}
321
return 0;
322
323
}
324
325
326
static int SDL_CPUCount = 0;
int
327
SDL_GetCPUCount(void)
328
329
{
if (!SDL_CPUCount) {
330
#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
331
332
333
334
if (SDL_CPUCount <= 0) {
SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
}
#endif
335
#ifdef HAVE_SYSCTLBYNAME
336
if (SDL_CPUCount <= 0) {
337
338
339
340
size_t size = sizeof(SDL_CPUCount);
sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
}
#endif
341
#ifdef __WIN32__
342
if (SDL_CPUCount <= 0) {
343
344
345
346
SYSTEM_INFO info;
GetSystemInfo(&info);
SDL_CPUCount = info.dwNumberOfProcessors;
}
347
348
#endif
/* There has to be at least 1, right? :) */
349
if (SDL_CPUCount <= 0) {
350
351
352
353
354
355
356
SDL_CPUCount = 1;
}
}
return SDL_CPUCount;
}
/* Oh, such a sweet sweet trick, just not very useful. :) */
357
static const char *
358
SDL_GetCPUType(void)
359
{
360
static char SDL_CPUType[13];
361
362
363
if (!SDL_CPUType[0]) {
int i = 0;
364
int a, b, c, d;
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
if (CPU_haveCPUID()) {
cpuid(0x00000000, a, b, c, d);
SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
}
if (!SDL_CPUType[0]) {
SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
}
}
return SDL_CPUType;
}
static const char *
389
SDL_GetCPUName(void)
390
391
392
393
394
395
396
{
static char SDL_CPUName[48];
if (!SDL_CPUName[0]) {
int i = 0;
int a, b, c, d;
397
if (CPU_haveCPUID()) {
398
399
400
cpuid(0x80000000, a, b, c, d);
if (a >= 0x80000004) {
cpuid(0x80000002, a, b, c, d);
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
417
cpuid(0x80000003, a, b, c, d);
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
434
cpuid(0x80000004, a, b, c, d);
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
451
452
}
}
453
454
if (!SDL_CPUName[0]) {
SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
455
456
}
}
457
458
459
return SDL_CPUName;
}
460
461
int
SDL_GetCPUCacheLineSize(void)
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
{
const char *cpuType = SDL_GetCPUType();
if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
int a, b, c, d;
cpuid(0x00000001, a, b, c, d);
return (((b >> 8) & 0xff) * 8);
} else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
int a, b, c, d;
cpuid(0x80000005, a, b, c, d);
return (c & 0xff);
} else {
/* Just make a guess here... */
return SDL_CACHELINE_SIZE;
}
479
480
}
481
482
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
483
484
static Uint32
SDL_GetCPUFeatures(void)
485
{
486
487
488
489
490
if (SDL_CPUFeatures == 0xFFFFFFFF) {
SDL_CPUFeatures = 0;
if (CPU_haveRDTSC()) {
SDL_CPUFeatures |= CPU_HAS_RDTSC;
}
491
492
493
if (CPU_haveAltiVec()) {
SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
}
494
495
496
if (CPU_haveMMX()) {
SDL_CPUFeatures |= CPU_HAS_MMX;
}
497
498
499
if (CPU_have3DNow()) {
SDL_CPUFeatures |= CPU_HAS_3DNOW;
}
500
501
502
503
504
505
if (CPU_haveSSE()) {
SDL_CPUFeatures |= CPU_HAS_SSE;
}
if (CPU_haveSSE2()) {
SDL_CPUFeatures |= CPU_HAS_SSE2;
}
506
507
508
if (CPU_haveSSE3()) {
SDL_CPUFeatures |= CPU_HAS_SSE3;
}
509
510
511
512
513
if (CPU_haveSSE41()) {
SDL_CPUFeatures |= CPU_HAS_SSE41;
}
if (CPU_haveSSE42()) {
SDL_CPUFeatures |= CPU_HAS_SSE42;
514
515
516
}
}
return SDL_CPUFeatures;
517
518
}
519
520
SDL_bool
SDL_HasRDTSC(void)
521
{
522
523
524
525
if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
return SDL_TRUE;
}
return SDL_FALSE;
526
527
}
528
529
530
531
532
533
534
535
536
SDL_bool
SDL_HasAltiVec(void)
{
if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
return SDL_TRUE;
}
return SDL_FALSE;
}
537
538
SDL_bool
SDL_HasMMX(void)
539
{
540
541
542
543
if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
return SDL_TRUE;
}
return SDL_FALSE;
544
545
}
546
547
548
549
550
551
552
553
554
SDL_bool
SDL_Has3DNow(void)
{
if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
return SDL_TRUE;
}
return SDL_FALSE;
}
555
SDL_bool
556
SDL_HasSSE(void)
557
{
558
if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
559
560
561
return SDL_TRUE;
}
return SDL_FALSE;
562
563
}
564
SDL_bool
565
SDL_HasSSE2(void)
566
{
567
if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
568
569
570
return SDL_TRUE;
}
return SDL_FALSE;
571
572
}
573
SDL_bool
574
SDL_HasSSE3(void)
575
{
576
if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
577
578
579
return SDL_TRUE;
}
return SDL_FALSE;
580
581
}
582
SDL_bool
583
584
585
586
587
588
589
590
591
592
SDL_HasSSE41(void)
{
if (SDL_GetCPUFeatures() & CPU_HAS_SSE41) {
return SDL_TRUE;
}
return SDL_FALSE;
}
SDL_bool
SDL_HasSSE42(void)
593
{
594
if (SDL_GetCPUFeatures() & CPU_HAS_SSE42) {
595
596
597
return SDL_TRUE;
}
return SDL_FALSE;
598
599
}
600
601
602
603
#ifdef TEST_MAIN
#include <stdio.h>
604
605
int
main()
606
{
607
printf("CPU count: %d\n", SDL_GetCPUCount());
608
609
610
printf("CPU type: %s\n", SDL_GetCPUType());
printf("CPU name: %s\n", SDL_GetCPUName());
printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
611
printf("RDTSC: %d\n", SDL_HasRDTSC());
612
printf("Altivec: %d\n", SDL_HasAltiVec());
613
printf("MMX: %d\n", SDL_HasMMX());
614
printf("3DNow: %d\n", SDL_Has3DNow());
615
616
printf("SSE: %d\n", SDL_HasSSE());
printf("SSE2: %d\n", SDL_HasSSE2());
617
printf("SSE3: %d\n", SDL_HasSSE3());
618
619
printf("SSE4.1: %d\n", SDL_HasSSE41());
printf("SSE4.2: %d\n", SDL_HasSSE42());
620
return 0;
621
622
623
}
#endif /* TEST_MAIN */
624
625
/* vi: set ts=4 sw=4 expandtab: */