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