2 * Copyright © 2000 SuSE, Inc.
3 * Copyright © 2007 Red Hat, Inc.
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.
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.
28 #if defined(USE_ARM_SIMD) && defined(_MSC_VER)
29 /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
33 #include "pixman-private.h"
37 /* The CPU detection code needs to be in a file not compiled with
38 * "-maltivec -mabi=altivec", as gcc would try to save vector register
39 * across function calls causing SIGILL on cpus without Altivec/vmx.
41 static pixman_bool_t initialized = FALSE;
42 static volatile pixman_bool_t have_vmx = TRUE;
45 #include <sys/sysctl.h>
48 pixman_have_vmx (void)
52 size_t length = sizeof(have_vmx);
54 sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
64 #elif defined (__OpenBSD__)
65 #include <sys/param.h>
66 #include <sys/sysctl.h>
67 #include <machine/cpu.h>
70 pixman_have_vmx (void)
74 int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
75 size_t length = sizeof(have_vmx);
77 sysctl (mib, 2, &have_vmx, &length, NULL, 0);
87 #elif defined (__linux__)
88 #include <sys/types.h>
93 #include <linux/auxvec.h>
94 #include <asm/cputable.h>
97 pixman_have_vmx (void)
102 unsigned long buf[64];
108 snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
110 fd = open (fname, O_RDONLY);
113 for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
115 /* Read more if buf is empty... */
116 if (i == (count / sizeof(unsigned long)))
118 count = read (fd, buf, sizeof(buf));
124 if (buf[i] == AT_HWCAP)
126 have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
130 else if (buf[i] == AT_NULL)
140 /* Something went wrong. Assume 'no' rather than playing
141 fragile tricks with catching SIGILL. */
149 #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
153 static jmp_buf jump_env;
160 longjmp (jump_env, 1);
164 pixman_have_vmx (void)
166 struct sigaction sa, osa;
171 sa.sa_flags = SA_SIGINFO;
172 sigemptyset (&sa.sa_mask);
173 sa.sa_sigaction = vmx_test;
174 sigaction (SIGILL, &sa, &osa);
175 jmp_result = setjmp (jump_env);
178 asm volatile ( "vor 0, 0, 0" );
180 sigaction (SIGILL, &osa, NULL);
181 have_vmx = (jmp_result == 0);
187 #endif /* __APPLE__ */
190 #if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT)
192 #if defined(_MSC_VER)
194 #if defined(USE_ARM_SIMD)
195 extern int pixman_msvc_try_arm_simd_op ();
198 pixman_have_arm_simd (void)
200 static pixman_bool_t initialized = FALSE;
201 static pixman_bool_t have_arm_simd = FALSE;
206 pixman_msvc_try_arm_simd_op ();
207 have_arm_simd = TRUE;
208 } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
209 have_arm_simd = FALSE;
214 return have_arm_simd;
217 #endif /* USE_ARM_SIMD */
219 #if defined(USE_ARM_NEON)
220 extern int pixman_msvc_try_arm_neon_op ();
223 pixman_have_arm_neon (void)
225 static pixman_bool_t initialized = FALSE;
226 static pixman_bool_t have_arm_neon = FALSE;
232 pixman_msvc_try_arm_neon_op ();
233 have_arm_neon = TRUE;
235 __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
237 have_arm_neon = FALSE;
242 return have_arm_neon;
245 #endif /* USE_ARM_NEON */
247 #elif defined (__linux__) /* linux ELF */
251 #include <sys/types.h>
252 #include <sys/stat.h>
253 #include <sys/mman.h>
258 static pixman_bool_t arm_has_v7 = FALSE;
259 static pixman_bool_t arm_has_v6 = FALSE;
260 static pixman_bool_t arm_has_vfp = FALSE;
261 static pixman_bool_t arm_has_neon = FALSE;
262 static pixman_bool_t arm_has_iwmmxt = FALSE;
263 static pixman_bool_t arm_tests_initialized = FALSE;
266 pixman_arm_read_auxv ()
271 fd = open ("/proc/self/auxv", O_RDONLY);
274 while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
276 if (aux.a_type == AT_HWCAP)
278 uint32_t hwcap = aux.a_un.a_val;
279 /* hardcode these values to avoid depending on specific
280 * versions of the hwcap header, e.g. HWCAP_NEON
282 arm_has_vfp = (hwcap & 64) != 0;
283 arm_has_iwmmxt = (hwcap & 512) != 0;
284 /* this flag is only present on kernel 2.6.29 */
285 arm_has_neon = (hwcap & 4096) != 0;
287 else if (aux.a_type == AT_PLATFORM)
289 const char *plat = (const char*) aux.a_un.a_val;
290 if (strncmp (plat, "v7l", 3) == 0)
295 else if (strncmp (plat, "v6l", 3) == 0)
304 arm_tests_initialized = TRUE;
307 #if defined(USE_ARM_SIMD)
309 pixman_have_arm_simd (void)
311 if (!arm_tests_initialized)
312 pixman_arm_read_auxv ();
317 #endif /* USE_ARM_SIMD */
319 #if defined(USE_ARM_NEON)
321 pixman_have_arm_neon (void)
323 if (!arm_tests_initialized)
324 pixman_arm_read_auxv ();
329 #endif /* USE_ARM_NEON */
331 #if defined(USE_ARM_IWMMXT)
333 pixman_have_arm_iwmmxt (void)
335 if (!arm_tests_initialized)
336 pixman_arm_read_auxv ();
338 return arm_has_iwmmxt;
341 #endif /* USE_ARM_IWMMXT */
343 #else /* linux ELF */
345 #define pixman_have_arm_simd() FALSE
346 #define pixman_have_arm_neon() FALSE
347 #define pixman_have_arm_iwmmxt() FALSE
351 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
353 #if defined(USE_X86_MMX) || defined(USE_SSE2)
354 /* The CPU detection code needs to be in a file not compiled with
355 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
356 * that would lead to SIGILL instructions on old CPUs that don't have
359 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
362 #include <sys/auxv.h>
369 MMX_EXTENSIONS = 0x2,
377 detect_cpu_features (void)
379 unsigned int features = 0;
380 unsigned int result = 0;
383 if (getisax (&result, 1))
385 if (result & AV_386_CMOV)
387 if (result & AV_386_MMX)
389 if (result & AV_386_AMD_MMX)
390 features |= MMX_EXTENSIONS;
391 if (result & AV_386_SSE)
393 if (result & AV_386_SSE2)
399 int vendor0 = 0, vendor1, vendor2;
405 /* see p. 118 of amd64 instruction set manual Vol3 */
406 /* We need to be careful about the handling of %ebx and
407 * %esp here. We can't declare either one as clobbered
408 * since they are special registers (%ebx is the "PIC
409 * register" holding an offset to global data, %esp the
410 * stack pointer), so we need to make sure they have their
411 * original values when we access the output operands.
417 "xor $0x00200000, %%eax\n"
426 "mov $0x00000000, %%eax\n"
434 "mov $0x00000001, %%eax\n"
445 : "%eax", "%ecx", "%edx"
448 #elif defined (_MSC_VER)
478 memmove (vendor + 0, &vendor0, 4);
479 memmove (vendor + 4, &vendor1, 4);
480 memmove (vendor + 8, &vendor2, 4);
483 # error unsupported compiler
489 /* result now contains the standard feature bits */
490 if (result & (1 << 15))
492 if (result & (1 << 23))
494 if (result & (1 << 25))
496 if (result & (1 << 26))
498 if ((features & MMX) && !(features & SSE) &&
499 (strcmp (vendor, "AuthenticAMD") == 0 ||
500 strcmp (vendor, "Geode by NSC") == 0))
502 /* check for AMD MMX extensions */
506 " mov $0x80000000, %%eax\n"
508 " xor %%edx, %%edx\n"
511 " mov $0x80000001, %%eax\n"
518 : "%eax", "%ecx", "%edx"
520 #elif defined _MSC_VER
535 if (result & (1 << 22))
536 features |= MMX_EXTENSIONS;
539 #endif /* HAVE_GETISAX */
545 pixman_have_mmx (void)
547 static pixman_bool_t initialized = FALSE;
548 static pixman_bool_t mmx_present;
552 unsigned int features = detect_cpu_features ();
553 mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
562 pixman_have_sse2 (void)
564 static pixman_bool_t initialized = FALSE;
565 static pixman_bool_t sse2_present;
569 unsigned int features = detect_cpu_features ();
570 sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
579 #else /* __amd64__ */
581 #define pixman_have_mmx() TRUE
584 #define pixman_have_sse2() TRUE
586 #endif /* __amd64__ */
589 pixman_implementation_t *
590 _pixman_choose_implementation (void)
592 pixman_implementation_t *imp;
594 imp = _pixman_implementation_create_general();
595 imp = _pixman_implementation_create_fast_path (imp);
598 if (pixman_have_mmx ())
599 imp = _pixman_implementation_create_mmx (imp);
603 if (pixman_have_sse2 ())
604 imp = _pixman_implementation_create_sse2 (imp);
608 if (pixman_have_arm_simd ())
609 imp = _pixman_implementation_create_arm_simd (imp);
612 #ifdef USE_ARM_IWMMXT
613 if (pixman_have_arm_iwmmxt ())
614 imp = _pixman_implementation_create_mmx (imp);
618 if (pixman_have_arm_neon ())
619 imp = _pixman_implementation_create_arm_neon (imp);
623 if (pixman_have_vmx ())
624 imp = _pixman_implementation_create_vmx (imp);
627 imp = _pixman_implementation_create_noop (imp);