armcpu.c (9535B)
1 /* Copyright (c) 2010 Xiph.Org Foundation 2 * Copyright (c) 2013 Parrot */ 3 /* 4 Redistribution and use in source and binary forms, with or without 5 modification, are permitted provided that the following conditions 6 are met: 7 8 - Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 11 - Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 19 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* Original code from libtheora modified to suit to Opus */ 29 30 #ifdef HAVE_CONFIG_H 31 #include "config.h" 32 #endif 33 34 #ifdef OPUS_HAVE_RTCD 35 36 #include "armcpu.h" 37 #include "cpu_support.h" 38 #include "os_support.h" 39 #include "opus_types.h" 40 #include "arch.h" 41 42 #define OPUS_CPU_ARM_V4_FLAG (1<<OPUS_ARCH_ARM_V4) 43 #define OPUS_CPU_ARM_EDSP_FLAG (1<<OPUS_ARCH_ARM_EDSP) 44 #define OPUS_CPU_ARM_MEDIA_FLAG (1<<OPUS_ARCH_ARM_MEDIA) 45 #define OPUS_CPU_ARM_NEON_FLAG (1<<OPUS_ARCH_ARM_NEON) 46 #define OPUS_CPU_ARM_DOTPROD_FLAG (1<<OPUS_ARCH_ARM_DOTPROD) 47 48 #if defined(_MSC_VER) 49 /*For GetExceptionCode() and EXCEPTION_ILLEGAL_INSTRUCTION.*/ 50 # define WIN32_LEAN_AND_MEAN 51 # define WIN32_EXTRA_LEAN 52 # include <windows.h> 53 54 static OPUS_INLINE opus_uint32 opus_cpu_capabilities(void){ 55 opus_uint32 flags; 56 flags=0; 57 /* MSVC has no OPUS_INLINE __asm support for ARM, but it does let you __emit 58 * instructions via their assembled hex code. 59 * All of these instructions should be essentially nops. */ 60 # if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 61 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 62 __try{ 63 /*PLD [r13]*/ 64 __emit(0xF5DDF000); 65 flags|=OPUS_CPU_ARM_EDSP_FLAG; 66 } 67 __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ 68 /*Ignore exception.*/ 69 } 70 # if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 71 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 72 __try{ 73 /*SHADD8 r3,r3,r3*/ 74 __emit(0xE6333F93); 75 flags|=OPUS_CPU_ARM_MEDIA_FLAG; 76 } 77 __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ 78 /*Ignore exception.*/ 79 } 80 # if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 81 __try{ 82 /*VORR q0,q0,q0*/ 83 __emit(0xF2200150); 84 flags|=OPUS_CPU_ARM_NEON_FLAG; 85 } 86 __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ 87 /*Ignore exception.*/ 88 } 89 # endif 90 # endif 91 # endif 92 return flags; 93 } 94 95 #elif defined(__linux__) 96 /* Linux based */ 97 #include <stdio.h> 98 99 static opus_uint32 opus_cpu_capabilities(void) 100 { 101 opus_uint32 flags = 0; 102 FILE *cpuinfo; 103 104 /* Reading /proc/self/auxv would be easier, but that doesn't work reliably on 105 * Android */ 106 cpuinfo = fopen("/proc/cpuinfo", "r"); 107 108 if(cpuinfo != NULL) 109 { 110 /* 512 should be enough for anybody (it's even enough for all the flags that 111 * x86 has accumulated... so far). */ 112 char buf[512]; 113 114 while(fgets(buf, 512, cpuinfo) != NULL) 115 { 116 # if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 117 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 118 /* Search for edsp and neon flag */ 119 if(memcmp(buf, "Features", 8) == 0) 120 { 121 char *p; 122 p = strstr(buf, " edsp"); 123 if(p != NULL && (p[5] == ' ' || p[5] == '\n')) 124 flags |= OPUS_CPU_ARM_EDSP_FLAG; 125 126 # if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 127 p = strstr(buf, " neon"); 128 if(p != NULL && (p[5] == ' ' || p[5] == '\n')) 129 flags |= OPUS_CPU_ARM_NEON_FLAG; 130 p = strstr(buf, " asimd"); 131 if(p != NULL && (p[6] == ' ' || p[6] == '\n')) 132 flags |= OPUS_CPU_ARM_NEON_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_EDSP_FLAG; 133 # endif 134 # if defined(OPUS_ARM_MAY_HAVE_DOTPROD) 135 p = strstr(buf, " asimddp"); 136 if(p != NULL && (p[8] == ' ' || p[8] == '\n')) 137 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 138 # endif 139 } 140 # endif 141 142 # if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 143 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 144 /* Search for media capabilities (>= ARMv6) */ 145 if(memcmp(buf, "CPU architecture:", 17) == 0) 146 { 147 int version; 148 version = atoi(buf+17); 149 150 if(version >= 6) 151 flags |= OPUS_CPU_ARM_MEDIA_FLAG; 152 } 153 # endif 154 } 155 156 #if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) 157 flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; 158 # if defined(OPUS_ARM_PRESUME_DOTPROD) 159 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 160 # endif 161 #endif 162 163 fclose(cpuinfo); 164 } 165 return flags; 166 } 167 168 #elif defined(__APPLE__) 169 #include <sys/types.h> 170 #include <sys/sysctl.h> 171 172 static opus_uint32 opus_cpu_capabilities(void) 173 { 174 opus_uint32 flags = 0; 175 176 #if defined(OPUS_ARM_MAY_HAVE_DOTPROD) 177 size_t size = sizeof(uint32_t); 178 uint32_t value = 0; 179 if (!sysctlbyname("hw.optional.arm.FEAT_DotProd", &value, &size, NULL, 0) && value) 180 { 181 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 182 } 183 #endif 184 185 #if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) 186 flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; 187 # if defined(OPUS_ARM_PRESUME_DOTPROD) 188 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 189 # endif 190 #endif 191 return flags; 192 } 193 194 #elif defined(HAVE_ELF_AUX_INFO) 195 #include <sys/auxv.h> 196 197 static opus_uint32 opus_cpu_capabilities(void) 198 { 199 long hwcap = 0; 200 opus_uint32 flags = 0; 201 202 # if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 203 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 204 /* FreeBSD requires armv6+, which always supports media instructions */ 205 flags |= OPUS_CPU_ARM_MEDIA_FLAG; 206 # endif 207 208 elf_aux_info(AT_HWCAP, &hwcap, sizeof hwcap); 209 210 # if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ 211 || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 212 # ifdef HWCAP_EDSP 213 if (hwcap & HWCAP_EDSP) 214 flags |= OPUS_CPU_ARM_EDSP_FLAG; 215 # endif 216 217 # if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) 218 # ifdef HWCAP_NEON 219 if (hwcap & HWCAP_NEON) 220 flags |= OPUS_CPU_ARM_NEON_FLAG; 221 # elif defined(HWCAP_ASIMD) 222 if (hwcap & HWCAP_ASIMD) 223 flags |= OPUS_CPU_ARM_NEON_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_EDSP_FLAG; 224 # endif 225 # endif 226 # if defined(OPUS_ARM_MAY_HAVE_DOTPROD) && defined(HWCAP_ASIMDDP) 227 if (hwcap & HWCAP_ASIMDDP) 228 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 229 # endif 230 # endif 231 232 #if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) 233 flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; 234 # if defined(OPUS_ARM_PRESUME_DOTPROD) 235 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 236 # endif 237 #endif 238 239 return (flags); 240 } 241 242 #elif defined(__OpenBSD__) 243 #include <sys/types.h> 244 #include <sys/sysctl.h> 245 #include <machine/armreg.h> 246 #include <machine/cpu.h> 247 248 static opus_uint32 opus_cpu_capabilities(void) 249 { 250 opus_uint32 flags = 0; 251 252 #if defined(OPUS_ARM_MAY_HAVE_DOTPROD) && defined(CPU_ID_AA64ISAR0) 253 const int isar0_mib[] = { CTL_MACHDEP, CPU_ID_AA64ISAR0 }; 254 uint64_t isar0; 255 size_t len = sizeof(isar0); 256 257 if (sysctl(isar0_mib, 2, &isar0, &len, NULL, 0) != -1) 258 { 259 if (ID_AA64ISAR0_DP(isar0) >= ID_AA64ISAR0_DP_IMPL) 260 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 261 } 262 #endif 263 264 #if defined(OPUS_ARM_PRESUME_NEON_INTR) \ 265 || defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) 266 flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; 267 # if defined(OPUS_ARM_PRESUME_DOTPROD) 268 flags |= OPUS_CPU_ARM_DOTPROD_FLAG; 269 # endif 270 #endif 271 return flags; 272 } 273 274 #else 275 /* The feature registers which can tell us what the processor supports are 276 * accessible in privileged modes only, so we can't have a general user-space 277 * detection method like on x86.*/ 278 # error "Configured to use ARM asm but no CPU detection method available for " \ 279 "your platform. Reconfigure with --disable-rtcd (or send patches)." 280 #endif 281 282 static int opus_select_arch_impl(void) 283 { 284 opus_uint32 flags = opus_cpu_capabilities(); 285 int arch = 0; 286 287 if(!(flags & OPUS_CPU_ARM_EDSP_FLAG)) { 288 /* Asserts ensure arch values are sequential */ 289 celt_assert(arch == OPUS_ARCH_ARM_V4); 290 return arch; 291 } 292 arch++; 293 294 if(!(flags & OPUS_CPU_ARM_MEDIA_FLAG)) { 295 celt_assert(arch == OPUS_ARCH_ARM_EDSP); 296 return arch; 297 } 298 arch++; 299 300 if(!(flags & OPUS_CPU_ARM_NEON_FLAG)) { 301 celt_assert(arch == OPUS_ARCH_ARM_MEDIA); 302 return arch; 303 } 304 arch++; 305 306 if(!(flags & OPUS_CPU_ARM_DOTPROD_FLAG)) { 307 celt_assert(arch == OPUS_ARCH_ARM_NEON); 308 return arch; 309 } 310 arch++; 311 312 celt_assert(arch == OPUS_ARCH_ARM_DOTPROD); 313 return arch; 314 } 315 316 int opus_select_arch(void) { 317 int arch = opus_select_arch_impl(); 318 #ifdef FUZZING 319 arch = rand()%(arch+1); 320 #endif 321 return arch; 322 } 323 #endif