1/*
2 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <stdlib.h>
12#include <string.h>
13
14#include "./vpx_config.h"
15#include "vpx_ports/arm.h"
16
17#ifdef WINAPI_FAMILY
18#include <winapifamily.h>
19#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
20#define getenv(x) NULL
21#endif
22#endif
23
24static int arm_cpu_env_flags(int *flags) {
25  char *env;
26  env = getenv("VPX_SIMD_CAPS");
27  if (env && *env) {
28    *flags = (int)strtol(env, NULL, 0);
29    return 0;
30  }
31  *flags = 0;
32  return -1;
33}
34
35static int arm_cpu_env_mask(void) {
36  char *env;
37  env = getenv("VPX_SIMD_CAPS_MASK");
38  return env && *env ? (int)strtol(env, NULL, 0) : ~0;
39}
40
41#if !CONFIG_RUNTIME_CPU_DETECT
42
43int arm_cpu_caps(void) {
44  /* This function should actually be a no-op. There is no way to adjust any of
45   * these because the RTCD tables do not exist: the functions are called
46   * statically */
47  int flags;
48  int mask;
49  if (!arm_cpu_env_flags(&flags)) {
50    return flags;
51  }
52  mask = arm_cpu_env_mask();
53#if HAVE_NEON || HAVE_NEON_ASM
54  flags |= HAS_NEON;
55#endif /* HAVE_NEON  || HAVE_NEON_ASM */
56  return flags & mask;
57}
58
59#elif defined(_MSC_VER) /* end !CONFIG_RUNTIME_CPU_DETECT */
60/*For GetExceptionCode() and EXCEPTION_ILLEGAL_INSTRUCTION.*/
61#ifndef WIN32_LEAN_AND_MEAN
62#define WIN32_LEAN_AND_MEAN
63#endif
64#ifndef WIN32_EXTRA_LEAN
65#define WIN32_EXTRA_LEAN
66#endif
67#include <windows.h>
68
69int arm_cpu_caps(void) {
70  int flags;
71  int mask;
72  if (!arm_cpu_env_flags(&flags)) {
73    return flags;
74  }
75  mask = arm_cpu_env_mask();
76/* MSVC has no inline __asm support for ARM, but it does let you __emit
77 *  instructions via their assembled hex code.
78 * All of these instructions should be essentially nops.
79 */
80#if HAVE_NEON || HAVE_NEON_ASM
81  if (mask & HAS_NEON) {
82    __try {
83      /*VORR q0,q0,q0*/
84      __emit(0xF2200150);
85      flags |= HAS_NEON;
86    } __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) {
87      /*Ignore exception.*/
88    }
89  }
90#endif /* HAVE_NEON || HAVE_NEON_ASM */
91  return flags & mask;
92}
93
94#elif defined(__ANDROID__) /* end _MSC_VER */
95#include <cpu-features.h>
96
97int arm_cpu_caps(void) {
98  int flags;
99  int mask;
100  uint64_t features;
101  if (!arm_cpu_env_flags(&flags)) {
102    return flags;
103  }
104  mask = arm_cpu_env_mask();
105  features = android_getCpuFeatures();
106
107#if HAVE_NEON || HAVE_NEON_ASM
108  if (features & ANDROID_CPU_ARM_FEATURE_NEON) flags |= HAS_NEON;
109#endif /* HAVE_NEON || HAVE_NEON_ASM */
110  return flags & mask;
111}
112
113#elif defined(__linux__) /* end __ANDROID__ */
114
115#include <stdio.h>
116
117int arm_cpu_caps(void) {
118  FILE *fin;
119  int flags;
120  int mask;
121  if (!arm_cpu_env_flags(&flags)) {
122    return flags;
123  }
124  mask = arm_cpu_env_mask();
125  /* Reading /proc/self/auxv would be easier, but that doesn't work reliably
126   *  on Android.
127   * This also means that detection will fail in Scratchbox.
128   */
129  fin = fopen("/proc/cpuinfo", "r");
130  if (fin != NULL) {
131    /* 512 should be enough for anybody (it's even enough for all the flags
132     * that x86 has accumulated... so far).
133     */
134    char buf[512];
135    while (fgets(buf, 511, fin) != NULL) {
136#if HAVE_NEON || HAVE_NEON_ASM
137      if (memcmp(buf, "Features", 8) == 0) {
138        char *p;
139        p = strstr(buf, " neon");
140        if (p != NULL && (p[5] == ' ' || p[5] == '\n')) {
141          flags |= HAS_NEON;
142        }
143      }
144#endif /* HAVE_NEON || HAVE_NEON_ASM */
145    }
146    fclose(fin);
147  }
148  return flags & mask;
149}
150#else  /* end __linux__ */
151#error \
152    "--enable-runtime-cpu-detect selected, but no CPU detection method " \
153"available for your platform. Reconfigure with --disable-runtime-cpu-detect."
154#endif
155