src/cpuinfo/SDL_cpuinfo.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 17 Nov 2016 16:04:00 -0500
changeset 10626 04035f36048e
parent 10625 d40f7ebf34eb
child 10627 35fe16a7960a
permissions -rw-r--r--
cpuinfo: Patched to compile on Android, Linux.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 #ifdef TEST_MAIN
    22 #include "SDL_config.h"
    23 #else
    24 #include "../SDL_internal.h"
    25 #endif
    26 
    27 #if defined(__WIN32__)
    28 #include "../core/windows/SDL_windows.h"
    29 #endif
    30 
    31 /* CPU feature detection for SDL */
    32 
    33 #include "SDL_cpuinfo.h"
    34 
    35 #ifdef HAVE_SYSCONF
    36 #include <unistd.h>
    37 #endif
    38 #ifdef HAVE_SYSCTLBYNAME
    39 #include <sys/types.h>
    40 #include <sys/sysctl.h>
    41 #endif
    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
    49 #include <signal.h>
    50 #include <setjmp.h>
    51 #endif
    52 
    53 #if (defined(__LINUX__) || defined(__ANDROID__)) && defined(__ARM_ARCH)
    54 #include <asm/hwcap.h>
    55 #if defined HAVE_GETAUXVAL
    56 #include <sys/auxv.h>
    57 #else
    58 #include <fcntl.h>
    59 #endif
    60 #endif
    61 
    62 #define CPU_HAS_RDTSC   0x00000001
    63 #define CPU_HAS_ALTIVEC 0x00000002
    64 #define CPU_HAS_MMX     0x00000004
    65 #define CPU_HAS_3DNOW   0x00000008
    66 #define CPU_HAS_SSE     0x00000010
    67 #define CPU_HAS_SSE2    0x00000020
    68 #define CPU_HAS_SSE3    0x00000040
    69 #define CPU_HAS_SSE41   0x00000100
    70 #define CPU_HAS_SSE42   0x00000200
    71 #define CPU_HAS_AVX     0x00000400
    72 #define CPU_HAS_AVX2    0x00000800
    73 #define CPU_HAS_NEON    0x00001000
    74 
    75 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
    76 /* This is the brute force way of detecting instruction sets...
    77    the idea is borrowed from the libmpeg2 library - thanks!
    78  */
    79 static jmp_buf jmpbuf;
    80 static void
    81 illegal_instruction(int sig)
    82 {
    83     longjmp(jmpbuf, 1);
    84 }
    85 #endif /* HAVE_SETJMP */
    86 
    87 static int
    88 CPU_haveCPUID(void)
    89 {
    90     int has_CPUID = 0;
    91 
    92 /* *INDENT-OFF* */
    93 #ifndef SDL_CPUINFO_DISABLED
    94 #if defined(__GNUC__) && defined(i386)
    95     __asm__ (
    96 "        pushfl                      # Get original EFLAGS             \n"
    97 "        popl    %%eax                                                 \n"
    98 "        movl    %%eax,%%ecx                                           \n"
    99 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
   100 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
   101 "        popfl                       # Replace current EFLAGS value    \n"
   102 "        pushfl                      # Get new EFLAGS                  \n"
   103 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
   104 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
   105 "        jz      1f                  # Processor=80486                 \n"
   106 "        movl    $1,%0               # We have CPUID support           \n"
   107 "1:                                                                    \n"
   108     : "=m" (has_CPUID)
   109     :
   110     : "%eax", "%ecx"
   111     );
   112 #elif defined(__GNUC__) && defined(__x86_64__)
   113 /* Technically, if this is being compiled under __x86_64__ then it has 
   114    CPUid by definition.  But it's nice to be able to prove it.  :)      */
   115     __asm__ (
   116 "        pushfq                      # Get original EFLAGS             \n"
   117 "        popq    %%rax                                                 \n"
   118 "        movq    %%rax,%%rcx                                           \n"
   119 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
   120 "        pushq   %%rax               # Save new EFLAGS value on stack  \n"
   121 "        popfq                       # Replace current EFLAGS value    \n"
   122 "        pushfq                      # Get new EFLAGS                  \n"
   123 "        popq    %%rax               # Store new EFLAGS in EAX         \n"
   124 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
   125 "        jz      1f                  # Processor=80486                 \n"
   126 "        movl    $1,%0               # We have CPUID support           \n"
   127 "1:                                                                    \n"
   128     : "=m" (has_CPUID)
   129     :
   130     : "%rax", "%rcx"
   131     );
   132 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   133     __asm {
   134         pushfd                      ; Get original EFLAGS
   135         pop     eax
   136         mov     ecx, eax
   137         xor     eax, 200000h        ; Flip ID bit in EFLAGS
   138         push    eax                 ; Save new EFLAGS value on stack
   139         popfd                       ; Replace current EFLAGS value
   140         pushfd                      ; Get new EFLAGS
   141         pop     eax                 ; Store new EFLAGS in EAX
   142         xor     eax, ecx            ; Can not toggle ID bit,
   143         jz      done                ; Processor=80486
   144         mov     has_CPUID,1         ; We have CPUID support
   145 done:
   146     }
   147 #elif defined(_MSC_VER) && defined(_M_X64)
   148     has_CPUID = 1;
   149 #elif defined(__sun) && defined(__i386)
   150     __asm (
   151 "       pushfl                 \n"
   152 "       popl    %eax           \n"
   153 "       movl    %eax,%ecx      \n"
   154 "       xorl    $0x200000,%eax \n"
   155 "       pushl   %eax           \n"
   156 "       popfl                  \n"
   157 "       pushfl                 \n"
   158 "       popl    %eax           \n"
   159 "       xorl    %ecx,%eax      \n"
   160 "       jz      1f             \n"
   161 "       movl    $1,-8(%ebp)    \n"
   162 "1:                            \n"
   163     );
   164 #elif defined(__sun) && defined(__amd64)
   165     __asm (
   166 "       pushfq                 \n"
   167 "       popq    %rax           \n"
   168 "       movq    %rax,%rcx      \n"
   169 "       xorl    $0x200000,%eax \n"
   170 "       pushq   %rax           \n"
   171 "       popfq                  \n"
   172 "       pushfq                 \n"
   173 "       popq    %rax           \n"
   174 "       xorl    %ecx,%eax      \n"
   175 "       jz      1f             \n"
   176 "       movl    $1,-8(%rbp)    \n"
   177 "1:                            \n"
   178     );
   179 #endif
   180 #endif
   181 /* *INDENT-ON* */
   182     return has_CPUID;
   183 }
   184 
   185 #if defined(__GNUC__) && defined(i386)
   186 #define cpuid(func, a, b, c, d) \
   187     __asm__ __volatile__ ( \
   188 "        pushl %%ebx        \n" \
   189 "        xorl %%ecx,%%ecx   \n" \
   190 "        cpuid              \n" \
   191 "        movl %%ebx, %%esi  \n" \
   192 "        popl %%ebx         \n" : \
   193             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   194 #elif defined(__GNUC__) && defined(__x86_64__)
   195 #define cpuid(func, a, b, c, d) \
   196     __asm__ __volatile__ ( \
   197 "        pushq %%rbx        \n" \
   198 "        xorq %%rcx,%%rcx   \n" \
   199 "        cpuid              \n" \
   200 "        movq %%rbx, %%rsi  \n" \
   201 "        popq %%rbx         \n" : \
   202             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   203 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   204 #define cpuid(func, a, b, c, d) \
   205     __asm { \
   206         __asm mov eax, func \
   207         __asm xor ecx, ecx \
   208         __asm cpuid \
   209         __asm mov a, eax \
   210         __asm mov b, ebx \
   211         __asm mov c, ecx \
   212         __asm mov d, edx \
   213 }
   214 #elif defined(_MSC_VER) && defined(_M_X64)
   215 #define cpuid(func, a, b, c, d) \
   216 { \
   217     int CPUInfo[4]; \
   218     __cpuid(CPUInfo, func); \
   219     a = CPUInfo[0]; \
   220     b = CPUInfo[1]; \
   221     c = CPUInfo[2]; \
   222     d = CPUInfo[3]; \
   223 }
   224 #else
   225 #define cpuid(func, a, b, c, d) \
   226     a = b = c = d = 0
   227 #endif
   228 
   229 static int CPU_CPUIDFeatures[4];
   230 static int CPU_CPUIDMaxFunction = 0;
   231 static SDL_bool CPU_OSSavesYMM = SDL_FALSE;
   232 
   233 static void
   234 CPU_calcCPUIDFeatures(void)
   235 {
   236     static SDL_bool checked = SDL_FALSE;
   237     if (!checked) {
   238         checked = SDL_TRUE;
   239         if (CPU_haveCPUID()) {
   240             int a, b, c, d;
   241             cpuid(0, a, b, c, d);
   242             CPU_CPUIDMaxFunction = a;
   243             if (CPU_CPUIDMaxFunction >= 1) {
   244                 cpuid(1, a, b, c, d);
   245                 CPU_CPUIDFeatures[0] = a;
   246                 CPU_CPUIDFeatures[1] = b;
   247                 CPU_CPUIDFeatures[2] = c;
   248                 CPU_CPUIDFeatures[3] = d;
   249 
   250                 /* Check to make sure we can call xgetbv */
   251                 if (c & 0x08000000) {
   252                     /* Call xgetbv to see if YMM register state is saved */
   253 #if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
   254                     asm(".byte 0x0f, 0x01, 0xd0" : "=a" (a) : "c" (0) : "%edx");
   255 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && (_MSC_FULL_VER >= 160040219) /* VS2010 SP1 */
   256                     a = (int)_xgetbv(0);
   257 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   258                     __asm
   259                     {
   260                         xor ecx, ecx
   261                         _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0
   262                         mov a, eax
   263                     }
   264 #endif
   265                     CPU_OSSavesYMM = ((a & 6) == 6) ? SDL_TRUE : SDL_FALSE;
   266                 }
   267             }
   268         }
   269     }
   270 }
   271 
   272 static int
   273 CPU_haveAltiVec(void)
   274 {
   275     volatile int altivec = 0;
   276 #ifndef SDL_CPUINFO_DISABLED
   277 #if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
   278 #ifdef __OpenBSD__
   279     int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
   280 #else
   281     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
   282 #endif
   283     int hasVectorUnit = 0;
   284     size_t length = sizeof(hasVectorUnit);
   285     int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
   286     if (0 == error)
   287         altivec = (hasVectorUnit != 0);
   288 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
   289     void (*handler) (int sig);
   290     handler = signal(SIGILL, illegal_instruction);
   291     if (setjmp(jmpbuf) == 0) {
   292         asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
   293         altivec = 1;
   294     }
   295     signal(SIGILL, handler);
   296 #endif
   297 #endif
   298     return altivec;
   299 }
   300 
   301 #if (defined(__LINUX__) || defined(__ANDROID__)) && defined(__ARM_ARCH) && !defined(HAVE_GETAUXVAL)
   302 static int
   303 readProcAuxvForNeon(void)
   304 {
   305     int neon = 0;
   306     int kv[2];
   307     const int fd = open("/proc/self/auxv", O_RDONLY);
   308     if (fd != -1) {
   309         while (read(fd, kv, sizeof (kv)) == sizeof (kv)) {
   310             if (kv[0] == AT_HWCAP) {
   311                 neon = ((kv[1] & HWCAP_NEON) == HWCAP_NEON);
   312                 break;
   313             }
   314         }
   315         close(fd);
   316     }
   317     return neon;
   318 }
   319 #endif
   320 
   321 
   322 static int
   323 CPU_haveNEON(void)
   324 {
   325 /* The way you detect NEON is a privileged instruction on ARM, so you have
   326    query the OS kernel in a platform-specific way. :/ */
   327 #if defined(SDL_CPUINFO_DISABLED) || !defined(__ARM_ARCH)
   328     return 0;  /* disabled or not an ARM CPU at all. */
   329 #elif __ARM_ARCH >= 8
   330     return 1;  // ARMv8 always has non-optional NEON support.
   331 #elif defined(__APPLE__)
   332     /* all hardware that runs iOS 5 and later support NEON, but check anyhow */
   333     int neon = 0;
   334     size_t length = sizeof (neon);
   335     const int error = sysctlbyname("hw.optional.neon", &neon, &length, NULL, 0);
   336     return (!error) && (neon != 0);
   337 #elif (defined(__LINUX__) || defined(__ANDROID__)) && defined(HAVE_GETAUXVAL)
   338     return ((getauxval(AT_HWCAP) & HWCAP_NEON) == HWCAP_NEON)
   339 #elif (defined(__LINUX__) || defined(__ANDROID__))
   340     return readProcAuxvForNeon();   /* Android offers a static library for this, but it just parses /proc/self/auxv */
   341 #elif (defined(__WINDOWS__) || defined(__WINRT__)) && defined(_M_ARM)
   342     /* All WinRT ARM devices are required to support NEON, but just in case. */
   343     return IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) != 0;
   344 #else
   345 #warning SDL_HasNEON is not implemented for this ARM platform. Write me.
   346     return 0;
   347 #endif
   348 }
   349 
   350 static int
   351 CPU_have3DNow(void)
   352 {
   353     if (CPU_CPUIDMaxFunction > 0) {  /* that is, do we have CPUID at all? */
   354         int a, b, c, d;
   355         cpuid(0x80000000, a, b, c, d);
   356         if (a >= 0x80000001) {
   357             cpuid(0x80000001, a, b, c, d);
   358             return (d & 0x80000000);
   359         }
   360     }
   361     return 0;
   362 }
   363 
   364 #define CPU_haveRDTSC() (CPU_CPUIDFeatures[3] & 0x00000010)
   365 #define CPU_haveMMX() (CPU_CPUIDFeatures[3] & 0x00800000)
   366 #define CPU_haveSSE() (CPU_CPUIDFeatures[3] & 0x02000000)
   367 #define CPU_haveSSE2() (CPU_CPUIDFeatures[3] & 0x04000000)
   368 #define CPU_haveSSE3() (CPU_CPUIDFeatures[2] & 0x00000001)
   369 #define CPU_haveSSE41() (CPU_CPUIDFeatures[2] & 0x00080000)
   370 #define CPU_haveSSE42() (CPU_CPUIDFeatures[2] & 0x00100000)
   371 #define CPU_haveAVX() (CPU_OSSavesYMM && (CPU_CPUIDFeatures[2] & 0x10000000))
   372 
   373 static int
   374 CPU_haveAVX2(void)
   375 {
   376     if (CPU_OSSavesYMM && (CPU_CPUIDMaxFunction >= 7)) {
   377         int a, b, c, d;
   378         (void) a; (void) b; (void) c; (void) d;  /* compiler warnings... */
   379         cpuid(7, a, b, c, d);
   380         return (b & 0x00000020);
   381     }
   382     return 0;
   383 }
   384 
   385 static int SDL_CPUCount = 0;
   386 
   387 int
   388 SDL_GetCPUCount(void)
   389 {
   390     if (!SDL_CPUCount) {
   391 #ifndef SDL_CPUINFO_DISABLED
   392 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
   393         if (SDL_CPUCount <= 0) {
   394             SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
   395         }
   396 #endif
   397 #ifdef HAVE_SYSCTLBYNAME
   398         if (SDL_CPUCount <= 0) {
   399             size_t size = sizeof(SDL_CPUCount);
   400             sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
   401         }
   402 #endif
   403 #ifdef __WIN32__
   404         if (SDL_CPUCount <= 0) {
   405             SYSTEM_INFO info;
   406             GetSystemInfo(&info);
   407             SDL_CPUCount = info.dwNumberOfProcessors;
   408         }
   409 #endif
   410 #endif
   411         /* There has to be at least 1, right? :) */
   412         if (SDL_CPUCount <= 0) {
   413             SDL_CPUCount = 1;
   414         }
   415     }
   416     return SDL_CPUCount;
   417 }
   418 
   419 /* Oh, such a sweet sweet trick, just not very useful. :) */
   420 static const char *
   421 SDL_GetCPUType(void)
   422 {
   423     static char SDL_CPUType[13];
   424 
   425     if (!SDL_CPUType[0]) {
   426         int i = 0;
   427 
   428         CPU_calcCPUIDFeatures();
   429         if (CPU_CPUIDMaxFunction > 0) {  /* do we have CPUID at all? */
   430             int a, b, c, d;
   431             cpuid(0x00000000, a, b, c, d);
   432             (void) a;
   433             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   434             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   435             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   436             SDL_CPUType[i++] = (char)(b & 0xff);
   437 
   438             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   439             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   440             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   441             SDL_CPUType[i++] = (char)(d & 0xff);
   442 
   443             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   444             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   445             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   446             SDL_CPUType[i++] = (char)(c & 0xff);
   447         }
   448         if (!SDL_CPUType[0]) {
   449             SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
   450         }
   451     }
   452     return SDL_CPUType;
   453 }
   454 
   455 
   456 #ifdef TEST_MAIN  /* !!! FIXME: only used for test at the moment. */
   457 static const char *
   458 SDL_GetCPUName(void)
   459 {
   460     static char SDL_CPUName[48];
   461 
   462     if (!SDL_CPUName[0]) {
   463         int i = 0;
   464         int a, b, c, d;
   465 
   466         CPU_calcCPUIDFeatures();
   467         if (CPU_CPUIDMaxFunction > 0) {  /* do we have CPUID at all? */
   468             cpuid(0x80000000, a, b, c, d);
   469             if (a >= 0x80000004) {
   470                 cpuid(0x80000002, a, b, c, d);
   471                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   472                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   473                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   474                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   475                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   476                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   477                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   478                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   479                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   480                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   481                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   482                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   483                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   484                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   485                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   486                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   487                 cpuid(0x80000003, a, b, c, d);
   488                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   489                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   490                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   491                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   492                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   493                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   494                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   495                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   496                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   497                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   498                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   499                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   500                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   501                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   502                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   503                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   504                 cpuid(0x80000004, a, b, c, d);
   505                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   506                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   507                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   508                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   509                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   510                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   511                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   512                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   513                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   514                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   515                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   516                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   517                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   518                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   519                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   520                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   521             }
   522         }
   523         if (!SDL_CPUName[0]) {
   524             SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
   525         }
   526     }
   527     return SDL_CPUName;
   528 }
   529 #endif
   530 
   531 int
   532 SDL_GetCPUCacheLineSize(void)
   533 {
   534     const char *cpuType = SDL_GetCPUType();
   535     int a, b, c, d;
   536     (void) a; (void) b; (void) c; (void) d;
   537     if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
   538         cpuid(0x00000001, a, b, c, d);
   539         return (((b >> 8) & 0xff) * 8);
   540     } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
   541         cpuid(0x80000005, a, b, c, d);
   542         return (c & 0xff);
   543     } else {
   544         /* Just make a guess here... */
   545         return SDL_CACHELINE_SIZE;
   546     }
   547 }
   548 
   549 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   550 
   551 static Uint32
   552 SDL_GetCPUFeatures(void)
   553 {
   554     if (SDL_CPUFeatures == 0xFFFFFFFF) {
   555         CPU_calcCPUIDFeatures();
   556         SDL_CPUFeatures = 0;
   557         if (CPU_haveRDTSC()) {
   558             SDL_CPUFeatures |= CPU_HAS_RDTSC;
   559         }
   560         if (CPU_haveAltiVec()) {
   561             SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   562         }
   563         if (CPU_haveMMX()) {
   564             SDL_CPUFeatures |= CPU_HAS_MMX;
   565         }
   566         if (CPU_have3DNow()) {
   567             SDL_CPUFeatures |= CPU_HAS_3DNOW;
   568         }
   569         if (CPU_haveSSE()) {
   570             SDL_CPUFeatures |= CPU_HAS_SSE;
   571         }
   572         if (CPU_haveSSE2()) {
   573             SDL_CPUFeatures |= CPU_HAS_SSE2;
   574         }
   575         if (CPU_haveSSE3()) {
   576             SDL_CPUFeatures |= CPU_HAS_SSE3;
   577         }
   578         if (CPU_haveSSE41()) {
   579             SDL_CPUFeatures |= CPU_HAS_SSE41;
   580         }
   581         if (CPU_haveSSE42()) {
   582             SDL_CPUFeatures |= CPU_HAS_SSE42;
   583         }
   584         if (CPU_haveAVX()) {
   585             SDL_CPUFeatures |= CPU_HAS_AVX;
   586         }
   587         if (CPU_haveAVX2()) {
   588             SDL_CPUFeatures |= CPU_HAS_AVX2;
   589         }
   590         if (CPU_haveNEON()) {
   591             SDL_CPUFeatures |= CPU_HAS_NEON;
   592         }
   593     }
   594     return SDL_CPUFeatures;
   595 }
   596 
   597 #define CPU_FEATURE_AVAILABLE(f) ((SDL_GetCPUFeatures() & f) ? SDL_TRUE : SDL_FALSE)
   598 
   599 SDL_bool SDL_HasRDTSC(void)
   600 {
   601     return CPU_FEATURE_AVAILABLE(CPU_HAS_RDTSC);
   602 }
   603 
   604 SDL_bool
   605 SDL_HasAltiVec(void)
   606 {
   607     return CPU_FEATURE_AVAILABLE(CPU_HAS_ALTIVEC);
   608 }
   609 
   610 SDL_bool
   611 SDL_HasMMX(void)
   612 {
   613     return CPU_FEATURE_AVAILABLE(CPU_HAS_MMX);
   614 }
   615 
   616 SDL_bool
   617 SDL_Has3DNow(void)
   618 {
   619     return CPU_FEATURE_AVAILABLE(CPU_HAS_3DNOW);
   620 }
   621 
   622 SDL_bool
   623 SDL_HasSSE(void)
   624 {
   625     return CPU_FEATURE_AVAILABLE(CPU_HAS_SSE);
   626 }
   627 
   628 SDL_bool
   629 SDL_HasSSE2(void)
   630 {
   631     return CPU_FEATURE_AVAILABLE(CPU_HAS_SSE2);
   632 }
   633 
   634 SDL_bool
   635 SDL_HasSSE3(void)
   636 {
   637     return CPU_FEATURE_AVAILABLE(CPU_HAS_SSE3);
   638 }
   639 
   640 SDL_bool
   641 SDL_HasSSE41(void)
   642 {
   643     return CPU_FEATURE_AVAILABLE(CPU_HAS_SSE41);
   644 }
   645 
   646 SDL_bool
   647 SDL_HasSSE42(void)
   648 {
   649     return CPU_FEATURE_AVAILABLE(CPU_HAS_SSE42);
   650 }
   651 
   652 SDL_bool
   653 SDL_HasAVX(void)
   654 {
   655     return CPU_FEATURE_AVAILABLE(CPU_HAS_AVX);
   656 }
   657 
   658 SDL_bool
   659 SDL_HasAVX2(void)
   660 {
   661     return CPU_FEATURE_AVAILABLE(CPU_HAS_AVX2);
   662 }
   663 
   664 SDL_bool
   665 SDL_HasNEON(void)
   666 {
   667     return CPU_FEATURE_AVAILABLE(CPU_HAS_NEON);
   668 }
   669 
   670 static int SDL_SystemRAM = 0;
   671 
   672 int
   673 SDL_GetSystemRAM(void)
   674 {
   675     if (!SDL_SystemRAM) {
   676 #ifndef SDL_CPUINFO_DISABLED
   677 #if defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
   678         if (SDL_SystemRAM <= 0) {
   679             SDL_SystemRAM = (int)((Sint64)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) / (1024*1024));
   680         }
   681 #endif
   682 #ifdef HAVE_SYSCTLBYNAME
   683         if (SDL_SystemRAM <= 0) {
   684 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
   685 #ifdef HW_REALMEM
   686             int mib[2] = {CTL_HW, HW_REALMEM};
   687 #else
   688             /* might only report up to 2 GiB */
   689             int mib[2] = {CTL_HW, HW_PHYSMEM};
   690 #endif /* HW_REALMEM */
   691 #else
   692             int mib[2] = {CTL_HW, HW_MEMSIZE};
   693 #endif /* __FreeBSD__ || __FreeBSD_kernel__ */
   694             Uint64 memsize = 0;
   695             size_t len = sizeof(memsize);
   696             
   697             if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0) {
   698                 SDL_SystemRAM = (int)(memsize / (1024*1024));
   699             }
   700         }
   701 #endif
   702 #ifdef __WIN32__
   703         if (SDL_SystemRAM <= 0) {
   704             MEMORYSTATUSEX stat;
   705             stat.dwLength = sizeof(stat);
   706             if (GlobalMemoryStatusEx(&stat)) {
   707                 SDL_SystemRAM = (int)(stat.ullTotalPhys / (1024 * 1024));
   708             }
   709         }
   710 #endif
   711 #endif
   712     }
   713     return SDL_SystemRAM;
   714 }
   715 
   716 
   717 #ifdef TEST_MAIN
   718 
   719 #include <stdio.h>
   720 
   721 int
   722 main()
   723 {
   724     printf("CPU count: %d\n", SDL_GetCPUCount());
   725     printf("CPU type: %s\n", SDL_GetCPUType());
   726     printf("CPU name: %s\n", SDL_GetCPUName());
   727     printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
   728     printf("RDTSC: %d\n", SDL_HasRDTSC());
   729     printf("Altivec: %d\n", SDL_HasAltiVec());
   730     printf("MMX: %d\n", SDL_HasMMX());
   731     printf("3DNow: %d\n", SDL_Has3DNow());
   732     printf("SSE: %d\n", SDL_HasSSE());
   733     printf("SSE2: %d\n", SDL_HasSSE2());
   734     printf("SSE3: %d\n", SDL_HasSSE3());
   735     printf("SSE4.1: %d\n", SDL_HasSSE41());
   736     printf("SSE4.2: %d\n", SDL_HasSSE42());
   737     printf("AVX: %d\n", SDL_HasAVX());
   738     printf("AVX2: %d\n", SDL_HasAVX2());
   739     printf("NEON: %d\n", SDL_HasNEON());
   740     printf("RAM: %d MB\n", SDL_GetSystemRAM());
   741     return 0;
   742 }
   743 
   744 #endif /* TEST_MAIN */
   745 
   746 /* vi: set ts=4 sw=4 expandtab: */