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