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__) || defined(__ANDROID__) || defined(ANDROID) /* linux ELF or ANDROID */
249 static pixman_bool_t arm_has_v7 = FALSE;
250 static pixman_bool_t arm_has_v6 = FALSE;
251 static pixman_bool_t arm_has_vfp = FALSE;
252 static pixman_bool_t arm_has_neon = FALSE;
253 static pixman_bool_t arm_has_iwmmxt = FALSE;
254 static pixman_bool_t arm_tests_initialized = FALSE;
256 #if defined(__ANDROID__) || defined(ANDROID) /* Android device support */
258 #include <cpu-features.h>
261 pixman_arm_read_auxv_or_cpu_features ()
263 AndroidCpuFamily cpu_family;
264 uint64_t cpu_features;
266 cpu_family = android_getCpuFamily();
267 cpu_features = android_getCpuFeatures();
269 if (cpu_family == ANDROID_CPU_FAMILY_ARM)
271 if (cpu_features & ANDROID_CPU_ARM_FEATURE_ARMv7)
274 if (cpu_features & ANDROID_CPU_ARM_FEATURE_VFPv3)
277 if (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON)
281 arm_tests_initialized = TRUE;
284 #elif defined (__linux__) /* linux ELF */
288 #include <sys/types.h>
289 #include <sys/stat.h>
290 #include <sys/mman.h>
296 pixman_arm_read_auxv_or_cpu_features ()
301 fd = open ("/proc/self/auxv", O_RDONLY);
304 while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
306 if (aux.a_type == AT_HWCAP)
308 uint32_t hwcap = aux.a_un.a_val;
309 /* hardcode these values to avoid depending on specific
310 * versions of the hwcap header, e.g. HWCAP_NEON
312 arm_has_vfp = (hwcap & 64) != 0;
313 arm_has_iwmmxt = (hwcap & 512) != 0;
314 /* this flag is only present on kernel 2.6.29 */
315 arm_has_neon = (hwcap & 4096) != 0;
317 else if (aux.a_type == AT_PLATFORM)
319 const char *plat = (const char*) aux.a_un.a_val;
320 if (strncmp (plat, "v7l", 3) == 0)
325 else if (strncmp (plat, "v6l", 3) == 0)
334 arm_tests_initialized = TRUE;
337 #endif /* Linux elf */
339 #if defined(USE_ARM_SIMD)
341 pixman_have_arm_simd (void)
343 if (!arm_tests_initialized)
344 pixman_arm_read_auxv_or_cpu_features ();
349 #endif /* USE_ARM_SIMD */
351 #if defined(USE_ARM_NEON)
353 pixman_have_arm_neon (void)
355 if (!arm_tests_initialized)
356 pixman_arm_read_auxv_or_cpu_features ();
361 #endif /* USE_ARM_NEON */
363 #if defined(USE_ARM_IWMMXT)
365 pixman_have_arm_iwmmxt (void)
367 if (!arm_tests_initialized)
368 pixman_arm_read_auxv_or_cpu_features ();
370 return arm_has_iwmmxt;
373 #endif /* USE_ARM_IWMMXT */
375 #else /* !_MSC_VER && !Linux elf && !Android */
377 #define pixman_have_arm_simd() FALSE
378 #define pixman_have_arm_neon() FALSE
379 #define pixman_have_arm_iwmmxt() FALSE
383 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
385 #if defined(USE_X86_MMX) || defined(USE_SSE2)
386 /* The CPU detection code needs to be in a file not compiled with
387 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
388 * that would lead to SIGILL instructions on old CPUs that don't have
391 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
394 #include <sys/auxv.h>
401 MMX_EXTENSIONS = 0x2,
409 detect_cpu_features (void)
411 unsigned int features = 0;
412 unsigned int result = 0;
415 if (getisax (&result, 1))
417 if (result & AV_386_CMOV)
419 if (result & AV_386_MMX)
421 if (result & AV_386_AMD_MMX)
422 features |= MMX_EXTENSIONS;
423 if (result & AV_386_SSE)
425 if (result & AV_386_SSE2)
431 int vendor0 = 0, vendor1, vendor2;
437 /* see p. 118 of amd64 instruction set manual Vol3 */
438 /* We need to be careful about the handling of %ebx and
439 * %esp here. We can't declare either one as clobbered
440 * since they are special registers (%ebx is the "PIC
441 * register" holding an offset to global data, %esp the
442 * stack pointer), so we need to make sure they have their
443 * original values when we access the output operands.
449 "xor $0x00200000, %%eax\n"
458 "mov $0x00000000, %%eax\n"
466 "mov $0x00000001, %%eax\n"
477 : "%eax", "%ecx", "%edx"
480 #elif defined (_MSC_VER)
510 memmove (vendor + 0, &vendor0, 4);
511 memmove (vendor + 4, &vendor1, 4);
512 memmove (vendor + 8, &vendor2, 4);
515 # error unsupported compiler
521 /* result now contains the standard feature bits */
522 if (result & (1 << 15))
524 if (result & (1 << 23))
526 if (result & (1 << 25))
528 if (result & (1 << 26))
530 if ((features & MMX) && !(features & SSE) &&
531 (strcmp (vendor, "AuthenticAMD") == 0 ||
532 strcmp (vendor, "Geode by NSC") == 0))
534 /* check for AMD MMX extensions */
538 " mov $0x80000000, %%eax\n"
540 " xor %%edx, %%edx\n"
543 " mov $0x80000001, %%eax\n"
550 : "%eax", "%ecx", "%edx"
552 #elif defined _MSC_VER
567 if (result & (1 << 22))
568 features |= MMX_EXTENSIONS;
571 #endif /* HAVE_GETISAX */
577 pixman_have_mmx (void)
579 static pixman_bool_t initialized = FALSE;
580 static pixman_bool_t mmx_present;
584 unsigned int features = detect_cpu_features ();
585 mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
594 pixman_have_sse2 (void)
596 static pixman_bool_t initialized = FALSE;
597 static pixman_bool_t sse2_present;
601 unsigned int features = detect_cpu_features ();
602 sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
611 #else /* __amd64__ */
613 #define pixman_have_mmx() TRUE
616 #define pixman_have_sse2() TRUE
618 #endif /* __amd64__ */
621 pixman_implementation_t *
622 _pixman_choose_implementation (void)
624 pixman_implementation_t *imp;
626 imp = _pixman_implementation_create_general();
627 imp = _pixman_implementation_create_fast_path (imp);
630 if (pixman_have_mmx ())
631 imp = _pixman_implementation_create_mmx (imp);
635 if (pixman_have_sse2 ())
636 imp = _pixman_implementation_create_sse2 (imp);
640 if (pixman_have_arm_simd ())
641 imp = _pixman_implementation_create_arm_simd (imp);
644 #ifdef USE_ARM_IWMMXT
645 if (pixman_have_arm_iwmmxt ())
646 imp = _pixman_implementation_create_mmx (imp);
650 if (pixman_have_arm_neon ())
651 imp = _pixman_implementation_create_arm_neon (imp);
655 if (pixman_have_vmx ())
656 imp = _pixman_implementation_create_vmx (imp);
659 imp = _pixman_implementation_create_noop (imp);