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