]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
* INSTALL.win32: added a small note about running vlc under the msvc debugger.
[vlc] / src / misc / cpu.c
1 /*****************************************************************************
2  * cpu.c: CPU detection code
3  *****************************************************************************
4  * Copyright (C) 1998-2002 VideoLAN
5  * $Id: cpu.c,v 1.11 2003/05/22 12:00:57 gbazin Exp $
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., 59 Temple Place - Suite 330, Boston, MA  02111, 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 #ifdef SYS_DARWIN
37 #   include <mach/mach.h>                               /* AltiVec detection */
38 #   include <mach/mach_error.h>       /* some day the header files||compiler *
39                                                        will define it for us */
40 #   include <mach/bootstrap.h>
41 #endif
42
43 #include "vlc_cpu.h"
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 #ifdef HAVE_SIGNAL_H
49 static void SigHandler   ( int );
50 #endif
51
52 /*****************************************************************************
53  * Global variables - they're needed for signal handling
54  *****************************************************************************/
55 #ifdef HAVE_SIGNAL_H
56 static jmp_buf env;
57 static int     i_illegal;
58 #if defined( __i386__ )
59 static char   *psz_capability;
60 #endif
61 #endif
62
63 /*****************************************************************************
64  * CPUCapabilities: get the CPU capabilities
65  *****************************************************************************
66  * This function is called to list extensions the CPU may have.
67  *****************************************************************************/
68 uint32_t CPUCapabilities( void )
69 {
70     volatile uint32_t i_capabilities = CPU_CAPABILITY_NONE;
71
72 #if defined( SYS_DARWIN )
73     struct host_basic_info hi;
74     kern_return_t          ret;
75     host_name_port_t       host;
76
77     int i_size;
78     char *psz_name, *psz_subname;
79
80     i_capabilities |= CPU_CAPABILITY_FPU;
81
82     /* Should 'never' fail? */
83     host = mach_host_self();
84
85     i_size = sizeof( hi ) / sizeof( int );
86     ret = host_info( host, HOST_BASIC_INFO, ( host_info_t )&hi, &i_size );
87
88     if( ret != KERN_SUCCESS )
89     {
90         fprintf( stderr, "error: couldn't get CPU information\n" );
91         return i_capabilities;
92     }
93
94     slot_name( hi.cpu_type, hi.cpu_subtype, &psz_name, &psz_subname );
95     /* FIXME: need better way to detect newer proccessors.
96      * could do strncmp(a,b,5), but that's real ugly */
97     if( !strcmp(psz_name, "ppc7400") || !strcmp(psz_name, "ppc7450") )
98     {
99         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
100     }
101
102     return i_capabilities;
103
104 #elif defined( __i386__ )
105     volatile unsigned int  i_eax, i_ebx, i_ecx, i_edx;
106     volatile vlc_bool_t    b_amd;
107
108     /* Needed for x86 CPU capabilities detection */
109 #   define cpuid( reg )                    \
110         asm volatile ( "pushl %%ebx\n\t"   \
111                        "cpuid\n\t"         \
112                        "movl %%ebx,%1\n\t" \
113                        "popl %%ebx\n\t"    \
114                      : "=a" ( i_eax ),     \
115                        "=r" ( i_ebx ),     \
116                        "=c" ( i_ecx ),     \
117                        "=d" ( i_edx )      \
118                      : "a"  ( reg )        \
119                      : "cc" );
120
121 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
122      && defined( HAVE_SIGNAL_H )
123     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
124 #   endif
125
126     i_capabilities |= CPU_CAPABILITY_FPU;
127
128     /* test for a 486 CPU */
129     asm volatile ( "pushl %%ebx\n\t"
130                    "pushfl\n\t"
131                    "popl %%eax\n\t"
132                    "movl %%eax, %%ebx\n\t"
133                    "xorl $0x200000, %%eax\n\t"
134                    "pushl %%eax\n\t"
135                    "popfl\n\t"
136                    "pushfl\n\t"
137                    "popl %%eax\n\t"
138                    "movl %%ebx,%1\n\t"
139                    "popl %%ebx\n\t"
140                  : "=a" ( i_eax ),
141                    "=r" ( i_ebx )
142                  :
143                  : "cc" );
144
145     if( i_eax == i_ebx )
146     {
147 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
148      && defined( HAVE_SIGNAL_H )
149         signal( SIGILL, pf_sigill );
150 #   endif
151         return i_capabilities;
152     }
153
154     i_capabilities |= CPU_CAPABILITY_486;
155
156     /* the CPU supports the CPUID instruction - get its level */
157     cpuid( 0x00000000 );
158
159     if( !i_eax )
160     {
161 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
162      && defined( HAVE_SIGNAL_H )
163         signal( SIGILL, pf_sigill );
164 #   endif
165         return i_capabilities;
166     }
167
168     /* FIXME: this isn't correct, since some 486s have cpuid */
169     i_capabilities |= CPU_CAPABILITY_586;
170
171     /* borrowed from mpeg2dec */
172     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
173                     && ( i_edx == 0x69746e65 );
174
175     /* test for the MMX flag */
176     cpuid( 0x00000001 );
177
178     if( ! (i_edx & 0x00800000) )
179     {
180 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
181      && defined( HAVE_SIGNAL_H )
182         signal( SIGILL, pf_sigill );
183 #   endif
184         return i_capabilities;
185     }
186
187     i_capabilities |= CPU_CAPABILITY_MMX;
188
189     if( i_edx & 0x02000000 )
190     {
191         i_capabilities |= CPU_CAPABILITY_MMXEXT;
192
193 #   ifdef CAN_COMPILE_SSE
194         /* We test if OS supports the SSE instructions */
195         psz_capability = "SSE";
196         i_illegal = 0;
197
198         if( setjmp( env ) == 0 )
199         {
200             /* Test a SSE instruction */
201             __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : );
202         }
203
204         if( i_illegal == 0 )
205         {
206             i_capabilities |= CPU_CAPABILITY_SSE;
207         }
208 #   endif
209     }
210
211     /* test for additional capabilities */
212     cpuid( 0x80000000 );
213
214     if( i_eax < 0x80000001 )
215     {
216 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
217      && defined( HAVE_SIGNAL_H )
218         signal( SIGILL, pf_sigill );
219 #   endif
220         return i_capabilities;
221     }
222
223     /* list these additional capabilities */
224     cpuid( 0x80000001 );
225
226 #   ifdef CAN_COMPILE_3DNOW
227     if( i_edx & 0x80000000 )
228     {
229         psz_capability = "3D Now!";
230         i_illegal = 0;
231
232         if( setjmp( env ) == 0 )
233         {
234             /* Test a 3D Now! instruction */
235             __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : );
236         }
237
238         if( i_illegal == 0 )
239         {
240             i_capabilities |= CPU_CAPABILITY_3DNOW;
241         }
242     }
243 #   endif
244
245     if( b_amd && ( i_edx & 0x00400000 ) )
246     {
247         i_capabilities |= CPU_CAPABILITY_MMXEXT;
248     }
249
250 #   if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \
251      && defined( HAVE_SIGNAL_H )
252     signal( SIGILL, pf_sigill );
253 #   endif
254     return i_capabilities;
255
256 #elif defined( __powerpc__ )
257
258 #   ifdef CAN_COMPILE_ALTIVEC && defined( HAVE_SIGNAL_H )
259     void (*pf_sigill) (int) = signal( SIGILL, SigHandler );
260
261     i_capabilities |= CPU_CAPABILITY_FPU;
262
263     i_illegal = 0;
264
265     if( setjmp( env ) == 0 )
266     {
267         asm volatile ("mtspr 256, %0\n\t"
268                       "vand %%v0, %%v0, %%v0"
269                       :
270                       : "r" (-1));
271     }
272
273     if( i_illegal == 0 )
274     {
275         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
276     }
277
278     signal( SIGILL, pf_sigill );
279 #   endif
280
281     return i_capabilities;
282
283 #elif defined( __sparc__ )
284
285     i_capabilities |= CPU_CAPABILITY_FPU;
286     return i_capabilities;
287
288 #elif defined( _MSC_VER )
289     i_capabilities |= CPU_CAPABILITY_FPU;
290     return i_capabilities;
291
292 #else
293     /* default behaviour */
294     return i_capabilities;
295
296 #endif
297 }
298
299 /*****************************************************************************
300  * SigHandler: system signal handler
301  *****************************************************************************
302  * This function is called when an illegal instruction signal is received by
303  * the program. We use this function to test OS and CPU capabilities
304  *****************************************************************************/
305 #if defined( HAVE_SIGNAL_H )
306 static void SigHandler( int i_signal )
307 {
308     /* Acknowledge the signal received */
309     i_illegal = 1;
310
311 #ifdef HAVE_SIGRELSE
312     sigrelse( i_signal );
313 #endif
314
315 #if defined( __i386__ )
316     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
317                      "operating system.\n", psz_capability );
318     fprintf( stderr, "         some optimizations will be disabled unless "
319                      "you upgrade your OS\n" );
320 #   if defined( SYS_LINUX )
321     fprintf( stderr, "         (for instance Linux kernel 2.4.x or later)\n" );
322 #   endif
323 #endif
324
325     longjmp( env, 1 );
326 }
327 #endif
328