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