mmx: fix formats in commented code
[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)
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 #else /* linux ELF */
332
333 #define pixman_have_arm_simd() FALSE
334 #define pixman_have_arm_neon() FALSE
335
336 #endif
337
338 #endif /* USE_ARM_SIMD || USE_ARM_NEON */
339
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
344  * it.
345  */
346 #if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
347
348 #ifdef HAVE_GETISAX
349 #include <sys/auxv.h>
350 #endif
351
352 typedef enum
353 {
354     NO_FEATURES = 0,
355     MMX = 0x1,
356     MMX_EXTENSIONS = 0x2,
357     SSE = 0x6,
358     SSE2 = 0x8,
359     CMOV = 0x10
360 } cpu_features_t;
361
362
363 static unsigned int
364 detect_cpu_features (void)
365 {
366     unsigned int features = 0;
367     unsigned int result = 0;
368
369 #ifdef HAVE_GETISAX
370     if (getisax (&result, 1))
371     {
372         if (result & AV_386_CMOV)
373             features |= CMOV;
374         if (result & AV_386_MMX)
375             features |= MMX;
376         if (result & AV_386_AMD_MMX)
377             features |= MMX_EXTENSIONS;
378         if (result & AV_386_SSE)
379             features |= SSE;
380         if (result & AV_386_SSE2)
381             features |= SSE2;
382     }
383 #else
384     char vendor[13];
385 #ifdef _MSC_VER
386     int vendor0 = 0, vendor1, vendor2;
387 #endif
388     vendor[0] = 0;
389     vendor[12] = 0;
390
391 #ifdef __GNUC__
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.
399      */
400     __asm__ (
401         "pushf\n"
402         "pop %%eax\n"
403         "mov %%eax, %%ecx\n"
404         "xor $0x00200000, %%eax\n"
405         "push %%eax\n"
406         "popf\n"
407         "pushf\n"
408         "pop %%eax\n"
409         "mov $0x0, %%edx\n"
410         "xor %%ecx, %%eax\n"
411         "jz 1f\n"
412
413         "mov $0x00000000, %%eax\n"
414         "push %%ebx\n"
415         "cpuid\n"
416         "mov %%ebx, %%eax\n"
417         "pop %%ebx\n"
418         "mov %%eax, %1\n"
419         "mov %%edx, %2\n"
420         "mov %%ecx, %3\n"
421         "mov $0x00000001, %%eax\n"
422         "push %%ebx\n"
423         "cpuid\n"
424         "pop %%ebx\n"
425         "1:\n"
426         "mov %%edx, %0\n"
427         : "=r" (result),
428         "=m" (vendor[0]),
429         "=m" (vendor[4]),
430         "=m" (vendor[8])
431         :
432         : "%eax", "%ecx", "%edx"
433         );
434
435 #elif defined (_MSC_VER)
436
437     _asm {
438         pushfd
439         pop eax
440         mov ecx, eax
441         xor eax, 00200000h
442         push eax
443         popfd
444         pushfd
445         pop eax
446         mov edx, 0
447         xor eax, ecx
448         jz nocpuid
449
450         mov eax, 0
451         push ebx
452         cpuid
453         mov eax, ebx
454         pop ebx
455         mov vendor0, eax
456         mov vendor1, edx
457         mov vendor2, ecx
458         mov eax, 1
459         push ebx
460         cpuid
461         pop ebx
462     nocpuid:
463         mov result, edx
464     }
465     memmove (vendor + 0, &vendor0, 4);
466     memmove (vendor + 4, &vendor1, 4);
467     memmove (vendor + 8, &vendor2, 4);
468
469 #else
470 #   error unsupported compiler
471 #endif
472
473     features = 0;
474     if (result)
475     {
476         /* result now contains the standard feature bits */
477         if (result & (1 << 15))
478             features |= CMOV;
479         if (result & (1 << 23))
480             features |= MMX;
481         if (result & (1 << 25))
482             features |= SSE;
483         if (result & (1 << 26))
484             features |= SSE2;
485         if ((features & MMX) && !(features & SSE) &&
486             (strcmp (vendor, "AuthenticAMD") == 0 ||
487              strcmp (vendor, "Geode by NSC") == 0))
488         {
489             /* check for AMD MMX extensions */
490 #ifdef __GNUC__
491             __asm__ (
492                 "       push %%ebx\n"
493                 "       mov $0x80000000, %%eax\n"
494                 "       cpuid\n"
495                 "       xor %%edx, %%edx\n"
496                 "       cmp $0x1, %%eax\n"
497                 "       jge 2f\n"
498                 "       mov $0x80000001, %%eax\n"
499                 "       cpuid\n"
500                 "2:\n"
501                 "       pop %%ebx\n"
502                 "       mov %%edx, %0\n"
503                 : "=r" (result)
504                 :
505                 : "%eax", "%ecx", "%edx"
506                 );
507 #elif defined _MSC_VER
508             _asm {
509                 push ebx
510                 mov eax, 80000000h
511                 cpuid
512                 xor edx, edx
513                 cmp eax, 1
514                 jge notamd
515                 mov eax, 80000001h
516                 cpuid
517             notamd:
518                 pop ebx
519                 mov result, edx
520             }
521 #endif
522             if (result & (1 << 22))
523                 features |= MMX_EXTENSIONS;
524         }
525     }
526 #endif /* HAVE_GETISAX */
527
528     return features;
529 }
530
531 static pixman_bool_t
532 pixman_have_mmx (void)
533 {
534     static pixman_bool_t initialized = FALSE;
535     static pixman_bool_t mmx_present;
536
537     if (!initialized)
538     {
539         unsigned int features = detect_cpu_features ();
540         mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
541         initialized = TRUE;
542     }
543
544     return mmx_present;
545 }
546
547 #ifdef USE_SSE2
548 static pixman_bool_t
549 pixman_have_sse2 (void)
550 {
551     static pixman_bool_t initialized = FALSE;
552     static pixman_bool_t sse2_present;
553
554     if (!initialized)
555     {
556         unsigned int features = detect_cpu_features ();
557         sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
558         initialized = TRUE;
559     }
560
561     return sse2_present;
562 }
563
564 #endif
565
566 #else /* __amd64__ */
567 #ifdef USE_MMX
568 #define pixman_have_mmx() TRUE
569 #endif
570 #ifdef USE_SSE2
571 #define pixman_have_sse2() TRUE
572 #endif
573 #endif /* __amd64__ */
574 #endif
575
576 pixman_implementation_t *
577 _pixman_choose_implementation (void)
578 {
579     pixman_implementation_t *imp;
580
581     imp = _pixman_implementation_create_general();
582     imp = _pixman_implementation_create_fast_path (imp);
583     
584 #ifdef USE_MMX
585     if (pixman_have_mmx ())
586         imp = _pixman_implementation_create_mmx (imp);
587 #endif
588
589 #ifdef USE_SSE2
590     if (pixman_have_sse2 ())
591         imp = _pixman_implementation_create_sse2 (imp);
592 #endif
593
594 #ifdef USE_ARM_SIMD
595     if (pixman_have_arm_simd ())
596         imp = _pixman_implementation_create_arm_simd (imp);
597 #endif
598
599 #ifdef USE_ARM_NEON
600     if (pixman_have_arm_neon ())
601         imp = _pixman_implementation_create_arm_neon (imp);
602 #endif
603     
604 #ifdef USE_VMX
605     if (pixman_have_vmx ())
606         imp = _pixman_implementation_create_vmx (imp);
607 #endif
608
609     imp = _pixman_implementation_create_noop (imp);
610     
611     return imp;
612 }
613