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