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