This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_cpuinfo.c
513 lines (467 loc) · 15.2 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
#ifdef __WIN32__
36
#include "../core/windows/SDL_windows.h"
37
#endif
38
39
40
#define CPU_HAS_RDTSC 0x00000001
#define CPU_HAS_MMX 0x00000002
41
42
43
44
45
#define CPU_HAS_SSE 0x00000010
#define CPU_HAS_SSE2 0x00000020
#define CPU_HAS_SSE3 0x00000040
#define CPU_HAS_SSE4 0x00000080
46
47
48
static __inline__ int
CPU_haveCPUID(void)
49
{
50
51
int has_CPUID = 0;
/* *INDENT-OFF* */
52
#if defined(__GNUC__) && defined(i386)
53
__asm__ (
54
55
56
57
58
59
60
61
62
63
64
65
" 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"
66
67
68
69
: "=m" (has_CPUID)
:
: "%eax", "%ecx"
);
70
71
72
#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. :) */
73
__asm__ (
74
75
76
77
78
79
80
81
82
83
84
85
" 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"
86
87
88
89
: "=m" (has_CPUID)
:
: "%rax", "%rcx"
);
90
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
91
__asm {
92
93
94
95
96
97
98
99
100
101
102
103
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:
104
}
105
#elif defined(__sun) && defined(__i386)
106
__asm (
107
" pushfl \n"
108
109
110
111
112
113
114
115
116
117
" 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"
118
"1: \n"
119
);
120
#elif defined(__sun) && defined(__amd64)
121
__asm (
122
123
124
125
126
127
128
129
130
131
132
133
" 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"
134
);
135
#endif
136
137
/* *INDENT-ON* */
return has_CPUID;
138
139
}
140
#if defined(__GNUC__) && defined(i386)
141
#define cpuid(func, a, b, c, d) \
142
143
144
145
146
147
__asm__ __volatile__ ( \
" pushl %%ebx \n" \
" cpuid \n" \
" movl %%ebx, %%esi \n" \
" popl %%ebx \n" : \
"=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
148
149
150
151
152
153
154
155
#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))
156
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
157
158
159
160
161
162
163
164
#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 \
165
166
}
#else
167
168
#define cpuid(func, a, b, c, d) \
a = b = c = d = 0
169
170
#endif
171
172
static __inline__ int
CPU_getCPUIDFeatures(void)
173
{
174
int features = 0;
175
int a, b, c, d;
176
177
178
179
180
cpuid(0, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
features = d;
181
}
182
return features;
183
184
}
185
186
static __inline__ int
CPU_haveRDTSC(void)
187
{
188
189
190
191
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00000010);
}
return 0;
192
193
}
194
195
static __inline__ int
CPU_haveMMX(void)
196
{
197
198
199
200
if (CPU_haveCPUID()) {
return (CPU_getCPUIDFeatures() & 0x00800000);
}
return 0;
201
202
}
203
static __inline__ int
204
CPU_haveSSE(void)
205
{
206
if (CPU_haveCPUID()) {
207
return (CPU_getCPUIDFeatures() & 0x02000000);
208
209
}
return 0;
210
211
}
212
static __inline__ int
213
CPU_haveSSE2(void)
214
{
215
if (CPU_haveCPUID()) {
216
return (CPU_getCPUIDFeatures() & 0x04000000);
217
218
}
return 0;
219
220
}
221
static __inline__ int
222
CPU_haveSSE3(void)
223
{
224
if (CPU_haveCPUID()) {
225
int a, b, c, d;
226
227
228
229
230
231
cpuid(0, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
return (c & 0x00000001);
}
232
233
}
return 0;
234
}
235
236
static __inline__ int
237
CPU_haveSSE4(void)
238
{
239
if (CPU_haveCPUID()) {
240
int a, b, c, d;
241
242
243
244
245
246
cpuid(0, a, b, c, d);
if (a >= 1) {
cpuid(1, a, b, c, d);
return (c & 0x00000100);
}
247
}
248
return 0;
249
250
}
251
252
253
static int SDL_CPUCount = 0;
int
254
SDL_GetCPUCount(void)
255
256
{
if (!SDL_CPUCount) {
257
#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
258
259
260
261
if (SDL_CPUCount <= 0) {
SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
}
#endif
262
#ifdef HAVE_SYSCTLBYNAME
263
if (SDL_CPUCount <= 0) {
264
265
266
267
size_t size = sizeof(SDL_CPUCount);
sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
}
#endif
268
#ifdef __WIN32__
269
if (SDL_CPUCount <= 0) {
270
271
272
273
SYSTEM_INFO info;
GetSystemInfo(&info);
SDL_CPUCount = info.dwNumberOfProcessors;
}
274
275
#endif
/* There has to be at least 1, right? :) */
276
if (SDL_CPUCount <= 0) {
277
278
279
280
281
282
283
SDL_CPUCount = 1;
}
}
return SDL_CPUCount;
}
/* Oh, such a sweet sweet trick, just not very useful. :) */
284
static const char *
285
SDL_GetCPUType(void)
286
{
287
static char SDL_CPUType[13];
288
289
290
if (!SDL_CPUType[0]) {
int i = 0;
291
int a, b, c, d;
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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 *
316
SDL_GetCPUName(void)
317
318
319
320
321
322
323
{
static char SDL_CPUName[48];
if (!SDL_CPUName[0]) {
int i = 0;
int a, b, c, d;
324
if (CPU_haveCPUID()) {
325
326
327
cpuid(0x80000000, a, b, c, d);
if (a >= 0x80000004) {
cpuid(0x80000002, a, b, c, d);
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
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;
344
cpuid(0x80000003, a, b, c, d);
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
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;
361
cpuid(0x80000004, a, b, c, d);
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
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;
378
379
}
}
380
381
if (!SDL_CPUName[0]) {
SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
382
383
}
}
384
385
386
return SDL_CPUName;
}
387
388
int
SDL_GetCPUCacheLineSize(void)
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
{
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;
}
406
407
}
408
409
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
410
411
static Uint32
SDL_GetCPUFeatures(void)
412
{
413
414
415
416
417
418
419
420
421
422
423
424
425
426
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_haveSSE()) {
SDL_CPUFeatures |= CPU_HAS_SSE;
}
if (CPU_haveSSE2()) {
SDL_CPUFeatures |= CPU_HAS_SSE2;
}
427
428
429
430
431
if (CPU_haveSSE3()) {
SDL_CPUFeatures |= CPU_HAS_SSE3;
}
if (CPU_haveSSE4()) {
SDL_CPUFeatures |= CPU_HAS_SSE4;
432
433
434
}
}
return SDL_CPUFeatures;
435
436
}
437
438
SDL_bool
SDL_HasRDTSC(void)
439
{
440
441
442
443
if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
return SDL_TRUE;
}
return SDL_FALSE;
444
445
}
446
447
SDL_bool
SDL_HasMMX(void)
448
{
449
450
451
452
if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
return SDL_TRUE;
}
return SDL_FALSE;
453
454
}
455
SDL_bool
456
SDL_HasSSE(void)
457
{
458
if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
459
460
461
return SDL_TRUE;
}
return SDL_FALSE;
462
463
}
464
SDL_bool
465
SDL_HasSSE2(void)
466
{
467
if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
468
469
470
return SDL_TRUE;
}
return SDL_FALSE;
471
472
}
473
SDL_bool
474
SDL_HasSSE3(void)
475
{
476
if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
477
478
479
return SDL_TRUE;
}
return SDL_FALSE;
480
481
}
482
SDL_bool
483
SDL_HasSSE4(void)
484
{
485
if (SDL_GetCPUFeatures() & CPU_HAS_SSE4) {
486
487
488
return SDL_TRUE;
}
return SDL_FALSE;
489
490
}
491
492
493
494
#ifdef TEST_MAIN
#include <stdio.h>
495
496
int
main()
497
{
498
printf("CPU count: %d\n", SDL_GetCPUCount());
499
500
501
printf("CPU type: %s\n", SDL_GetCPUType());
printf("CPU name: %s\n", SDL_GetCPUName());
printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
502
503
504
505
printf("RDTSC: %d\n", SDL_HasRDTSC());
printf("MMX: %d\n", SDL_HasMMX());
printf("SSE: %d\n", SDL_HasSSE());
printf("SSE2: %d\n", SDL_HasSSE2());
506
507
printf("SSE3: %d\n", SDL_HasSSE3());
printf("SSE4: %d\n", SDL_HasSSE4());
508
return 0;
509
510
511
}
#endif /* TEST_MAIN */
512
513
/* vi: set ts=4 sw=4 expandtab: */