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 #if defined(__GNUC__) && defined(i386)
84 " pushfl # Get original EFLAGS \n"
86 " movl %%eax,%%ecx \n"
87 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
88 " pushl %%eax # Save new EFLAGS value on stack \n"
89 " popfl # Replace current EFLAGS value \n"
90 " pushfl # Get new EFLAGS \n"
91 " popl %%eax # Store new EFLAGS in EAX \n"
92 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
93 " jz 1f # Processor=80486 \n"
94 " movl $1,%0 # We have CPUID support \n"
100 #elif defined(__GNUC__) && defined(__x86_64__)
101 /* Technically, if this is being compiled under __x86_64__ then it has
102 CPUid by definition. But it's nice to be able to prove it. :) */
104 " pushfq # Get original EFLAGS \n"
106 " movq %%rax,%%rcx \n"
107 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n"
108 " pushq %%rax # Save new EFLAGS value on stack \n"
109 " popfq # Replace current EFLAGS value \n"
110 " pushfq # Get new EFLAGS \n"
111 " popq %%rax # Store new EFLAGS in EAX \n"
112 " xorl %%ecx,%%eax # Can not toggle ID bit, \n"
113 " jz 1f # Processor=80486 \n"
114 " movl $1,%0 # We have CPUID support \n"
120 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
122 pushfd ; Get original EFLAGS
125 xor eax, 200000h ; Flip ID bit in EFLAGS
126 push eax ; Save new EFLAGS value on stack
127 popfd ; Replace current EFLAGS value
128 pushfd ; Get new EFLAGS
129 pop eax ; Store new EFLAGS in EAX
130 xor eax, ecx ; Can not toggle ID bit,
131 jz done ; Processor=80486
132 mov has_CPUID,1 ; We have CPUID support
135 #elif defined(_MSC_VER) && defined(_M_X64)
137 #elif defined(__sun) && defined(__i386)
142 " xorl $0x200000,%eax \n"
149 " movl $1,-8(%ebp) \n"
152 #elif defined(__sun) && defined(__amd64)
157 " xorl $0x200000,%eax \n"
164 " movl $1,-8(%rbp) \n"
172 #if defined(__GNUC__) && defined(i386)
173 #define cpuid(func, a, b, c, d) \
174 __asm__ __volatile__ ( \
176 " xorl %%ecx,%%ecx \n" \
178 " movl %%ebx, %%esi \n" \
180 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
181 #elif defined(__GNUC__) && defined(__x86_64__)
182 #define cpuid(func, a, b, c, d) \
183 __asm__ __volatile__ ( \
185 " xorq %%rcx,%%rcx \n" \
187 " movq %%rbx, %%rsi \n" \
189 "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
190 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
191 #define cpuid(func, a, b, c, d) \
193 __asm mov eax, func \
201 #elif defined(_MSC_VER) && defined(_M_X64)
202 #define cpuid(func, a, b, c, d) \
205 __cpuid(CPUInfo, func); \
212 #define cpuid(func, a, b, c, d) \
217 CPU_getCPUIDFeatures(void)
222 cpuid(0, a, b, c, d);
224 cpuid(1, a, b, c, d);
235 /* Check to make sure we can call xgetbv */
236 cpuid(0, a, b, c, d);
240 cpuid(1, a, b, c, d);
241 if (!(c & 0x08000000)) {
245 /* Call xgetbv to see if YMM register state is saved */
247 #if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
248 asm(".byte 0x0f, 0x01, 0xd0" : "=a" (a) : "c" (0) : "%edx");
249 #elif defined(_MSC_VER) && (_MSC_FULL_VER >= 160040219) /* VS2010 SP1 */
251 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
255 _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0
259 return ((a & 6) == 6) ? SDL_TRUE : SDL_FALSE;
265 if (CPU_haveCPUID()) {
266 return (CPU_getCPUIDFeatures() & 0x00000010);
272 CPU_haveAltiVec(void)
274 volatile int altivec = 0;
275 #if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
277 int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
279 int selectors[2] = { CTL_HW, HW_VECTORUNIT };
281 int hasVectorUnit = 0;
282 size_t length = sizeof(hasVectorUnit);
283 int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
285 altivec = (hasVectorUnit != 0);
286 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
287 void (*handler) (int sig);
288 handler = signal(SIGILL, illegal_instruction);
289 if (setjmp(jmpbuf) == 0) {
290 asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
293 signal(SIGILL, handler);
301 if (CPU_haveCPUID()) {
302 return (CPU_getCPUIDFeatures() & 0x00800000);
310 if (CPU_haveCPUID()) {
313 cpuid(0x80000000, a, b, c, d);
314 if (a >= 0x80000001) {
315 cpuid(0x80000001, a, b, c, d);
316 return (d & 0x80000000);
325 if (CPU_haveCPUID()) {
326 return (CPU_getCPUIDFeatures() & 0x02000000);
334 if (CPU_haveCPUID()) {
335 return (CPU_getCPUIDFeatures() & 0x04000000);
343 if (CPU_haveCPUID()) {
346 cpuid(0, a, b, c, d);
348 cpuid(1, a, b, c, d);
349 return (c & 0x00000001);
358 if (CPU_haveCPUID()) {
361 cpuid(0, a, b, c, d);
363 cpuid(1, a, b, c, d);
364 return (c & 0x00080000);
373 if (CPU_haveCPUID()) {
376 cpuid(0, a, b, c, d);
378 cpuid(1, a, b, c, d);
379 return (c & 0x00100000);
388 if (CPU_haveCPUID() && CPU_OSSavesYMM()) {
391 cpuid(0, a, b, c, d);
393 cpuid(1, a, b, c, d);
394 return (c & 0x10000000);
403 if (CPU_haveCPUID() && CPU_OSSavesYMM()) {
406 cpuid(0, a, b, c, d);
408 cpuid(7, a, b, c, d);
409 return (b & 0x00000020);
415 static int SDL_CPUCount = 0;
418 SDL_GetCPUCount(void)
421 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
422 if (SDL_CPUCount <= 0) {
423 SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
426 #ifdef HAVE_SYSCTLBYNAME
427 if (SDL_CPUCount <= 0) {
428 size_t size = sizeof(SDL_CPUCount);
429 sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
433 if (SDL_CPUCount <= 0) {
435 GetSystemInfo(&info);
436 SDL_CPUCount = info.dwNumberOfProcessors;
439 /* There has to be at least 1, right? :) */
440 if (SDL_CPUCount <= 0) {
447 /* Oh, such a sweet sweet trick, just not very useful. :) */
451 static char SDL_CPUType[13];
453 if (!SDL_CPUType[0]) {
457 if (CPU_haveCPUID()) {
458 cpuid(0x00000000, a, b, c, d);
459 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
460 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
461 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
462 SDL_CPUType[i++] = (char)(b & 0xff);
464 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
465 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
466 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
467 SDL_CPUType[i++] = (char)(d & 0xff);
469 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
470 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
471 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
472 SDL_CPUType[i++] = (char)(c & 0xff);
474 if (!SDL_CPUType[0]) {
475 SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
482 #ifdef TEST_MAIN /* !!! FIXME: only used for test at the moment. */
486 static char SDL_CPUName[48];
488 if (!SDL_CPUName[0]) {
492 if (CPU_haveCPUID()) {
493 cpuid(0x80000000, a, b, c, d);
494 if (a >= 0x80000004) {
495 cpuid(0x80000002, a, b, c, d);
496 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
497 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
498 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
499 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
500 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
501 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
502 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
503 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
504 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
505 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
506 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
507 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
508 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
509 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
510 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
511 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
512 cpuid(0x80000003, a, b, c, d);
513 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
514 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
515 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
516 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
517 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
518 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
519 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
520 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
521 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
522 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
523 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
524 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
525 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
526 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
527 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
528 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
529 cpuid(0x80000004, a, b, c, d);
530 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
531 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
532 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
533 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
534 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
535 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
536 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
537 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
538 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
539 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
540 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
541 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
542 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
543 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
544 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
545 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
548 if (!SDL_CPUName[0]) {
549 SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
557 SDL_GetCPUCacheLineSize(void)
559 const char *cpuType = SDL_GetCPUType();
561 if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
564 cpuid(0x00000001, a, b, c, d);
565 return (((b >> 8) & 0xff) * 8);
566 } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
569 cpuid(0x80000005, a, b, c, d);
572 /* Just make a guess here... */
573 return SDL_CACHELINE_SIZE;
577 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
580 SDL_GetCPUFeatures(void)
582 if (SDL_CPUFeatures == 0xFFFFFFFF) {
584 if (CPU_haveRDTSC()) {
585 SDL_CPUFeatures |= CPU_HAS_RDTSC;
587 if (CPU_haveAltiVec()) {
588 SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
591 SDL_CPUFeatures |= CPU_HAS_MMX;
593 if (CPU_have3DNow()) {
594 SDL_CPUFeatures |= CPU_HAS_3DNOW;
597 SDL_CPUFeatures |= CPU_HAS_SSE;
599 if (CPU_haveSSE2()) {
600 SDL_CPUFeatures |= CPU_HAS_SSE2;
602 if (CPU_haveSSE3()) {
603 SDL_CPUFeatures |= CPU_HAS_SSE3;
605 if (CPU_haveSSE41()) {
606 SDL_CPUFeatures |= CPU_HAS_SSE41;
608 if (CPU_haveSSE42()) {
609 SDL_CPUFeatures |= CPU_HAS_SSE42;
612 SDL_CPUFeatures |= CPU_HAS_AVX;
614 if (CPU_haveAVX2()) {
615 SDL_CPUFeatures |= CPU_HAS_AVX2;
618 return SDL_CPUFeatures;
624 if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
633 if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
642 if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
651 if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
660 if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
669 if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
678 if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
687 if (SDL_GetCPUFeatures() & CPU_HAS_SSE41) {
696 if (SDL_GetCPUFeatures() & CPU_HAS_SSE42) {
705 if (SDL_GetCPUFeatures() & CPU_HAS_AVX) {
714 if (SDL_GetCPUFeatures() & CPU_HAS_AVX2) {
720 static int SDL_SystemRAM = 0;
723 SDL_GetSystemRAM(void)
725 if (!SDL_SystemRAM) {
726 #if defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
727 if (SDL_SystemRAM <= 0) {
728 SDL_SystemRAM = (int)((Sint64)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) / (1024*1024));
731 #ifdef HAVE_SYSCTLBYNAME
732 if (SDL_SystemRAM <= 0) {
733 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
735 int mib[2] = {CTL_HW, HW_REALMEM};
737 /* might only report up to 2 GiB */
738 int mib[2] = {CTL_HW, HW_PHYSMEM};
739 #endif /* HW_REALMEM */
741 int mib[2] = {CTL_HW, HW_MEMSIZE};
742 #endif /* __FreeBSD__ || __FreeBSD_kernel__ */
744 size_t len = sizeof(memsize);
746 if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0) {
747 SDL_SystemRAM = (int)(memsize / (1024*1024));
752 if (SDL_SystemRAM <= 0) {
754 stat.dwLength = sizeof(stat);
755 if (GlobalMemoryStatusEx(&stat)) {
756 SDL_SystemRAM = (int)(stat.ullTotalPhys / (1024 * 1024));
761 return SDL_SystemRAM;
772 printf("CPU count: %d\n", SDL_GetCPUCount());
773 printf("CPU type: %s\n", SDL_GetCPUType());
774 printf("CPU name: %s\n", SDL_GetCPUName());
775 printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
776 printf("RDTSC: %d\n", SDL_HasRDTSC());
777 printf("Altivec: %d\n", SDL_HasAltiVec());
778 printf("MMX: %d\n", SDL_HasMMX());
779 printf("3DNow: %d\n", SDL_Has3DNow());
780 printf("SSE: %d\n", SDL_HasSSE());
781 printf("SSE2: %d\n", SDL_HasSSE2());
782 printf("SSE3: %d\n", SDL_HasSSE3());
783 printf("SSE4.1: %d\n", SDL_HasSSE41());
784 printf("SSE4.2: %d\n", SDL_HasSSE42());
785 printf("AVX: %d\n", SDL_HasAVX());
786 printf("AVX2: %d\n", SDL_HasAVX2());
787 printf("RAM: %d MB\n", SDL_GetSystemRAM());
791 #endif /* TEST_MAIN */
793 /* vi: set ts=4 sw=4 expandtab: */