/
SDL_cpuinfo.c
446 lines (412 loc) · 12.9 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2006 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__)
30
#include <sys/sysctl.h> /* For AltiVec check */
31
32
33
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
#include <signal.h>
#include <setjmp.h>
34
35
#endif
36
37
#define CPU_HAS_RDTSC 0x00000001
#define CPU_HAS_MMX 0x00000002
38
39
40
41
42
43
#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
44
45
#if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__
46
47
48
49
50
51
52
53
/* 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);
}
54
#endif /* HAVE_SETJMP */
55
56
static __inline__ int CPU_haveCPUID(void)
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{
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"
73
: "=m" (has_CPUID)
74
75
76
:
: "%eax", "%ecx"
);
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#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"
);
97
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
98
__asm {
99
100
101
102
103
104
105
106
107
108
109
110
111
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:
}
112
#elif defined(__sun) && defined(__i386)
113
__asm (
114
" pushfl \n"
115
116
117
118
119
120
121
122
123
124
" 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"
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"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"
);
142
143
144
145
#endif
return has_CPUID;
}
146
static __inline__ int CPU_getCPUIDFeatures(void)
147
148
{
int features = 0;
149
#if defined(__GNUC__) && ( defined(i386) || defined(__x86_64__) )
150
__asm__ (
151
" movl %%ebx,%%edi\n"
152
153
154
155
156
157
158
159
160
" xorl %%eax,%%eax # Set up for CPUID instruction \n"
" cpuid # Get and save vendor ID \n"
" 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"
" cpuid # Get family/model/stepping/features\n"
" movl %%edx,%0 \n"
"1: \n"
161
" movl %%edi,%%ebx\n"
162
: "=m" (features)
163
:
164
: "%eax", "%ecx", "%edx", "%edi"
165
);
166
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
167
__asm {
168
169
170
171
172
173
174
175
176
177
xor eax, eax ; Set up for CPUID instruction
cpuid ; Get and save vendor ID
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl done ; We dont have the CPUID instruction
xor eax, eax
inc eax
cpuid ; Get family/model/stepping/features
mov features, edx
done:
}
178
#elif defined(__sun) && (defined(__i386) || defined(__amd64))
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
__asm(
" movl %ebx,%edi\n"
" xorl %eax,%eax \n"
" cpuid \n"
" cmpl $1,%eax \n"
" jl 1f \n"
" xorl %eax,%eax \n"
" incl %eax \n"
" cpuid \n"
#ifdef __i386
" movl %edx,-8(%ebp) \n"
#else
" movl %edx,-8(%rbp) \n"
#endif
"1: \n"
" movl %edi,%ebx\n" );
195
196
197
198
#endif
return features;
}
199
static __inline__ int CPU_getCPUIDFeaturesExt(void)
200
{
201
int features = 0;
202
#if defined(__GNUC__) && (defined(i386) || defined (__x86_64__) )
203
__asm__ (
204
" movl %%ebx,%%edi\n"
205
206
207
" movl $0x80000000,%%eax # Query for extended functions \n"
" cpuid # Get extended function limit \n"
" cmpl $0x80000001,%%eax \n"
208
" jl 1f # Nope, we dont have function 800000001h\n"
209
210
" movl $0x80000001,%%eax # Setup extended function 800000001h\n"
" cpuid # and get the information \n"
211
" movl %%edx,%0 \n"
212
"1: \n"
213
214
" movl %%edi,%%ebx\n"
: "=m" (features)
215
:
216
: "%eax", "%ecx", "%edx", "%edi"
217
);
218
#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
219
__asm {
220
221
222
mov eax,80000000h ; Query for extended functions
cpuid ; Get extended function limit
cmp eax,80000001h
223
jl done ; Nope, we dont have function 800000001h
224
225
mov eax,80000001h ; Setup extended function 800000001h
cpuid ; and get the information
226
mov features,edx
227
228
done:
}
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#elif defined(__sun) && ( defined(__i386) || defined(__amd64) )
__asm (
" movl %ebx,%edi\n"
" movl $0x80000000,%eax \n"
" cpuid \n"
" cmpl $0x80000001,%eax \n"
" jl 1f \n"
" movl $0x80000001,%eax \n"
" cpuid \n"
#ifdef __i386
" movl %edx,-8(%ebp) \n"
#else
" movl %edx,-8(%rbp) \n"
#endif
"1: \n"
" movl %edi,%ebx\n"
);
246
#endif
247
248
249
return features;
}
250
static __inline__ int CPU_haveRDTSC(void)
251
252
253
254
255
256
257
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x00000010);
}
return 0;
}
258
static __inline__ int CPU_haveMMX(void)
259
260
261
262
263
264
265
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x00800000);
}
return 0;
}
266
static __inline__ int CPU_haveMMXExt(void)
267
268
269
270
271
272
273
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x00400000);
}
return 0;
}
274
static __inline__ int CPU_have3DNow(void)
275
276
277
278
279
280
281
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x80000000);
}
return 0;
}
282
static __inline__ int CPU_have3DNowExt(void)
283
284
285
286
287
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeaturesExt() & 0x40000000);
}
return 0;
288
289
}
290
static __inline__ int CPU_haveSSE(void)
291
292
293
294
295
296
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x02000000);
}
return 0;
}
297
298
static __inline__ int CPU_haveSSE2(void)
299
300
301
302
303
304
305
{
if ( CPU_haveCPUID() ) {
return (CPU_getCPUIDFeatures() & 0x04000000);
}
return 0;
}
306
static __inline__ int CPU_haveAltiVec(void)
307
{
308
volatile int altivec = 0;
309
#if defined(__MACOSX__) && defined(__ppc__)
310
311
312
313
314
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 )
315
altivec = (hasVectorUnit != 0);
316
#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
317
318
319
320
321
322
323
324
325
326
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);
327
#endif
328
return altivec;
329
330
}
331
332
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
333
static Uint32 SDL_GetCPUFeatures(void)
334
335
336
{
if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
SDL_CPUFeatures = 0;
337
338
339
if ( CPU_haveRDTSC() ) {
SDL_CPUFeatures |= CPU_HAS_RDTSC;
}
340
341
342
if ( CPU_haveMMX() ) {
SDL_CPUFeatures |= CPU_HAS_MMX;
}
343
344
345
if ( CPU_haveMMXExt() ) {
SDL_CPUFeatures |= CPU_HAS_MMXEXT;
}
346
347
348
if ( CPU_have3DNow() ) {
SDL_CPUFeatures |= CPU_HAS_3DNOW;
}
349
350
351
if ( CPU_have3DNowExt() ) {
SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
}
352
353
354
if ( CPU_haveSSE() ) {
SDL_CPUFeatures |= CPU_HAS_SSE;
}
355
356
357
if ( CPU_haveSSE2() ) {
SDL_CPUFeatures |= CPU_HAS_SSE2;
}
358
359
360
if ( CPU_haveAltiVec() ) {
SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
}
361
362
363
364
}
return SDL_CPUFeatures;
}
365
SDL_bool SDL_HasRDTSC(void)
366
367
368
369
370
371
372
{
if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
return SDL_TRUE;
}
return SDL_FALSE;
}
373
SDL_bool SDL_HasMMX(void)
374
375
376
377
378
379
380
{
if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
return SDL_TRUE;
}
return SDL_FALSE;
}
381
SDL_bool SDL_HasMMXExt(void)
382
{
383
if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
384
385
386
387
388
return SDL_TRUE;
}
return SDL_FALSE;
}
389
SDL_bool SDL_Has3DNow(void)
390
{
391
if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
392
393
394
395
396
return SDL_TRUE;
}
return SDL_FALSE;
}
397
SDL_bool SDL_Has3DNowExt(void)
398
{
399
if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
400
401
402
403
404
return SDL_TRUE;
}
return SDL_FALSE;
}
405
SDL_bool SDL_HasSSE(void)
406
{
407
408
409
410
if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
return SDL_TRUE;
}
return SDL_FALSE;
411
412
}
413
SDL_bool SDL_HasSSE2(void)
414
{
415
416
417
418
if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
return SDL_TRUE;
}
return SDL_FALSE;
419
420
}
421
SDL_bool SDL_HasAltiVec(void)
422
{
423
424
425
426
if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
return SDL_TRUE;
}
return SDL_FALSE;
427
428
}
429
430
431
432
433
434
#ifdef TEST_MAIN
#include <stdio.h>
int main()
{
435
printf("RDTSC: %d\n", SDL_HasRDTSC());
436
printf("MMX: %d\n", SDL_HasMMX());
437
printf("MMXExt: %d\n", SDL_HasMMXExt());
438
printf("3DNow: %d\n", SDL_Has3DNow());
439
printf("3DNowExt: %d\n", SDL_Has3DNowExt());
440
printf("SSE: %d\n", SDL_HasSSE());
441
printf("SSE2: %d\n", SDL_HasSSE2());
442
printf("AltiVec: %d\n", SDL_HasAltiVec());
443
return 0;
444
445
446
}
#endif /* TEST_MAIN */