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