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.
29 #if defined(USE_ARM_SIMD) && defined(_MSC_VER)
30 /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
34 #if defined(__APPLE__)
35 #include "TargetConditionals.h"
38 #include "pixman-private.h"
42 /* The CPU detection code needs to be in a file not compiled with
43 * "-maltivec -mabi=altivec", as gcc would try to save vector register
44 * across function calls causing SIGILL on cpus without Altivec/vmx.
46 static pixman_bool_t initialized = FALSE;
47 static volatile pixman_bool_t have_vmx = TRUE;
50 #include <sys/sysctl.h>
53 pixman_have_vmx (void)
57 size_t length = sizeof(have_vmx);
59 sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
69 #elif defined (__OpenBSD__)
70 #include <sys/param.h>
71 #include <sys/sysctl.h>
72 #include <machine/cpu.h>
75 pixman_have_vmx (void)
79 int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
80 size_t length = sizeof(have_vmx);
82 sysctl (mib, 2, &have_vmx, &length, NULL, 0);
92 #elif defined (__linux__)
93 #include <sys/types.h>
98 #include <linux/auxvec.h>
99 #include <asm/cputable.h>
102 pixman_have_vmx (void)
107 unsigned long buf[64];
113 snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
115 fd = open (fname, O_RDONLY);
118 for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
120 /* Read more if buf is empty... */
121 if (i == (count / sizeof(unsigned long)))
123 count = read (fd, buf, sizeof(buf));
129 if (buf[i] == AT_HWCAP)
131 have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
135 else if (buf[i] == AT_NULL)
145 /* Something went wrong. Assume 'no' rather than playing
146 fragile tricks with catching SIGILL. */
154 #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
158 static jmp_buf jump_env;
165 longjmp (jump_env, 1);
169 pixman_have_vmx (void)
171 struct sigaction sa, osa;
176 sa.sa_flags = SA_SIGINFO;
177 sigemptyset (&sa.sa_mask);
178 sa.sa_sigaction = vmx_test;
179 sigaction (SIGILL, &sa, &osa);
180 jmp_result = setjmp (jump_env);
183 asm volatile ( "vor 0, 0, 0" );
185 sigaction (SIGILL, &osa, NULL);
186 have_vmx = (jmp_result == 0);
192 #endif /* __APPLE__ */
195 #if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT)
197 #if defined(_MSC_VER)
199 #if defined(USE_ARM_SIMD)
200 extern int pixman_msvc_try_arm_simd_op ();
203 pixman_have_arm_simd (void)
205 static pixman_bool_t initialized = FALSE;
206 static pixman_bool_t have_arm_simd = FALSE;
211 pixman_msvc_try_arm_simd_op ();
212 have_arm_simd = TRUE;
213 } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
214 have_arm_simd = FALSE;
219 return have_arm_simd;
222 #endif /* USE_ARM_SIMD */
224 #if defined(USE_ARM_NEON)
225 extern int pixman_msvc_try_arm_neon_op ();
228 pixman_have_arm_neon (void)
230 static pixman_bool_t initialized = FALSE;
231 static pixman_bool_t have_arm_neon = FALSE;
237 pixman_msvc_try_arm_neon_op ();
238 have_arm_neon = TRUE;
240 __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
242 have_arm_neon = FALSE;
247 return have_arm_neon;
250 #endif /* USE_ARM_NEON */
252 #elif (defined (__APPLE__) && defined(TARGET_OS_IPHONE)) /* iOS (iPhone/iPad/iPod touch) */
254 /* Detection of ARM NEON on iOS is fairly simple because iOS binaries
255 * contain separate executable images for each processor architecture.
256 * So all we have to do is detect the armv7 architecture build. The
257 * operating system automatically runs the armv7 binary for armv7 devices
258 * and the armv6 binary for armv6 devices.
262 pixman_have_arm_simd (void)
264 #if defined(USE_ARM_SIMD)
272 pixman_have_arm_neon (void)
274 #if defined(USE_ARM_NEON) && defined(__ARM_NEON__)
275 /* This is an armv7 cpu build */
278 /* This is an armv6 cpu build */
284 pixman_have_arm_iwmmxt (void)
286 #if defined(USE_ARM_IWMMXT)
293 #elif defined (__linux__) || defined(__ANDROID__) || defined(ANDROID) /* linux ELF or ANDROID */
295 static pixman_bool_t arm_has_v7 = FALSE;
296 static pixman_bool_t arm_has_v6 = FALSE;
297 static pixman_bool_t arm_has_vfp = FALSE;
298 static pixman_bool_t arm_has_neon = FALSE;
299 static pixman_bool_t arm_has_iwmmxt = FALSE;
300 static pixman_bool_t arm_tests_initialized = FALSE;
302 #if defined(__ANDROID__) || defined(ANDROID) /* Android device support */
304 #include <cpu-features.h>
307 pixman_arm_read_auxv_or_cpu_features ()
309 AndroidCpuFamily cpu_family;
310 uint64_t cpu_features;
312 cpu_family = android_getCpuFamily();
313 cpu_features = android_getCpuFeatures();
315 if (cpu_family == ANDROID_CPU_FAMILY_ARM)
317 if (cpu_features & ANDROID_CPU_ARM_FEATURE_ARMv7)
320 if (cpu_features & ANDROID_CPU_ARM_FEATURE_VFPv3)
323 if (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON)
327 arm_tests_initialized = TRUE;
330 #elif defined (__linux__) /* linux ELF */
333 #include <sys/types.h>
334 #include <sys/stat.h>
335 #include <sys/mman.h>
341 pixman_arm_read_auxv_or_cpu_features ()
346 fd = open ("/proc/self/auxv", O_RDONLY);
349 while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
351 if (aux.a_type == AT_HWCAP)
353 uint32_t hwcap = aux.a_un.a_val;
354 /* hardcode these values to avoid depending on specific
355 * versions of the hwcap header, e.g. HWCAP_NEON
357 arm_has_vfp = (hwcap & 64) != 0;
358 arm_has_iwmmxt = (hwcap & 512) != 0;
359 /* this flag is only present on kernel 2.6.29 */
360 arm_has_neon = (hwcap & 4096) != 0;
362 else if (aux.a_type == AT_PLATFORM)
364 const char *plat = (const char*) aux.a_un.a_val;
365 if (strncmp (plat, "v7l", 3) == 0)
370 else if (strncmp (plat, "v6l", 3) == 0)
379 arm_tests_initialized = TRUE;
382 #endif /* Linux elf */
384 #if defined(USE_ARM_SIMD)
386 pixman_have_arm_simd (void)
388 if (!arm_tests_initialized)
389 pixman_arm_read_auxv_or_cpu_features ();
394 #endif /* USE_ARM_SIMD */
396 #if defined(USE_ARM_NEON)
398 pixman_have_arm_neon (void)
400 if (!arm_tests_initialized)
401 pixman_arm_read_auxv_or_cpu_features ();
406 #endif /* USE_ARM_NEON */
408 #if defined(USE_ARM_IWMMXT)
410 pixman_have_arm_iwmmxt (void)
412 if (!arm_tests_initialized)
413 pixman_arm_read_auxv_or_cpu_features ();
415 return arm_has_iwmmxt;
418 #endif /* USE_ARM_IWMMXT */
420 #else /* !_MSC_VER && !Linux elf && !Android */
422 #define pixman_have_arm_simd() FALSE
423 #define pixman_have_arm_neon() FALSE
424 #define pixman_have_arm_iwmmxt() FALSE
428 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
430 #if defined(USE_MIPS_DSPR2) || defined(USE_LOONGSON_MMI)
432 #if defined (__linux__) /* linux ELF */
435 pixman_have_mips_feature (const char *search_string)
437 const char *file_name = "/proc/cpuinfo";
438 /* Simple detection of MIPS features at runtime for Linux.
439 * It is based on /proc/cpuinfo, which reveals hardware configuration
440 * to user-space applications. According to MIPS (early 2010), no similar
441 * facility is universally available on the MIPS architectures, so it's up
442 * to individual OSes to provide such.
445 char cpuinfo_line[256];
449 if ((f = fopen (file_name, "r")) == NULL)
452 while (fgets (cpuinfo_line, sizeof (cpuinfo_line), f) != NULL)
454 if (strstr (cpuinfo_line, search_string) != NULL)
463 /* Did not find string in the proc file. */
467 #if defined(USE_MIPS_DSPR2)
469 pixman_have_mips_dspr2 (void)
471 /* Only currently available MIPS core that supports DSPr2 is 74K. */
472 return pixman_have_mips_feature ("MIPS 74K");
476 #if defined(USE_LOONGSON_MMI)
478 pixman_have_loongson_mmi (void)
480 /* I really don't know if some Loongson CPUs don't have MMI. */
481 return pixman_have_mips_feature ("Loongson");
485 #else /* linux ELF */
487 #define pixman_have_mips_dspr2() FALSE
488 #define pixman_have_loongson_mmi() FALSE
490 #endif /* linux ELF */
492 #endif /* USE_MIPS_DSPR2 || USE_LOONGSON_MMI */
494 #if defined(USE_X86_MMX) || defined(USE_SSE2)
495 /* The CPU detection code needs to be in a file not compiled with
496 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
497 * that would lead to SIGILL instructions on old CPUs that don't have
500 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
503 #include <sys/auxv.h>
510 MMX_EXTENSIONS = 0x2,
518 detect_cpu_features (void)
520 unsigned int features = 0;
521 unsigned int result = 0;
524 if (getisax (&result, 1))
526 if (result & AV_386_CMOV)
528 if (result & AV_386_MMX)
530 if (result & AV_386_AMD_MMX)
531 features |= MMX_EXTENSIONS;
532 if (result & AV_386_SSE)
534 if (result & AV_386_SSE2)
540 int vendor0 = 0, vendor1, vendor2;
546 /* see p. 118 of amd64 instruction set manual Vol3 */
547 /* We need to be careful about the handling of %ebx and
548 * %esp here. We can't declare either one as clobbered
549 * since they are special registers (%ebx is the "PIC
550 * register" holding an offset to global data, %esp the
551 * stack pointer), so we need to make sure they have their
552 * original values when we access the output operands.
558 "xor $0x00200000, %%eax\n"
567 "mov $0x00000000, %%eax\n"
575 "mov $0x00000001, %%eax\n"
586 : "%eax", "%ecx", "%edx"
589 #elif defined (_MSC_VER)
619 memmove (vendor + 0, &vendor0, 4);
620 memmove (vendor + 4, &vendor1, 4);
621 memmove (vendor + 8, &vendor2, 4);
624 # error unsupported compiler
630 /* result now contains the standard feature bits */
631 if (result & (1 << 15))
633 if (result & (1 << 23))
635 if (result & (1 << 25))
637 if (result & (1 << 26))
639 if ((features & MMX) && !(features & SSE) &&
640 (strcmp (vendor, "AuthenticAMD") == 0 ||
641 strcmp (vendor, "Geode by NSC") == 0))
643 /* check for AMD MMX extensions */
647 " mov $0x80000000, %%eax\n"
649 " xor %%edx, %%edx\n"
652 " mov $0x80000001, %%eax\n"
659 : "%eax", "%ecx", "%edx"
661 #elif defined _MSC_VER
676 if (result & (1 << 22))
677 features |= MMX_EXTENSIONS;
680 #endif /* HAVE_GETISAX */
687 pixman_have_mmx (void)
689 static pixman_bool_t initialized = FALSE;
690 static pixman_bool_t mmx_present;
694 unsigned int features = detect_cpu_features ();
695 mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
705 pixman_have_sse2 (void)
707 static pixman_bool_t initialized = FALSE;
708 static pixman_bool_t sse2_present;
712 unsigned int features = detect_cpu_features ();
713 sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
722 #else /* __amd64__ */
724 #define pixman_have_mmx() TRUE
727 #define pixman_have_sse2() TRUE
729 #endif /* __amd64__ */
733 disabled (const char *name)
737 if ((env = getenv ("PIXMAN_DISABLE")))
744 if ((end = strchr (env, ' ')))
749 if (strlen (name) == len && strncmp (name, env, len) == 0)
751 printf ("pixman: Disabled %s implementation\n", name);
763 pixman_implementation_t *
764 _pixman_choose_implementation (void)
766 pixman_implementation_t *imp;
768 imp = _pixman_implementation_create_general();
770 if (!disabled ("fast"))
771 imp = _pixman_implementation_create_fast_path (imp);
774 if (!disabled ("mmx") && pixman_have_mmx ())
775 imp = _pixman_implementation_create_mmx (imp);
779 if (!disabled ("sse2") && pixman_have_sse2 ())
780 imp = _pixman_implementation_create_sse2 (imp);
784 if (!disabled ("arm-simd") && pixman_have_arm_simd ())
785 imp = _pixman_implementation_create_arm_simd (imp);
788 #ifdef USE_ARM_IWMMXT
789 if (!disabled ("arm-iwmmxt") && pixman_have_arm_iwmmxt ())
790 imp = _pixman_implementation_create_mmx (imp);
792 #ifdef USE_LOONGSON_MMI
793 if (!disabled ("loongson-mmi") && pixman_have_loongson_mmi ())
794 imp = _pixman_implementation_create_mmx (imp);
797 if (!disabled ("arm-neon") && pixman_have_arm_neon ())
798 imp = _pixman_implementation_create_arm_neon (imp);
801 #ifdef USE_MIPS_DSPR2
802 if (!disabled ("mips-dspr2") && pixman_have_mips_dspr2 ())
803 imp = _pixman_implementation_create_mips_dspr2 (imp);
807 if (!disabled ("vmx") && pixman_have_vmx ())
808 imp = _pixman_implementation_create_vmx (imp);
811 imp = _pixman_implementation_create_noop (imp);