Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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
26 #include <string.h>
27 #include <stdlib.h>
29 #if defined(USE_ARM_SIMD) && defined(_MSC_VER)
30 /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
31 #include <windows.h>
32 #endif
34 #if defined(__APPLE__)
35 #include "TargetConditionals.h"
36 #endif
38 #include "pixman-private.h"
40 #ifdef USE_VMX
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.
45 */
46 static pixman_bool_t initialized = FALSE;
47 static volatile pixman_bool_t have_vmx = TRUE;
49 #ifdef __APPLE__
50 #include <sys/sysctl.h>
52 static pixman_bool_t
53 pixman_have_vmx (void)
54 {
55 if (!initialized)
56 {
57 size_t length = sizeof(have_vmx);
58 int error =
59 sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
61 if (error)
62 have_vmx = FALSE;
64 initialized = TRUE;
65 }
66 return have_vmx;
67 }
69 #elif defined (__OpenBSD__)
70 #include <sys/param.h>
71 #include <sys/sysctl.h>
72 #include <machine/cpu.h>
74 static pixman_bool_t
75 pixman_have_vmx (void)
76 {
77 if (!initialized)
78 {
79 int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
80 size_t length = sizeof(have_vmx);
81 int error =
82 sysctl (mib, 2, &have_vmx, &length, NULL, 0);
84 if (error != 0)
85 have_vmx = FALSE;
87 initialized = TRUE;
88 }
89 return have_vmx;
90 }
92 #elif defined (__linux__)
93 #include <sys/types.h>
94 #include <sys/stat.h>
95 #include <fcntl.h>
96 #include <unistd.h>
97 #include <stdio.h>
98 #include <linux/auxvec.h>
99 #include <asm/cputable.h>
101 static pixman_bool_t
102 pixman_have_vmx (void)
103 {
104 if (!initialized)
105 {
106 char fname[64];
107 unsigned long buf[64];
108 ssize_t count = 0;
109 pid_t pid;
110 int fd, i;
112 pid = getpid ();
113 snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
115 fd = open (fname, O_RDONLY);
116 if (fd >= 0)
117 {
118 for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
119 {
120 /* Read more if buf is empty... */
121 if (i == (count / sizeof(unsigned long)))
122 {
123 count = read (fd, buf, sizeof(buf));
124 if (count <= 0)
125 break;
126 i = 0;
127 }
129 if (buf[i] == AT_HWCAP)
130 {
131 have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
132 initialized = TRUE;
133 break;
134 }
135 else if (buf[i] == AT_NULL)
136 {
137 break;
138 }
139 }
140 close (fd);
141 }
142 }
143 if (!initialized)
144 {
145 /* Something went wrong. Assume 'no' rather than playing
146 fragile tricks with catching SIGILL. */
147 have_vmx = FALSE;
148 initialized = TRUE;
149 }
151 return have_vmx;
152 }
154 #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
155 #include <signal.h>
156 #include <setjmp.h>
158 static jmp_buf jump_env;
160 static void
161 vmx_test (int sig,
162 siginfo_t *si,
163 void * unused)
164 {
165 longjmp (jump_env, 1);
166 }
168 static pixman_bool_t
169 pixman_have_vmx (void)
170 {
171 struct sigaction sa, osa;
172 int jmp_result;
174 if (!initialized)
175 {
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);
181 if (jmp_result == 0)
182 {
183 asm volatile ( "vor 0, 0, 0" );
184 }
185 sigaction (SIGILL, &osa, NULL);
186 have_vmx = (jmp_result == 0);
187 initialized = TRUE;
188 }
189 return have_vmx;
190 }
192 #endif /* __APPLE__ */
193 #endif /* USE_VMX */
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 ();
202 pixman_bool_t
203 pixman_have_arm_simd (void)
204 {
205 static pixman_bool_t initialized = FALSE;
206 static pixman_bool_t have_arm_simd = FALSE;
208 if (!initialized)
209 {
210 __try {
211 pixman_msvc_try_arm_simd_op ();
212 have_arm_simd = TRUE;
213 } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
214 have_arm_simd = FALSE;
215 }
216 initialized = TRUE;
217 }
219 return have_arm_simd;
220 }
222 #endif /* USE_ARM_SIMD */
224 #if defined(USE_ARM_NEON)
225 extern int pixman_msvc_try_arm_neon_op ();
227 pixman_bool_t
228 pixman_have_arm_neon (void)
229 {
230 static pixman_bool_t initialized = FALSE;
231 static pixman_bool_t have_arm_neon = FALSE;
233 if (!initialized)
234 {
235 __try
236 {
237 pixman_msvc_try_arm_neon_op ();
238 have_arm_neon = TRUE;
239 }
240 __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
241 {
242 have_arm_neon = FALSE;
243 }
244 initialized = TRUE;
245 }
247 return have_arm_neon;
248 }
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.
259 */
261 pixman_bool_t
262 pixman_have_arm_simd (void)
263 {
264 #if defined(USE_ARM_SIMD)
265 return TRUE;
266 #else
267 return FALSE;
268 #endif
269 }
271 pixman_bool_t
272 pixman_have_arm_neon (void)
273 {
274 #if defined(USE_ARM_NEON) && defined(__ARM_NEON__)
275 /* This is an armv7 cpu build */
276 return TRUE;
277 #else
278 /* This is an armv6 cpu build */
279 return FALSE;
280 #endif
281 }
283 pixman_bool_t
284 pixman_have_arm_iwmmxt (void)
285 {
286 #if defined(USE_ARM_IWMMXT)
287 return FALSE;
288 #else
289 return FALSE;
290 #endif
291 }
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 static void
305 pixman_arm_read_auxv_or_cpu_features ()
306 {
307 char buf[1024];
308 char* pos;
309 const char* ver_token = "CPU architecture: ";
310 FILE* f = fopen("/proc/cpuinfo", "r");
311 if (!f) {
312 arm_tests_initialized = TRUE;
313 return;
314 }
316 fread(buf, sizeof(char), sizeof(buf), f);
317 fclose(f);
318 pos = strstr(buf, ver_token);
319 if (pos) {
320 char vchar = *(pos + strlen(ver_token));
321 if (vchar >= '0' && vchar <= '9') {
322 int ver = vchar - '0';
323 arm_has_v7 = ver >= 7;
324 arm_has_v6 = ver >= 6;
325 }
326 }
327 arm_has_neon = strstr(buf, "neon") != NULL;
328 arm_has_vfp = strstr(buf, "vfp") != NULL;
329 arm_has_iwmmxt = strstr(buf, "iwmmxt") != NULL;
330 arm_tests_initialized = TRUE;
331 }
333 #elif defined (__linux__) /* linux ELF */
335 #include <unistd.h>
336 #include <sys/types.h>
337 #include <sys/stat.h>
338 #include <sys/mman.h>
339 #include <fcntl.h>
340 #include <string.h>
341 #include <elf.h>
343 static void
344 pixman_arm_read_auxv_or_cpu_features ()
345 {
346 int fd;
347 Elf32_auxv_t aux;
349 fd = open ("/proc/self/auxv", O_RDONLY);
350 if (fd >= 0)
351 {
352 while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
353 {
354 if (aux.a_type == AT_HWCAP)
355 {
356 uint32_t hwcap = aux.a_un.a_val;
357 /* hardcode these values to avoid depending on specific
358 * versions of the hwcap header, e.g. HWCAP_NEON
359 */
360 arm_has_vfp = (hwcap & 64) != 0;
361 arm_has_iwmmxt = (hwcap & 512) != 0;
362 /* this flag is only present on kernel 2.6.29 */
363 arm_has_neon = (hwcap & 4096) != 0;
364 }
365 else if (aux.a_type == AT_PLATFORM)
366 {
367 const char *plat = (const char*) aux.a_un.a_val;
368 if (strncmp (plat, "v7l", 3) == 0)
369 {
370 arm_has_v7 = TRUE;
371 arm_has_v6 = TRUE;
372 }
373 else if (strncmp (plat, "v6l", 3) == 0)
374 {
375 arm_has_v6 = TRUE;
376 }
377 }
378 }
379 close (fd);
380 }
382 arm_tests_initialized = TRUE;
383 }
385 #endif /* Linux elf */
387 #if defined(USE_ARM_SIMD)
388 pixman_bool_t
389 pixman_have_arm_simd (void)
390 {
391 if (!arm_tests_initialized)
392 pixman_arm_read_auxv_or_cpu_features ();
394 return arm_has_v6;
395 }
397 #endif /* USE_ARM_SIMD */
399 #if defined(USE_ARM_NEON)
400 pixman_bool_t
401 pixman_have_arm_neon (void)
402 {
403 if (!arm_tests_initialized)
404 pixman_arm_read_auxv_or_cpu_features ();
406 return arm_has_neon;
407 }
409 #endif /* USE_ARM_NEON */
411 #if defined(USE_ARM_IWMMXT)
412 pixman_bool_t
413 pixman_have_arm_iwmmxt (void)
414 {
415 if (!arm_tests_initialized)
416 pixman_arm_read_auxv_or_cpu_features ();
418 return arm_has_iwmmxt;
419 }
421 #endif /* USE_ARM_IWMMXT */
423 #else /* !_MSC_VER && !Linux elf && !Android */
425 #define pixman_have_arm_simd() FALSE
426 #define pixman_have_arm_neon() FALSE
427 #define pixman_have_arm_iwmmxt() FALSE
429 #endif
431 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
433 #if defined(USE_MIPS_DSPR2)
435 #if defined (__linux__) /* linux ELF */
437 pixman_bool_t
438 pixman_have_mips_dspr2 (void)
439 {
440 const char *search_string = "MIPS 74K";
441 const char *file_name = "/proc/cpuinfo";
442 /* Simple detection of MIPS DSP ASE (revision 2) at runtime for Linux.
443 * It is based on /proc/cpuinfo, which reveals hardware configuration
444 * to user-space applications. According to MIPS (early 2010), no similar
445 * facility is universally available on the MIPS architectures, so it's up
446 * to individual OSes to provide such.
447 *
448 * Only currently available MIPS core that supports DSPr2 is 74K.
449 */
451 char cpuinfo_line[256];
453 FILE *f = NULL;
455 if ((f = fopen (file_name, "r")) == NULL)
456 return FALSE;
458 while (fgets (cpuinfo_line, sizeof (cpuinfo_line), f) != NULL)
459 {
460 if (strstr (cpuinfo_line, search_string) != NULL)
461 {
462 fclose (f);
463 return TRUE;
464 }
465 }
467 fclose (f);
469 /* Did not find string in the proc file. */
470 return FALSE;
471 }
473 #else /* linux ELF */
475 #define pixman_have_mips_dspr2() FALSE
477 #endif /* linux ELF */
479 #endif /* USE_MIPS_DSPR2 */
481 #if defined(USE_X86_MMX) || defined(USE_SSE2)
482 /* The CPU detection code needs to be in a file not compiled with
483 * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
484 * that would lead to SIGILL instructions on old CPUs that don't have
485 * it.
486 */
487 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
489 #ifdef HAVE_GETISAX
490 #include <sys/auxv.h>
491 #endif
493 typedef enum
494 {
495 NO_FEATURES = 0,
496 MMX = 0x1,
497 MMX_EXTENSIONS = 0x2,
498 SSE = 0x6,
499 SSE2 = 0x8,
500 CMOV = 0x10
501 } cpu_features_t;
504 static unsigned int
505 detect_cpu_features (void)
506 {
507 unsigned int features = 0;
508 unsigned int result = 0;
510 #ifdef HAVE_GETISAX
511 if (getisax (&result, 1))
512 {
513 if (result & AV_386_CMOV)
514 features |= CMOV;
515 if (result & AV_386_MMX)
516 features |= MMX;
517 if (result & AV_386_AMD_MMX)
518 features |= MMX_EXTENSIONS;
519 if (result & AV_386_SSE)
520 features |= SSE;
521 if (result & AV_386_SSE2)
522 features |= SSE2;
523 }
524 #else
525 char vendor[13];
526 #ifdef _MSC_VER
527 int vendor0 = 0, vendor1, vendor2;
528 #endif
529 vendor[0] = 0;
530 vendor[12] = 0;
532 #ifdef __GNUC__
533 /* see p. 118 of amd64 instruction set manual Vol3 */
534 /* We need to be careful about the handling of %ebx and
535 * %esp here. We can't declare either one as clobbered
536 * since they are special registers (%ebx is the "PIC
537 * register" holding an offset to global data, %esp the
538 * stack pointer), so we need to make sure they have their
539 * original values when we access the output operands.
540 */
541 __asm__ (
542 "pushf\n"
543 "pop %%eax\n"
544 "mov %%eax, %%ecx\n"
545 "xor $0x00200000, %%eax\n"
546 "push %%eax\n"
547 "popf\n"
548 "pushf\n"
549 "pop %%eax\n"
550 "mov $0x0, %%edx\n"
551 "xor %%ecx, %%eax\n"
552 "jz 1f\n"
554 "mov $0x00000000, %%eax\n"
555 "push %%ebx\n"
556 "cpuid\n"
557 "mov %%ebx, %%eax\n"
558 "pop %%ebx\n"
559 "mov %%eax, %1\n"
560 "mov %%edx, %2\n"
561 "mov %%ecx, %3\n"
562 "mov $0x00000001, %%eax\n"
563 "push %%ebx\n"
564 "cpuid\n"
565 "pop %%ebx\n"
566 "1:\n"
567 "mov %%edx, %0\n"
568 : "=r" (result),
569 "=m" (vendor[0]),
570 "=m" (vendor[4]),
571 "=m" (vendor[8])
572 :
573 : "%eax", "%ecx", "%edx"
574 );
576 #elif defined (_MSC_VER)
578 _asm {
579 pushfd
580 pop eax
581 mov ecx, eax
582 xor eax, 00200000h
583 push eax
584 popfd
585 pushfd
586 pop eax
587 mov edx, 0
588 xor eax, ecx
589 jz nocpuid
591 mov eax, 0
592 push ebx
593 cpuid
594 mov eax, ebx
595 pop ebx
596 mov vendor0, eax
597 mov vendor1, edx
598 mov vendor2, ecx
599 mov eax, 1
600 push ebx
601 cpuid
602 pop ebx
603 nocpuid:
604 mov result, edx
605 }
606 memmove (vendor + 0, &vendor0, 4);
607 memmove (vendor + 4, &vendor1, 4);
608 memmove (vendor + 8, &vendor2, 4);
610 #else
611 # error unsupported compiler
612 #endif
614 features = 0;
615 if (result)
616 {
617 /* result now contains the standard feature bits */
618 if (result & (1 << 15))
619 features |= CMOV;
620 if (result & (1 << 23))
621 features |= MMX;
622 if (result & (1 << 25))
623 features |= SSE;
624 if (result & (1 << 26))
625 features |= SSE2;
626 if ((features & MMX) && !(features & SSE) &&
627 (strcmp (vendor, "AuthenticAMD") == 0 ||
628 strcmp (vendor, "Geode by NSC") == 0))
629 {
630 /* check for AMD MMX extensions */
631 #ifdef __GNUC__
632 __asm__ (
633 " push %%ebx\n"
634 " mov $0x80000000, %%eax\n"
635 " cpuid\n"
636 " xor %%edx, %%edx\n"
637 " cmp $0x1, %%eax\n"
638 " jge 2f\n"
639 " mov $0x80000001, %%eax\n"
640 " cpuid\n"
641 "2:\n"
642 " pop %%ebx\n"
643 " mov %%edx, %0\n"
644 : "=r" (result)
645 :
646 : "%eax", "%ecx", "%edx"
647 );
648 #elif defined _MSC_VER
649 _asm {
650 push ebx
651 mov eax, 80000000h
652 cpuid
653 xor edx, edx
654 cmp eax, 1
655 jge notamd
656 mov eax, 80000001h
657 cpuid
658 notamd:
659 pop ebx
660 mov result, edx
661 }
662 #endif
663 if (result & (1 << 22))
664 features |= MMX_EXTENSIONS;
665 }
666 }
667 #endif /* HAVE_GETISAX */
669 return features;
670 }
672 #ifdef USE_X86_MMX
673 static pixman_bool_t
674 pixman_have_mmx (void)
675 {
676 static pixman_bool_t initialized = FALSE;
677 static pixman_bool_t mmx_present;
679 if (!initialized)
680 {
681 unsigned int features = detect_cpu_features ();
682 mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
683 initialized = TRUE;
684 }
686 return mmx_present;
687 }
688 #endif
690 #ifdef USE_SSE2
691 static pixman_bool_t
692 pixman_have_sse2 (void)
693 {
694 static pixman_bool_t initialized = FALSE;
695 static pixman_bool_t sse2_present;
697 if (!initialized)
698 {
699 unsigned int features = detect_cpu_features ();
700 sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
701 initialized = TRUE;
702 }
704 return sse2_present;
705 }
707 #endif
709 #else /* __amd64__ */
710 #ifdef USE_X86_MMX
711 #define pixman_have_mmx() TRUE
712 #endif
713 #ifdef USE_SSE2
714 #define pixman_have_sse2() TRUE
715 #endif
716 #endif /* __amd64__ */
717 #endif
719 static pixman_bool_t
720 disabled (const char *name)
721 {
722 const char *env;
724 if ((env = getenv ("PIXMAN_DISABLE")))
725 {
726 do
727 {
728 const char *end;
729 int len;
731 if ((end = strchr (env, ' ')))
732 len = end - env;
733 else
734 len = strlen (env);
736 if (strlen (name) == len && strncmp (name, env, len) == 0)
737 {
738 printf ("pixman: Disabled %s implementation\n", name);
739 return TRUE;
740 }
742 env += len;
743 }
744 while (*env++);
745 }
747 return FALSE;
748 }
750 pixman_implementation_t *
751 _pixman_choose_implementation (void)
752 {
753 pixman_implementation_t *imp;
755 imp = _pixman_implementation_create_general();
757 if (!disabled ("fast"))
758 imp = _pixman_implementation_create_fast_path (imp);
760 #ifdef USE_X86_MMX
761 if (!disabled ("mmx") && pixman_have_mmx ())
762 imp = _pixman_implementation_create_mmx (imp);
763 #endif
765 #ifdef USE_SSE2
766 if (!disabled ("sse2") && pixman_have_sse2 ())
767 imp = _pixman_implementation_create_sse2 (imp);
768 #endif
770 #ifdef USE_ARM_SIMD
771 if (!disabled ("arm-simd") && pixman_have_arm_simd ())
772 imp = _pixman_implementation_create_arm_simd (imp);
773 #endif
775 #ifdef USE_ARM_IWMMXT
776 if (!disabled ("arm-iwmmxt") && pixman_have_arm_iwmmxt ())
777 imp = _pixman_implementation_create_mmx (imp);
778 #endif
780 #ifdef USE_ARM_NEON
781 if (!disabled ("arm-neon") && pixman_have_arm_neon ())
782 imp = _pixman_implementation_create_arm_neon (imp);
783 #endif
785 #ifdef USE_MIPS_DSPR2
786 if (!disabled ("mips-dspr2") && pixman_have_mips_dspr2 ())
787 imp = _pixman_implementation_create_mips_dspr2 (imp);
788 #endif
790 #ifdef USE_VMX
791 if (!disabled ("vmx") && pixman_have_vmx ())
792 imp = _pixman_implementation_create_vmx (imp);
793 #endif
795 imp = _pixman_implementation_create_noop (imp);
797 return imp;
798 }