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)
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 #else /* linux ELF */
333 #define pixman_have_arm_simd() FALSE
334 #define pixman_have_arm_neon() FALSE
338 #endif /* USE_ARM_SIMD || USE_ARM_NEON */
340 #if defined(USE_MMX) || defined(USE_SSE2)
341 /* The CPU detection code needs to be in a file not compiled with
342 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
343 * that would lead to SIGILL instructions on old CPUs that don't have
346 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
349 #include <sys/auxv.h>
356 MMX_EXTENSIONS = 0x2,
364 detect_cpu_features (void)
366 unsigned int features = 0;
367 unsigned int result = 0;
370 if (getisax (&result, 1))
372 if (result & AV_386_CMOV)
374 if (result & AV_386_MMX)
376 if (result & AV_386_AMD_MMX)
377 features |= MMX_EXTENSIONS;
378 if (result & AV_386_SSE)
380 if (result & AV_386_SSE2)
386 int vendor0 = 0, vendor1, vendor2;
392 /* see p. 118 of amd64 instruction set manual Vol3 */
393 /* We need to be careful about the handling of %ebx and
394 * %esp here. We can't declare either one as clobbered
395 * since they are special registers (%ebx is the "PIC
396 * register" holding an offset to global data, %esp the
397 * stack pointer), so we need to make sure they have their
398 * original values when we access the output operands.
404 "xor $0x00200000, %%eax\n"
413 "mov $0x00000000, %%eax\n"
421 "mov $0x00000001, %%eax\n"
432 : "%eax", "%ecx", "%edx"
435 #elif defined (_MSC_VER)
465 memmove (vendor + 0, &vendor0, 4);
466 memmove (vendor + 4, &vendor1, 4);
467 memmove (vendor + 8, &vendor2, 4);
470 # error unsupported compiler
476 /* result now contains the standard feature bits */
477 if (result & (1 << 15))
479 if (result & (1 << 23))
481 if (result & (1 << 25))
483 if (result & (1 << 26))
485 if ((features & MMX) && !(features & SSE) &&
486 (strcmp (vendor, "AuthenticAMD") == 0 ||
487 strcmp (vendor, "Geode by NSC") == 0))
489 /* check for AMD MMX extensions */
493 " mov $0x80000000, %%eax\n"
495 " xor %%edx, %%edx\n"
498 " mov $0x80000001, %%eax\n"
505 : "%eax", "%ecx", "%edx"
507 #elif defined _MSC_VER
522 if (result & (1 << 22))
523 features |= MMX_EXTENSIONS;
526 #endif /* HAVE_GETISAX */
532 pixman_have_mmx (void)
534 static pixman_bool_t initialized = FALSE;
535 static pixman_bool_t mmx_present;
539 unsigned int features = detect_cpu_features ();
540 mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
549 pixman_have_sse2 (void)
551 static pixman_bool_t initialized = FALSE;
552 static pixman_bool_t sse2_present;
556 unsigned int features = detect_cpu_features ();
557 sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
566 #else /* __amd64__ */
568 #define pixman_have_mmx() TRUE
571 #define pixman_have_sse2() TRUE
573 #endif /* __amd64__ */
576 pixman_implementation_t *
577 _pixman_choose_implementation (void)
579 pixman_implementation_t *imp;
581 imp = _pixman_implementation_create_general();
582 imp = _pixman_implementation_create_fast_path (imp);
585 if (pixman_have_mmx ())
586 imp = _pixman_implementation_create_mmx (imp);
590 if (pixman_have_sse2 ())
591 imp = _pixman_implementation_create_sse2 (imp);
595 if (pixman_have_arm_simd ())
596 imp = _pixman_implementation_create_arm_simd (imp);
600 if (pixman_have_arm_neon ())
601 imp = _pixman_implementation_create_arm_neon (imp);
605 if (pixman_have_vmx ())
606 imp = _pixman_implementation_create_vmx (imp);
609 imp = _pixman_implementation_create_noop (imp);