]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
* Replaced "SYS_DARWIN" with either __APPLE__ (meaning OS X) or __ppc__ and
[vlc] / src / misc / cpu.c
1 /*****************************************************************************
2  * cpu.c: CPU detection code
3  *****************************************************************************
4  * Copyright (C) 1998-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Eugenio Jarosiewicz <ej0@cise.ufl.eduEujenio>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30
31 #ifdef HAVE_SIGNAL_H
32 #   include <signal.h>                            /* SIGHUP, SIGINT, SIGKILL */
33 #   include <setjmp.h>                                    /* longjmp, setjmp */
34 #endif
35
36 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__))
37 #include <sys/sysctl.h>
38 #endif
39
40 #include "vlc_cpu.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 #ifdef HAVE_SIGNAL_H
46 static void SigHandler   ( int );
47 #endif
48
49 /*****************************************************************************
50  * Global variables - they're needed for signal handling
51  *****************************************************************************/
52 #ifdef HAVE_SIGNAL_H
53 static jmp_buf env;
54 static int     i_illegal;
55 #if defined( __i386__ ) || defined( __x86_64__ )
56 static char   *psz_capability;
57 #endif
58 #endif
59
60 /*****************************************************************************
61  * CPUCapabilities: get the CPU capabilities
62  *****************************************************************************
63  * This function is called to list extensions the CPU may have.
64  *****************************************************************************/
65 uint32_t CPUCapabilities( void )
66 {
67     volatile uint32_t i_capabilities = CPU_CAPABILITY_NONE;
68
69 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__))
70     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
71     int i_has_altivec = 0;
72     size_t i_length = sizeof( i_has_altivec );
73     int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
74
75     i_capabilities |= CPU_CAPABILITY_FPU;
76
77     if( i_error == 0 && i_has_altivec != 0 )
78         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
79
80     return i_capabilities;
81
82 #elif defined( __i386__ ) || defined( __x86_64__ )
83     volatile unsigned int  i_eax, i_ebx, i_ecx, i_edx;
84     volatile vlc_bool_t    b_amd;
85
86     /* Needed for x86 CPU capabilities detection */
87 #   if defined( __x86_64__ )
88 #       define cpuid( reg )                    \
89             asm volatile ( "cpuid\n\t"         \
90                            "movl %%ebx,%1\n\t" \
91                          : "=a" ( i_eax ),     \
92                            "=b" ( i_ebx ),     \
93                            "=c" ( i_ecx ),     \
94                            "=d" ( i_edx )      \
95                          : "a"  ( reg )        \
96                          : "cc" );
97 #   else
98 #       define cpuid( reg )                    \
99             asm volatile ( "push %%ebx\n\t"    \
100                            "cpuid\n\t"         \
101                            "movl %%ebx,%1\n\t" \
102                            "pop %%ebx\n\t"     \
103                          : "=a" ( i_eax ),     \
104                            "=r" ( i_ebx ),     \
105                            "=c" ( i_ecx ),     \
106                            "=d" ( i_edx )      \
107                          : "a"  ( reg )        \
108                          : "cc" );
109 #   endif
110
111 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
112      && defined( HAVE_SIGNAL_H )
113     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
114 #   endif
115
116     i_capabilities |= CPU_CAPABILITY_FPU;
117
118 #   if defined( __i386__ )
119     /* check if cpuid instruction is supported */
120     asm volatile ( "push %%ebx\n\t"
121                    "pushf\n\t"
122                    "pop %%eax\n\t"
123                    "movl %%eax, %%ebx\n\t"
124                    "xorl $0x200000, %%eax\n\t"
125                    "push %%eax\n\t"
126                    "popf\n\t"
127                    "pushf\n\t"
128                    "pop %%eax\n\t"
129                    "movl %%ebx,%1\n\t"
130                    "pop %%ebx\n\t"
131                  : "=a" ( i_eax ),
132                    "=r" ( i_ebx )
133                  :
134                  : "cc" );
135
136     if( i_eax == i_ebx )
137     {
138 #       if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
139             && defined( HAVE_SIGNAL_H )
140         signal( SIGILL, pf_sigill );
141 #       endif
142         return i_capabilities;
143     }
144 #   else
145     /* x86_64 supports cpuid instruction, so we dont need to check it */
146 #   endif
147
148     i_capabilities |= CPU_CAPABILITY_486;
149
150     /* the CPU supports the CPUID instruction - get its level */
151     cpuid( 0x00000000 );
152
153     if( !i_eax )
154     {
155 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
156      && defined( HAVE_SIGNAL_H )
157         signal( SIGILL, pf_sigill );
158 #   endif
159         return i_capabilities;
160     }
161
162     /* FIXME: this isn't correct, since some 486s have cpuid */
163     i_capabilities |= CPU_CAPABILITY_586;
164
165     /* borrowed from mpeg2dec */
166     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
167                     && ( i_edx == 0x69746e65 );
168
169     /* test for the MMX flag */
170     cpuid( 0x00000001 );
171
172     if( ! (i_edx & 0x00800000) )
173     {
174 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
175      && defined( HAVE_SIGNAL_H )
176         signal( SIGILL, pf_sigill );
177 #   endif
178         return i_capabilities;
179     }
180
181     i_capabilities |= CPU_CAPABILITY_MMX;
182
183     if( i_edx & 0x02000000 )
184     {
185         i_capabilities |= CPU_CAPABILITY_MMXEXT;
186
187 #   ifdef CAN_COMPILE_SSE
188         /* We test if OS supports the SSE instructions */
189         psz_capability = "SSE";
190         i_illegal = 0;
191
192         if( setjmp( env ) == 0 )
193         {
194             /* Test a SSE instruction */
195             __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : );
196         }
197
198         if( i_illegal == 0 )
199         {
200             i_capabilities |= CPU_CAPABILITY_SSE;
201         }
202 #   endif
203     }
204
205     if( i_edx & 0x04000000 )
206     {
207 #   if defined(CAN_COMPILE_SSE)
208         /* We test if OS supports the SSE instructions */
209         psz_capability = "SSE2";
210         i_illegal = 0;
211
212         if( setjmp( env ) == 0 )
213         {
214             /* Test a SSE2 instruction */
215             __asm__ __volatile__ ( "movupd %%xmm0, %%xmm0\n" : : );
216         }
217
218         if( i_illegal == 0 )
219         {
220             i_capabilities |= CPU_CAPABILITY_SSE2;
221         }
222 #   endif
223     }
224
225     /* test for additional capabilities */
226     cpuid( 0x80000000 );
227
228     if( i_eax < 0x80000001 )
229     {
230 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
231      && defined( HAVE_SIGNAL_H )
232         signal( SIGILL, pf_sigill );
233 #   endif
234         return i_capabilities;
235     }
236
237     /* list these additional capabilities */
238     cpuid( 0x80000001 );
239
240 #   ifdef CAN_COMPILE_3DNOW
241     if( i_edx & 0x80000000 )
242     {
243         psz_capability = "3D Now!";
244         i_illegal = 0;
245
246         if( setjmp( env ) == 0 )
247         {
248             /* Test a 3D Now! instruction */
249             __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : );
250         }
251
252         if( i_illegal == 0 )
253         {
254             i_capabilities |= CPU_CAPABILITY_3DNOW;
255         }
256     }
257 #   endif
258
259     if( b_amd && ( i_edx & 0x00400000 ) )
260     {
261         i_capabilities |= CPU_CAPABILITY_MMXEXT;
262     }
263
264 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
265      && defined( HAVE_SIGNAL_H )
266     signal( SIGILL, pf_sigill );
267 #   endif
268     return i_capabilities;
269
270 #elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
271
272 #   ifdef CAN_COMPILE_ALTIVEC && defined( HAVE_SIGNAL_H )
273     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
274
275     i_capabilities |= CPU_CAPABILITY_FPU;
276
277     i_illegal = 0;
278
279     if( setjmp( env ) == 0 )
280     {
281         asm volatile ("mtspr 256, %0\n\t"
282                       "vand %%v0, %%v0, %%v0"
283                       :
284                       : "r" (-1));
285     }
286
287     if( i_illegal == 0 )
288     {
289         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
290     }
291
292     signal( SIGILL, pf_sigill );
293 #   endif
294
295     return i_capabilities;
296
297 #elif defined( __sparc__ )
298
299     i_capabilities |= CPU_CAPABILITY_FPU;
300     return i_capabilities;
301
302 #elif defined( _MSC_VER ) && !defined( UNDER_CE )
303     i_capabilities |= CPU_CAPABILITY_FPU;
304     return i_capabilities;
305
306 #else
307     /* default behaviour */
308     return i_capabilities;
309
310 #endif
311 }
312
313 /*****************************************************************************
314  * SigHandler: system signal handler
315  *****************************************************************************
316  * This function is called when an illegal instruction signal is received by
317  * the program. We use this function to test OS and CPU capabilities
318  *****************************************************************************/
319 #if defined( HAVE_SIGNAL_H )
320 static void SigHandler( int i_signal )
321 {
322     /* Acknowledge the signal received */
323     i_illegal = 1;
324
325 #ifdef HAVE_SIGRELSE
326     sigrelse( i_signal );
327 #endif
328
329 #if defined( __i386__ )
330     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
331                      "operating system.\n", psz_capability );
332     fprintf( stderr, "         some optimizations will be disabled unless "
333                      "you upgrade your OS\n" );
334 #   if defined( SYS_LINUX )
335     fprintf( stderr, "         (for instance Linux kernel 2.4.x or later)\n" );
336 #   endif
337 #endif
338
339     longjmp( env, 1 );
340 }
341 #endif
342