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 (at) 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; 50 static void illegal_instruction(int sig) 51 { 52 longjmp(jmpbuf, 1); 53 } 54 #endif /* HAVE_SETJMP */ 55 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 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