src/cpuinfo/SDL_cpuinfo.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 17 Nov 2016 16:01:59 -0500
changeset 10625 d40f7ebf34eb
parent 10624 294ee69a9f11
child 10626 04035f36048e
permissions -rw-r--r--
cpuinfo: patched to compile.

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