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