src/cpuinfo/SDL_cpuinfo.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 05 Jan 2006 08:17:35 +0000
changeset 1229 1430f5fe092a
parent 1198 b616ac4f39d1
child 1312 c9b51268668f
permissions -rw-r--r--
Patch from Michael Bonfils to add CPUID support for x86 Solaris.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* CPU feature detection for SDL */
    29 
    30 #ifdef unix /* FIXME: Better setjmp detection? */
    31 #define USE_SETJMP
    32 #include <signal.h>
    33 #include <setjmp.h>
    34 #endif
    35 
    36 #include "SDL.h"
    37 #include "SDL_cpuinfo.h"
    38 
    39 #ifdef MACOSX
    40 #include <sys/sysctl.h> /* For AltiVec check */
    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 defined(USE_SETJMP) && defined(GCC_ALTIVEC)
    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 illegal_instruction(int sig)
    58 {
    59 	longjmp(jmpbuf, 1);
    60 }
    61 #endif // USE_SETJMP
    62 
    63 static __inline__ int CPU_haveCPUID()
    64 {
    65 	int has_CPUID = 0;
    66 #if defined(__GNUC__) && defined(i386)
    67 	__asm__ (
    68 "        pushfl                      # Get original EFLAGS             \n"
    69 "        popl    %%eax                                                 \n"
    70 "        movl    %%eax,%%ecx                                           \n"
    71 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    72 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    73 "        popfl                       # Replace current EFLAGS value    \n"
    74 "        pushfl                      # Get new EFLAGS                  \n"
    75 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    76 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    77 "        jz      1f                  # Processor=80486                 \n"
    78 "        movl    $1,%0               # We have CPUID support           \n"
    79 "1:                                                                    \n"
    80 	: "=m" (has_CPUID)
    81 	:
    82 	: "%eax", "%ecx"
    83 	);
    84 #elif defined(__GNUC__) && defined(__x86_64__)
    85 /* Technically, if this is being compiled under __x86_64__ then it has 
    86 CPUid by definition.  But it's nice to be able to prove it.  :)      */
    87 	__asm__ (
    88 "        pushfq                      # Get original EFLAGS             \n"
    89 "        popq    %%rax                                                 \n"
    90 "        movq    %%rax,%%rcx                                           \n"
    91 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    92 "        pushq   %%rax               # Save new EFLAGS value on stack  \n"
    93 "        popfq                       # Replace current EFLAGS value    \n"
    94 "        pushfq                      # Get new EFLAGS                  \n"
    95 "        popq    %%rax               # Store new EFLAGS in EAX         \n"
    96 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    97 "        jz      1f                  # Processor=80486                 \n"
    98 "        movl    $1,%0               # We have CPUID support           \n"
    99 "1:                                                                    \n"
   100 	: "=m" (has_CPUID)
   101 	:
   102 	: "%rax", "%rcx"
   103 	);
   104 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_X86_))
   105 	__asm {
   106         pushfd                      ; Get original EFLAGS
   107         pop     eax
   108         mov     ecx, eax
   109         xor     eax, 200000h        ; Flip ID bit in EFLAGS
   110         push    eax                 ; Save new EFLAGS value on stack
   111         popfd                       ; Replace current EFLAGS value
   112         pushfd                      ; Get new EFLAGS
   113         pop     eax                 ; Store new EFLAGS in EAX
   114         xor     eax, ecx            ; Can not toggle ID bit,
   115         jz      done                ; Processor=80486
   116         mov     has_CPUID,1         ; We have CPUID support
   117 done:
   118 	}
   119 #elif defined(__sun) && defined(__x86)
   120 	    __asm (
   121 "       pushfl                 \n"
   122 "	    popl    %eax           \n"
   123 "	    movl    %eax,%ecx      \n"
   124 "	    xorl    $0x200000,%eax \n"
   125 "	    pushl   %eax           \n"
   126 "	    popfl                  \n"
   127 "	    pushfl                 \n"
   128 "	    popl    %eax           \n"
   129 "	    xorl    %ecx,%eax      \n"
   130 "	    jz      1f             \n"
   131 "	    movl    $1,-8(%ebp)    \n"
   132 "1:                            \n"
   133 	);
   134 #elif defined(__sun) && defined(__amd64)
   135 	__asm (
   136 "       pushfq                 \n"
   137 "       popq    %rax           \n"
   138 "       movq    %rax,%rcx      \n"
   139 "       xorl    $0x200000,%eax \n"
   140 "       pushq   %rax           \n"
   141 "       popfq                  \n"
   142 "       pushfq                 \n"
   143 "       popq    %rax           \n"
   144 "       xorl    %ecx,%eax      \n"
   145 "       jz      1f             \n"
   146 "       movl    $1,-8(%rbp)    \n"
   147 "1:                            \n"
   148 	);
   149 #endif
   150 	return has_CPUID;
   151 }
   152 
   153 static __inline__ int CPU_getCPUIDFeatures()
   154 {
   155 	int features = 0;
   156 #if defined(__GNUC__) && ( defined(i386) || defined(__x86_64__) )
   157 	__asm__ (
   158 "        movl    %%ebx,%%edi\n"
   159 "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n"
   160 "        cpuid                       # Get and save vendor ID          \n"
   161 "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n"
   162 "        jl      1f                  # We dont have the CPUID instruction\n"
   163 "        xorl    %%eax,%%eax                                           \n"
   164 "        incl    %%eax                                                 \n"
   165 "        cpuid                       # Get family/model/stepping/features\n"
   166 "        movl    %%edx,%0                                              \n"
   167 "1:                                                                    \n"
   168 "        movl    %%edi,%%ebx\n"
   169 	: "=m" (features)
   170 	:
   171 	: "%eax", "%ecx", "%edx", "%edi"
   172 	);
   173 #elif defined(_MSC_VER)  && (defined(_M_IX86) || defined(_X86_))
   174 	__asm {
   175         xor     eax, eax            ; Set up for CPUID instruction
   176         cpuid                       ; Get and save vendor ID
   177         cmp     eax, 1              ; Make sure 1 is valid input for CPUID
   178         jl      done                ; We dont have the CPUID instruction
   179         xor     eax, eax
   180         inc     eax
   181         cpuid                       ; Get family/model/stepping/features
   182         mov     features, edx
   183 done:
   184 	}
   185 #elif defined(__sun) && (defined(__x86) || defined(__amd64))
   186 	    __asm(
   187 "        movl    %ebx,%edi\n"
   188 "        xorl    %eax,%eax         \n"
   189 "        cpuid                     \n"
   190 "        cmpl    $1,%eax           \n"
   191 "        jl      1f                \n"
   192 "        xorl    %eax,%eax         \n"
   193 "        incl    %eax              \n"
   194 "        cpuid                     \n"
   195 #ifdef __i386
   196 "        movl    %edx,-8(%ebp)     \n"
   197 #else
   198 "        movl    %edx,-8(%rbp)     \n"
   199 #endif
   200 "1:                                \n"
   201 "        movl    %edi,%ebx\n" );
   202 #endif
   203 	return features;
   204 }
   205 
   206 static __inline__ int CPU_getCPUIDFeaturesExt()
   207 {
   208 	int features = 0;
   209 #if defined(__GNUC__) && (defined(i386) || defined (__x86_64__) )
   210 	__asm__ (
   211 "        movl    %%ebx,%%edi\n"
   212 "        movl    $0x80000000,%%eax   # Query for extended functions    \n"
   213 "        cpuid                       # Get extended function limit     \n"
   214 "        cmpl    $0x80000001,%%eax                                     \n"
   215 "        jl      1f                  # Nope, we dont have function 800000001h\n"
   216 "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n"
   217 "        cpuid                       # and get the information         \n"
   218 "        movl    %%edx,%0                                              \n"
   219 "1:                                                                    \n"
   220 "        movl    %%edi,%%ebx\n"
   221 	: "=m" (features)
   222 	:
   223 	: "%eax", "%ecx", "%edx", "%edi"
   224 	);
   225 #elif defined(_MSC_VER)  && (defined(_M_IX86) || defined(_X86_))
   226 	__asm {
   227         mov     eax,80000000h       ; Query for extended functions
   228         cpuid                       ; Get extended function limit
   229         cmp     eax,80000001h
   230         jl      done                ; Nope, we dont have function 800000001h
   231         mov     eax,80000001h       ; Setup extended function 800000001h
   232         cpuid                       ; and get the information
   233         mov     features,edx
   234 done:
   235 	}
   236 #elif defined(__sun) && ( defined(__i386) || defined(__amd64) )
   237 	    __asm (
   238 "        movl    %ebx,%edi\n"
   239 "        movl    $0x80000000,%eax \n"
   240 "        cpuid                    \n"
   241 "        cmpl    $0x80000001,%eax \n"
   242 "        jl      1f               \n"
   243 "        movl    $0x80000001,%eax \n"
   244 "        cpuid                    \n"
   245 #ifdef __i386
   246 "        movl    %edx,-8(%ebp)   \n"
   247 #else
   248 "        movl    %edx,-8(%rbp)   \n"
   249 #endif
   250 "1:                               \n"
   251 "        movl    %edi,%ebx\n"
   252 	    );
   253 #endif
   254 	return features;
   255 }
   256 
   257 static __inline__ int CPU_haveRDTSC()
   258 {
   259 	if ( CPU_haveCPUID() ) {
   260 		return (CPU_getCPUIDFeatures() & 0x00000010);
   261 	}
   262 	return 0;
   263 }
   264 
   265 static __inline__ int CPU_haveMMX()
   266 {
   267 	if ( CPU_haveCPUID() ) {
   268 		return (CPU_getCPUIDFeatures() & 0x00800000);
   269 	}
   270 	return 0;
   271 }
   272 
   273 static __inline__ int CPU_haveMMXExt()
   274 {
   275 	if ( CPU_haveCPUID() ) {
   276 		return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   277 	}
   278 	return 0;
   279 }
   280 
   281 static __inline__ int CPU_have3DNow()
   282 {
   283 	if ( CPU_haveCPUID() ) {
   284 		return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   285 	}
   286 	return 0;
   287 }
   288 
   289 static __inline__ int CPU_have3DNowExt()
   290 {
   291 	if ( CPU_haveCPUID() ) {
   292 		return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   293 	}
   294 	return 0;
   295 }
   296 
   297 static __inline__ int CPU_haveSSE()
   298 {
   299 	if ( CPU_haveCPUID() ) {
   300 		return (CPU_getCPUIDFeatures() & 0x02000000);
   301 	}
   302 	return 0;
   303 }
   304 
   305 static __inline__ int CPU_haveSSE2()
   306 {
   307 	if ( CPU_haveCPUID() ) {
   308 		return (CPU_getCPUIDFeatures() & 0x04000000);
   309 	}
   310 	return 0;
   311 }
   312 
   313 static __inline__ int CPU_haveAltiVec()
   314 {
   315 	volatile int altivec = 0;
   316 #ifdef MACOSX
   317 	int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 
   318 	int hasVectorUnit = 0; 
   319 	size_t length = sizeof(hasVectorUnit); 
   320 	int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 
   321 	if( 0 == error )
   322 		altivec = (hasVectorUnit != 0); 
   323 #elif defined(USE_SETJMP) && defined(GCC_ALTIVEC)
   324 	void (*handler)(int sig);
   325 	handler = signal(SIGILL, illegal_instruction);
   326 	if ( setjmp(jmpbuf) == 0 ) {
   327 		asm volatile ("mtspr 256, %0\n\t"
   328 			      "vand %%v0, %%v0, %%v0"
   329 			      :
   330 			      : "r" (-1));
   331 		altivec = 1;
   332 	}
   333 	signal(SIGILL, handler);
   334 #endif
   335 	return altivec; 
   336 }
   337 
   338 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   339 
   340 static Uint32 SDL_GetCPUFeatures()
   341 {
   342 	if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
   343 		SDL_CPUFeatures = 0;
   344 		if ( CPU_haveRDTSC() ) {
   345 			SDL_CPUFeatures |= CPU_HAS_RDTSC;
   346 		}
   347 		if ( CPU_haveMMX() ) {
   348 			SDL_CPUFeatures |= CPU_HAS_MMX;
   349 		}
   350 		if ( CPU_haveMMXExt() ) {
   351 			SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   352 		}
   353 		if ( CPU_have3DNow() ) {
   354 			SDL_CPUFeatures |= CPU_HAS_3DNOW;
   355 		}
   356 		if ( CPU_have3DNowExt() ) {
   357 			SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   358 		}
   359 		if ( CPU_haveSSE() ) {
   360 			SDL_CPUFeatures |= CPU_HAS_SSE;
   361 		}
   362 		if ( CPU_haveSSE2() ) {
   363 			SDL_CPUFeatures |= CPU_HAS_SSE2;
   364 		}
   365 		if ( CPU_haveAltiVec() ) {
   366 			SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   367 		}
   368 	}
   369 	return SDL_CPUFeatures;
   370 }
   371 
   372 SDL_bool SDL_HasRDTSC()
   373 {
   374 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
   375 		return SDL_TRUE;
   376 	}
   377 	return SDL_FALSE;
   378 }
   379 
   380 SDL_bool SDL_HasMMX()
   381 {
   382 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
   383 		return SDL_TRUE;
   384 	}
   385 	return SDL_FALSE;
   386 }
   387 
   388 SDL_bool SDL_HasMMXExt()
   389 {
   390 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
   391 		return SDL_TRUE;
   392 	}
   393 	return SDL_FALSE;
   394 }
   395 
   396 SDL_bool SDL_Has3DNow()
   397 {
   398 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
   399 		return SDL_TRUE;
   400 	}
   401 	return SDL_FALSE;
   402 }
   403 
   404 SDL_bool SDL_Has3DNowExt()
   405 {
   406 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
   407 		return SDL_TRUE;
   408 	}
   409 	return SDL_FALSE;
   410 }
   411 
   412 SDL_bool SDL_HasSSE()
   413 {
   414 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
   415 		return SDL_TRUE;
   416 	}
   417 	return SDL_FALSE;
   418 }
   419 
   420 SDL_bool SDL_HasSSE2()
   421 {
   422 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
   423 		return SDL_TRUE;
   424 	}
   425 	return SDL_FALSE;
   426 }
   427 
   428 SDL_bool SDL_HasAltiVec()
   429 {
   430 	if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
   431 		return SDL_TRUE;
   432 	}
   433 	return SDL_FALSE;
   434 }
   435 
   436 #ifdef TEST_MAIN
   437 
   438 #include <stdio.h>
   439 
   440 int main()
   441 {
   442 	printf("RDTSC: %d\n", SDL_HasRDTSC());
   443 	printf("MMX: %d\n", SDL_HasMMX());
   444 	printf("MMXExt: %d\n", SDL_HasMMXExt());
   445 	printf("3DNow: %d\n", SDL_Has3DNow());
   446 	printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   447 	printf("SSE: %d\n", SDL_HasSSE());
   448 	printf("SSE2: %d\n", SDL_HasSSE2());
   449 	printf("AltiVec: %d\n", SDL_HasAltiVec());
   450 	return 0;
   451 }
   452 
   453 #endif /* TEST_MAIN */