src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 17 Dec 2009 03:04:04 +0000
changeset 3586 b6758aee0dd4
parent 3584 41d01d70659c
child 3587 2080e8d75ac6
permissions -rw-r--r--
Added support for querying the number of CPUs available on Linux. This also happens to work on Mac OS X.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 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 #if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
    36 #include <sys/sysctl.h>         /* For AltiVec check */
    37 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
    38 #include <signal.h>
    39 #include <setjmp.h>
    40 #endif
    41 #ifdef __WIN32__
    42 #define WIN32_LEAN_AND_MEAN
    43 #include <windows.h>
    44 #endif
    45 
    46 #define CPU_HAS_RDTSC   0x00000001
    47 #define CPU_HAS_MMX     0x00000002
    48 #define CPU_HAS_MMXEXT  0x00000004
    49 #define CPU_HAS_3DNOW   0x00000010
    50 #define CPU_HAS_3DNOWEXT 0x00000020
    51 #define CPU_HAS_SSE     0x00000040
    52 #define CPU_HAS_SSE2    0x00000080
    53 #define CPU_HAS_ALTIVEC 0x00000100
    54 
    55 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__
    56 /* This is the brute force way of detecting instruction sets...
    57    the idea is borrowed from the libmpeg2 library - thanks!
    58  */
    59 static jmp_buf jmpbuf;
    60 static void
    61 illegal_instruction(int sig)
    62 {
    63     longjmp(jmpbuf, 1);
    64 }
    65 #endif /* HAVE_SETJMP */
    66 
    67 static __inline__ int
    68 CPU_haveCPUID(void)
    69 {
    70     int has_CPUID = 0;
    71 /* *INDENT-OFF* */
    72 #if defined(__GNUC__) && defined(i386)
    73     __asm__ (
    74 "        pushfl                      # Get original EFLAGS             \n"
    75 "        popl    %%eax                                                 \n"
    76 "        movl    %%eax,%%ecx                                           \n"
    77 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    78 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    79 "        popfl                       # Replace current EFLAGS value    \n"
    80 "        pushfl                      # Get new EFLAGS                  \n"
    81 "        popl    %%eax               # 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     : "%eax", "%ecx"
    89     );
    90 #elif defined(__GNUC__) && defined(__x86_64__)
    91 /* Technically, if this is being compiled under __x86_64__ then it has 
    92 CPUid by definition.  But it's nice to be able to prove it.  :)      */
    93     __asm__ (
    94 "        pushfq                      # Get original EFLAGS             \n"
    95 "        popq    %%rax                                                 \n"
    96 "        movq    %%rax,%%rcx                                           \n"
    97 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    98 "        pushq   %%rax               # Save new EFLAGS value on stack  \n"
    99 "        popfq                       # Replace current EFLAGS value    \n"
   100 "        pushfq                      # Get new EFLAGS                  \n"
   101 "        popq    %%rax               # Store new EFLAGS in EAX         \n"
   102 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
   103 "        jz      1f                  # Processor=80486                 \n"
   104 "        movl    $1,%0               # We have CPUID support           \n"
   105 "1:                                                                    \n"
   106     : "=m" (has_CPUID)
   107     :
   108     : "%rax", "%rcx"
   109     );
   110 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   111     __asm {
   112         pushfd                      ; Get original EFLAGS
   113         pop     eax
   114         mov     ecx, eax
   115         xor     eax, 200000h        ; Flip ID bit in EFLAGS
   116         push    eax                 ; Save new EFLAGS value on stack
   117         popfd                       ; Replace current EFLAGS value
   118         pushfd                      ; Get new EFLAGS
   119         pop     eax                 ; Store new EFLAGS in EAX
   120         xor     eax, ecx            ; Can not toggle ID bit,
   121         jz      done                ; Processor=80486
   122         mov     has_CPUID,1         ; We have CPUID support
   123 done:
   124     }
   125 #elif defined(__sun) && defined(__i386)
   126     __asm (
   127 "       pushfl                 \n"
   128 "       popl    %eax           \n"
   129 "       movl    %eax,%ecx      \n"
   130 "       xorl    $0x200000,%eax \n"
   131 "       pushl   %eax           \n"
   132 "       popfl                  \n"
   133 "       pushfl                 \n"
   134 "       popl    %eax           \n"
   135 "       xorl    %ecx,%eax      \n"
   136 "       jz      1f             \n"
   137 "       movl    $1,-8(%ebp)    \n"
   138 "1:                            \n"
   139     );
   140 #elif defined(__sun) && defined(__amd64)
   141     __asm (
   142 "       pushfq                 \n"
   143 "       popq    %rax           \n"
   144 "       movq    %rax,%rcx      \n"
   145 "       xorl    $0x200000,%eax \n"
   146 "       pushq   %rax           \n"
   147 "       popfq                  \n"
   148 "       pushfq                 \n"
   149 "       popq    %rax           \n"
   150 "       xorl    %ecx,%eax      \n"
   151 "       jz      1f             \n"
   152 "       movl    $1,-8(%rbp)    \n"
   153 "1:                            \n"
   154     );
   155 #endif
   156 /* *INDENT-ON* */
   157     return has_CPUID;
   158 }
   159 
   160 #if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
   161 #define cpuid(func, a, b, c, d) \
   162     __asm__ __volatile__ ( \
   163 "        pushl %%ebx        \n" \
   164 "        cpuid              \n" \
   165 "        movl %%ebx, %%esi  \n" \
   166 "        popl %%ebx         \n" : \
   167             "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func))
   168 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   169 #define cpuid(func, a, b, c, d) \
   170     __asm { \
   171         __asm mov eax, func \
   172         __asm cpuid \
   173         __asm mov a, eax \
   174         __asm mov b, ebx \
   175         __asm mov c, ecx \
   176         __asm mov d, edx \
   177     }
   178 #else
   179 #define cpuid(func, a, b, c, d) \
   180     a = b = c = d = 0
   181 #endif
   182 
   183 static __inline__ int
   184 CPU_getCPUIDFeatures(void)
   185 {
   186     int features = 0;
   187     int a, b, c, d;
   188 
   189     cpuid(0, a, b, c, d);
   190     if (a >= 1) {
   191         cpuid(1, a, b, c, d);
   192         features = d;
   193     }
   194     return features;
   195 }
   196 
   197 static __inline__ int
   198 CPU_getCPUIDFeaturesExt(void)
   199 {
   200     int features = 0;
   201     int a, b, c, d;
   202 
   203     cpuid(0x80000000, a, b, c, d);
   204     if (a >= 0x80000001) {
   205         cpuid(0x80000001, a, b, c, d);
   206         features = d;
   207     }
   208     return features;
   209 }
   210 
   211 static __inline__ int
   212 CPU_haveRDTSC(void)
   213 {
   214     if (CPU_haveCPUID()) {
   215         return (CPU_getCPUIDFeatures() & 0x00000010);
   216     }
   217     return 0;
   218 }
   219 
   220 static __inline__ int
   221 CPU_haveMMX(void)
   222 {
   223     if (CPU_haveCPUID()) {
   224         return (CPU_getCPUIDFeatures() & 0x00800000);
   225     }
   226     return 0;
   227 }
   228 
   229 static __inline__ int
   230 CPU_haveMMXExt(void)
   231 {
   232     if (CPU_haveCPUID()) {
   233         return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   234     }
   235     return 0;
   236 }
   237 
   238 static __inline__ int
   239 CPU_have3DNow(void)
   240 {
   241     if (CPU_haveCPUID()) {
   242         return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   243     }
   244     return 0;
   245 }
   246 
   247 static __inline__ int
   248 CPU_have3DNowExt(void)
   249 {
   250     if (CPU_haveCPUID()) {
   251         return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   252     }
   253     return 0;
   254 }
   255 
   256 static __inline__ int
   257 CPU_haveSSE(void)
   258 {
   259     if (CPU_haveCPUID()) {
   260         return (CPU_getCPUIDFeatures() & 0x02000000);
   261     }
   262     return 0;
   263 }
   264 
   265 static __inline__ int
   266 CPU_haveSSE2(void)
   267 {
   268     if (CPU_haveCPUID()) {
   269         return (CPU_getCPUIDFeatures() & 0x04000000);
   270     }
   271     return 0;
   272 }
   273 
   274 static __inline__ int
   275 CPU_haveAltiVec(void)
   276 {
   277     volatile int altivec = 0;
   278 #if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))
   279     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
   280     int hasVectorUnit = 0;
   281     size_t length = sizeof(hasVectorUnit);
   282     int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
   283     if (0 == error)
   284         altivec = (hasVectorUnit != 0);
   285 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
   286     void (*handler) (int sig);
   287     handler = signal(SIGILL, illegal_instruction);
   288     if (setjmp(jmpbuf) == 0) {
   289         asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
   290         altivec = 1;
   291     }
   292     signal(SIGILL, handler);
   293 #endif
   294     return altivec;
   295 }
   296 
   297 static int SDL_CPUCount = 0;
   298 
   299 int
   300 SDL_GetCPUCount()
   301 {
   302     if (!SDL_CPUCount) {
   303 #ifdef HAVE_SYSCONF
   304         if (SDL_CPUCount <= 0) {
   305             SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
   306         }
   307 #endif
   308 #ifdef HAVE_SYSCTLBYNAME
   309         if (SDL_CPUCount <= 0) {
   310             size_t size = sizeof(SDL_CPUCount);
   311             sysctlbyname("hw.ncpu", &SDL_CPUCount, &size, NULL, 0);
   312         }
   313 #endif
   314 #ifdef __WIN32__
   315         if (SDL_CPUCount <= 0) {
   316             SYSTEM_INFO info;
   317             GetSystemInfo(&info);
   318             SDL_CPUCount = info.dwNumberOfProcessors;
   319         }
   320 #endif
   321         /* There has to be at least 1, right? :) */
   322         if (SDL_CPUCount <= 0) {
   323             SDL_CPUCount = 1;
   324         }
   325     }
   326     return SDL_CPUCount;
   327 }
   328 
   329 /* Oh, such a sweet sweet trick, just not very useful. :) */
   330 const char *
   331 SDL_GetCPUType()
   332 {
   333     static char SDL_CPUType[48];
   334 
   335     if (!SDL_CPUType[0]) {
   336         int i = 0;
   337         int a, b, c, d;
   338 
   339         if (CPU_haveCPUID()) {
   340             cpuid(0x80000000, a, b, c, d);
   341             if (a >= 0x80000004) {
   342                 cpuid(0x80000002, a, b, c, d);
   343                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   344                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   345                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   346                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   347                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   348                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   349                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   350                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   351                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   352                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   353                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   354                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   355                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   356                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   357                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   358                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   359                 cpuid(0x80000003, a, b, c, d);
   360                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   361                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   362                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   363                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   364                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   365                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   366                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   367                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   368                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   369                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   370                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   371                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   372                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   373                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   374                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   375                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   376                 cpuid(0x80000004, a, b, c, d);
   377                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   378                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   379                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   380                 SDL_CPUType[i++] = (char)(a & 0xff); a >>= 8;
   381                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   382                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   383                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   384                 SDL_CPUType[i++] = (char)(b & 0xff); b >>= 8;
   385                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   386                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   387                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   388                 SDL_CPUType[i++] = (char)(c & 0xff); c >>= 8;
   389                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   390                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   391                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   392                 SDL_CPUType[i++] = (char)(d & 0xff); d >>= 8;
   393             }
   394         }
   395         if (!SDL_CPUType[0]) {
   396             SDL_strlcpy(SDL_CPUType, "Unknown", sizeof(SDL_CPUType));
   397         }
   398     }
   399     return SDL_CPUType;
   400 }
   401 
   402 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   403 
   404 static Uint32
   405 SDL_GetCPUFeatures(void)
   406 {
   407     if (SDL_CPUFeatures == 0xFFFFFFFF) {
   408         SDL_CPUFeatures = 0;
   409         if (CPU_haveRDTSC()) {
   410             SDL_CPUFeatures |= CPU_HAS_RDTSC;
   411         }
   412         if (CPU_haveMMX()) {
   413             SDL_CPUFeatures |= CPU_HAS_MMX;
   414         }
   415         if (CPU_haveMMXExt()) {
   416             SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   417         }
   418         if (CPU_have3DNow()) {
   419             SDL_CPUFeatures |= CPU_HAS_3DNOW;
   420         }
   421         if (CPU_have3DNowExt()) {
   422             SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   423         }
   424         if (CPU_haveSSE()) {
   425             SDL_CPUFeatures |= CPU_HAS_SSE;
   426         }
   427         if (CPU_haveSSE2()) {
   428             SDL_CPUFeatures |= CPU_HAS_SSE2;
   429         }
   430         if (CPU_haveAltiVec()) {
   431             SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   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_HasMMXExt(void)
   457 {
   458     if (SDL_GetCPUFeatures() & CPU_HAS_MMXEXT) {
   459         return SDL_TRUE;
   460     }
   461     return SDL_FALSE;
   462 }
   463 
   464 SDL_bool
   465 SDL_Has3DNow(void)
   466 {
   467     if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
   468         return SDL_TRUE;
   469     }
   470     return SDL_FALSE;
   471 }
   472 
   473 SDL_bool
   474 SDL_Has3DNowExt(void)
   475 {
   476     if (SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT) {
   477         return SDL_TRUE;
   478     }
   479     return SDL_FALSE;
   480 }
   481 
   482 SDL_bool
   483 SDL_HasSSE(void)
   484 {
   485     if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
   486         return SDL_TRUE;
   487     }
   488     return SDL_FALSE;
   489 }
   490 
   491 SDL_bool
   492 SDL_HasSSE2(void)
   493 {
   494     if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
   495         return SDL_TRUE;
   496     }
   497     return SDL_FALSE;
   498 }
   499 
   500 SDL_bool
   501 SDL_HasAltiVec(void)
   502 {
   503     if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
   504         return SDL_TRUE;
   505     }
   506     return SDL_FALSE;
   507 }
   508 
   509 #ifdef TEST_MAIN
   510 
   511 #include <stdio.h>
   512 
   513 int
   514 main()
   515 {
   516     printf("CPU count: %d\n", SDL_GetCPUCount());
   517     printf("CPU name: %s\n", SDL_GetCPUType());
   518     printf("RDTSC: %d\n", SDL_HasRDTSC());
   519     printf("MMX: %d\n", SDL_HasMMX());
   520     printf("MMXExt: %d\n", SDL_HasMMXExt());
   521     printf("3DNow: %d\n", SDL_Has3DNow());
   522     printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   523     printf("SSE: %d\n", SDL_HasSSE());
   524     printf("SSE2: %d\n", SDL_HasSSE2());
   525     printf("AltiVec: %d\n", SDL_HasAltiVec());
   526     return 0;
   527 }
   528 
   529 #endif /* TEST_MAIN */
   530 
   531 /* vi: set ts=4 sw=4 expandtab: */