This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_cpuinfo.c
539 lines (496 loc) · 15.6 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2009 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
#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
36
#include <sys/sysctl.h> /* For AltiVec check */
37
38
39
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
#include <signal.h>
#include <setjmp.h>
40
#endif
41
42
43
44
#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
45
46
47
48
49
#define CPU_HAS_RDTSC 0x00000001
#define CPU_HAS_MMX 0x00000002
#define CPU_HAS_MMXEXT 0x00000004
#define CPU_HAS_3DNOW 0x00000010
50
#define CPU_HAS_3DNOWEXT 0x00000020
51
52
53
#define CPU_HAS_SSE 0x00000040
#define CPU_HAS_SSE2 0x00000080
#define CPU_HAS_ALTIVEC 0x00000100
54
55
#if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__
56
57
58
59
/* This is the brute force way of detecting instruction sets...
the idea is borrowed from the libmpeg2 library - thanks!
*/
static jmp_buf jmpbuf;
60
61
static void
illegal_instruction(int sig)
62
{
63
longjmp(jmpbuf, 1);
64
}
65
#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_getCPUIDFeaturesExt(void)
207
{
208
int features = 0;
209
int a, b, c, d;
210
211
212
213
214
cpuid(0x80000000, a, b, c, d);
if (a >= 0x80000001) {
cpuid(0x80000001, a, b, c, d);
features = d;
215
}
216
return features;
217
218
}
219
220
static __inline__ int
CPU_haveRDTSC(void)
221
{
222
223
224
225
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00000010);
}
return 0;
226
227
}
228
229
static __inline__ int
CPU_haveMMX(void)
230
{
231
232
233
234
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00800000);
}
return 0;
235
236
}
237
238
static __inline__ int
CPU_haveMMXExt(void)
239
{
240
241
242
243
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeaturesExt() & 0x00400000);
}
return 0;
244
245
}
246
247
static __inline__ int
CPU_have3DNow(void)
248
{
249
250
251
252
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeaturesExt() & 0x80000000);
}
return 0;
253
254
}
255
256
static __inline__ int
CPU_have3DNowExt(void)
257
{
258
259
260
261
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeaturesExt() & 0x40000000);
}
return 0;
262
263
}
264
265
static __inline__ int
CPU_haveSSE(void)
266
{
267
268
269
270
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x02000000);
}
return 0;
271
}
272
273
274
static __inline__ int
CPU_haveSSE2(void)
275
{
276
277
278
279
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x04000000);
}
return 0;
280
281
}
282
283
static __inline__ int
CPU_haveAltiVec(void)
284
{
285
volatile int altivec = 0;
286
#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
287
288
289
290
291
292
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);
293
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
294
295
296
297
298
299
300
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);
301
#endif
302
return altivec;
303
304
}
305
306
307
308
309
310
static int SDL_CPUCount = 0;
int
SDL_GetCPUCount()
{
if (!SDL_CPUCount) {
311
#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
312
313
314
315
if (SDL_CPUCount <= 0) {
SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
}
#endif
316
#ifdef HAVE_SYSCTLBYNAME
317
if (SDL_CPUCount <= 0) {
318
319
320
321
322
size_t size = sizeof(SDL_CPUCount);
sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
}
#endif
#ifdef __WIN32__
323
if (SDL_CPUCount <= 0) {
324
325
326
327
SYSTEM_INFO info;
GetSystemInfo(&info);
SDL_CPUCount = info.dwNumberOfProcessors;
}
328
329
#endif
/* There has to be at least 1, right? :) */
330
if (SDL_CPUCount <= 0) {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
SDL_CPUCount = 1;
}
}
return SDL_CPUCount;
}
/* Oh, such a sweet sweet trick, just not very useful. :) */
const char *
SDL_GetCPUType()
{
static char SDL_CPUType[48];
if (!SDL_CPUType[0]) {
int i = 0;
345
int a, b, c, d;
346
347
if (CPU_haveCPUID()) {
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
cpuid(0x80000000, a, b, c, d);
if (a >= 0x80000004) {
cpuid(0x80000002, a, b, c, d);
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 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)(b & 0xff); b >>= 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;
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;
cpuid(0x80000003, a, b, c, d);
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 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)(b & 0xff); b >>= 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;
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;
cpuid(0x80000004, a, b, c, d);
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
SDL_CPUType[i++] = (char)(a & 0xff); a >>= 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)(b & 0xff); b >>= 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;
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;
401
402
403
404
405
406
407
408
409
}
}
if (!SDL_CPUType[0]) {
SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
}
}
return SDL_CPUType;
}
410
411
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
412
413
static Uint32
SDL_GetCPUFeatures(void)
414
{
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
if (SDL_CPUFeatures == 0xFFFFFFFF) {
SDL_CPUFeatures = 0;
if (CPU_haveRDTSC()) {
SDL_CPUFeatures |= CPU_HAS_RDTSC;
}
if (CPU_haveMMX()) {
SDL_CPUFeatures |= CPU_HAS_MMX;
}
if (CPU_haveMMXExt()) {
SDL_CPUFeatures |= CPU_HAS_MMXEXT;
}
if (CPU_have3DNow()) {
SDL_CPUFeatures |= CPU_HAS_3DNOW;
}
if (CPU_have3DNowExt()) {
SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
}
if (CPU_haveSSE()) {
SDL_CPUFeatures |= CPU_HAS_SSE;
}
if (CPU_haveSSE2()) {
SDL_CPUFeatures |= CPU_HAS_SSE2;
}
if (CPU_haveAltiVec()) {
SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
}
}
return SDL_CPUFeatures;
443
444
}
445
446
SDL_bool
SDL_HasRDTSC(void)
447
{
448
449
450
451
if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
return SDL_TRUE;
}
return SDL_FALSE;
452
453
}
454
455
SDL_bool
SDL_HasMMX(void)
456
{
457
458
459
460
if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
return SDL_TRUE;
}
return SDL_FALSE;
461
462
}
463
464
SDL_bool
SDL_HasMMXExt(void)
465
{
466
467
468
469
if (SDL_GetCPUFeatures() & CPU_HAS_MMXEXT) {
return SDL_TRUE;
}
return SDL_FALSE;
470
471
}
472
473
SDL_bool
SDL_Has3DNow(void)
474
{
475
476
477
478
if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
return SDL_TRUE;
}
return SDL_FALSE;
479
480
}
481
482
SDL_bool
SDL_Has3DNowExt(void)
483
{
484
485
486
487
if (SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT) {
return SDL_TRUE;
}
return SDL_FALSE;
488
489
}
490
491
SDL_bool
SDL_HasSSE(void)
492
{
493
494
495
496
if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
return SDL_TRUE;
}
return SDL_FALSE;
497
498
}
499
500
SDL_bool
SDL_HasSSE2(void)
501
{
502
503
504
505
if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
return SDL_TRUE;
}
return SDL_FALSE;
506
507
}
508
509
SDL_bool
SDL_HasAltiVec(void)
510
{
511
512
513
514
if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
return SDL_TRUE;
}
return SDL_FALSE;
515
516
}
517
518
519
520
#ifdef TEST_MAIN
#include <stdio.h>
521
522
int
main()
523
{
524
525
printf("CPU count: %d\n", SDL_GetCPUCount());
printf("CPU name: %s\n", SDL_GetCPUType());
526
527
528
529
530
531
532
533
534
printf("RDTSC: %d\n", SDL_HasRDTSC());
printf("MMX: %d\n", SDL_HasMMX());
printf("MMXExt: %d\n", SDL_HasMMXExt());
printf("3DNow: %d\n", SDL_Has3DNow());
printf("3DNowExt: %d\n", SDL_Has3DNowExt());
printf("SSE: %d\n", SDL_HasSSE());
printf("SSE2: %d\n", SDL_HasSSE2());
printf("AltiVec: %d\n", SDL_HasAltiVec());
return 0;
535
536
537
}
#endif /* TEST_MAIN */
538
539
/* vi: set ts=4 sw=4 expandtab: */