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