Home | History | Annotate | Download | only in x86
      1 /*
      2  * Mesa 3-D graphics library
      3  * Version:  6.5.1
      4  *
      5  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the "Software"),
      9  * to deal in the Software without restriction, including without limitation
     10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     11  * and/or sell copies of the Software, and to permit persons to whom the
     12  * Software is furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included
     15  * in all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     21  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 /**
     26  * \file common_x86.c
     27  *
     28  * Check CPU capabilities & initialize optimized funtions for this particular
     29  * processor.
     30  *
     31  * Changed by Andre Werthmann for using the new SSE functions.
     32  *
     33  * \author Holger Waechtler <holger (at) akaflieg.extern.tu-berlin.de>
     34  * \author Andre Werthmann <wertmann (at) cs.uni-potsdam.de>
     35  */
     36 
     37 /* XXX these includes should probably go into imports.h or glheader.h */
     38 #if defined(USE_SSE_ASM) && defined(__linux__)
     39 #include <linux/version.h>
     40 #endif
     41 #if defined(USE_SSE_ASM) && defined(__FreeBSD__)
     42 #include <sys/types.h>
     43 #include <sys/sysctl.h>
     44 #endif
     45 #if defined(USE_SSE_ASM) && defined(__OpenBSD__)
     46 #include <sys/param.h>
     47 #include <sys/sysctl.h>
     48 #include <machine/cpu.h>
     49 #endif
     50 
     51 #include "main/imports.h"
     52 #include "common_x86_asm.h"
     53 
     54 
     55 /** Bitmask of X86_FEATURE_x bits */
     56 int _mesa_x86_cpu_features = 0x0;
     57 
     58 static int detection_debug = GL_FALSE;
     59 
     60 /* No reason for this to be public.
     61  */
     62 extern GLuint	_ASMAPI _mesa_x86_has_cpuid(void);
     63 extern void	_ASMAPI _mesa_x86_cpuid(GLuint op, GLuint *reg_eax, GLuint *reg_ebx, GLuint *reg_ecx, GLuint *reg_edx);
     64 extern GLuint	_ASMAPI _mesa_x86_cpuid_eax(GLuint op);
     65 extern GLuint	_ASMAPI _mesa_x86_cpuid_ebx(GLuint op);
     66 extern GLuint	_ASMAPI _mesa_x86_cpuid_ecx(GLuint op);
     67 extern GLuint	_ASMAPI _mesa_x86_cpuid_edx(GLuint op);
     68 
     69 
     70 #if defined(USE_SSE_ASM)
     71 /*
     72  * We must verify that the Streaming SIMD Extensions are truly supported
     73  * on this processor before we go ahead and hook out the optimized code.
     74  *
     75  * However, I have been told by Alan Cox that all 2.4 (and later) Linux
     76  * kernels provide full SSE support on all processors that expose SSE via
     77  * the CPUID mechanism.
     78  */
     79 
     80 /* These are assembly functions: */
     81 extern void _mesa_test_os_sse_support( void );
     82 extern void _mesa_test_os_sse_exception_support( void );
     83 
     84 
     85 #if defined(WIN32)
     86 #ifndef STATUS_FLOAT_MULTIPLE_TRAPS
     87 # define STATUS_FLOAT_MULTIPLE_TRAPS (0xC00002B5L)
     88 #endif
     89 static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS exp)
     90 {
     91    PEXCEPTION_RECORD rec = exp->ExceptionRecord;
     92    PCONTEXT ctx = exp->ContextRecord;
     93 
     94    if ( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION ) {
     95       _mesa_debug(NULL, "EXCEPTION_ILLEGAL_INSTRUCTION\n" );
     96       _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM);
     97    } else if ( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS ) {
     98       _mesa_debug(NULL, "STATUS_FLOAT_MULTIPLE_TRAPS\n");
     99       /* Windows seems to clear the exception flag itself, we just have to increment Eip */
    100    } else {
    101       _mesa_debug(NULL, "UNEXPECTED EXCEPTION (0x%08x), terminating!\n" );
    102       return EXCEPTION_EXECUTE_HANDLER;
    103    }
    104 
    105    if ( (ctx->ContextFlags & CONTEXT_CONTROL) != CONTEXT_CONTROL ) {
    106       _mesa_debug(NULL, "Context does not contain control registers, terminating!\n");
    107       return EXCEPTION_EXECUTE_HANDLER;
    108    }
    109    ctx->Eip += 3;
    110 
    111    return EXCEPTION_CONTINUE_EXECUTION;
    112 }
    113 #endif /* WIN32 */
    114 
    115 
    116 /**
    117  * Check if SSE is supported.
    118  * If not, turn off the X86_FEATURE_XMM flag in _mesa_x86_cpu_features.
    119  */
    120 void _mesa_check_os_sse_support( void )
    121 {
    122 #if defined(__FreeBSD__)
    123    {
    124       int ret, enabled;
    125       unsigned int len;
    126       len = sizeof(enabled);
    127       ret = sysctlbyname("hw.instruction_sse", &enabled, &len, NULL, 0);
    128       if (ret || !enabled)
    129          _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM);
    130    }
    131 #elif defined (__NetBSD__)
    132    {
    133       int ret, enabled;
    134       size_t len = sizeof(enabled);
    135       ret = sysctlbyname("machdep.sse", &enabled, &len, (void *)NULL, 0);
    136       if (ret || !enabled)
    137          _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM);
    138    }
    139 #elif defined(__OpenBSD__)
    140    {
    141       int mib[2];
    142       int ret, enabled;
    143       size_t len = sizeof(enabled);
    144 
    145       mib[0] = CTL_MACHDEP;
    146       mib[1] = CPU_SSE;
    147 
    148       ret = sysctl(mib, 2, &enabled, &len, NULL, 0);
    149       if (ret || !enabled)
    150          _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM);
    151    }
    152 #elif defined(WIN32)
    153    LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
    154 
    155    /* Install our ExceptionFilter */
    156    oldFilter = SetUnhandledExceptionFilter( ExceptionFilter );
    157 
    158    if ( cpu_has_xmm ) {
    159       _mesa_debug(NULL, "Testing OS support for SSE...\n");
    160 
    161       _mesa_test_os_sse_support();
    162 
    163       if ( cpu_has_xmm ) {
    164 	 _mesa_debug(NULL, "Yes.\n");
    165       } else {
    166 	 _mesa_debug(NULL, "No!\n");
    167       }
    168    }
    169 
    170    if ( cpu_has_xmm ) {
    171       _mesa_debug(NULL, "Testing OS support for SSE unmasked exceptions...\n");
    172 
    173       _mesa_test_os_sse_exception_support();
    174 
    175       if ( cpu_has_xmm ) {
    176 	 _mesa_debug(NULL, "Yes.\n");
    177       } else {
    178 	 _mesa_debug(NULL, "No!\n");
    179       }
    180    }
    181 
    182    /* Restore previous exception filter */
    183    SetUnhandledExceptionFilter( oldFilter );
    184 
    185    if ( cpu_has_xmm ) {
    186       _mesa_debug(NULL, "Tests of OS support for SSE passed.\n");
    187    } else {
    188       _mesa_debug(NULL, "Tests of OS support for SSE failed!\n");
    189    }
    190 #else
    191    /* Do nothing on other platforms for now.
    192     */
    193    if (detection_debug)
    194       _mesa_debug(NULL, "Not testing OS support for SSE, leaving enabled.\n");
    195 #endif /* __FreeBSD__ */
    196 }
    197 
    198 #endif /* USE_SSE_ASM */
    199 
    200 
    201 /**
    202  * Initialize the _mesa_x86_cpu_features bitfield.
    203  * This is a no-op if called more than once.
    204  */
    205 void
    206 _mesa_get_x86_features(void)
    207 {
    208    static int called = 0;
    209 
    210    if (called)
    211       return;
    212 
    213    called = 1;
    214 
    215 #ifdef USE_X86_ASM
    216    _mesa_x86_cpu_features = 0x0;
    217 
    218    if (_mesa_getenv( "MESA_NO_ASM")) {
    219       return;
    220    }
    221 
    222    if (!_mesa_x86_has_cpuid()) {
    223        _mesa_debug(NULL, "CPUID not detected\n");
    224    }
    225    else {
    226        GLuint cpu_features;
    227        GLuint cpu_ext_features;
    228        GLuint cpu_ext_info;
    229        char cpu_vendor[13];
    230        GLuint result;
    231 
    232        /* get vendor name */
    233        _mesa_x86_cpuid(0, &result, (GLuint *)(cpu_vendor + 0), (GLuint *)(cpu_vendor + 8), (GLuint *)(cpu_vendor + 4));
    234        cpu_vendor[12] = '\0';
    235 
    236        if (detection_debug)
    237 	  _mesa_debug(NULL, "CPU vendor: %s\n", cpu_vendor);
    238 
    239        /* get cpu features */
    240        cpu_features = _mesa_x86_cpuid_edx(1);
    241 
    242        if (cpu_features & X86_CPU_FPU)
    243 	   _mesa_x86_cpu_features |= X86_FEATURE_FPU;
    244        if (cpu_features & X86_CPU_CMOV)
    245 	   _mesa_x86_cpu_features |= X86_FEATURE_CMOV;
    246 
    247 #ifdef USE_MMX_ASM
    248        if (cpu_features & X86_CPU_MMX)
    249 	   _mesa_x86_cpu_features |= X86_FEATURE_MMX;
    250 #endif
    251 
    252 #ifdef USE_SSE_ASM
    253        if (cpu_features & X86_CPU_XMM)
    254 	   _mesa_x86_cpu_features |= X86_FEATURE_XMM;
    255        if (cpu_features & X86_CPU_XMM2)
    256 	   _mesa_x86_cpu_features |= X86_FEATURE_XMM2;
    257 #endif
    258 
    259        /* query extended cpu features */
    260        if ((cpu_ext_info = _mesa_x86_cpuid_eax(0x80000000)) > 0x80000000) {
    261 	   if (cpu_ext_info >= 0x80000001) {
    262 
    263 	       cpu_ext_features = _mesa_x86_cpuid_edx(0x80000001);
    264 
    265 	       if (cpu_features & X86_CPU_MMX) {
    266 
    267 #ifdef USE_3DNOW_ASM
    268 		   if (cpu_ext_features & X86_CPUEXT_3DNOW)
    269 		       _mesa_x86_cpu_features |= X86_FEATURE_3DNOW;
    270 		   if (cpu_ext_features & X86_CPUEXT_3DNOW_EXT)
    271 		       _mesa_x86_cpu_features |= X86_FEATURE_3DNOWEXT;
    272 #endif
    273 
    274 #ifdef USE_MMX_ASM
    275 		   if (cpu_ext_features & X86_CPUEXT_MMX_EXT)
    276 		       _mesa_x86_cpu_features |= X86_FEATURE_MMXEXT;
    277 #endif
    278 	       }
    279 	   }
    280 
    281 	   /* query cpu name */
    282 	   if (cpu_ext_info >= 0x80000002) {
    283 	       GLuint ofs;
    284 	       char cpu_name[49];
    285 	       for (ofs = 0; ofs < 3; ofs++)
    286 		   _mesa_x86_cpuid(0x80000002+ofs, (GLuint *)(cpu_name + (16*ofs)+0), (GLuint *)(cpu_name + (16*ofs)+4), (GLuint *)(cpu_name + (16*ofs)+8), (GLuint *)(cpu_name + (16*ofs)+12));
    287 	       cpu_name[48] = '\0'; /* the name should be NULL terminated, but just to be sure */
    288 
    289 	       if (detection_debug)
    290 		  _mesa_debug(NULL, "CPU name: %s\n", cpu_name);
    291 	   }
    292        }
    293 
    294    }
    295 
    296 #ifdef USE_MMX_ASM
    297    if ( cpu_has_mmx ) {
    298       if ( _mesa_getenv( "MESA_NO_MMX" ) == 0 ) {
    299 	 if (detection_debug)
    300 	    _mesa_debug(NULL, "MMX cpu detected.\n");
    301       } else {
    302          _mesa_x86_cpu_features &= ~(X86_FEATURE_MMX);
    303       }
    304    }
    305 #endif
    306 
    307 #ifdef USE_3DNOW_ASM
    308    if ( cpu_has_3dnow ) {
    309       if ( _mesa_getenv( "MESA_NO_3DNOW" ) == 0 ) {
    310 	 if (detection_debug)
    311 	    _mesa_debug(NULL, "3DNow! cpu detected.\n");
    312       } else {
    313          _mesa_x86_cpu_features &= ~(X86_FEATURE_3DNOW);
    314       }
    315    }
    316 #endif
    317 
    318 #ifdef USE_SSE_ASM
    319    if ( cpu_has_xmm ) {
    320       if ( _mesa_getenv( "MESA_NO_SSE" ) == 0 ) {
    321 	 if (detection_debug)
    322 	    _mesa_debug(NULL, "SSE cpu detected.\n");
    323          if ( _mesa_getenv( "MESA_FORCE_SSE" ) == 0 ) {
    324             _mesa_check_os_sse_support();
    325          }
    326       } else {
    327          _mesa_debug(NULL, "SSE cpu detected, but switched off by user.\n");
    328          _mesa_x86_cpu_features &= ~(X86_FEATURE_XMM);
    329       }
    330    }
    331 #endif
    332 
    333 #endif /* USE_X86_ASM */
    334 
    335    (void) detection_debug;
    336 }
    337