X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fcpu.c;h=eca79b5911178ec05fd82c46f7c368b4c7d83ff7;hb=31268f2df54a433f2b50440bb504546bd19ed16a;hp=41206685792de83f356b63584a42ec4f8fe8b944;hpb=145b1961b4825d2a8d807bf02db90440a8c45380;p=vlc diff --git a/src/misc/cpu.c b/src/misc/cpu.c index 4120668579..eca79b5911 100644 --- a/src/misc/cpu.c +++ b/src/misc/cpu.c @@ -1,163 +1,185 @@ /***************************************************************************** * cpu.c: CPU detection code ***************************************************************************** - * Copyright (C) 1998-2002 VideoLAN - * $Id: cpu.c,v 1.7 2002/10/03 13:21:55 sam Exp $ + * Copyright (C) 1998-2004 VLC authors and VideoLAN + * $Id$ * * Authors: Samuel Hocevar * Christophe Massiot * Eugenio Jarosiewicz * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* SIGHUP, SIGINT, SIGKILL */ -#include /* longjmp, setjmp */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include +#include +#include +#include "libvlc.h" -#ifdef SYS_DARWIN -# include /* AltiVec detection */ -# include /* some day the header files||compiler * - will define it for us */ -# include -#endif +#include -#include "vlc_cpu.h" +#ifndef __linux__ +#include +#ifndef _WIN32 +#include +#include +#include +#else +#include +#endif -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static void SigHandler ( int ); +#ifdef __APPLE__ +#include +#endif +#ifdef __ANDROID__ +#include +#endif -/***************************************************************************** - * Global variables - they're needed for signal handling - *****************************************************************************/ -static jmp_buf env; -static int i_illegal; -#if defined( __i386__ ) -static char *psz_capability; +#if defined(__OpenBSD__) && defined(__powerpc__) +#include +#include +#include #endif -/***************************************************************************** - * CPUCapabilities: get the CPU capabilities - ***************************************************************************** - * This function is called to list extensions the CPU may have. - *****************************************************************************/ -u32 CPUCapabilities( void ) +static uint32_t cpu_flags; + +#if defined (__i386__) || defined (__x86_64__) || defined (__powerpc__) \ + || defined (__ppc__) || defined (__ppc64__) || defined (__powerpc64__) +# if !defined (_WIN32) && !defined (__OS2__) +static bool vlc_CPU_check (const char *name, void (*func) (void)) { - volatile u32 i_capabilities = CPU_CAPABILITY_NONE; + pid_t pid = fork(); -#if defined( SYS_DARWIN ) - struct host_basic_info hi; - kern_return_t ret; - host_name_port_t host; + switch (pid) + { + case 0: + signal (SIGILL, SIG_DFL); + func (); + //__asm__ __volatile__ ( code : : input ); + _exit (0); + case -1: + return false; + } + //i_capabilities |= (flag); - int i_size; - char *psz_name, *psz_subname; + int status; + while( waitpid( pid, &status, 0 ) == -1 ); - i_capabilities |= CPU_CAPABILITY_FPU; + if( WIFEXITED( status ) && WEXITSTATUS( status ) == 0 ) + return true; - /* Should 'never' fail? */ - host = mach_host_self(); + fprintf (stderr, "Warning: your CPU has %s instructions, but not your " + "operating system.\n", name); + fprintf( stderr, " some optimizations will be disabled unless " + "you upgrade your OS\n" ); + return false; +} - i_size = sizeof( hi ) / sizeof( int ); - ret = host_info( host, HOST_BASIC_INFO, ( host_info_t )&hi, &i_size ); +#if defined (CAN_COMPILE_SSE) && !defined (__SSE__) +VLC_SSE static void SSE_test (void) +{ + asm volatile ("xorps %%xmm0,%%xmm0\n" : : : "xmm0", "xmm1"); +} +#endif +#if defined (CAN_COMPILE_3DNOW) +VLC_MMX static void ThreeD_Now_test (void) +{ + asm volatile ("pfadd %%mm0,%%mm0\n" "femms\n" : : : "mm0"); +} +#endif - if( ret != KERN_SUCCESS ) - { - fprintf( stderr, "error: couldn't get CPU information\n" ); - return i_capabilities; - } +#if defined (CAN_COMPILE_ALTIVEC) +static void Altivec_test (void) +{ + asm volatile ("mtspr 256, %0\n" "vand %%v0, %%v0, %%v0\n" : : "r" (-1)); +} +#endif - slot_name( hi.cpu_type, hi.cpu_subtype, &psz_name, &psz_subname ); - /* FIXME: need better way to detect newer proccessors. - * could do strncmp(a,b,5), but that's real ugly */ - if( !strcmp(psz_name, "ppc7400") || !strcmp(psz_name, "ppc7450") ) - { - i_capabilities |= CPU_CAPABILITY_ALTIVEC; - } +#else /* _WIN32 || __OS2__ */ +# define vlc_CPU_check(name, func) (1) +#endif +#endif - return i_capabilities; +/** + * Determines the CPU capabilities and stores them in cpu_flags. + * The result can be retrieved with vlc_CPU(). + */ +void vlc_CPU_init (void) +{ + uint32_t i_capabilities = 0; -#elif defined( __i386__ ) - volatile unsigned int i_eax, i_ebx, i_ecx, i_edx; - volatile vlc_bool_t b_amd; +#if defined( __i386__ ) || defined( __x86_64__ ) + unsigned int i_eax, i_ebx, i_ecx, i_edx; + bool b_amd; /* Needed for x86 CPU capabilities detection */ -# define cpuid( a ) \ - asm volatile ( "pushl %%ebx\n\t" \ - "cpuid\n\t" \ - "movl %%ebx,%1\n\t" \ - "popl %%ebx\n\t" \ - : "=a" ( i_eax ), \ - "=r" ( i_ebx ), \ - "=c" ( i_ecx ), \ - "=d" ( i_edx ) \ - : "a" ( a ) \ - : "cc" ); - -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - void (*pf_sigill) (int) = signal( SIGILL, SigHandler ); -# endif - - i_capabilities |= CPU_CAPABILITY_FPU; - - /* test for a 486 CPU */ - asm volatile ( "pushl %%ebx\n\t" - "pushfl\n\t" - "popl %%eax\n\t" +# if defined (__i386__) && defined (__PIC__) +# define cpuid(reg) \ + asm volatile ("xchgl %%ebx,%1\n\t" \ + "cpuid\n\t" \ + "xchgl %%ebx,%1\n\t" \ + : "=a" (i_eax), "=r" (i_ebx), "=c" (i_ecx), "=d" (i_edx) \ + : "a" (reg) \ + : "cc"); +# else +# define cpuid(reg) \ + asm volatile ("cpuid\n\t" \ + : "=a" (i_eax), "=b" (i_ebx), "=c" (i_ecx), "=d" (i_edx) \ + : "a" (reg) \ + : "cc"); +# endif + /* Check if the OS really supports the requested instructions */ +# if defined (__i386__) && !defined (__i486__) && !defined (__i586__) \ + && !defined (__i686__) && !defined (__pentium4__) \ + && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__) + /* check if cpuid instruction is supported */ + asm volatile ( "push %%ebx\n\t" + "pushf\n\t" + "pop %%eax\n\t" "movl %%eax, %%ebx\n\t" "xorl $0x200000, %%eax\n\t" - "pushl %%eax\n\t" - "popfl\n\t" - "pushfl\n\t" - "popl %%eax\n\t" + "push %%eax\n\t" + "popf\n\t" + "pushf\n\t" + "pop %%eax\n\t" "movl %%ebx,%1\n\t" - "popl %%ebx\n\t" + "pop %%ebx\n\t" : "=a" ( i_eax ), "=r" ( i_ebx ) : : "cc" ); if( i_eax == i_ebx ) - { -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - signal( SIGILL, pf_sigill ); -# endif - return i_capabilities; - } - - i_capabilities |= CPU_CAPABILITY_486; + goto out; +# endif /* the CPU supports the CPUID instruction - get its level */ cpuid( 0x00000000 ); +# if defined (__i386__) && !defined (__i586__) \ + && !defined (__i686__) && !defined (__pentium4__) \ + && !defined (__k6__) && !defined (__athlon__) && !defined (__k8__) if( !i_eax ) - { -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - signal( SIGILL, pf_sigill ); -# endif - return i_capabilities; - } - - /* FIXME: this isn't correct, since some 486s have cpuid */ - i_capabilities |= CPU_CAPABILITY_586; + goto out; +#endif /* borrowed from mpeg2dec */ b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 ) @@ -165,146 +187,130 @@ u32 CPUCapabilities( void ) /* test for the MMX flag */ cpuid( 0x00000001 ); - +# if !defined (__MMX__) if( ! (i_edx & 0x00800000) ) - { -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - signal( SIGILL, pf_sigill ); -# endif - return i_capabilities; - } - - i_capabilities |= CPU_CAPABILITY_MMX; + goto out; +# endif + i_capabilities |= VLC_CPU_MMX; if( i_edx & 0x02000000 ) + i_capabilities |= VLC_CPU_MMXEXT; +# if defined (CAN_COMPILE_SSE) && !defined (__SSE__) + if (( i_edx & 0x02000000 ) && vlc_CPU_check ("SSE", SSE_test)) +# endif { - i_capabilities |= CPU_CAPABILITY_MMXEXT; - -# ifdef CAN_COMPILE_SSE - /* We test if OS supports the SSE instructions */ - psz_capability = "SSE"; - i_illegal = 0; - - if( setjmp( env ) == 0 ) - { - /* Test a SSE instruction */ - __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : ); - } - - if( i_illegal == 0 ) - { - i_capabilities |= CPU_CAPABILITY_SSE; - } -# endif + /*if( i_edx & 0x02000000 )*/ + i_capabilities |= VLC_CPU_SSE; + if (i_edx & 0x04000000) + i_capabilities |= VLC_CPU_SSE2; + if (i_ecx & 0x00000001) + i_capabilities |= VLC_CPU_SSE3; + if (i_ecx & 0x00000200) + i_capabilities |= VLC_CPU_SSSE3; + if (i_ecx & 0x00080000) + i_capabilities |= VLC_CPU_SSE4_1; + if (i_ecx & 0x00100000) + i_capabilities |= VLC_CPU_SSE4_2; } /* test for additional capabilities */ cpuid( 0x80000000 ); if( i_eax < 0x80000001 ) - { -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - signal( SIGILL, pf_sigill ); -# endif - return i_capabilities; - } + goto out; /* list these additional capabilities */ cpuid( 0x80000001 ); -# ifdef CAN_COMPILE_3DNOW - if( i_edx & 0x80000000 ) - { - psz_capability = "3D Now!"; - i_illegal = 0; - - if( setjmp( env ) == 0 ) - { - /* Test a 3D Now! instruction */ - __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : ); - } - - if( i_illegal == 0 ) - { - i_capabilities |= CPU_CAPABILITY_3DNOW; - } - } -# endif +# if defined (CAN_COMPILE_3DNOW) && !defined (__3dNOW__) + if ((i_edx & 0x80000000) && vlc_CPU_check ("3D Now!", ThreeD_Now_test)) +# endif + i_capabilities |= VLC_CPU_3dNOW; if( b_amd && ( i_edx & 0x00400000 ) ) - { - i_capabilities |= CPU_CAPABILITY_MMXEXT; - } - -# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) - signal( SIGILL, pf_sigill ); -# endif - return i_capabilities; + i_capabilities |= VLC_CPU_MMXEXT; +out: -#elif defined( __powerpc__ ) +#elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __powerpc64__ ) \ + || defined( __ppc64__ ) -# ifdef CAN_COMPILE_ALTIVEC - void (*pf_sigill) (int) = signal( SIGILL, SigHandler ); - - i_capabilities |= CPU_CAPABILITY_FPU; - - i_illegal = 0; +# if defined(__APPLE__) || defined(__OpenBSD__) +# if defined(__OpenBSD__) + int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC }; +# else + int selectors[2] = { CTL_HW, HW_VECTORUNIT }; +# endif + int i_has_altivec = 0; + size_t i_length = sizeof( i_has_altivec ); + int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0); - if( setjmp( env ) == 0 ) - { - asm volatile ("mtspr 256, %0\n\t" - "vand %%v0, %%v0, %%v0" - : - : "r" (-1)); - } + if( i_error == 0 && i_has_altivec != 0 ) + i_capabilities |= VLC_CPU_ALTIVEC; - if( i_illegal == 0 ) - { - i_capabilities |= CPU_CAPABILITY_ALTIVEC; - } +# elif defined( CAN_COMPILE_ALTIVEC ) + if (vlc_CPU_check ("Altivec", Altivec_test)) + i_capabilities |= VLC_CPU_ALTIVEC; - signal( SIGILL, pf_sigill ); # endif - return i_capabilities; - -#elif defined( __sparc__ ) +#elif defined ( __arm__) +# ifdef __ANDROID__ + if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) + i_capabilities |= VLC_CPU_ARM_NEON; +# endif - i_capabilities |= CPU_CAPABILITY_FPU; - return i_capabilities; +#endif -#else - /* default behaviour */ - return i_capabilities; + cpu_flags = i_capabilities; +} +/** + * Retrieves pre-computed CPU capability flags + */ +unsigned vlc_CPU (void) +{ +/* On Windows and OS/2, + * initialized from DllMain() and _DLL_InitTerm() respectively, instead */ +#if !defined(_WIN32) && !defined(__OS2__) + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once (&once, vlc_CPU_init); #endif + return cpu_flags; } +#endif -/***************************************************************************** - * SigHandler: system signal handler - ***************************************************************************** - * This function is called when an illegal instruction signal is received by - * the program. We use this function to test OS and CPU capabilities - *****************************************************************************/ -static void SigHandler( int i_signal ) +void vlc_CPU_dump (vlc_object_t *obj) { - /* Acknowledge the signal received */ - i_illegal = 1; + char buf[200], *p = buf; + +#if defined (__i386__) || defined (__x86_64__) + if (vlc_CPU_MMX()) p += sprintf (p, "MMX "); + if (vlc_CPU_MMXEXT()) p += sprintf (p, "MMXEXT "); + if (vlc_CPU_SSE()) p += sprintf (p, "SSE "); + if (vlc_CPU_SSE2()) p += sprintf (p, "SSE2 "); + if (vlc_CPU_SSE3()) p += sprintf (p, "SSE3 "); + if (vlc_CPU_SSSE3()) p += sprintf (p, "SSSE3 "); + if (vlc_CPU_SSE4_1()) p += sprintf (p, "SSE4.1 "); + if (vlc_CPU_SSE4_2()) p += sprintf (p, "SSE4.2 "); + if (vlc_CPU_SSE4A()) p += sprintf (p, "SSE4A "); + if (vlc_CPU_AVX()) p += sprintf (p, "AVX "); + if (vlc_CPU_AVX2()) p += sprintf (p, "AVX "); + if (vlc_CPU_3dNOW()) p += sprintf (p, "3DNow! "); + if (vlc_CPU_XOP()) p += sprintf (p, "XOP "); + if (vlc_CPU_FMA4()) p += sprintf (p, "FMA4 "); + +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + if (vlc_CPU_ALTIVEC()) p += sprintf (p, "AltiVec"); + +#elif defined (__arm__) + if (vlc_CPU_ARM_NEON()) p += sprintf (p, "ARM_NEON "); -#ifdef HAVE_SIGRELSE - sigrelse( i_signal ); #endif -#if defined( __i386__ ) - fprintf( stderr, "warning: your CPU has %s instructions, but not your " - "operating system.\n", psz_capability ); - fprintf( stderr, " some optimizations will be disabled unless " - "you upgrade your OS\n" ); -# if defined( SYS_LINUX ) - fprintf( stderr, " (for instance Linux kernel 2.4.x or later)\n" ); -# endif +#if HAVE_FPU + p += sprintf (p, "FPU "); #endif - longjmp( env, 1 ); + if (p > buf) + msg_Dbg (obj, "CPU has capabilities %s", buf); } -