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