Home | History | Annotate | Download | only in pixman
      1 /*
      2  * Copyright  2000 SuSE, Inc.
      3  * Copyright  2007 Red Hat, Inc.
      4  *
      5  * Permission to use, copy, modify, distribute, and sell this software and its
      6  * documentation for any purpose is hereby granted without fee, provided that
      7  * the above copyright notice appear in all copies and that both that
      8  * copyright notice and this permission notice appear in supporting
      9  * documentation, and that the name of SuSE not be used in advertising or
     10  * publicity pertaining to distribution of the software without specific,
     11  * written prior permission.  SuSE makes no representations about the
     12  * suitability of this software for any purpose.  It is provided "as is"
     13  * without express or implied warranty.
     14  *
     15  * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
     17  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     19  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     21  */
     22 #ifdef HAVE_CONFIG_H
     23 #include <config.h>
     24 #endif
     25 
     26 #include "pixman-private.h"
     27 
     28 #if defined(USE_X86_MMX) || defined (USE_SSE2)
     29 
     30 /* The CPU detection code needs to be in a file not compiled with
     31  * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
     32  * that would lead to SIGILL instructions on old CPUs that don't have
     33  * it.
     34  */
     35 
     36 typedef enum
     37 {
     38     X86_MMX			= (1 << 0),
     39     X86_MMX_EXTENSIONS		= (1 << 1),
     40     X86_SSE			= (1 << 2) | X86_MMX_EXTENSIONS,
     41     X86_SSE2			= (1 << 3),
     42     X86_CMOV			= (1 << 4)
     43 } cpu_features_t;
     44 
     45 #ifdef HAVE_GETISAX
     46 
     47 #include <sys/auxv.h>
     48 
     49 static cpu_features_t
     50 detect_cpu_features (void)
     51 {
     52     cpu_features_t features = 0;
     53     unsigned int result = 0;
     54 
     55     if (getisax (&result, 1))
     56     {
     57 	if (result & AV_386_CMOV)
     58 	    features |= X86_CMOV;
     59 	if (result & AV_386_MMX)
     60 	    features |= X86_MMX;
     61 	if (result & AV_386_AMD_MMX)
     62 	    features |= X86_MMX_EXTENSIONS;
     63 	if (result & AV_386_SSE)
     64 	    features |= X86_SSE;
     65 	if (result & AV_386_SSE2)
     66 	    features |= X86_SSE2;
     67     }
     68 
     69     return features;
     70 }
     71 
     72 #else
     73 
     74 #define _PIXMAN_X86_64							\
     75     (defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64))
     76 
     77 static pixman_bool_t
     78 have_cpuid (void)
     79 {
     80 #if _PIXMAN_X86_64 || defined (_MSC_VER)
     81 
     82     return TRUE;
     83 
     84 #elif defined (__GNUC__)
     85     uint32_t result;
     86 
     87     __asm__ volatile (
     88         "pushf"				"\n\t"
     89         "pop %%eax"			"\n\t"
     90         "mov %%eax, %%ecx"		"\n\t"
     91         "xor $0x00200000, %%eax"	"\n\t"
     92         "push %%eax"			"\n\t"
     93         "popf"				"\n\t"
     94         "pushf"				"\n\t"
     95         "pop %%eax"			"\n\t"
     96         "xor %%ecx, %%eax"		"\n\t"
     97 	"mov %%eax, %0"			"\n\t"
     98 	: "=r" (result)
     99 	:
    100 	: "%eax", "%ecx");
    101 
    102     return !!result;
    103 
    104 #else
    105 #error "Unknown compiler"
    106 #endif
    107 }
    108 
    109 static void
    110 pixman_cpuid (uint32_t feature,
    111 	      uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
    112 {
    113 #if defined (__GNUC__)
    114 
    115 #if _PIXMAN_X86_64
    116     __asm__ volatile (
    117         "cpuid"				"\n\t"
    118 	: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
    119 	: "a" (feature));
    120 #else
    121     /* On x86-32 we need to be careful about the handling of %ebx
    122      * and %esp. We can't declare either one as clobbered
    123      * since they are special registers (%ebx is the "PIC
    124      * register" holding an offset to global data, %esp the
    125      * stack pointer), so we need to make sure that %ebx is
    126      * preserved, and that %esp has its original value when
    127      * accessing the output operands.
    128      */
    129     __asm__ volatile (
    130 	"xchg %%ebx, %1"		"\n\t"
    131 	"cpuid"				"\n\t"
    132 	"xchg %%ebx, %1"		"\n\t"
    133 	: "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
    134 	: "a" (feature));
    135 #endif
    136 
    137 #elif defined (_MSC_VER)
    138     int info[4];
    139 
    140     __cpuid (info, feature);
    141 
    142     *a = info[0];
    143     *b = info[1];
    144     *c = info[2];
    145     *d = info[3];
    146 #else
    147 #error Unknown compiler
    148 #endif
    149 }
    150 
    151 static cpu_features_t
    152 detect_cpu_features (void)
    153 {
    154     uint32_t a, b, c, d;
    155     cpu_features_t features = 0;
    156 
    157     if (!have_cpuid())
    158 	return features;
    159 
    160     /* Get feature bits */
    161     pixman_cpuid (0x01, &a, &b, &c, &d);
    162     if (d & (1 << 15))
    163 	features |= X86_CMOV;
    164     if (d & (1 << 23))
    165 	features |= X86_MMX;
    166     if (d & (1 << 25))
    167 	features |= X86_SSE;
    168     if (d & (1 << 26))
    169 	features |= X86_SSE2;
    170 
    171     /* Check for AMD specific features */
    172     if ((features & X86_MMX) && !(features & X86_SSE))
    173     {
    174 	char vendor[13];
    175 
    176 	/* Get vendor string */
    177 	memset (vendor, 0, sizeof vendor);
    178 
    179 	pixman_cpuid (0x00, &a, &b, &c, &d);
    180 	memcpy (vendor + 0, &b, 4);
    181 	memcpy (vendor + 4, &d, 4);
    182 	memcpy (vendor + 8, &c, 4);
    183 
    184 	if (strcmp (vendor, "AuthenticAMD") == 0 ||
    185 	    strcmp (vendor, "Geode by NSC") == 0)
    186 	{
    187 	    pixman_cpuid (0x80000000, &a, &b, &c, &d);
    188 	    if (a >= 0x80000001)
    189 	    {
    190 		pixman_cpuid (0x80000001, &a, &b, &c, &d);
    191 
    192 		if (d & (1 << 22))
    193 		    features |= X86_MMX_EXTENSIONS;
    194 	    }
    195 	}
    196     }
    197 
    198     return features;
    199 }
    200 
    201 #endif
    202 
    203 static pixman_bool_t
    204 have_feature (cpu_features_t feature)
    205 {
    206     static pixman_bool_t initialized;
    207     static cpu_features_t features;
    208 
    209     if (!initialized)
    210     {
    211 	features = detect_cpu_features();
    212 	initialized = TRUE;
    213     }
    214 
    215     return (features & feature) == feature;
    216 }
    217 
    218 #endif
    219 
    220 pixman_implementation_t *
    221 _pixman_x86_get_implementations (pixman_implementation_t *imp)
    222 {
    223 #define MMX_BITS  (X86_MMX | X86_MMX_EXTENSIONS)
    224 #define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2)
    225 
    226 #ifdef USE_X86_MMX
    227     if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS))
    228 	imp = _pixman_implementation_create_mmx (imp);
    229 #endif
    230 
    231 #ifdef USE_SSE2
    232     if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS))
    233 	imp = _pixman_implementation_create_sse2 (imp);
    234 #endif
    235 
    236     return imp;
    237 }
    238