2 Simple DirectMedia Layer
3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 #include "SDL_config.h"
24 #include "../SDL_internal.h"
27 #if defined(__WIN32__)
28 #include "../core/windows/SDL_windows.h"
31 /* CPU feature detection for SDL */
33 #include "SDL_cpuinfo.h"
38 #ifdef HAVE_SYSCTLBYNAME
39 #include <sys/types.h>
40 #include <sys/sysctl.h>
42 #if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
43 #include <sys/sysctl.h> /* For AltiVec check */
44 #elif defined(__OpenBSD__) && defined(__powerpc__)
45 #include <sys/param.h>
46 #include <sys/sysctl.h> /* For AltiVec check */
47 #include <machine/cpu.h>
48 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
53 #define CPU_HAS_RDTSC 0x00000001
54 #define CPU_HAS_ALTIVEC 0x00000002
55 #define CPU_HAS_MMX 0x00000004
56 #define CPU_HAS_3DNOW 0x00000008
57 #define CPU_HAS_SSE 0x00000010
58 #define CPU_HAS_SSE2 0x00000020
59 #define CPU_HAS_SSE3 0x00000040
60 #define CPU_HAS_SSE41 0x00000100
61 #define CPU_HAS_SSE42 0x00000200
62 #define CPU_HAS_AVX 0x00000400
64 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
65 /* This is the brute force way of detecting instruction sets...
66 the idea is borrowed from the libmpeg2 library - thanks!
68 static jmp_buf jmpbuf;
70 illegal_instruction(int sig)
74 #endif /* HAVE_SETJMP */
81 #if defined(__GNUC__) && defined(i386)
83 " pushfl # Get original EFLAGS \n"
85 " movl %%eax,%%ecx \n"
86 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
87 " pushl %%eax # Save new EFLAGS value on stack \n"
88 " popfl # Replace current EFLAGS value \n"
89 " pushfl # Get new EFLAGS \n"
90 " popl %%eax # Store new EFLAGS in EAX \n"
91 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
92 " jz 1f # Processor=80486 \n"
93 " movl $1,%0 # We have CPUID support \n"
99 #elif defined(__GNUC__) && defined(__x86_64__)
100 /* Technically, if this is being compiled under __x86_64__ then it has
101 CPUid by definition. But it's nice to be able to prove it. :) */
103 " pushfq # Get original EFLAGS \n"
105 " movq %%rax,%%rcx \n"
106 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
107 " pushq %%rax # Save new EFLAGS value on stack \n"
108 " popfq # Replace current EFLAGS value \n"
109 " pushfq # Get new EFLAGS \n"
110 " popq %%rax # Store new EFLAGS in EAX \n"
111 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
112 " jz 1f # Processor=80486 \n"
113 " movl $1,%0 # We have CPUID support \n"
119 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
121 pushfd ; Get original EFLAGS
124 xor eax, 200000h ; Flip ID bit in EFLAGS
125 push eax ; Save new EFLAGS value on stack
126 popfd ; Replace current EFLAGS value
127 pushfd ; Get new EFLAGS
128 pop eax ; Store new EFLAGS in EAX
129 xor eax, ecx ; Can not toggle ID bit,
130 jz done ; Processor=80486
131 mov has_CPUID,1 ; We have CPUID support
134 #elif defined(_MSC_VER) && defined(_M_X64)
136 #elif defined(__sun) && defined(__i386)
141 " xorl $0x200000,%eax \n"
148 " movl $1,-8(%ebp) \n"
151 #elif defined(__sun) && defined(__amd64)
156 " xorl $0x200000,%eax \n"
163 " movl $1,-8(%rbp) \n"
171 #if defined(__GNUC__) && defined(i386)
172 #define cpuid(func, a, b, c, d) \
173 __asm__ __volatile__ ( \
176 " movl %%ebx, %%esi \n" \
178 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
179 #elif defined(__GNUC__) && defined(__x86_64__)
180 #define cpuid(func, a, b, c, d) \
181 __asm__ __volatile__ ( \
184 " movq %%rbx, %%rsi \n" \
186 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
187 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
188 #define cpuid(func, a, b, c, d) \
190 __asm mov eax, func \
197 #elif defined(_MSC_VER) && defined(_M_X64)
198 #define cpuid(func, a, b, c, d) \
201 __cpuid(CPUInfo, func); \
208 #define cpuid(func, a, b, c, d) \
212 static SDL_INLINE int
213 CPU_getCPUIDFeatures(void)
218 cpuid(0, a, b, c, d);
220 cpuid(1, a, b, c, d);
226 static SDL_INLINE int
229 if (CPU_haveCPUID()) {
230 return (CPU_getCPUIDFeatures() & 0x00000010);
235 static SDL_INLINE int
236 CPU_haveAltiVec(void)
238 volatile int altivec = 0;
239 #if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
241 int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
243 int selectors[2] = { CTL_HW, HW_VECTORUNIT };
245 int hasVectorUnit = 0;
246 size_t length = sizeof(hasVectorUnit);
247 int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
249 altivec = (hasVectorUnit != 0);
250 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
251 void (*handler) (int sig);
252 handler = signal(SIGILL, illegal_instruction);
253 if (setjmp(jmpbuf) == 0) {
254 asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
257 signal(SIGILL, handler);
262 static SDL_INLINE int
265 if (CPU_haveCPUID()) {
266 return (CPU_getCPUIDFeatures() & 0x00800000);
271 static SDL_INLINE int
274 if (CPU_haveCPUID()) {
277 cpuid(0x80000000, a, b, c, d);
278 if (a >= 0x80000001) {
279 cpuid(0x80000001, a, b, c, d);
280 return (d & 0x80000000);
286 static SDL_INLINE int
289 if (CPU_haveCPUID()) {
290 return (CPU_getCPUIDFeatures() & 0x02000000);
295 static SDL_INLINE int
298 if (CPU_haveCPUID()) {
299 return (CPU_getCPUIDFeatures() & 0x04000000);
304 static SDL_INLINE int
307 if (CPU_haveCPUID()) {
310 cpuid(0, a, b, c, d);
312 cpuid(1, a, b, c, d);
313 return (c & 0x00000001);
319 static SDL_INLINE int
322 if (CPU_haveCPUID()) {
325 cpuid(1, a, b, c, d);
327 cpuid(1, a, b, c, d);
328 return (c & 0x00080000);
334 static SDL_INLINE int
337 if (CPU_haveCPUID()) {
340 cpuid(1, a, b, c, d);
342 cpuid(1, a, b, c, d);
343 return (c & 0x00100000);
349 static SDL_INLINE int
352 if (CPU_haveCPUID()) {
355 cpuid(1, a, b, c, d);
357 cpuid(1, a, b, c, d);
358 return (c & 0x10000000);
364 static int SDL_CPUCount = 0;
367 SDL_GetCPUCount(void)
370 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
371 if (SDL_CPUCount <= 0) {
372 SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
375 #ifdef HAVE_SYSCTLBYNAME
376 if (SDL_CPUCount <= 0) {
377 size_t size = sizeof(SDL_CPUCount);
378 sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
382 if (SDL_CPUCount <= 0) {
384 GetSystemInfo(&info);
385 SDL_CPUCount = info.dwNumberOfProcessors;
388 /* There has to be at least 1, right? :) */
389 if (SDL_CPUCount <= 0) {
396 /* Oh, such a sweet sweet trick, just not very useful. :) */
400 static char SDL_CPUType[13];
402 if (!SDL_CPUType[0]) {
406 if (CPU_haveCPUID()) {
407 cpuid(0x00000000, a, b, c, d);
408 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
409 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
410 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
411 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
412 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
413 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
414 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
415 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
416 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
417 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
418 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
419 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
421 if (!SDL_CPUType[0]) {
422 SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
429 #ifdef TEST_MAIN /* !!! FIXME: only used for test at the moment. */
433 static char SDL_CPUName[48];
435 if (!SDL_CPUName[0]) {
439 if (CPU_haveCPUID()) {
440 cpuid(0x80000000, a, b, c, d);
441 if (a >= 0x80000004) {
442 cpuid(0x80000002, a, b, c, d);
443 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
444 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
445 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
446 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
447 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
448 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
449 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
450 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
451 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
452 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
453 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
454 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
455 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
456 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
457 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
458 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
459 cpuid(0x80000003, a, b, c, d);
460 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
461 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
462 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
463 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
464 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
465 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
466 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
467 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
468 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
469 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
470 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
471 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
472 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
473 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
474 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
475 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
476 cpuid(0x80000004, a, b, c, d);
477 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
478 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
479 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
480 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
481 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
482 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
483 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
484 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
485 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
486 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
487 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
488 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
489 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
490 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
491 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
492 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
495 if (!SDL_CPUName[0]) {
496 SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
504 SDL_GetCPUCacheLineSize(void)
506 const char *cpuType = SDL_GetCPUType();
508 if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
511 cpuid(0x00000001, a, b, c, d);
512 return (((b >> 8) & 0xff) * 8);
513 } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
516 cpuid(0x80000005, a, b, c, d);
519 /* Just make a guess here... */
520 return SDL_CACHELINE_SIZE;
524 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
527 SDL_GetCPUFeatures(void)
529 if (SDL_CPUFeatures == 0xFFFFFFFF) {
531 if (CPU_haveRDTSC()) {
532 SDL_CPUFeatures |= CPU_HAS_RDTSC;
534 if (CPU_haveAltiVec()) {
535 SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
538 SDL_CPUFeatures |= CPU_HAS_MMX;
540 if (CPU_have3DNow()) {
541 SDL_CPUFeatures |= CPU_HAS_3DNOW;
544 SDL_CPUFeatures |= CPU_HAS_SSE;
546 if (CPU_haveSSE2()) {
547 SDL_CPUFeatures |= CPU_HAS_SSE2;
549 if (CPU_haveSSE3()) {
550 SDL_CPUFeatures |= CPU_HAS_SSE3;
552 if (CPU_haveSSE41()) {
553 SDL_CPUFeatures |= CPU_HAS_SSE41;
555 if (CPU_haveSSE42()) {
556 SDL_CPUFeatures |= CPU_HAS_SSE42;
559 SDL_CPUFeatures |= CPU_HAS_AVX;
562 return SDL_CPUFeatures;
568 if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
577 if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
586 if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
595 if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
604 if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
613 if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
622 if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
631 if (SDL_GetCPUFeatures() & CPU_HAS_SSE41) {
640 if (SDL_GetCPUFeatures() & CPU_HAS_SSE42) {
649 if (SDL_GetCPUFeatures() & CPU_HAS_AVX) {
655 static int SDL_SystemRAM = 0;
658 SDL_GetSystemRAM(void)
660 if (!SDL_SystemRAM) {
661 #if defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
662 if (SDL_SystemRAM <= 0) {
663 SDL_SystemRAM = (int)((Sint64)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) / (1024*1024));
666 #ifdef HAVE_SYSCTLBYNAME
667 if (SDL_SystemRAM <= 0) {
668 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
670 int mib[2] = {CTL_HW, HW_REALMEM};
672 /* might only report up to 2 GiB */
673 int mib[2] = {CTL_HW, HW_PHYSMEM};
674 #endif /* HW_REALMEM */
676 int mib[2] = {CTL_HW, HW_MEMSIZE};
677 #endif /* __FreeBSD__ || __FreeBSD_kernel__ */
679 size_t len = sizeof(memsize);
681 if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0) {
682 SDL_SystemRAM = (int)(memsize / (1024*1024));
687 if (SDL_SystemRAM <= 0) {
689 stat.dwLength = sizeof(stat);
690 if (GlobalMemoryStatusEx(&stat)) {
691 SDL_SystemRAM = (int)(stat.ullTotalPhys / (1024 * 1024));
696 return SDL_SystemRAM;
707 printf("CPU count: %d\n", SDL_GetCPUCount());
708 printf("CPU type: %s\n", SDL_GetCPUType());
709 printf("CPU name: %s\n", SDL_GetCPUName());
710 printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
711 printf("RDTSC: %d\n", SDL_HasRDTSC());
712 printf("Altivec: %d\n", SDL_HasAltiVec());
713 printf("MMX: %d\n", SDL_HasMMX());
714 printf("3DNow: %d\n", SDL_Has3DNow());
715 printf("SSE: %d\n", SDL_HasSSE());
716 printf("SSE2: %d\n", SDL_HasSSE2());
717 printf("SSE3: %d\n", SDL_HasSSE3());
718 printf("SSE4.1: %d\n", SDL_HasSSE41());
719 printf("SSE4.2: %d\n", SDL_HasSSE42());
720 printf("AVX: %d\n", SDL_HasAVX());
721 printf("RAM: %d MB\n", SDL_GetSystemRAM());
725 #endif /* TEST_MAIN */
727 /* vi: set ts=4 sw=4 expandtab: */