]> git.sesse.net Git - vlc/blob - src/misc/cpu.c
programs is a string, which is a string, which is not a list
[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_common.h>
34 #include <vlc_cpu.h>
35
36 #include <sys/types.h>
37 #ifndef WIN32
38 #include <unistd.h>
39 #include <sys/wait.h>
40 #include <signal.h>
41 #else
42 #include <errno.h>
43 #endif
44 #include <assert.h>
45
46 #include "libvlc.h"
47
48 #if defined(__APPLE__)
49 #include <sys/sysctl.h>
50 #endif
51
52 #if defined( __i386__ ) || defined( __x86_64__ ) || defined( __powerpc__ ) \
53  || defined( __ppc__ ) || defined( __ppc64__ ) || defined( __powerpc64__ )
54 # ifndef WIN32
55 static bool check_OS_capability( const char *psz_capability, pid_t pid )
56 {
57     int status;
58
59     if( pid == -1 )
60         return false; /* fail safe :-/ */
61
62     while( waitpid( pid, &status, 0 ) == -1 );
63
64     if( WIFEXITED( status ) && WEXITSTATUS( status ) == 0 )
65         return true;
66
67     fprintf( stderr, "warning: your CPU has %s instructions, but not your "
68                      "operating system.\n", psz_capability );
69     fprintf( stderr, "         some optimizations will be disabled unless "
70                      "you upgrade your OS\n" );
71     return false;
72 }
73
74 #  define check_capability(name, flag, code)   \
75      do {                                      \
76         pid_t pid = fork();                    \
77         if( pid == 0 )                         \
78         {                                      \
79             signal(SIGILL, SIG_DFL);           \
80             __asm__ __volatile__ ( code : : ); \
81             _exit(0);                          \
82         }                                      \
83         if( check_OS_capability((name), pid )) \
84             i_capabilities |= (flag);          \
85      } while(0)
86
87 # else /* WIN32 */
88 #  define check_capability(name, flag, code)   \
89         i_capabilities |= (flag);
90 # endif
91 #endif
92
93 /*****************************************************************************
94  * CPUCapabilities: get the CPU capabilities
95  *****************************************************************************
96  * This function is called to list extensions the CPU may have.
97  *****************************************************************************/
98 uint32_t CPUCapabilities( void )
99 {
100     uint32_t i_capabilities = 0;
101
102 #if defined( __i386__ ) || defined( __x86_64__ )
103      unsigned int i_eax, i_ebx, i_ecx, i_edx;
104      bool b_amd;
105
106     /* Needed for x86 CPU capabilities detection */
107 #   if defined( __x86_64__ )
108 #       define cpuid( reg )                    \
109             asm volatile ( "cpuid\n\t"         \
110                            "movl %%ebx,%1\n\t" \
111                          : "=a" ( i_eax ),     \
112                            "=b" ( i_ebx ),     \
113                            "=c" ( i_ecx ),     \
114                            "=d" ( i_edx )      \
115                          : "a"  ( reg )        \
116                          : "cc" );
117 #   else
118 #       define cpuid( reg )                    \
119             asm volatile ( "push %%ebx\n\t"    \
120                            "cpuid\n\t"         \
121                            "movl %%ebx,%1\n\t" \
122                            "pop %%ebx\n\t"     \
123                          : "=a" ( i_eax ),     \
124                            "=r" ( i_ebx ),     \
125                            "=c" ( i_ecx ),     \
126                            "=d" ( i_edx )      \
127                          : "a"  ( reg )        \
128                          : "cc" );
129 #   endif
130      /* Check if the OS really supports the requested instructions */
131 # if defined (__i386__) && !defined (__i486__) && !defined (__i586__) \
132   && !defined (__i686__) && !defined (__pentium4__) \
133   && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__)
134     /* check if cpuid instruction is supported */
135     asm volatile ( "push %%ebx\n\t"
136                    "pushf\n\t"
137                    "pop %%eax\n\t"
138                    "movl %%eax, %%ebx\n\t"
139                    "xorl $0x200000, %%eax\n\t"
140                    "push %%eax\n\t"
141                    "popf\n\t"
142                    "pushf\n\t"
143                    "pop %%eax\n\t"
144                    "movl %%ebx,%1\n\t"
145                    "pop %%ebx\n\t"
146                  : "=a" ( i_eax ),
147                    "=r" ( i_ebx )
148                  :
149                  : "cc" );
150
151     if( i_eax == i_ebx )
152         goto out;
153 # endif
154
155     /* the CPU supports the CPUID instruction - get its level */
156     cpuid( 0x00000000 );
157
158 # if defined (__i386__) && !defined (__i586__) \
159   && !defined (__i686__) && !defined (__pentium4__) \
160   && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__)
161     if( !i_eax )
162         goto out;
163 #endif
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 # if !defined (__MMX__)
172     if( ! (i_edx & 0x00800000) )
173         goto out;
174 # endif
175     i_capabilities |= CPU_CAPABILITY_MMX;
176
177 # if defined (__SSE__)
178     i_capabilities |= CPU_CAPABILITY_MMXEXT | CPU_CAPABILITY_SSE;
179 # else
180     if( i_edx & 0x02000000 )
181     {
182         i_capabilities |= CPU_CAPABILITY_MMXEXT;
183
184 #   ifdef CAN_COMPILE_SSE
185         check_capability( "SSE", CPU_CAPABILITY_SSE,
186                           "xorps %%xmm0,%%xmm0\n" );
187 #   endif
188     }
189 # endif
190
191 # if defined (__SSE2__)
192     i_capabilities |= CPU_CAPABILITY_SSE2;
193 # elif defined (CAN_COMPILE_SSE2)
194     if( i_edx & 0x04000000 )
195         check_capability( "SSE2", CPU_CAPABILITY_SSE2,
196                           "movupd %%xmm0, %%xmm0\n" );
197 # endif
198
199 # if defined (__SSE3__)
200     i_capabilities |= CPU_CAPABILITY_SSE3;
201 # elif defined (CAN_COMPILE_SSE3)
202     if( i_ecx & 0x00000001 )
203         check_capability( "SSE3", CPU_CAPABILITY_SSE3,
204                           "movsldup %%xmm1, %%xmm0\n" );
205 # endif
206
207 # if defined (__SSSE3__)
208     i_capabilities |= CPU_CAPABILITY_SSSE3;
209 # elif defined (CAN_COMPILE_SSSE3)
210     if( i_ecx & 0x00000200 )
211         check_capability( "SSSE3", CPU_CAPABILITY_SSSE3,
212                           "pabsw %%xmm1, %%xmm0\n" );
213 # endif
214
215 # if defined (__SSE4_1__)
216     i_capabilities |= CPU_CAPABILITY_SSE4_1;
217 # elif defined (CAN_COMPILE_SSE4_1)
218     if( i_ecx & 0x00080000 )
219         check_capability( "SSE4.1", CPU_CAPABILITY_SSE4_1,
220                           "pmaxsb %%xmm1, %%xmm0\n" );
221 # endif
222
223 # if defined (__SSE4_2__)
224     i_capabilities |= CPU_CAPABILITY_SSE4_2;
225 # elif defined (CAN_COMPILE_SSE4_2)
226     if( i_ecx & 0x00100000 )
227         check_capability( "SSE4.2", CPU_CAPABILITY_SSE4_2,
228                           "pcmpgtq %%xmm1, %%xmm0\n" );
229 # endif
230
231     /* test for additional capabilities */
232     cpuid( 0x80000000 );
233
234     if( i_eax < 0x80000001 )
235         goto out;
236
237     /* list these additional capabilities */
238     cpuid( 0x80000001 );
239
240 # if defined (__3dNOW__)
241     i_capabilities |= CPU_CAPABILITY_3DNOW;
242 # elif defined (CAN_COMPILE_3DNOW)
243     if( i_edx & 0x80000000 )
244         check_capability( "3D Now!", CPU_CAPABILITY_3DNOW,
245                           "pfadd %%mm0,%%mm0\n" "femms\n" );
246 # endif
247
248     if( b_amd && ( i_edx & 0x00400000 ) )
249     {
250         i_capabilities |= CPU_CAPABILITY_MMXEXT;
251     }
252 out:
253
254 #elif defined( __arm__ )
255 #   if defined( __ARM_NEON__ )
256     i_capabilities |= CPU_CAPABILITY_NEON;
257 #   endif
258
259 #elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __powerpc64__ ) \
260     || defined( __ppc64__ )
261
262 #   if defined(__APPLE__)
263     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
264     int i_has_altivec = 0;
265     size_t i_length = sizeof( i_has_altivec );
266     int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
267
268     if( i_error == 0 && i_has_altivec != 0 )
269         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
270
271 #   elif defined( CAN_COMPILE_ALTIVEC )
272     pid_t pid = fork();
273     if( pid == 0 )
274     {
275         signal(SIGILL, SIG_DFL);
276         asm volatile ("mtspr 256, %0\n\t"
277                       "vand %%v0, %%v0, %%v0"
278                       :
279                       : "r" (-1));
280         _exit(0);
281     }
282
283     if( check_OS_capability( "Altivec", pid ) )
284         i_capabilities |= CPU_CAPABILITY_ALTIVEC;
285
286 #   endif
287
288 #endif
289     return i_capabilities;
290 }
291
292 uint32_t cpu_flags = 0;
293
294
295 /*****************************************************************************
296  * vlc_CPU: get pre-computed CPU capability flags
297  ****************************************************************************/
298 unsigned vlc_CPU (void)
299 {
300     return cpu_flags;
301 }
302
303 const struct
304 {
305     uint32_t value;
306     char name[12];
307 } cap_dirs[] = {
308 #if defined ( __i386__ ) || defined ( __x86_64__ )
309     { CPU_CAPABILITY_MMX,     "mmx" },
310     { CPU_CAPABILITY_MMXEXT,  "mmxext" },
311     { CPU_CAPABILITY_3DNOW,   "3dnow" },
312     { CPU_CAPABILITY_SSE,     "sse" },
313 #endif
314 #if defined (__ppc__) || defined (__ppc64__) || defined (__powerpc__)
315     { CPU_CAPABILITY_ALTIVEC, "altivec" },
316 #endif
317 #if defined (__arm__)
318     { CPU_CAPABILITY_NEON,    "arm_neon" },
319 #endif
320 };
321
322 /**
323  * Return the number of available logical CPU.
324  */
325 unsigned vlc_GetCPUCount(void)
326 {
327 #if defined(WIN32) && !defined(UNDER_CE)
328     DWORD process_mask;
329     DWORD system_mask;
330     if (!GetProcessAffinityMask(GetCurrentProcess(), &process_mask, &system_mask))
331         return 1;
332
333     unsigned count = 0;
334     while (system_mask) {
335         count++;
336         system_mask >>= 1;
337     }
338     return count;
339 #elif defined(HAVE_SCHED_GETAFFINITY)
340     cpu_set_t cpu;
341     CPU_ZERO(&cpu);
342     if (sched_getaffinity(0, sizeof(cpu), &cpu) < 0)
343         return 1;
344     unsigned count = 0;
345     for (unsigned i = 0; i < CPU_SETSIZE; i++)
346         count += CPU_ISSET(i, &cpu) != 0;
347     return count;
348 #elif defined(__APPLE__)
349     int count;
350     size_t size = sizeof(count) ;
351     if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0))
352         return 1; /* Failure */
353     return count;
354 #else
355 #   warning "vlc_GetCPUCount is not implemented for your platform"
356     return 1;
357 #endif
358 }
359
360 /**
361  * Check if a directory name contains usable plugins w.r.t. the hardware
362  * capabilities. Loading a plugin when the hardware has insufficient
363  * capabilities may lead to illegal instructions (SIGILL) and must be avoided.
364  *
365  * @param name the name of the directory (<b>not</b> the path)
366  *
367  * @return true if the hardware has sufficient capabilities or the directory
368  * does not require any special capability; false if the running hardware has
369  * insufficient capabilities.
370  */
371 bool vlc_CPU_CheckPluginDir (const char *name)
372 {
373     const unsigned flags = vlc_CPU ();
374     for (size_t i = 0; i < sizeof (cap_dirs) / sizeof (cap_dirs[0]); i++)
375     {
376         if (strcmp (name, cap_dirs[i].name))
377             continue;
378         return (flags & cap_dirs[i].value) != 0;
379     }
380     return true;
381 }
382
383 static vlc_memcpy_t pf_vlc_memcpy = memcpy;
384 static vlc_memset_t pf_vlc_memset = memset;
385
386 void vlc_fastmem_register (vlc_memcpy_t cpy, vlc_memset_t set)
387 {
388     if (cpy)
389         pf_vlc_memcpy = cpy;
390     if (set)
391         pf_vlc_memset = set;
392 }
393
394 /**
395  * vlc_memcpy: fast CPU-dependent memcpy
396  */
397 void *vlc_memcpy (void *tgt, const void *src, size_t n)
398 {
399     return pf_vlc_memcpy (tgt, src, n);
400 }
401
402 /**
403  * vlc_memset: fast CPU-dependent memset
404  */
405 void *vlc_memset (void *tgt, int c, size_t n)
406 {
407     return pf_vlc_memset (tgt, c, n);
408 }
409
410 /**
411  * Returned an aligned pointer on newly allocated memory.
412  * \param alignment must be a power of 2 and a multiple of sizeof(void*)
413  * \param size is the size of the usable memory returned.
414  *
415  * It must not be freed directly, *base must.
416  */
417 void *vlc_memalign(void **base, size_t alignment, size_t size)
418 {
419     assert(alignment >= sizeof(void*));
420     for (size_t t = alignment; t > 1; t >>= 1)
421         assert((t&1) == 0);
422 #if defined(HAVE_POSIX_MEMALIGN)
423     if (posix_memalign(base, alignment, size)) {
424         *base = NULL;
425         return NULL;
426     }
427     return *base;
428 #elif defined(HAVE_MEMALIGN)
429     return *base = memalign(alignment, size);
430 #else
431     unsigned char *p = *base = malloc(size + alignment - 1);
432     if (!p)
433         return NULL;
434     return (void*)((uintptr_t)(p + alignment - 1) & ~(alignment - 1));
435 #endif
436 }
437