]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
picture: merge p_data_orig into gc.p_sys
[vlc] / src / misc / cpu.c
1 /*****************************************************************************
2  * cpu.c: CPU detection code
3  *****************************************************************************
4  * Copyright (C) 1998-2004 VLC authors and VideoLAN
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 it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * 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_common.h>
34 #include <vlc_cpu.h>
35 #include "libvlc.h"
36
37 #include <assert.h>
38
39 #ifndef __linux__
40 #include <sys/types.h>
41 #ifndef WIN32
42 #include <unistd.h>
43 #include <sys/wait.h>
44 #include <signal.h>
45 #else
46 #include <errno.h>
47 #endif
48
49 #ifdef __APPLE__
50 #include <sys/sysctl.h>
51 #endif
52 #ifdef __ANDROID__
53 #include <cpu-features.h>
54 #endif
55
56 #if defined(__OpenBSD__) && defined(__powerpc__)
57 #include <sys/param.h>
58 #include <sys/sysctl.h>
59 #include <machine/cpu.h>
60 #endif
61
62 static uint32_t cpu_flags;
63
64 #if defined (__i386__) || defined (__x86_64__) || defined (__powerpc__) \
65  || defined (__ppc__) || defined (__ppc64__) || defined (__powerpc64__)
66 # if !defined (WIN32) && !defined (__OS2__)
67 static bool vlc_CPU_check (const char *name, void (*func) (void))
68 {
69     pid_t pid = fork();
70
71     switch (pid)
72     {
73         case 0:
74             signal (SIGILL, SIG_DFL);
75             func ();
76             //__asm__ __volatile__ ( code : : input );
77             _exit (0);
78         case -1:
79             return false;
80     }
81     //i_capabilities |= (flag);
82
83     int status;
84     while( waitpid( pid, &status, 0 ) == -1 );
85
86     if( WIFEXITED( status ) && WEXITSTATUS( status ) == 0 )
87         return true;
88
89     fprintf (stderr, "Warning: your CPU has %s instructions, but not your "
90                      "operating system.\n", name);
91     fprintf( stderr, "         some optimizations will be disabled unless "
92                      "you upgrade your OS\n" );
93     return false;
94 }
95
96 #if defined (CAN_COMPILE_SSE) && !defined (__SSE__)
97 VLC_SSE static void SSE_test (void)
98 {
99     asm volatile ("xorps %%xmm0,%%xmm0\n" : : : "xmm0", "xmm1");
100 }
101 #endif
102 #if defined (CAN_COMPILE_3DNOW)
103 VLC_MMX static void ThreeD_Now_test (void)
104 {
105     asm volatile ("pfadd %%mm0,%%mm0\n" "femms\n" : : : "mm0");
106 }
107 #endif
108
109 #if defined (CAN_COMPILE_ALTIVEC)
110 static void Altivec_test (void)
111 {
112     asm volatile ("mtspr 256, %0\n" "vand %%v0, %%v0, %%v0\n" : : "r" (-1));
113 }
114 #endif
115
116 #else /* WIN32 || __OS2__ */
117 # define vlc_CPU_check(name, func) (1)
118 #endif
119 #endif
120
121 /**
122  * Determines the CPU capabilities and stores them in cpu_flags.
123  * The result can be retrieved with vlc_CPU().
124  */
125 void vlc_CPU_init (void)
126 {
127     uint32_t i_capabilities = 0;
128
129 #if defined( __i386__ ) || defined( __x86_64__ )
130      unsigned int i_eax, i_ebx, i_ecx, i_edx;
131      bool b_amd;
132
133     /* Needed for x86 CPU capabilities detection */
134 # if defined (__i386__) && defined (__PIC__)
135 #  define cpuid(reg) \
136      asm volatile ("xchgl %%ebx,%1\n\t" \
137                    "cpuid\n\t" \
138                    "xchgl %%ebx,%1\n\t" \
139                    : "=a" (i_eax), "=r" (i_ebx), "=c" (i_ecx), "=d" (i_edx) \
140                    : "a" (reg) \
141                    : "cc");
142 # else
143 #  define cpuid(reg) \
144      asm volatile ("cpuid\n\t" \
145                    : "=a" (i_eax), "=b" (i_ebx), "=c" (i_ecx), "=d" (i_edx) \
146                    : "a" (reg) \
147                    : "cc");
148 # endif
149      /* Check if the OS really supports the requested instructions */
150 # if defined (__i386__) && !defined (__i486__) && !defined (__i586__) \
151   && !defined (__i686__) && !defined (__pentium4__) \
152   && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__)
153     /* check if cpuid instruction is supported */
154     asm volatile ( "push %%ebx\n\t"
155                    "pushf\n\t"
156                    "pop %%eax\n\t"
157                    "movl %%eax, %%ebx\n\t"
158                    "xorl $0x200000, %%eax\n\t"
159                    "push %%eax\n\t"
160                    "popf\n\t"
161                    "pushf\n\t"
162                    "pop %%eax\n\t"
163                    "movl %%ebx,%1\n\t"
164                    "pop %%ebx\n\t"
165                  : "=a" ( i_eax ),
166                    "=r" ( i_ebx )
167                  :
168                  : "cc" );
169
170     if( i_eax == i_ebx )
171         goto out;
172 # endif
173
174     /* the CPU supports the CPUID instruction - get its level */
175     cpuid( 0x00000000 );
176
177 # if defined (__i386__) && !defined (__i586__) \
178   && !defined (__i686__) && !defined (__pentium4__) \
179   && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__)
180     if( !i_eax )
181         goto out;
182 #endif
183
184     /* borrowed from mpeg2dec */
185     b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 )
186                     && ( i_edx == 0x69746e65 );
187
188     /* test for the MMX flag */
189     cpuid( 0x00000001 );
190 # if !defined (__MMX__)
191     if( ! (i_edx & 0x00800000) )
192         goto out;
193 # endif
194     i_capabilities |= VLC_CPU_MMX;
195
196     if( i_edx & 0x02000000 )
197         i_capabilities |= VLC_CPU_MMXEXT;
198 # if defined (CAN_COMPILE_SSE) && !defined (__SSE__)
199     if (( i_edx & 0x02000000 ) && vlc_CPU_check ("SSE", SSE_test))
200 # endif
201     {
202         /*if( i_edx & 0x02000000 )*/
203             i_capabilities |= VLC_CPU_SSE;
204         if (i_edx & 0x04000000)
205             i_capabilities |= VLC_CPU_SSE2;
206         if (i_ecx & 0x00000001)
207             i_capabilities |= VLC_CPU_SSE3;
208         if (i_ecx & 0x00000200)
209             i_capabilities |= VLC_CPU_SSSE3;
210         if (i_ecx & 0x00080000)
211             i_capabilities |= VLC_CPU_SSE4_1;
212         if (i_ecx & 0x00100000)
213             i_capabilities |= VLC_CPU_SSE4_2;
214     }
215
216     /* test for additional capabilities */
217     cpuid( 0x80000000 );
218
219     if( i_eax < 0x80000001 )
220         goto out;
221
222     /* list these additional capabilities */
223     cpuid( 0x80000001 );
224
225 # if defined (CAN_COMPILE_3DNOW) && !defined (__3dNOW__)
226     if ((i_edx & 0x80000000) && vlc_CPU_check ("3D Now!", ThreeD_Now_test))
227 # endif
228         i_capabilities |= VLC_CPU_3dNOW;
229
230     if( b_amd && ( i_edx & 0x00400000 ) )
231         i_capabilities |= VLC_CPU_MMXEXT;
232 out:
233
234 #elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __powerpc64__ ) \
235     || defined( __ppc64__ )
236
237 #   if defined(__APPLE__) || defined(__OpenBSD__)
238 #   if defined(__OpenBSD__)
239     int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
240 #   else
241     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
242 #   endif
243     int i_has_altivec = 0;
244     size_t i_length = sizeof( i_has_altivec );
245     int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
246
247     if( i_error == 0 && i_has_altivec != 0 )
248         i_capabilities |= VLC_CPU_ALTIVEC;
249
250 #   elif defined( CAN_COMPILE_ALTIVEC )
251     if (vlc_CPU_check ("Altivec", Altivec_test))
252         i_capabilities |= VLC_CPU_ALTIVEC;
253
254 #   endif
255
256 #elif defined ( __arm__)
257 # ifdef __ANDROID__
258     if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON)
259         i_capabilities |= VLC_CPU_ARM_NEON;
260 # endif
261
262 #endif
263
264     cpu_flags = i_capabilities;
265 }
266
267 /**
268  * Retrieves pre-computed CPU capability flags
269  */
270 unsigned vlc_CPU (void)
271 {
272 /* On Windows and OS/2,
273  * initialized from DllMain() and _DLL_InitTerm() respectively, instead */
274 #if !defined(WIN32) && !defined(__OS2__)
275     static pthread_once_t once = PTHREAD_ONCE_INIT;
276     pthread_once (&once, vlc_CPU_init);
277 #endif
278     return cpu_flags;
279 }
280 #endif
281
282 void vlc_CPU_dump (vlc_object_t *obj)
283 {
284     char buf[200], *p = buf;
285
286 #if defined (__i386__) || defined (__x86_64__)
287     if (vlc_CPU_MMX()) p += sprintf (p, "MMX ");
288     if (vlc_CPU_MMXEXT()) p += sprintf (p, "MMXEXT ");
289     if (vlc_CPU_SSE()) p += sprintf (p, "SSE ");
290     if (vlc_CPU_SSE2()) p += sprintf (p, "SSE2 ");
291     if (vlc_CPU_SSE3()) p += sprintf (p, "SSE3 ");
292     if (vlc_CPU_SSSE3()) p += sprintf (p, "SSSE3 ");
293     if (vlc_CPU_SSE4_1()) p += sprintf (p, "SSE4.1 ");
294     if (vlc_CPU_SSE4_2()) p += sprintf (p, "SSE4.2 ");
295     if (vlc_CPU_SSE4A()) p += sprintf (p, "SSE4A ");
296     if (vlc_CPU_AVX()) p += sprintf (p, "AVX ");
297     if (vlc_CPU_AVX2()) p += sprintf (p, "AVX ");
298     if (vlc_CPU_3dNOW()) p += sprintf (p, "3DNow! ");
299     if (vlc_CPU_XOP()) p += sprintf (p, "XOP ");
300     if (vlc_CPU_FMA4()) p += sprintf (p, "FMA4 ");
301
302 #elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
303     if (vlc_CPU_ALTIVEC())  p += sprintf (p, "AltiVec");
304
305 #elif defined (__arm__)
306     if (vlc_CPU_ARM_NEON()) p += sprintf (p, "ARM_NEON ");
307
308 #endif
309
310 #if HAVE_FPU
311     p += sprintf (p, "FPU ");
312 #endif
313
314     if (p > buf)
315         msg_Dbg (obj, "CPU has capabilities %s", buf);
316 }