src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 11 Apr 2004 19:49:34 +0000
changeset 881 9eb85a211abd
parent 804 b2fda076b02e
child 887 b4b64bb88f2f
permissions -rw-r--r--
Date: Tue, 30 Mar 2004 18:18:13 -0600
From: Tyler Montbriand
Subject: [SDL] Detecting Opteron CPU features

I can now get SDL_cpuinfo.c to detect the AMD Opteron's RDTSC, MMX, MMXEXT,
3DNOW, 3DNOWEXT, SSE, and SSE2 instruction set extensions under Linux. It
took one #ifdef'ed block of new asm code to account for the 64-bit flags
register, but the other two blocks worked fine without modification, just
needed to modify the #ifdef's a bit.
     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 #ifdef USE_SETJMP
    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)
   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 #endif
   120 	return has_CPUID;
   121 }
   122 
   123 static __inline__ int CPU_getCPUIDFeatures()
   124 {
   125 	int features = 0;
   126 #if defined(__GNUC__) && ( defined(i386) || defined(__x86_64__) )
   127 	__asm__ (
   128 "        movl    %%ebx,%%edi\n"
   129 "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n"
   130 "        cpuid                       # Get and save vendor ID          \n"
   131 "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n"
   132 "        jl      1f                  # We dont have the CPUID instruction\n"
   133 "        xorl    %%eax,%%eax                                           \n"
   134 "        incl    %%eax                                                 \n"
   135 "        cpuid                       # Get family/model/stepping/features\n"
   136 "        movl    %%edx,%0                                              \n"
   137 "1:                                                                    \n"
   138 "        movl    %%edi,%%ebx\n"
   139 	: "=m" (features)
   140 	:
   141 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   142 	);
   143 #elif defined(_MSC_VER)
   144 	__asm {
   145         xor     eax, eax            ; Set up for CPUID instruction
   146         cpuid                       ; Get and save vendor ID
   147         cmp     eax, 1              ; Make sure 1 is valid input for CPUID
   148         jl      done                ; We dont have the CPUID instruction
   149         xor     eax, eax
   150         inc     eax
   151         cpuid                       ; Get family/model/stepping/features
   152         mov     features, edx
   153 done:
   154 	}
   155 #endif
   156 	return features;
   157 }
   158 
   159 static __inline__ int CPU_getCPUIDFeaturesExt()
   160 {
   161 	int features = 0;
   162 #if defined(__GNUC__) && (defined(i386) || defined (__x86_64__) )
   163 	__asm__ (
   164 "        movl    %%ebx,%%edi\n"
   165 "        movl    $0x80000000,%%eax   # Query for extended functions    \n"
   166 "        cpuid                       # Get extended function limit     \n"
   167 "        cmpl    $0x80000001,%%eax                                     \n"
   168 "        jl      1f                  # Nope, we dont have function 800000001h\n"
   169 "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n"
   170 "        cpuid                       # and get the information         \n"
   171 "        movl    %%edx,%0                                              \n"
   172 "1:                                                                    \n"
   173 "        movl    %%edi,%%ebx\n"
   174 	: "=m" (features)
   175 	:
   176 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   177 	);
   178 #elif defined(_MSC_VER)
   179 	__asm {
   180         mov     eax,80000000h       ; Query for extended functions
   181         cpuid                       ; Get extended function limit
   182         cmp     eax,80000001h
   183         jl      done                ; Nope, we dont have function 800000001h
   184         mov     eax,80000001h       ; Setup extended function 800000001h
   185         cpuid                       ; and get the information
   186         mov     features,edx
   187 done:
   188 	}
   189 #endif
   190 	return features;
   191 }
   192 
   193 static __inline__ int CPU_haveRDTSC()
   194 {
   195 	if ( CPU_haveCPUID() ) {
   196 		return (CPU_getCPUIDFeatures() & 0x00000010);
   197 	}
   198 	return 0;
   199 }
   200 
   201 static __inline__ int CPU_haveMMX()
   202 {
   203 	if ( CPU_haveCPUID() ) {
   204 		return (CPU_getCPUIDFeatures() & 0x00800000);
   205 	}
   206 	return 0;
   207 }
   208 
   209 static __inline__ int CPU_haveMMXExt()
   210 {
   211 	if ( CPU_haveCPUID() ) {
   212 		return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   213 	}
   214 	return 0;
   215 }
   216 
   217 static __inline__ int CPU_have3DNow()
   218 {
   219 	if ( CPU_haveCPUID() ) {
   220 		return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   221 	}
   222 	return 0;
   223 }
   224 
   225 static __inline__ int CPU_have3DNowExt()
   226 {
   227 	if ( CPU_haveCPUID() ) {
   228 		return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   229 	}
   230 	return 0;
   231 }
   232 
   233 static __inline__ int CPU_haveSSE()
   234 {
   235 	if ( CPU_haveCPUID() ) {
   236 		return (CPU_getCPUIDFeatures() & 0x02000000);
   237 	}
   238 	return 0;
   239 }
   240 
   241 static __inline__ int CPU_haveSSE2()
   242 {
   243 	if ( CPU_haveCPUID() ) {
   244 		return (CPU_getCPUIDFeatures() & 0x04000000);
   245 	}
   246 	return 0;
   247 }
   248 
   249 static __inline__ int CPU_haveAltiVec()
   250 {
   251 	volatile int altivec = 0;
   252 #ifdef MACOSX
   253 	int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 
   254 	int hasVectorUnit = 0; 
   255 	size_t length = sizeof(hasVectorUnit); 
   256 	int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 
   257 	if( 0 == error )
   258 		altivec = (hasVectorUnit != 0); 
   259 #elif defined(USE_SETJMP) && defined(GCC_ALTIVEC)
   260 	void (*handler)(int sig);
   261 	handler = signal(SIGILL, illegal_instruction);
   262 	if ( setjmp(jmpbuf) == 0 ) {
   263 		asm volatile ("mtspr 256, %0\n\t"
   264 			      "vand %%v0, %%v0, %%v0"
   265 			      :
   266 			      : "r" (-1));
   267 		altivec = 1;
   268 	}
   269 	signal(SIGILL, handler);
   270 #endif
   271 	return altivec; 
   272 }
   273 
   274 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   275 
   276 static Uint32 SDL_GetCPUFeatures()
   277 {
   278 	if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
   279 		SDL_CPUFeatures = 0;
   280 		if ( CPU_haveRDTSC() ) {
   281 			SDL_CPUFeatures |= CPU_HAS_RDTSC;
   282 		}
   283 		if ( CPU_haveMMX() ) {
   284 			SDL_CPUFeatures |= CPU_HAS_MMX;
   285 		}
   286 		if ( CPU_haveMMXExt() ) {
   287 			SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   288 		}
   289 		if ( CPU_have3DNow() ) {
   290 			SDL_CPUFeatures |= CPU_HAS_3DNOW;
   291 		}
   292 		if ( CPU_have3DNowExt() ) {
   293 			SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   294 		}
   295 		if ( CPU_haveSSE() ) {
   296 			SDL_CPUFeatures |= CPU_HAS_SSE;
   297 		}
   298 		if ( CPU_haveSSE2() ) {
   299 			SDL_CPUFeatures |= CPU_HAS_SSE2;
   300 		}
   301 		if ( CPU_haveAltiVec() ) {
   302 			SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   303 		}
   304 	}
   305 	return SDL_CPUFeatures;
   306 }
   307 
   308 SDL_bool SDL_HasRDTSC()
   309 {
   310 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
   311 		return SDL_TRUE;
   312 	}
   313 	return SDL_FALSE;
   314 }
   315 
   316 SDL_bool SDL_HasMMX()
   317 {
   318 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
   319 		return SDL_TRUE;
   320 	}
   321 	return SDL_FALSE;
   322 }
   323 
   324 SDL_bool SDL_HasMMXExt()
   325 {
   326 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
   327 		return SDL_TRUE;
   328 	}
   329 	return SDL_FALSE;
   330 }
   331 
   332 SDL_bool SDL_Has3DNow()
   333 {
   334 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
   335 		return SDL_TRUE;
   336 	}
   337 	return SDL_FALSE;
   338 }
   339 
   340 SDL_bool SDL_Has3DNowExt()
   341 {
   342 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
   343 		return SDL_TRUE;
   344 	}
   345 	return SDL_FALSE;
   346 }
   347 
   348 SDL_bool SDL_HasSSE()
   349 {
   350 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
   351 		return SDL_TRUE;
   352 	}
   353 	return SDL_FALSE;
   354 }
   355 
   356 SDL_bool SDL_HasSSE2()
   357 {
   358 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
   359 		return SDL_TRUE;
   360 	}
   361 	return SDL_FALSE;
   362 }
   363 
   364 SDL_bool SDL_HasAltiVec()
   365 {
   366 	if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
   367 		return SDL_TRUE;
   368 	}
   369 	return SDL_FALSE;
   370 }
   371 
   372 #ifdef TEST_MAIN
   373 
   374 #include <stdio.h>
   375 
   376 int main()
   377 {
   378 	printf("RDTSC: %d\n", SDL_HasRDTSC());
   379 	printf("MMX: %d\n", SDL_HasMMX());
   380 	printf("MMXExt: %d\n", SDL_HasMMXExt());
   381 	printf("3DNow: %d\n", SDL_Has3DNow());
   382 	printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   383 	printf("SSE: %d\n", SDL_HasSSE());
   384 	printf("SSE2: %d\n", SDL_HasSSE2());
   385 	printf("AltiVec: %d\n", SDL_HasAltiVec());
   386 	return 0;
   387 }
   388 
   389 #endif /* TEST_MAIN */