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