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