Reject trapezoids where top (botttom) is above (below) the edges
[profile/ivi/pixman.git] / pixman / pixman-cpu.c
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
25
26 #include <string.h>
27
28 #if defined(USE_ARM_SIMD) && defined(_MSC_VER)
29 /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
30 #include <windows.h>
31 #endif
32
33 #include "pixman-private.h"
34
35 #ifdef USE_VMX
36
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.
40  */
41 static pixman_bool_t initialized = FALSE;
42 static volatile pixman_bool_t have_vmx = TRUE;
43
44 #ifdef __APPLE__
45 #include <sys/sysctl.h>
46
47 static pixman_bool_t
48 pixman_have_vmx (void)
49 {
50     if (!initialized)
51     {
52         size_t length = sizeof(have_vmx);
53         int error =
54             sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
55
56         if (error)
57             have_vmx = FALSE;
58
59         initialized = TRUE;
60     }
61     return have_vmx;
62 }
63
64 #elif defined (__OpenBSD__)
65 #include <sys/param.h>
66 #include <sys/sysctl.h>
67 #include <machine/cpu.h>
68
69 static pixman_bool_t
70 pixman_have_vmx (void)
71 {
72     if (!initialized)
73     {
74         int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
75         size_t length = sizeof(have_vmx);
76         int error =
77             sysctl (mib, 2, &have_vmx, &length, NULL, 0);
78
79         if (error != 0)
80             have_vmx = FALSE;
81
82         initialized = TRUE;
83     }
84     return have_vmx;
85 }
86
87 #elif defined (__linux__)
88 #include <sys/types.h>
89 #include <sys/stat.h>
90 #include <fcntl.h>
91 #include <unistd.h>
92 #include <stdio.h>
93 #include <linux/auxvec.h>
94 #include <asm/cputable.h>
95
96 static pixman_bool_t
97 pixman_have_vmx (void)
98 {
99     if (!initialized)
100     {
101         char fname[64];
102         unsigned long buf[64];
103         ssize_t count = 0;
104         pid_t pid;
105         int fd, i;
106
107         pid = getpid ();
108         snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
109
110         fd = open (fname, O_RDONLY);
111         if (fd >= 0)
112         {
113             for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
114             {
115                 /* Read more if buf is empty... */
116                 if (i == (count / sizeof(unsigned long)))
117                 {
118                     count = read (fd, buf, sizeof(buf));
119                     if (count <= 0)
120                         break;
121                     i = 0;
122                 }
123
124                 if (buf[i] == AT_HWCAP)
125                 {
126                     have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
127                     initialized = TRUE;
128                     break;
129                 }
130                 else if (buf[i] == AT_NULL)
131                 {
132                     break;
133                 }
134             }
135             close (fd);
136         }
137     }
138     if (!initialized)
139     {
140         /* Something went wrong. Assume 'no' rather than playing
141            fragile tricks with catching SIGILL. */
142         have_vmx = FALSE;
143         initialized = TRUE;
144     }
145
146     return have_vmx;
147 }
148
149 #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
150 #include <signal.h>
151 #include <setjmp.h>
152
153 static jmp_buf jump_env;
154
155 static void
156 vmx_test (int        sig,
157           siginfo_t *si,
158           void *     unused)
159 {
160     longjmp (jump_env, 1);
161 }
162
163 static pixman_bool_t
164 pixman_have_vmx (void)
165 {
166     struct sigaction sa, osa;
167     int jmp_result;
168
169     if (!initialized)
170     {
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);
176         if (jmp_result == 0)
177         {
178             asm volatile ( "vor 0, 0, 0" );
179         }
180         sigaction (SIGILL, &osa, NULL);
181         have_vmx = (jmp_result == 0);
182         initialized = TRUE;
183     }
184     return have_vmx;
185 }
186
187 #endif /* __APPLE__ */
188 #endif /* USE_VMX */
189
190 #if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT)
191
192 #if defined(_MSC_VER)
193
194 #if defined(USE_ARM_SIMD)
195 extern int pixman_msvc_try_arm_simd_op ();
196
197 pixman_bool_t
198 pixman_have_arm_simd (void)
199 {
200     static pixman_bool_t initialized = FALSE;
201     static pixman_bool_t have_arm_simd = FALSE;
202
203     if (!initialized)
204     {
205         __try {
206             pixman_msvc_try_arm_simd_op ();
207             have_arm_simd = TRUE;
208         } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
209             have_arm_simd = FALSE;
210         }
211         initialized = TRUE;
212     }
213
214     return have_arm_simd;
215 }
216
217 #endif /* USE_ARM_SIMD */
218
219 #if defined(USE_ARM_NEON)
220 extern int pixman_msvc_try_arm_neon_op ();
221
222 pixman_bool_t
223 pixman_have_arm_neon (void)
224 {
225     static pixman_bool_t initialized = FALSE;
226     static pixman_bool_t have_arm_neon = FALSE;
227
228     if (!initialized)
229     {
230         __try
231         {
232             pixman_msvc_try_arm_neon_op ();
233             have_arm_neon = TRUE;
234         }
235         __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
236         {
237             have_arm_neon = FALSE;
238         }
239         initialized = TRUE;
240     }
241
242     return have_arm_neon;
243 }
244
245 #endif /* USE_ARM_NEON */
246
247 #elif defined (__linux__) || defined(__ANDROID__) || defined(ANDROID) /* linux ELF or ANDROID */
248
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;
255
256 #if defined(__ANDROID__) || defined(ANDROID) /* Android device support */
257
258 #include <cpu-features.h>
259
260 static void
261 pixman_arm_read_auxv_or_cpu_features ()
262 {
263     AndroidCpuFamily cpu_family;
264     uint64_t cpu_features;
265
266     cpu_family = android_getCpuFamily();
267     cpu_features = android_getCpuFeatures();
268
269     if (cpu_family == ANDROID_CPU_FAMILY_ARM)
270     {
271         if (cpu_features & ANDROID_CPU_ARM_FEATURE_ARMv7)
272             arm_has_v7 = TRUE;
273         
274         if (cpu_features & ANDROID_CPU_ARM_FEATURE_VFPv3)
275             arm_has_vfp = TRUE;
276         
277         if (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON)
278             arm_has_neon = TRUE;
279     }
280
281     arm_tests_initialized = TRUE;
282 }
283
284 #elif defined (__linux__) /* linux ELF */
285
286 #include <stdlib.h>
287 #include <unistd.h>
288 #include <sys/types.h>
289 #include <sys/stat.h>
290 #include <sys/mman.h>
291 #include <fcntl.h>
292 #include <string.h>
293 #include <elf.h>
294
295 static void
296 pixman_arm_read_auxv_or_cpu_features ()
297 {
298     int fd;
299     Elf32_auxv_t aux;
300
301     fd = open ("/proc/self/auxv", O_RDONLY);
302     if (fd >= 0)
303     {
304         while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
305         {
306             if (aux.a_type == AT_HWCAP)
307             {
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
311                  */
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;
316             }
317             else if (aux.a_type == AT_PLATFORM)
318             {
319                 const char *plat = (const char*) aux.a_un.a_val;
320                 if (strncmp (plat, "v7l", 3) == 0)
321                 {
322                     arm_has_v7 = TRUE;
323                     arm_has_v6 = TRUE;
324                 }
325                 else if (strncmp (plat, "v6l", 3) == 0)
326                 {
327                     arm_has_v6 = TRUE;
328                 }
329             }
330         }
331         close (fd);
332     }
333
334     arm_tests_initialized = TRUE;
335 }
336
337 #endif /* Linux elf */
338
339 #if defined(USE_ARM_SIMD)
340 pixman_bool_t
341 pixman_have_arm_simd (void)
342 {
343     if (!arm_tests_initialized)
344         pixman_arm_read_auxv_or_cpu_features ();
345
346     return arm_has_v6;
347 }
348
349 #endif /* USE_ARM_SIMD */
350
351 #if defined(USE_ARM_NEON)
352 pixman_bool_t
353 pixman_have_arm_neon (void)
354 {
355     if (!arm_tests_initialized)
356         pixman_arm_read_auxv_or_cpu_features ();
357
358     return arm_has_neon;
359 }
360
361 #endif /* USE_ARM_NEON */
362
363 #if defined(USE_ARM_IWMMXT)
364 pixman_bool_t
365 pixman_have_arm_iwmmxt (void)
366 {
367     if (!arm_tests_initialized)
368         pixman_arm_read_auxv_or_cpu_features ();
369
370     return arm_has_iwmmxt;
371 }
372
373 #endif /* USE_ARM_IWMMXT */
374
375 #else /* !_MSC_VER && !Linux elf && !Android */
376
377 #define pixman_have_arm_simd() FALSE
378 #define pixman_have_arm_neon() FALSE
379 #define pixman_have_arm_iwmmxt() FALSE
380
381 #endif
382
383 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
384
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
389  * it.
390  */
391 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
392
393 #ifdef HAVE_GETISAX
394 #include <sys/auxv.h>
395 #endif
396
397 typedef enum
398 {
399     NO_FEATURES = 0,
400     MMX = 0x1,
401     MMX_EXTENSIONS = 0x2,
402     SSE = 0x6,
403     SSE2 = 0x8,
404     CMOV = 0x10
405 } cpu_features_t;
406
407
408 static unsigned int
409 detect_cpu_features (void)
410 {
411     unsigned int features = 0;
412     unsigned int result = 0;
413
414 #ifdef HAVE_GETISAX
415     if (getisax (&result, 1))
416     {
417         if (result & AV_386_CMOV)
418             features |= CMOV;
419         if (result & AV_386_MMX)
420             features |= MMX;
421         if (result & AV_386_AMD_MMX)
422             features |= MMX_EXTENSIONS;
423         if (result & AV_386_SSE)
424             features |= SSE;
425         if (result & AV_386_SSE2)
426             features |= SSE2;
427     }
428 #else
429     char vendor[13];
430 #ifdef _MSC_VER
431     int vendor0 = 0, vendor1, vendor2;
432 #endif
433     vendor[0] = 0;
434     vendor[12] = 0;
435
436 #ifdef __GNUC__
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.
444      */
445     __asm__ (
446         "pushf\n"
447         "pop %%eax\n"
448         "mov %%eax, %%ecx\n"
449         "xor $0x00200000, %%eax\n"
450         "push %%eax\n"
451         "popf\n"
452         "pushf\n"
453         "pop %%eax\n"
454         "mov $0x0, %%edx\n"
455         "xor %%ecx, %%eax\n"
456         "jz 1f\n"
457
458         "mov $0x00000000, %%eax\n"
459         "push %%ebx\n"
460         "cpuid\n"
461         "mov %%ebx, %%eax\n"
462         "pop %%ebx\n"
463         "mov %%eax, %1\n"
464         "mov %%edx, %2\n"
465         "mov %%ecx, %3\n"
466         "mov $0x00000001, %%eax\n"
467         "push %%ebx\n"
468         "cpuid\n"
469         "pop %%ebx\n"
470         "1:\n"
471         "mov %%edx, %0\n"
472         : "=r" (result),
473         "=m" (vendor[0]),
474         "=m" (vendor[4]),
475         "=m" (vendor[8])
476         :
477         : "%eax", "%ecx", "%edx"
478         );
479
480 #elif defined (_MSC_VER)
481
482     _asm {
483         pushfd
484         pop eax
485         mov ecx, eax
486         xor eax, 00200000h
487         push eax
488         popfd
489         pushfd
490         pop eax
491         mov edx, 0
492         xor eax, ecx
493         jz nocpuid
494
495         mov eax, 0
496         push ebx
497         cpuid
498         mov eax, ebx
499         pop ebx
500         mov vendor0, eax
501         mov vendor1, edx
502         mov vendor2, ecx
503         mov eax, 1
504         push ebx
505         cpuid
506         pop ebx
507     nocpuid:
508         mov result, edx
509     }
510     memmove (vendor + 0, &vendor0, 4);
511     memmove (vendor + 4, &vendor1, 4);
512     memmove (vendor + 8, &vendor2, 4);
513
514 #else
515 #   error unsupported compiler
516 #endif
517
518     features = 0;
519     if (result)
520     {
521         /* result now contains the standard feature bits */
522         if (result & (1 << 15))
523             features |= CMOV;
524         if (result & (1 << 23))
525             features |= MMX;
526         if (result & (1 << 25))
527             features |= SSE;
528         if (result & (1 << 26))
529             features |= SSE2;
530         if ((features & MMX) && !(features & SSE) &&
531             (strcmp (vendor, "AuthenticAMD") == 0 ||
532              strcmp (vendor, "Geode by NSC") == 0))
533         {
534             /* check for AMD MMX extensions */
535 #ifdef __GNUC__
536             __asm__ (
537                 "       push %%ebx\n"
538                 "       mov $0x80000000, %%eax\n"
539                 "       cpuid\n"
540                 "       xor %%edx, %%edx\n"
541                 "       cmp $0x1, %%eax\n"
542                 "       jge 2f\n"
543                 "       mov $0x80000001, %%eax\n"
544                 "       cpuid\n"
545                 "2:\n"
546                 "       pop %%ebx\n"
547                 "       mov %%edx, %0\n"
548                 : "=r" (result)
549                 :
550                 : "%eax", "%ecx", "%edx"
551                 );
552 #elif defined _MSC_VER
553             _asm {
554                 push ebx
555                 mov eax, 80000000h
556                 cpuid
557                 xor edx, edx
558                 cmp eax, 1
559                 jge notamd
560                 mov eax, 80000001h
561                 cpuid
562             notamd:
563                 pop ebx
564                 mov result, edx
565             }
566 #endif
567             if (result & (1 << 22))
568                 features |= MMX_EXTENSIONS;
569         }
570     }
571 #endif /* HAVE_GETISAX */
572
573     return features;
574 }
575
576 static pixman_bool_t
577 pixman_have_mmx (void)
578 {
579     static pixman_bool_t initialized = FALSE;
580     static pixman_bool_t mmx_present;
581
582     if (!initialized)
583     {
584         unsigned int features = detect_cpu_features ();
585         mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
586         initialized = TRUE;
587     }
588
589     return mmx_present;
590 }
591
592 #ifdef USE_SSE2
593 static pixman_bool_t
594 pixman_have_sse2 (void)
595 {
596     static pixman_bool_t initialized = FALSE;
597     static pixman_bool_t sse2_present;
598
599     if (!initialized)
600     {
601         unsigned int features = detect_cpu_features ();
602         sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
603         initialized = TRUE;
604     }
605
606     return sse2_present;
607 }
608
609 #endif
610
611 #else /* __amd64__ */
612 #ifdef USE_X86_MMX
613 #define pixman_have_mmx() TRUE
614 #endif
615 #ifdef USE_SSE2
616 #define pixman_have_sse2() TRUE
617 #endif
618 #endif /* __amd64__ */
619 #endif
620
621 pixman_implementation_t *
622 _pixman_choose_implementation (void)
623 {
624     pixman_implementation_t *imp;
625
626     imp = _pixman_implementation_create_general();
627     imp = _pixman_implementation_create_fast_path (imp);
628     
629 #ifdef USE_X86_MMX
630     if (pixman_have_mmx ())
631         imp = _pixman_implementation_create_mmx (imp);
632 #endif
633
634 #ifdef USE_SSE2
635     if (pixman_have_sse2 ())
636         imp = _pixman_implementation_create_sse2 (imp);
637 #endif
638
639 #ifdef USE_ARM_SIMD
640     if (pixman_have_arm_simd ())
641         imp = _pixman_implementation_create_arm_simd (imp);
642 #endif
643
644 #ifdef USE_ARM_IWMMXT
645     if (pixman_have_arm_iwmmxt ())
646         imp = _pixman_implementation_create_mmx (imp);
647 #endif
648
649 #ifdef USE_ARM_NEON
650     if (pixman_have_arm_neon ())
651         imp = _pixman_implementation_create_arm_neon (imp);
652 #endif
653
654 #ifdef USE_VMX
655     if (pixman_have_vmx ())
656         imp = _pixman_implementation_create_vmx (imp);
657 #endif
658
659     imp = _pixman_implementation_create_noop (imp);
660     
661     return imp;
662 }
663