src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 11 Feb 2011 14:51:04 -0800
changeset 5259 6a65c1fc07af
parent 5120 b3f075368b1f
child 5262 b530ef003506
permissions -rw-r--r--
Updated CPU detection code for SSE3 and SSE4 and removed obsolete 3DNow! and Altivec support.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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_SSE4    0x00000080
    45 
    46 
    47 static __inline__ int
    48 CPU_haveCPUID(void)
    49 {
    50     int has_CPUID = 0;
    51 /* *INDENT-OFF* */
    52 #if defined(__GNUC__) && defined(i386)
    53     __asm__ (
    54 "        pushfl                      # Get original EFLAGS             \n"
    55 "        popl    %%eax                                                 \n"
    56 "        movl    %%eax,%%ecx                                           \n"
    57 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    58 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    59 "        popfl                       # Replace current EFLAGS value    \n"
    60 "        pushfl                      # Get new EFLAGS                  \n"
    61 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    62 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    63 "        jz      1f                  # Processor=80486                 \n"
    64 "        movl    $1,%0               # We have CPUID support           \n"
    65 "1:                                                                    \n"
    66     : "=m" (has_CPUID)
    67     :
    68     : "%eax", "%ecx"
    69     );
    70 #elif defined(__GNUC__) && defined(__x86_64__)
    71 /* Technically, if this is being compiled under __x86_64__ then it has 
    72 CPUid by definition.  But it's nice to be able to prove it.  :)      */
    73     __asm__ (
    74 "        pushfq                      # Get original EFLAGS             \n"
    75 "        popq    %%rax                                                 \n"
    76 "        movq    %%rax,%%rcx                                           \n"
    77 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    78 "        pushq   %%rax               # Save new EFLAGS value on stack  \n"
    79 "        popfq                       # Replace current EFLAGS value    \n"
    80 "        pushfq                      # Get new EFLAGS                  \n"
    81 "        popq    %%rax               # Store new EFLAGS in EAX         \n"
    82 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    83 "        jz      1f                  # Processor=80486                 \n"
    84 "        movl    $1,%0               # We have CPUID support           \n"
    85 "1:                                                                    \n"
    86     : "=m" (has_CPUID)
    87     :
    88     : "%rax", "%rcx"
    89     );
    90 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
    91     __asm {
    92         pushfd                      ; Get original EFLAGS
    93         pop     eax
    94         mov     ecx, eax
    95         xor     eax, 200000h        ; Flip ID bit in EFLAGS
    96         push    eax                 ; Save new EFLAGS value on stack
    97         popfd                       ; Replace current EFLAGS value
    98         pushfd                      ; Get new EFLAGS
    99         pop     eax                 ; Store new EFLAGS in EAX
   100         xor     eax, ecx            ; Can not toggle ID bit,
   101         jz      done                ; Processor=80486
   102         mov     has_CPUID,1         ; We have CPUID support
   103 done:
   104     }
   105 #elif defined(__sun) && defined(__i386)
   106     __asm (
   107 "       pushfl                 \n"
   108 "       popl    %eax           \n"
   109 "       movl    %eax,%ecx      \n"
   110 "       xorl    $0x200000,%eax \n"
   111 "       pushl   %eax           \n"
   112 "       popfl                  \n"
   113 "       pushfl                 \n"
   114 "       popl    %eax           \n"
   115 "       xorl    %ecx,%eax      \n"
   116 "       jz      1f             \n"
   117 "       movl    $1,-8(%ebp)    \n"
   118 "1:                            \n"
   119     );
   120 #elif defined(__sun) && defined(__amd64)
   121     __asm (
   122 "       pushfq                 \n"
   123 "       popq    %rax           \n"
   124 "       movq    %rax,%rcx      \n"
   125 "       xorl    $0x200000,%eax \n"
   126 "       pushq   %rax           \n"
   127 "       popfq                  \n"
   128 "       pushfq                 \n"
   129 "       popq    %rax           \n"
   130 "       xorl    %ecx,%eax      \n"
   131 "       jz      1f             \n"
   132 "       movl    $1,-8(%rbp)    \n"
   133 "1:                            \n"
   134     );
   135 #endif
   136 /* *INDENT-ON* */
   137     return has_CPUID;
   138 }
   139 
   140 #if defined(__GNUC__) && defined(i386)
   141 #define cpuid(func, a, b, c, d) \
   142     __asm__ __volatile__ ( \
   143 "        pushl %%ebx        \n" \
   144 "        cpuid              \n" \
   145 "        movl %%ebx, %%esi  \n" \
   146 "        popl %%ebx         \n" : \
   147             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   148 #elif defined(__GNUC__) && defined(__x86_64__)
   149 #define cpuid(func, a, b, c, d) \
   150     __asm__ __volatile__ ( \
   151 "        pushq %%rbx        \n" \
   152 "        cpuid              \n" \
   153 "        movq %%rbx, %%rsi  \n" \
   154 "        popq %%rbx         \n" : \
   155             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   156 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   157 #define cpuid(func, a, b, c, d) \
   158     __asm { \
   159         __asm mov eax, func \
   160         __asm cpuid \
   161         __asm mov a, eax \
   162         __asm mov b, ebx \
   163         __asm mov c, ecx \
   164         __asm mov d, edx \
   165     }
   166 #else
   167 #define cpuid(func, a, b, c, d) \
   168     a = b = c = d = 0
   169 #endif
   170 
   171 static __inline__ int
   172 CPU_getCPUIDFeatures(void)
   173 {
   174     int features = 0;
   175     int a, b, c, d;
   176 
   177     cpuid(0, a, b, c, d);
   178     if (a >= 1) {
   179         cpuid(1, a, b, c, d);
   180         features = d;
   181     }
   182     return features;
   183 }
   184 
   185 static __inline__ int
   186 CPU_haveRDTSC(void)
   187 {
   188     if (CPU_haveCPUID()) {
   189         return (CPU_getCPUIDFeatures() & 0x00000010);
   190     }
   191     return 0;
   192 }
   193 
   194 static __inline__ int
   195 CPU_haveMMX(void)
   196 {
   197     if (CPU_haveCPUID()) {
   198         return (CPU_getCPUIDFeatures() & 0x00800000);
   199     }
   200     return 0;
   201 }
   202 
   203 static __inline__ int
   204 CPU_haveSSE(void)
   205 {
   206     if (CPU_haveCPUID()) {
   207         return (CPU_getCPUIDFeatures() & 0x02000000);
   208     }
   209     return 0;
   210 }
   211 
   212 static __inline__ int
   213 CPU_haveSSE2(void)
   214 {
   215     if (CPU_haveCPUID()) {
   216         return (CPU_getCPUIDFeatures() & 0x04000000);
   217     }
   218     return 0;
   219 }
   220 
   221 static __inline__ int
   222 CPU_haveSSE3(void)
   223 {
   224     if (CPU_haveCPUID()) {
   225         int a, b, c, d;
   226 
   227         cpuid(0, a, b, c, d);
   228         if (a >= 1) {
   229             cpuid(1, a, b, c, d);
   230             return (c & 0x00000001);
   231         }
   232     }
   233     return 0;
   234 }
   235 
   236 static __inline__ int
   237 CPU_haveSSE4(void)
   238 {
   239     if (CPU_haveCPUID()) {
   240         int a, b, c, d;
   241 
   242         cpuid(0, a, b, c, d);
   243         if (a >= 1) {
   244             cpuid(1, a, b, c, d);
   245             return (c & 0x00000100);
   246         }
   247     }
   248     return 0;
   249 }
   250 
   251 static int SDL_CPUCount = 0;
   252 
   253 int
   254 SDL_GetCPUCount(void)
   255 {
   256     if (!SDL_CPUCount) {
   257 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
   258         if (SDL_CPUCount <= 0) {
   259             SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
   260         }
   261 #endif
   262 #ifdef HAVE_SYSCTLBYNAME
   263         if (SDL_CPUCount <= 0) {
   264             size_t size = sizeof(SDL_CPUCount);
   265             sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
   266         }
   267 #endif
   268 #ifdef __WIN32__
   269         if (SDL_CPUCount <= 0) {
   270             SYSTEM_INFO info;
   271             GetSystemInfo(&info);
   272             SDL_CPUCount = info.dwNumberOfProcessors;
   273         }
   274 #endif
   275         /* There has to be at least 1, right? :) */
   276         if (SDL_CPUCount <= 0) {
   277             SDL_CPUCount = 1;
   278         }
   279     }
   280     return SDL_CPUCount;
   281 }
   282 
   283 /* Oh, such a sweet sweet trick, just not very useful. :) */
   284 static const char *
   285 SDL_GetCPUType(void)
   286 {
   287     static char SDL_CPUType[13];
   288 
   289     if (!SDL_CPUType[0]) {
   290         int i = 0;
   291         int a, b, c, d;
   292 
   293         if (CPU_haveCPUID()) {
   294             cpuid(0x00000000, a, b, c, d);
   295             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   296             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   297             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   298             SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   299             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   300             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   301             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   302             SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   303             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   304             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   305             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   306             SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   307         }
   308         if (!SDL_CPUType[0]) {
   309             SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
   310         }
   311     }
   312     return SDL_CPUType;
   313 }
   314 
   315 static const char *
   316 SDL_GetCPUName(void)
   317 {
   318     static char SDL_CPUName[48];
   319 
   320     if (!SDL_CPUName[0]) {
   321         int i = 0;
   322         int a, b, c, d;
   323 
   324         if (CPU_haveCPUID()) {
   325             cpuid(0x80000000, a, b, c, d);
   326             if (a >= 0x80000004) {
   327                 cpuid(0x80000002, a, b, c, d);
   328                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   329                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   330                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   331                 SDL_CPUName[i++] = (char)(a & 0xff); a >>= 8;
   332                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   333                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   334                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   335                 SDL_CPUName[i++] = (char)(b & 0xff); b >>= 8;
   336                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   337                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   338                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   339                 SDL_CPUName[i++] = (char)(c & 0xff); c >>= 8;
   340                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   341                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   342                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   343                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   344                 cpuid(0x80000003, a, b, c, d);
   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)(a & 0xff); a >>= 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)(b & 0xff); b >>= 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)(c & 0xff); c >>= 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                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   361                 cpuid(0x80000004, a, b, c, d);
   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)(a & 0xff); a >>= 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)(b & 0xff); b >>= 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)(c & 0xff); c >>= 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                 SDL_CPUName[i++] = (char)(d & 0xff); d >>= 8;
   378             }
   379         }
   380         if (!SDL_CPUName[0]) {
   381             SDL_strlcpy(SDL_CPUName, "Unknown", sizeof(SDL_CPUName));
   382         }
   383     }
   384     return SDL_CPUName;
   385 }
   386 
   387 int
   388 SDL_GetCPUCacheLineSize(void)
   389 {
   390     const char *cpuType = SDL_GetCPUType();
   391 
   392     if (SDL_strcmp(cpuType, "GenuineIntel") == 0) {
   393         int a, b, c, d;
   394 
   395         cpuid(0x00000001, a, b, c, d);
   396         return (((b >> 8) & 0xff) * 8);
   397     } else if (SDL_strcmp(cpuType, "AuthenticAMD") == 0) {
   398         int a, b, c, d;
   399 
   400         cpuid(0x80000005, a, b, c, d);
   401         return (c & 0xff);
   402     } else {
   403         /* Just make a guess here... */
   404         return SDL_CACHELINE_SIZE;
   405     }
   406 }
   407 
   408 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   409 
   410 static Uint32
   411 SDL_GetCPUFeatures(void)
   412 {
   413     if (SDL_CPUFeatures == 0xFFFFFFFF) {
   414         SDL_CPUFeatures = 0;
   415         if (CPU_haveRDTSC()) {
   416             SDL_CPUFeatures |= CPU_HAS_RDTSC;
   417         }
   418         if (CPU_haveMMX()) {
   419             SDL_CPUFeatures |= CPU_HAS_MMX;
   420         }
   421         if (CPU_haveSSE()) {
   422             SDL_CPUFeatures |= CPU_HAS_SSE;
   423         }
   424         if (CPU_haveSSE2()) {
   425             SDL_CPUFeatures |= CPU_HAS_SSE2;
   426         }
   427         if (CPU_haveSSE3()) {
   428             SDL_CPUFeatures |= CPU_HAS_SSE3;
   429         }
   430         if (CPU_haveSSE4()) {
   431             SDL_CPUFeatures |= CPU_HAS_SSE4;
   432         }
   433     }
   434     return SDL_CPUFeatures;
   435 }
   436 
   437 SDL_bool
   438 SDL_HasRDTSC(void)
   439 {
   440     if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
   441         return SDL_TRUE;
   442     }
   443     return SDL_FALSE;
   444 }
   445 
   446 SDL_bool
   447 SDL_HasMMX(void)
   448 {
   449     if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
   450         return SDL_TRUE;
   451     }
   452     return SDL_FALSE;
   453 }
   454 
   455 SDL_bool
   456 SDL_HasSSE(void)
   457 {
   458     if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
   459         return SDL_TRUE;
   460     }
   461     return SDL_FALSE;
   462 }
   463 
   464 SDL_bool
   465 SDL_HasSSE2(void)
   466 {
   467     if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
   468         return SDL_TRUE;
   469     }
   470     return SDL_FALSE;
   471 }
   472 
   473 SDL_bool
   474 SDL_HasSSE3(void)
   475 {
   476     if (SDL_GetCPUFeatures() & CPU_HAS_SSE3) {
   477         return SDL_TRUE;
   478     }
   479     return SDL_FALSE;
   480 }
   481 
   482 SDL_bool
   483 SDL_HasSSE4(void)
   484 {
   485     if (SDL_GetCPUFeatures() & CPU_HAS_SSE4) {
   486         return SDL_TRUE;
   487     }
   488     return SDL_FALSE;
   489 }
   490 
   491 #ifdef TEST_MAIN
   492 
   493 #include <stdio.h>
   494 
   495 int
   496 main()
   497 {
   498     printf("CPU count: %d\n", SDL_GetCPUCount());
   499     printf("CPU type: %s\n", SDL_GetCPUType());
   500     printf("CPU name: %s\n", SDL_GetCPUName());
   501     printf("CacheLine size: %d\n", SDL_GetCPUCacheLineSize());
   502     printf("RDTSC: %d\n", SDL_HasRDTSC());
   503     printf("MMX: %d\n", SDL_HasMMX());
   504     printf("SSE: %d\n", SDL_HasSSE());
   505     printf("SSE2: %d\n", SDL_HasSSE2());
   506     printf("SSE3: %d\n", SDL_HasSSE3());
   507     printf("SSE4: %d\n", SDL_HasSSE4());
   508     return 0;
   509 }
   510 
   511 #endif /* TEST_MAIN */
   512 
   513 /* vi: set ts=4 sw=4 expandtab: */