src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 11 Feb 2011 23:02:35 -0800
changeset 5263 e1122f31fec5
parent 5262 b530ef003506
child 5389 24903690f48a
permissions -rw-r--r--
Fixed SSE4 detection, and split it into SSE 4.1 and 4.2
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* CPU feature detection for SDL */
    25 
    26 #include "SDL_cpuinfo.h"
    27 
    28 #ifdef HAVE_SYSCONF
    29 #include <unistd.h>
    30 #endif
    31 #ifdef HAVE_SYSCTLBYNAME
    32 #include <sys/types.h>
    33 #include <sys/sysctl.h>
    34 #endif
    35 #ifdef __WIN32__
    36 #include "../core/windows/SDL_windows.h"
    37 #endif
    38 
    39 #define CPU_HAS_RDTSC   0x00000001
    40 #define CPU_HAS_MMX     0x00000002
    41 #define CPU_HAS_SSE     0x00000010
    42 #define CPU_HAS_SSE2    0x00000020
    43 #define CPU_HAS_SSE3    0x00000040
    44 #define CPU_HAS_SSE41   0x00000080
    45 #define CPU_HAS_SSE42   0x00000100
    46 
    47 
    48 static __inline__ int
    49 CPU_haveCPUID(void)
    50 {
    51     int has_CPUID = 0;
    52 /* *INDENT-OFF* */
    53 #if defined(__GNUC__) && defined(i386)
    54     __asm__ (
    55 "        pushfl                      # Get original EFLAGS             \n"
    56 "        popl    %%eax                                                 \n"
    57 "        movl    %%eax,%%ecx                                           \n"
    58 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    59 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    60 "        popfl                       # Replace current EFLAGS value    \n"
    61 "        pushfl                      # Get new EFLAGS                  \n"
    62 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    63 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    64 "        jz      1f                  # Processor=80486                 \n"
    65 "        movl    $1,%0               # We have CPUID support           \n"
    66 "1:                                                                    \n"
    67     : "=m" (has_CPUID)
    68     :
    69     : "%eax", "%ecx"
    70     );
    71 #elif defined(__GNUC__) && defined(__x86_64__)
    72 /* Technically, if this is being compiled under __x86_64__ then it has 
    73 CPUid by definition.  But it's nice to be able to prove it.  :)      */
    74     __asm__ (
    75 "        pushfq                      # Get original EFLAGS             \n"
    76 "        popq    %%rax                                                 \n"
    77 "        movq    %%rax,%%rcx                                           \n"
    78 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    79 "        pushq   %%rax               # Save new EFLAGS value on stack  \n"
    80 "        popfq                       # Replace current EFLAGS value    \n"
    81 "        pushfq                      # Get new EFLAGS                  \n"
    82 "        popq    %%rax               # Store new EFLAGS in EAX         \n"
    83 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    84 "        jz      1f                  # Processor=80486                 \n"
    85 "        movl    $1,%0               # We have CPUID support           \n"
    86 "1:                                                                    \n"
    87     : "=m" (has_CPUID)
    88     :
    89     : "%rax", "%rcx"
    90     );
    91 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
    92     __asm {
    93         pushfd                      ; Get original EFLAGS
    94         pop     eax
    95         mov     ecx, eax
    96         xor     eax, 200000h        ; Flip ID bit in EFLAGS
    97         push    eax                 ; Save new EFLAGS value on stack
    98         popfd                       ; Replace current EFLAGS value
    99         pushfd                      ; Get new EFLAGS
   100         pop     eax                 ; Store new EFLAGS in EAX
   101         xor     eax, ecx            ; Can not toggle ID bit,
   102         jz      done                ; Processor=80486
   103         mov     has_CPUID,1         ; We have CPUID support
   104 done:
   105     }
   106 #elif defined(__sun) && defined(__i386)
   107     __asm (
   108 "       pushfl                 \n"
   109 "       popl    %eax           \n"
   110 "       movl    %eax,%ecx      \n"
   111 "       xorl    $0x200000,%eax \n"
   112 "       pushl   %eax           \n"
   113 "       popfl                  \n"
   114 "       pushfl                 \n"
   115 "       popl    %eax           \n"
   116 "       xorl    %ecx,%eax      \n"
   117 "       jz      1f             \n"
   118 "       movl    $1,-8(%ebp)    \n"
   119 "1:                            \n"
   120     );
   121 #elif defined(__sun) && defined(__amd64)
   122     __asm (
   123 "       pushfq                 \n"
   124 "       popq    %rax           \n"
   125 "       movq    %rax,%rcx      \n"
   126 "       xorl    $0x200000,%eax \n"
   127 "       pushq   %rax           \n"
   128 "       popfq                  \n"
   129 "       pushfq                 \n"
   130 "       popq    %rax           \n"
   131 "       xorl    %ecx,%eax      \n"
   132 "       jz      1f             \n"
   133 "       movl    $1,-8(%rbp)    \n"
   134 "1:                            \n"
   135     );
   136 #endif
   137 /* *INDENT-ON* */
   138     return has_CPUID;
   139 }
   140 
   141 #if defined(__GNUC__) && defined(i386)
   142 #define cpuid(func, a, b, c, d) \
   143     __asm__ __volatile__ ( \
   144 "        pushl %%ebx        \n" \
   145 "        cpuid              \n" \
   146 "        movl %%ebx, %%esi  \n" \
   147 "        popl %%ebx         \n" : \
   148             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   149 #elif defined(__GNUC__) && defined(__x86_64__)
   150 #define cpuid(func, a, b, c, d) \
   151     __asm__ __volatile__ ( \
   152 "        pushq %%rbx        \n" \
   153 "        cpuid              \n" \
   154 "        movq %%rbx, %%rsi  \n" \
   155 "        popq %%rbx         \n" : \
   156             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   157 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   158 #define cpuid(func, a, b, c, d) \
   159     __asm { \
   160         __asm mov eax, func \
   161         __asm cpuid \
   162         __asm mov a, eax \
   163         __asm mov b, ebx \
   164         __asm mov c, ecx \
   165         __asm mov d, edx \
   166     }
   167 #else
   168 #define cpuid(func, a, b, c, d) \
   169     a = b = c = d = 0
   170 #endif
   171 
   172 static __inline__ int
   173 CPU_getCPUIDFeatures(void)
   174 {
   175     int features = 0;
   176     int a, b, c, d;
   177 
   178     cpuid(0, a, b, c, d);
   179     if (a >= 1) {
   180         cpuid(1, a, b, c, d);
   181         features = d;
   182     }
   183     return features;
   184 }
   185 
   186 static __inline__ int
   187 CPU_haveRDTSC(void)
   188 {
   189     if (CPU_haveCPUID()) {
   190         return (CPU_getCPUIDFeatures() & 0x00000010);
   191     }
   192     return 0;
   193 }
   194 
   195 static __inline__ int
   196 CPU_haveMMX(void)
   197 {
   198     if (CPU_haveCPUID()) {
   199         return (CPU_getCPUIDFeatures() & 0x00800000);
   200     }
   201     return 0;
   202 }
   203 
   204 static __inline__ int
   205 CPU_haveSSE(void)
   206 {
   207     if (CPU_haveCPUID()) {
   208         return (CPU_getCPUIDFeatures() & 0x02000000);
   209     }
   210     return 0;
   211 }
   212 
   213 static __inline__ int
   214 CPU_haveSSE2(void)
   215 {
   216     if (CPU_haveCPUID()) {
   217         return (CPU_getCPUIDFeatures() & 0x04000000);
   218     }
   219     return 0;
   220 }
   221 
   222 static __inline__ int
   223 CPU_haveSSE3(void)
   224 {
   225     if (CPU_haveCPUID()) {
   226         int a, b, c, d;
   227 
   228         cpuid(0, a, b, c, d);
   229         if (a >= 1) {
   230             cpuid(1, a, b, c, d);
   231             return (c & 0x00000001);
   232         }
   233     }
   234     return 0;
   235 }
   236 
   237 static __inline__ int
   238 CPU_haveSSE41(void)
   239 {
   240     if (CPU_haveCPUID()) {
   241         int a, b, c, d;
   242 
   243         cpuid(1, a, b, c, d);
   244         if (a >= 1) {
   245             cpuid(1, a, b, c, d);
   246             return (c & 0x00080000);
   247         }
   248     }
   249     return 0;
   250 }
   251 
   252 static __inline__ int
   253 CPU_haveSSE42(void)
   254 {
   255     if (CPU_haveCPUID()) {
   256         int a, b, c, d;
   257 
   258         cpuid(1, a, b, c, d);
   259         if (a >= 1) {
   260             cpuid(1, a, b, c, d);
   261             return (c & 0x00100000);
   262         }
   263     }
   264     return 0;
   265 }
   266 
   267 static int SDL_CPUCount = 0;
   268 
   269 int
   270 SDL_GetCPUCount(void)
   271 {
   272     if (!SDL_CPUCount) {
   273 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
   274         if (SDL_CPUCount <= 0) {
   275             SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
   276         }
   277 #endif
   278 #ifdef HAVE_SYSCTLBYNAME
   279         if (SDL_CPUCount <= 0) {
   280             size_t size = sizeof(SDL_CPUCount);
   281             sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
   282         }
   283 #endif
   284 #ifdef __WIN32__
   285         if (SDL_CPUCount <= 0) {
   286             SYSTEM_INFO info;
   287             GetSystemInfo(&info);
   288             SDL_CPUCount = info.dwNumberOfProcessors;
   289         }
   290 #endif
   291         /* There has to be at least 1, right? :) */
   292         if (SDL_CPUCount <= 0) {
   293             SDL_CPUCount = 1;
   294         }
   295     }
   296     return SDL_CPUCount;
   297 }
   298 
   299 /* Oh, such a sweet sweet trick, just not very useful. :) */
   300 static const char *
   301 SDL_GetCPUType(void)
   302 {
   303     static char SDL_CPUType[13];
   304 
   305     if (!SDL_CPUType[0]) {
   306         int i = 0;
   307         int a, b, c, d;
   308 
   309         if (CPU_haveCPUID()) {
   310             cpuid(0x00000000, a, b, c, d);
   311             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   312             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   313             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   314             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   315             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   316             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   317             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   318             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   319             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   320             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   321             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   322             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   323         }
   324         if (!SDL_CPUType[0]) {
   325             SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
   326         }
   327     }
   328     return SDL_CPUType;
   329 }
   330 
   331 static const char *
   332 SDL_GetCPUName(void)
   333 {
   334     static char SDL_CPUName[48];
   335 
   336     if (!SDL_CPUName[0]) {
   337         int i = 0;
   338         int a, b, c, d;
   339 
   340         if (CPU_haveCPUID()) {
   341             cpuid(0x80000000, a, b, c, d);
   342             if (a >= 0x80000004) {
   343                 cpuid(0x80000002, a, b, c, d);
   344                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   345                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   346                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   347                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   348                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   349                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   350                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   351                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   352                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   353                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   354                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   355                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   356                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   357                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   358                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   359                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   360                 cpuid(0x80000003, a, b, c, d);
   361                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   362                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   363                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   364                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   365                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   366                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   367                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   368                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   369                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   370                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   371                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   372                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   373                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   374                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   375                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   376                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   377                 cpuid(0x80000004, a, b, c, d);
   378                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   379                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   380                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   381                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   382                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   383                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   384                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   385                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   386                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   387                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   388                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   389                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   390                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   391                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   392                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   393                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   394             }
   395         }
   396         if (!SDL_CPUName[0]) {
   397             SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
   398         }
   399     }
   400     return SDL_CPUName;
   401 }
   402 
   403 int
   404 SDL_GetCPUCacheLineSize(void)
   405 {
   406     const char *cpuType = SDL_GetCPUType();
   407 
   408     if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
   409         int a, b, c, d;
   410 
   411         cpuid(0x00000001, a, b, c, d);
   412         return (((b >> 8) & 0xff) * 8);
   413     } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
   414         int a, b, c, d;
   415 
   416         cpuid(0x80000005, a, b, c, d);
   417         return (c & 0xff);
   418     } else {
   419         /* Just make a guess here... */
   420         return SDL_CACHELINE_SIZE;
   421     }
   422 }
   423 
   424 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   425 
   426 static Uint32
   427 SDL_GetCPUFeatures(void)
   428 {
   429     if (SDL_CPUFeatures == 0xFFFFFFFF) {
   430         SDL_CPUFeatures = 0;
   431         if (CPU_haveRDTSC()) {
   432             SDL_CPUFeatures |= CPU_HAS_RDTSC;
   433         }
   434         if (CPU_haveMMX()) {
   435             SDL_CPUFeatures |= CPU_HAS_MMX;
   436         }
   437         if (CPU_haveSSE()) {
   438             SDL_CPUFeatures |= CPU_HAS_SSE;
   439         }
   440         if (CPU_haveSSE2()) {
   441             SDL_CPUFeatures |= CPU_HAS_SSE2;
   442         }
   443         if (CPU_haveSSE3()) {
   444             SDL_CPUFeatures |= CPU_HAS_SSE3;
   445         }
   446         if (CPU_haveSSE41()) {
   447             SDL_CPUFeatures |= CPU_HAS_SSE41;
   448         }
   449         if (CPU_haveSSE42()) {
   450             SDL_CPUFeatures |= CPU_HAS_SSE42;
   451         }
   452     }
   453     return SDL_CPUFeatures;
   454 }
   455 
   456 SDL_bool
   457 SDL_HasRDTSC(void)
   458 {
   459     if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
   460         return SDL_TRUE;
   461     }
   462     return SDL_FALSE;
   463 }
   464 
   465 SDL_bool
   466 SDL_HasMMX(void)
   467 {
   468     if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
   469         return SDL_TRUE;
   470     }
   471     return SDL_FALSE;
   472 }
   473 
   474 SDL_bool
   475 SDL_HasSSE(void)
   476 {
   477     if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
   478         return SDL_TRUE;
   479     }
   480     return SDL_FALSE;
   481 }
   482 
   483 SDL_bool
   484 SDL_HasSSE2(void)
   485 {
   486     if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
   487         return SDL_TRUE;
   488     }
   489     return SDL_FALSE;
   490 }
   491 
   492 SDL_bool
   493 SDL_HasSSE3(void)
   494 {
   495     if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
   496         return SDL_TRUE;
   497     }
   498     return SDL_FALSE;
   499 }
   500 
   501 SDL_bool
   502 SDL_HasSSE41(void)
   503 {
   504     if (SDL_GetCPUFeatures() & CPU_HAS_SSE41) {
   505         return SDL_TRUE;
   506     }
   507     return SDL_FALSE;
   508 }
   509 
   510 SDL_bool
   511 SDL_HasSSE42(void)
   512 {
   513     if (SDL_GetCPUFeatures() & CPU_HAS_SSE42) {
   514         return SDL_TRUE;
   515     }
   516     return SDL_FALSE;
   517 }
   518 
   519 #ifdef TEST_MAIN
   520 
   521 #include <stdio.h>
   522 
   523 int
   524 main()
   525 {
   526     printf("CPU count: %d\n", SDL_GetCPUCount());
   527     printf("CPU type: %s\n", SDL_GetCPUType());
   528     printf("CPU name: %s\n", SDL_GetCPUName());
   529     printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
   530     printf("RDTSC: %d\n", SDL_HasRDTSC());
   531     printf("MMX: %d\n", SDL_HasMMX());
   532     printf("SSE: %d\n", SDL_HasSSE());
   533     printf("SSE2: %d\n", SDL_HasSSE2());
   534     printf("SSE3: %d\n", SDL_HasSSE3());
   535     printf("SSE4.1: %d\n", SDL_HasSSE41());
   536     printf("SSE4.2: %d\n", SDL_HasSSE42());
   537     return 0;
   538 }
   539 
   540 #endif /* TEST_MAIN */
   541 
   542 /* vi: set ts=4 sw=4 expandtab: */