src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 26 Sep 2009 10:04:38 +0000
changeset 3314 8b01b0648f01
parent 3248 cde30895105d
child 3321 714a352c8869
permissions -rw-r--r--
Fixed bug #734

Nicholas Phillips 2009-04-26 21:34:05 PDT

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