dff27d1ea4783d40a02f5857402f3659b072772f
[framework/graphics/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__) /* linux ELF */
248
249 #include <stdlib.h>
250 #include <unistd.h>
251 #include <sys/types.h>
252 #include <sys/stat.h>
253 #include <sys/mman.h>
254 #include <fcntl.h>
255 #include <string.h>
256 #include <elf.h>
257
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;
264
265 static void
266 pixman_arm_read_auxv ()
267 {
268     int fd;
269     Elf32_auxv_t aux;
270
271     fd = open ("/proc/self/auxv", O_RDONLY);
272     if (fd >= 0)
273     {
274         while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
275         {
276             if (aux.a_type == AT_HWCAP)
277             {
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
281                  */
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;
286             }
287             else if (aux.a_type == AT_PLATFORM)
288             {
289                 const char *plat = (const char*) aux.a_un.a_val;
290                 if (strncmp (plat, "v7l", 3) == 0)
291                 {
292                     arm_has_v7 = TRUE;
293                     arm_has_v6 = TRUE;
294                 }
295                 else if (strncmp (plat, "v6l", 3) == 0)
296                 {
297                     arm_has_v6 = TRUE;
298                 }
299             }
300         }
301         close (fd);
302     }
303
304     arm_tests_initialized = TRUE;
305 }
306
307 #if defined(USE_ARM_SIMD)
308 pixman_bool_t
309 pixman_have_arm_simd (void)
310 {
311     if (!arm_tests_initialized)
312         pixman_arm_read_auxv ();
313
314     return arm_has_v6;
315 }
316
317 #endif /* USE_ARM_SIMD */
318
319 #if defined(USE_ARM_NEON)
320 pixman_bool_t
321 pixman_have_arm_neon (void)
322 {
323     if (!arm_tests_initialized)
324         pixman_arm_read_auxv ();
325
326     return arm_has_neon;
327 }
328
329 #endif /* USE_ARM_NEON */
330
331 #if defined(USE_ARM_IWMMXT)
332 pixman_bool_t
333 pixman_have_arm_iwmmxt (void)
334 {
335     if (!arm_tests_initialized)
336         pixman_arm_read_auxv ();
337
338     return arm_has_iwmmxt;
339 }
340
341 #endif /* USE_ARM_IWMMXT */
342
343 #else /* linux ELF */
344
345 #define pixman_have_arm_simd() FALSE
346 #define pixman_have_arm_neon() FALSE
347 #define pixman_have_arm_iwmmxt() FALSE
348
349 #endif
350
351 #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
352
353 #if defined(USE_X86_MMX) || defined(USE_SSE2)
354 /* The CPU detection code needs to be in a file not compiled with
355  * "-mmmx -msse", as gcc would generate CMOV instructions otherwise
356  * that would lead to SIGILL instructions on old CPUs that don't have
357  * it.
358  */
359 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
360
361 #ifdef HAVE_GETISAX
362 #include <sys/auxv.h>
363 #endif
364
365 typedef enum
366 {
367     NO_FEATURES = 0,
368     MMX = 0x1,
369     MMX_EXTENSIONS = 0x2,
370     SSE = 0x6,
371     SSE2 = 0x8,
372     CMOV = 0x10
373 } cpu_features_t;
374
375
376 static unsigned int
377 detect_cpu_features (void)
378 {
379     unsigned int features = 0;
380     unsigned int result = 0;
381
382 #ifdef HAVE_GETISAX
383     if (getisax (&result, 1))
384     {
385         if (result & AV_386_CMOV)
386             features |= CMOV;
387         if (result & AV_386_MMX)
388             features |= MMX;
389         if (result & AV_386_AMD_MMX)
390             features |= MMX_EXTENSIONS;
391         if (result & AV_386_SSE)
392             features |= SSE;
393         if (result & AV_386_SSE2)
394             features |= SSE2;
395     }
396 #else
397     char vendor[13];
398 #ifdef _MSC_VER
399     int vendor0 = 0, vendor1, vendor2;
400 #endif
401     vendor[0] = 0;
402     vendor[12] = 0;
403
404 #ifdef __GNUC__
405     /* see p. 118 of amd64 instruction set manual Vol3 */
406     /* We need to be careful about the handling of %ebx and
407      * %esp here. We can't declare either one as clobbered
408      * since they are special registers (%ebx is the "PIC
409      * register" holding an offset to global data, %esp the
410      * stack pointer), so we need to make sure they have their
411      * original values when we access the output operands.
412      */
413     __asm__ (
414         "pushf\n"
415         "pop %%eax\n"
416         "mov %%eax, %%ecx\n"
417         "xor $0x00200000, %%eax\n"
418         "push %%eax\n"
419         "popf\n"
420         "pushf\n"
421         "pop %%eax\n"
422         "mov $0x0, %%edx\n"
423         "xor %%ecx, %%eax\n"
424         "jz 1f\n"
425
426         "mov $0x00000000, %%eax\n"
427         "push %%ebx\n"
428         "cpuid\n"
429         "mov %%ebx, %%eax\n"
430         "pop %%ebx\n"
431         "mov %%eax, %1\n"
432         "mov %%edx, %2\n"
433         "mov %%ecx, %3\n"
434         "mov $0x00000001, %%eax\n"
435         "push %%ebx\n"
436         "cpuid\n"
437         "pop %%ebx\n"
438         "1:\n"
439         "mov %%edx, %0\n"
440         : "=r" (result),
441         "=m" (vendor[0]),
442         "=m" (vendor[4]),
443         "=m" (vendor[8])
444         :
445         : "%eax", "%ecx", "%edx"
446         );
447
448 #elif defined (_MSC_VER)
449
450     _asm {
451         pushfd
452         pop eax
453         mov ecx, eax
454         xor eax, 00200000h
455         push eax
456         popfd
457         pushfd
458         pop eax
459         mov edx, 0
460         xor eax, ecx
461         jz nocpuid
462
463         mov eax, 0
464         push ebx
465         cpuid
466         mov eax, ebx
467         pop ebx
468         mov vendor0, eax
469         mov vendor1, edx
470         mov vendor2, ecx
471         mov eax, 1
472         push ebx
473         cpuid
474         pop ebx
475     nocpuid:
476         mov result, edx
477     }
478     memmove (vendor + 0, &vendor0, 4);
479     memmove (vendor + 4, &vendor1, 4);
480     memmove (vendor + 8, &vendor2, 4);
481
482 #else
483 #   error unsupported compiler
484 #endif
485
486     features = 0;
487     if (result)
488     {
489         /* result now contains the standard feature bits */
490         if (result & (1 << 15))
491             features |= CMOV;
492         if (result & (1 << 23))
493             features |= MMX;
494         if (result & (1 << 25))
495             features |= SSE;
496         if (result & (1 << 26))
497             features |= SSE2;
498         if ((features & MMX) && !(features & SSE) &&
499             (strcmp (vendor, "AuthenticAMD") == 0 ||
500              strcmp (vendor, "Geode by NSC") == 0))
501         {
502             /* check for AMD MMX extensions */
503 #ifdef __GNUC__
504             __asm__ (
505                 "       push %%ebx\n"
506                 "       mov $0x80000000, %%eax\n"
507                 "       cpuid\n"
508                 "       xor %%edx, %%edx\n"
509                 "       cmp $0x1, %%eax\n"
510                 "       jge 2f\n"
511                 "       mov $0x80000001, %%eax\n"
512                 "       cpuid\n"
513                 "2:\n"
514                 "       pop %%ebx\n"
515                 "       mov %%edx, %0\n"
516                 : "=r" (result)
517                 :
518                 : "%eax", "%ecx", "%edx"
519                 );
520 #elif defined _MSC_VER
521             _asm {
522                 push ebx
523                 mov eax, 80000000h
524                 cpuid
525                 xor edx, edx
526                 cmp eax, 1
527                 jge notamd
528                 mov eax, 80000001h
529                 cpuid
530             notamd:
531                 pop ebx
532                 mov result, edx
533             }
534 #endif
535             if (result & (1 << 22))
536                 features |= MMX_EXTENSIONS;
537         }
538     }
539 #endif /* HAVE_GETISAX */
540
541     return features;
542 }
543
544 static pixman_bool_t
545 pixman_have_mmx (void)
546 {
547     static pixman_bool_t initialized = FALSE;
548     static pixman_bool_t mmx_present;
549
550     if (!initialized)
551     {
552         unsigned int features = detect_cpu_features ();
553         mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
554         initialized = TRUE;
555     }
556
557     return mmx_present;
558 }
559
560 #ifdef USE_SSE2
561 static pixman_bool_t
562 pixman_have_sse2 (void)
563 {
564     static pixman_bool_t initialized = FALSE;
565     static pixman_bool_t sse2_present;
566
567     if (!initialized)
568     {
569         unsigned int features = detect_cpu_features ();
570         sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
571         initialized = TRUE;
572     }
573
574     return sse2_present;
575 }
576
577 #endif
578
579 #else /* __amd64__ */
580 #ifdef USE_X86_MMX
581 #define pixman_have_mmx() TRUE
582 #endif
583 #ifdef USE_SSE2
584 #define pixman_have_sse2() TRUE
585 #endif
586 #endif /* __amd64__ */
587 #endif
588
589 pixman_implementation_t *
590 _pixman_choose_implementation (void)
591 {
592     pixman_implementation_t *imp;
593
594     imp = _pixman_implementation_create_general();
595     imp = _pixman_implementation_create_fast_path (imp);
596     
597 #ifdef USE_X86_MMX
598     if (pixman_have_mmx ())
599         imp = _pixman_implementation_create_mmx (imp);
600 #endif
601
602 #ifdef USE_SSE2
603     if (pixman_have_sse2 ())
604         imp = _pixman_implementation_create_sse2 (imp);
605 #endif
606
607 #ifdef USE_ARM_SIMD
608     if (pixman_have_arm_simd ())
609         imp = _pixman_implementation_create_arm_simd (imp);
610 #endif
611
612 #ifdef USE_ARM_IWMMXT
613     if (pixman_have_arm_iwmmxt ())
614         imp = _pixman_implementation_create_mmx (imp);
615 #endif
616
617 #ifdef USE_ARM_NEON
618     if (pixman_have_arm_neon ())
619         imp = _pixman_implementation_create_arm_neon (imp);
620 #endif
621
622 #ifdef USE_VMX
623     if (pixman_have_vmx ())
624         imp = _pixman_implementation_create_vmx (imp);
625 #endif
626
627     imp = _pixman_implementation_create_noop (imp);
628     
629     return imp;
630 }
631