]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
update module LIST file.
[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 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc/vlc.h>
34
35 #ifdef HAVE_SIGNAL_H
36 #   include <signal.h>                            /* SIGHUP, SIGINT, SIGKILL */
37 #   include <setjmp.h>                                    /* longjmp, setjmp */
38 #endif
39
40 #include "libvlc.h"
41
42 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__))
43 #include <sys/sysctl.h>
44 #endif
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 #ifdef HAVE_SIGNAL_H
50 static void SigHandler   ( int );
51 #endif
52
53 /*****************************************************************************
54  * Global variables - they're needed for signal handling
55  *****************************************************************************/
56 #ifdef HAVE_SIGNAL_H
57 static jmp_buf env;
58 static int     i_illegal;
59 #if defined( __i386__ ) || defined( __x86_64__ )
60 static const char *psz_capability;
61 #endif
62 #endif
63
64 /*****************************************************************************
65  * CPUCapabilities: get the CPU capabilities
66  *****************************************************************************
67  * This function is called to list extensions the CPU may have.
68  *****************************************************************************/
69 uint32_t CPUCapabilities( void )
70 {
71     volatile uint32_t i_capabilities = CPU_CAPABILITY_NONE;
72
73 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__))
74     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
75     int i_has_altivec = 0;
76     size_t i_length = sizeof( i_has_altivec );
77     int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
78
79     i_capabilities |= CPU_CAPABILITY_FPU;
80
81     if( i_error == 0 && i_has_altivec != 0 )
82         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
83
84     return i_capabilities;
85
86 #elif defined( __i386__ ) || defined( __x86_64__ )
87     volatile unsigned int  i_eax, i_ebx, i_ecx, i_edx;
88     volatile vlc_bool_t    b_amd;
89
90     /* Needed for x86 CPU capabilities detection */
91 #   if defined( __x86_64__ )
92 #       define cpuid( reg )                    \
93             asm volatile ( "cpuid\n\t"         \
94                            "movl %%ebx,%1\n\t" \
95                          : "=a" ( i_eax ),     \
96                            "=b" ( i_ebx ),     \
97                            "=c" ( i_ecx ),     \
98                            "=d" ( i_edx )      \
99                          : "a"  ( reg )        \
100                          : "cc" );
101 #   else
102 #       define cpuid( reg )                    \
103             asm volatile ( "push %%ebx\n\t"    \
104                            "cpuid\n\t"         \
105                            "movl %%ebx,%1\n\t" \
106                            "pop %%ebx\n\t"     \
107                          : "=a" ( i_eax ),     \
108                            "=r" ( i_ebx ),     \
109                            "=c" ( i_ecx ),     \
110                            "=d" ( i_edx )      \
111                          : "a"  ( reg )        \
112                          : "cc" );
113 #   endif
114
115 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
116      && defined( HAVE_SIGNAL_H )
117     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
118 #   endif
119
120     i_capabilities |= CPU_CAPABILITY_FPU;
121
122 #   if defined( __i386__ )
123     /* check if cpuid instruction is supported */
124     asm volatile ( "push %%ebx\n\t"
125                    "pushf\n\t"
126                    "pop %%eax\n\t"
127                    "movl %%eax, %%ebx\n\t"
128                    "xorl $0x200000, %%eax\n\t"
129                    "push %%eax\n\t"
130                    "popf\n\t"
131                    "pushf\n\t"
132                    "pop %%eax\n\t"
133                    "movl %%ebx,%1\n\t"
134                    "pop %%ebx\n\t"
135                  : "=a" ( i_eax ),
136                    "=r" ( i_ebx )
137                  :
138                  : "cc" );
139
140     if( i_eax == i_ebx )
141     {
142 #       if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
143             && defined( HAVE_SIGNAL_H )
144         signal( SIGILL, pf_sigill );
145 #       endif
146         return i_capabilities;
147     }
148 #   else
149     /* x86_64 supports cpuid instruction, so we dont need to check it */
150 #   endif
151
152     i_capabilities |= CPU_CAPABILITY_486;
153
154     /* the CPU supports the CPUID instruction - get its level */
155     cpuid( 0x00000000 );
156
157     if( !i_eax )
158     {
159 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
160      && defined( HAVE_SIGNAL_H )
161         signal( SIGILL, pf_sigill );
162 #   endif
163         return i_capabilities;
164     }
165
166     /* FIXME: this isn't correct, since some 486s have cpuid */
167     i_capabilities |= CPU_CAPABILITY_586;
168
169     /* borrowed from mpeg2dec */
170     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
171                     && ( i_edx == 0x69746e65 );
172
173     /* test for the MMX flag */
174     cpuid( 0x00000001 );
175
176     if( ! (i_edx & 0x00800000) )
177     {
178 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
179      && defined( HAVE_SIGNAL_H )
180         signal( SIGILL, pf_sigill );
181 #   endif
182         return i_capabilities;
183     }
184
185     i_capabilities |= CPU_CAPABILITY_MMX;
186
187     if( i_edx & 0x02000000 )
188     {
189         i_capabilities |= CPU_CAPABILITY_MMXEXT;
190
191 #   ifdef CAN_COMPILE_SSE
192         /* We test if OS supports the SSE instructions */
193         psz_capability = "SSE";
194         i_illegal = 0;
195
196         if( setjmp( env ) == 0 )
197         {
198             /* Test a SSE instruction */
199             __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : );
200         }
201
202         if( i_illegal == 0 )
203         {
204             i_capabilities |= CPU_CAPABILITY_SSE;
205         }
206 #   endif
207     }
208
209     if( i_edx & 0x04000000 )
210     {
211 #   if defined(CAN_COMPILE_SSE)
212         /* We test if OS supports the SSE instructions */
213         psz_capability = "SSE2";
214         i_illegal = 0;
215
216         if( setjmp( env ) == 0 )
217         {
218             /* Test a SSE2 instruction */
219             __asm__ __volatile__ ( "movupd %%xmm0, %%xmm0\n" : : );
220         }
221
222         if( i_illegal == 0 )
223         {
224             i_capabilities |= CPU_CAPABILITY_SSE2;
225         }
226 #   endif
227     }
228
229     /* test for additional capabilities */
230     cpuid( 0x80000000 );
231
232     if( i_eax < 0x80000001 )
233     {
234 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
235      && defined( HAVE_SIGNAL_H )
236         signal( SIGILL, pf_sigill );
237 #   endif
238         return i_capabilities;
239     }
240
241     /* list these additional capabilities */
242     cpuid( 0x80000001 );
243
244 #   ifdef CAN_COMPILE_3DNOW
245     if( i_edx & 0x80000000 )
246     {
247         psz_capability = "3D Now!";
248         i_illegal = 0;
249
250         if( setjmp( env ) == 0 )
251         {
252             /* Test a 3D Now! instruction */
253             __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : );
254         }
255
256         if( i_illegal == 0 )
257         {
258             i_capabilities |= CPU_CAPABILITY_3DNOW;
259         }
260     }
261 #   endif
262
263     if( b_amd && ( i_edx & 0x00400000 ) )
264     {
265         i_capabilities |= CPU_CAPABILITY_MMXEXT;
266     }
267
268 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
269      && defined( HAVE_SIGNAL_H )
270     signal( SIGILL, pf_sigill );
271 #   endif
272     return i_capabilities;
273
274 #elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
275
276 #   ifdef CAN_COMPILE_ALTIVEC && defined( HAVE_SIGNAL_H )
277     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
278
279     i_capabilities |= CPU_CAPABILITY_FPU;
280
281     i_illegal = 0;
282
283     if( setjmp( env ) == 0 )
284     {
285         asm volatile ("mtspr 256, %0\n\t"
286                       "vand %%v0, %%v0, %%v0"
287                       :
288                       : "r" (-1));
289     }
290
291     if( i_illegal == 0 )
292     {
293         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
294     }
295
296     signal( SIGILL, pf_sigill );
297 #   endif
298
299     return i_capabilities;
300
301 #elif defined( __sparc__ )
302
303     i_capabilities |= CPU_CAPABILITY_FPU;
304     return i_capabilities;
305
306 #elif defined( _MSC_VER ) && !defined( UNDER_CE )
307     i_capabilities |= CPU_CAPABILITY_FPU;
308     return i_capabilities;
309
310 #else
311     /* default behaviour */
312     return i_capabilities;
313
314 #endif
315 }
316
317 /*****************************************************************************
318  * SigHandler: system signal handler
319  *****************************************************************************
320  * This function is called when an illegal instruction signal is received by
321  * the program. We use this function to test OS and CPU capabilities
322  *****************************************************************************/
323 #if defined( HAVE_SIGNAL_H )
324 static void SigHandler( int i_signal )
325 {
326     /* Acknowledge the signal received */
327     i_illegal = 1;
328
329 #ifdef HAVE_SIGRELSE
330     sigrelse( i_signal );
331 #endif
332
333 #if defined( __i386__ )
334     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
335                      "operating system.\n", psz_capability );
336     fprintf( stderr, "         some optimizations will be disabled unless "
337                      "you upgrade your OS\n" );
338 #   if defined( SYS_LINUX )
339     fprintf( stderr, "         (for instance Linux kernel 2.4.x or later)\n" );
340 #   endif
341 #endif
342
343     longjmp( env, 1 );
344 }
345 #endif
346
347
348 uint32_t cpu_flags = 0;
349
350
351 /*****************************************************************************
352  * vlc_CPU: get pre-computed CPU capability flags
353  ****************************************************************************/
354 unsigned vlc_CPU (void)
355 {
356     return cpu_flags;
357 }
358