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
63 #define CPU_HAS_AVX2 0x00000800
65 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
66 /* This is the brute force way of detecting instruction sets...
67 the idea is borrowed from the libmpeg2 library - thanks!
69 static jmp_buf jmpbuf;
71 illegal_instruction(int sig)
75 #endif /* HAVE_SETJMP */
82 #ifndef SDL_CPUINFO_DISABLED
83 #if defined(__GNUC__) && defined(i386)
85 " pushfl # Get original EFLAGS \n"
87 " movl %%eax,%%ecx \n"
88 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
89 " pushl %%eax # Save new EFLAGS value on stack \n"
90 " popfl # Replace current EFLAGS value \n"
91 " pushfl # Get new EFLAGS \n"
92 " popl %%eax # Store new EFLAGS in EAX \n"
93 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
94 " jz 1f # Processor=80486 \n"
95 " movl $1,%0 # We have CPUID support \n"
101 #elif defined(__GNUC__) && defined(__x86_64__)
102 /* Technically, if this is being compiled under __x86_64__ then it has
103 CPUid by definition. But it's nice to be able to prove it. :) */
105 " pushfq # Get original EFLAGS \n"
107 " movq %%rax,%%rcx \n"
108 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
109 " pushq %%rax # Save new EFLAGS value on stack \n"
110 " popfq # Replace current EFLAGS value \n"
111 " pushfq # Get new EFLAGS \n"
112 " popq %%rax # Store new EFLAGS in EAX \n"
113 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
114 " jz 1f # Processor=80486 \n"
115 " movl $1,%0 # We have CPUID support \n"
121 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
123 pushfd ; Get original EFLAGS
126 xor eax, 200000h ; Flip ID bit in EFLAGS
127 push eax ; Save new EFLAGS value on stack
128 popfd ; Replace current EFLAGS value
129 pushfd ; Get new EFLAGS
130 pop eax ; Store new EFLAGS in EAX
131 xor eax, ecx ; Can not toggle ID bit,
132 jz done ; Processor=80486
133 mov has_CPUID,1 ; We have CPUID support
136 #elif defined(_MSC_VER) && defined(_M_X64)
138 #elif defined(__sun) && defined(__i386)
143 " xorl $0x200000,%eax \n"
150 " movl $1,-8(%ebp) \n"
153 #elif defined(__sun) && defined(__amd64)
158 " xorl $0x200000,%eax \n"
165 " movl $1,-8(%rbp) \n"
174 #if defined(__GNUC__) && defined(i386)
175 #define cpuid(func, a, b, c, d) \
176 __asm__ __volatile__ ( \
178 " xorl %%ecx,%%ecx \n" \
180 " movl %%ebx, %%esi \n" \
182 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
183 #elif defined(__GNUC__) && defined(__x86_64__)
184 #define cpuid(func, a, b, c, d) \
185 __asm__ __volatile__ ( \
187 " xorq %%rcx,%%rcx \n" \
189 " movq %%rbx, %%rsi \n" \
191 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
192 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
193 #define cpuid(func, a, b, c, d) \
195 __asm mov eax, func \
203 #elif defined(_MSC_VER) && defined(_M_X64)
204 #define cpuid(func, a, b, c, d) \
207 __cpuid(CPUInfo, func); \
214 #define cpuid(func, a, b, c, d) \
219 CPU_getCPUIDFeatures(void)
224 cpuid(0, a, b, c, d);
226 cpuid(1, a, b, c, d);
237 /* Check to make sure we can call xgetbv */
238 cpuid(0, a, b, c, d);
242 cpuid(1, a, b, c, d);
243 if (!(c & 0x08000000)) {
247 /* Call xgetbv to see if YMM register state is saved */
249 #if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
250 asm(".byte 0x0f, 0x01, 0xd0" : "=a" (a) : "c" (0) : "%edx");
251 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && (_MSC_FULL_VER >= 160040219) /* VS2010 SP1 */
253 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
257 _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0
261 return ((a & 6) == 6) ? SDL_TRUE : SDL_FALSE;
267 if (CPU_haveCPUID()) {
268 return (CPU_getCPUIDFeatures() & 0x00000010);
274 CPU_haveAltiVec(void)
276 volatile int altivec = 0;
277 #ifndef SDL_CPUINFO_DISABLED
278 #if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
280 int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
282 int selectors[2] = { CTL_HW, HW_VECTORUNIT };
284 int hasVectorUnit = 0;
285 size_t length = sizeof(hasVectorUnit);
286 int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
288 altivec = (hasVectorUnit != 0);
289 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
290 void (*handler) (int sig);
291 handler = signal(SIGILL, illegal_instruction);
292 if (setjmp(jmpbuf) == 0) {
293 asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
296 signal(SIGILL, handler);
305 if (CPU_haveCPUID()) {
306 return (CPU_getCPUIDFeatures() & 0x00800000);
314 if (CPU_haveCPUID()) {
317 cpuid(0x80000000, a, b, c, d);
318 if (a >= 0x80000001) {
319 cpuid(0x80000001, a, b, c, d);
320 return (d & 0x80000000);
329 if (CPU_haveCPUID()) {
330 return (CPU_getCPUIDFeatures() & 0x02000000);
338 if (CPU_haveCPUID()) {
339 return (CPU_getCPUIDFeatures() & 0x04000000);
347 if (CPU_haveCPUID()) {
350 cpuid(0, a, b, c, d);
352 cpuid(1, a, b, c, d);
353 return (c & 0x00000001);
362 if (CPU_haveCPUID()) {
365 cpuid(0, a, b, c, d);
367 cpuid(1, a, b, c, d);
368 return (c & 0x00080000);
377 if (CPU_haveCPUID()) {
380 cpuid(0, a, b, c, d);
382 cpuid(1, a, b, c, d);
383 return (c & 0x00100000);
392 if (CPU_haveCPUID() && CPU_OSSavesYMM()) {
395 cpuid(0, a, b, c, d);
397 cpuid(1, a, b, c, d);
398 return (c & 0x10000000);
407 if (CPU_haveCPUID() && CPU_OSSavesYMM()) {
410 cpuid(0, a, b, c, d);
412 cpuid(7, a, b, c, d);
413 return (b & 0x00000020);
419 static int SDL_CPUCount = 0;
422 SDL_GetCPUCount(void)
425 #ifndef SDL_CPUINFO_DISABLED
426 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
427 if (SDL_CPUCount <= 0) {
428 SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
431 #ifdef HAVE_SYSCTLBYNAME
432 if (SDL_CPUCount <= 0) {
433 size_t size = sizeof(SDL_CPUCount);
434 sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
438 if (SDL_CPUCount <= 0) {
440 GetSystemInfo(&info);
441 SDL_CPUCount = info.dwNumberOfProcessors;
445 /* There has to be at least 1, right? :) */
446 if (SDL_CPUCount <= 0) {
453 /* Oh, such a sweet sweet trick, just not very useful. :) */
457 static char SDL_CPUType[13];
459 if (!SDL_CPUType[0]) {
463 if (CPU_haveCPUID()) {
464 cpuid(0x00000000, a, b, c, d);
465 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
466 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
467 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
468 SDL_CPUType[i++] = (char)(b & 0xff);
470 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
471 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
472 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
473 SDL_CPUType[i++] = (char)(d & 0xff);
475 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
476 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
477 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
478 SDL_CPUType[i++] = (char)(c & 0xff);
480 if (!SDL_CPUType[0]) {
481 SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
488 #ifdef TEST_MAIN /* !!! FIXME: only used for test at the moment. */
492 static char SDL_CPUName[48];
494 if (!SDL_CPUName[0]) {
498 if (CPU_haveCPUID()) {
499 cpuid(0x80000000, a, b, c, d);
500 if (a >= 0x80000004) {
501 cpuid(0x80000002, a, b, c, d);
502 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
503 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
504 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
505 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
506 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
507 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
508 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
509 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
510 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
511 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
512 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
513 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
514 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
515 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
516 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
517 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
518 cpuid(0x80000003, a, b, c, d);
519 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
520 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
521 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
522 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
523 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
524 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
525 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
526 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
527 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
528 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
529 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
530 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
531 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
532 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
533 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
534 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
535 cpuid(0x80000004, a, b, c, d);
536 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
537 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
538 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
539 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
540 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
541 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
542 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
543 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
544 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
545 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
546 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
547 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
548 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
549 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
550 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
551 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
554 if (!SDL_CPUName[0]) {
555 SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
563 SDL_GetCPUCacheLineSize(void)
565 const char *cpuType = SDL_GetCPUType();
567 if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
570 cpuid(0x00000001, a, b, c, d);
571 return (((b >> 8) & 0xff) * 8);
572 } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
575 cpuid(0x80000005, a, b, c, d);
578 /* Just make a guess here... */
579 return SDL_CACHELINE_SIZE;
583 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
586 SDL_GetCPUFeatures(void)
588 if (SDL_CPUFeatures == 0xFFFFFFFF) {
590 if (CPU_haveRDTSC()) {
591 SDL_CPUFeatures |= CPU_HAS_RDTSC;
593 if (CPU_haveAltiVec()) {
594 SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
597 SDL_CPUFeatures |= CPU_HAS_MMX;
599 if (CPU_have3DNow()) {
600 SDL_CPUFeatures |= CPU_HAS_3DNOW;
603 SDL_CPUFeatures |= CPU_HAS_SSE;
605 if (CPU_haveSSE2()) {
606 SDL_CPUFeatures |= CPU_HAS_SSE2;
608 if (CPU_haveSSE3()) {
609 SDL_CPUFeatures |= CPU_HAS_SSE3;
611 if (CPU_haveSSE41()) {
612 SDL_CPUFeatures |= CPU_HAS_SSE41;
614 if (CPU_haveSSE42()) {
615 SDL_CPUFeatures |= CPU_HAS_SSE42;
618 SDL_CPUFeatures |= CPU_HAS_AVX;
620 if (CPU_haveAVX2()) {
621 SDL_CPUFeatures |= CPU_HAS_AVX2;
624 return SDL_CPUFeatures;
630 if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
639 if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
648 if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
657 if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
666 if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
675 if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
684 if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
693 if (SDL_GetCPUFeatures() & CPU_HAS_SSE41) {
702 if (SDL_GetCPUFeatures() & CPU_HAS_SSE42) {
711 if (SDL_GetCPUFeatures() & CPU_HAS_AVX) {
720 if (SDL_GetCPUFeatures() & CPU_HAS_AVX2) {
726 static int SDL_SystemRAM = 0;
729 SDL_GetSystemRAM(void)
731 if (!SDL_SystemRAM) {
732 #ifndef SDL_CPUINFO_DISABLED
733 #if defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
734 if (SDL_SystemRAM <= 0) {
735 SDL_SystemRAM = (int)((Sint64)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) / (1024*1024));
738 #ifdef HAVE_SYSCTLBYNAME
739 if (SDL_SystemRAM <= 0) {
740 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
742 int mib[2] = {CTL_HW, HW_REALMEM};
744 /* might only report up to 2 GiB */
745 int mib[2] = {CTL_HW, HW_PHYSMEM};
746 #endif /* HW_REALMEM */
748 int mib[2] = {CTL_HW, HW_MEMSIZE};
749 #endif /* __FreeBSD__ || __FreeBSD_kernel__ */
751 size_t len = sizeof(memsize);
753 if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0) {
754 SDL_SystemRAM = (int)(memsize / (1024*1024));
759 if (SDL_SystemRAM <= 0) {
761 stat.dwLength = sizeof(stat);
762 if (GlobalMemoryStatusEx(&stat)) {
763 SDL_SystemRAM = (int)(stat.ullTotalPhys / (1024 * 1024));
769 return SDL_SystemRAM;
780 printf("CPU count: %d\n", SDL_GetCPUCount());
781 printf("CPU type: %s\n", SDL_GetCPUType());
782 printf("CPU name: %s\n", SDL_GetCPUName());
783 printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
784 printf("RDTSC: %d\n", SDL_HasRDTSC());
785 printf("Altivec: %d\n", SDL_HasAltiVec());
786 printf("MMX: %d\n", SDL_HasMMX());
787 printf("3DNow: %d\n", SDL_Has3DNow());
788 printf("SSE: %d\n", SDL_HasSSE());
789 printf("SSE2: %d\n", SDL_HasSSE2());
790 printf("SSE3: %d\n", SDL_HasSSE3());
791 printf("SSE4.1: %d\n", SDL_HasSSE41());
792 printf("SSE4.2: %d\n", SDL_HasSSE42());
793 printf("AVX: %d\n", SDL_HasAVX());
794 printf("AVX2: %d\n", SDL_HasAVX2());
795 printf("RAM: %d MB\n", SDL_GetSystemRAM());
799 #endif /* TEST_MAIN */
801 /* vi: set ts=4 sw=4 expandtab: */