Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved. |
michael@0 | 3 | * |
michael@0 | 4 | * Use of this source code is governed by a BSD-style license |
michael@0 | 5 | * that can be found in the LICENSE file in the root of the source |
michael@0 | 6 | * tree. An additional intellectual property rights grant can be found |
michael@0 | 7 | * in the file PATENTS. All contributing project authors may |
michael@0 | 8 | * be found in the AUTHORS file in the root of the source tree. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "libyuv/cpu_id.h" |
michael@0 | 12 | |
michael@0 | 13 | #ifdef _MSC_VER |
michael@0 | 14 | #include <intrin.h> // For __cpuidex() |
michael@0 | 15 | #endif |
michael@0 | 16 | #if !defined(__pnacl__) && !defined(__CLR_VER) && \ |
michael@0 | 17 | !defined(__native_client__) && defined(_M_X64) && \ |
michael@0 | 18 | defined(_MSC_VER) && (_MSC_FULL_VER >= 160040219) |
michael@0 | 19 | #include <immintrin.h> // For _xgetbv() |
michael@0 | 20 | #endif |
michael@0 | 21 | |
michael@0 | 22 | #if !defined(__native_client__) |
michael@0 | 23 | #include <stdlib.h> // For getenv() |
michael@0 | 24 | #endif |
michael@0 | 25 | |
michael@0 | 26 | // For ArmCpuCaps() but unittested on all platforms |
michael@0 | 27 | #include <stdio.h> |
michael@0 | 28 | #include <string.h> |
michael@0 | 29 | |
michael@0 | 30 | #include "libyuv/basic_types.h" // For CPU_X86 |
michael@0 | 31 | |
michael@0 | 32 | #ifdef __cplusplus |
michael@0 | 33 | namespace libyuv { |
michael@0 | 34 | extern "C" { |
michael@0 | 35 | #endif |
michael@0 | 36 | |
michael@0 | 37 | // For functions that use the stack and have runtime checks for overflow, |
michael@0 | 38 | // use SAFEBUFFERS to avoid additional check. |
michael@0 | 39 | #if defined(_MSC_VER) && (_MSC_FULL_VER >= 160040219) |
michael@0 | 40 | #define SAFEBUFFERS __declspec(safebuffers) |
michael@0 | 41 | #else |
michael@0 | 42 | #define SAFEBUFFERS |
michael@0 | 43 | #endif |
michael@0 | 44 | |
michael@0 | 45 | // Low level cpuid for X86. Returns zeros on other CPUs. |
michael@0 | 46 | #if !defined(__pnacl__) && !defined(__CLR_VER) && \ |
michael@0 | 47 | (defined(_M_IX86) || defined(_M_X64) || \ |
michael@0 | 48 | defined(__i386__) || defined(__x86_64__)) |
michael@0 | 49 | LIBYUV_API |
michael@0 | 50 | void CpuId(uint32 info_eax, uint32 info_ecx, uint32* cpu_info) { |
michael@0 | 51 | #if defined(_MSC_VER) |
michael@0 | 52 | #if (_MSC_FULL_VER >= 160040219) |
michael@0 | 53 | __cpuidex((int*)(cpu_info), info_eax, info_ecx); |
michael@0 | 54 | #elif defined(_M_IX86) |
michael@0 | 55 | __asm { |
michael@0 | 56 | mov eax, info_eax |
michael@0 | 57 | mov ecx, info_ecx |
michael@0 | 58 | mov edi, cpu_info |
michael@0 | 59 | cpuid |
michael@0 | 60 | mov [edi], eax |
michael@0 | 61 | mov [edi + 4], ebx |
michael@0 | 62 | mov [edi + 8], ecx |
michael@0 | 63 | mov [edi + 12], edx |
michael@0 | 64 | } |
michael@0 | 65 | #else |
michael@0 | 66 | if (info_ecx == 0) { |
michael@0 | 67 | __cpuid((int*)(cpu_info), info_eax); |
michael@0 | 68 | } else { |
michael@0 | 69 | cpu_info[3] = cpu_info[2] = cpu_info[1] = cpu_info[0] = 0; |
michael@0 | 70 | } |
michael@0 | 71 | #endif |
michael@0 | 72 | #else // defined(_MSC_VER) |
michael@0 | 73 | uint32 info_ebx, info_edx; |
michael@0 | 74 | asm volatile ( // NOLINT |
michael@0 | 75 | #if defined( __i386__) && defined(__PIC__) |
michael@0 | 76 | // Preserve ebx for fpic 32 bit. |
michael@0 | 77 | "mov %%ebx, %%edi \n" |
michael@0 | 78 | "cpuid \n" |
michael@0 | 79 | "xchg %%edi, %%ebx \n" |
michael@0 | 80 | : "=D" (info_ebx), |
michael@0 | 81 | #else |
michael@0 | 82 | "cpuid \n" |
michael@0 | 83 | : "=b" (info_ebx), |
michael@0 | 84 | #endif // defined( __i386__) && defined(__PIC__) |
michael@0 | 85 | "+a" (info_eax), "+c" (info_ecx), "=d" (info_edx)); |
michael@0 | 86 | cpu_info[0] = info_eax; |
michael@0 | 87 | cpu_info[1] = info_ebx; |
michael@0 | 88 | cpu_info[2] = info_ecx; |
michael@0 | 89 | cpu_info[3] = info_edx; |
michael@0 | 90 | #endif // defined(_MSC_VER) |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | #if !defined(__native_client__) |
michael@0 | 94 | #define HAS_XGETBV |
michael@0 | 95 | // X86 CPUs have xgetbv to detect OS saves high parts of ymm registers. |
michael@0 | 96 | int TestOsSaveYmm() { |
michael@0 | 97 | uint32 xcr0 = 0u; |
michael@0 | 98 | #if defined(_MSC_VER) && defined(_XCR_XFEATURE_ENABLED_MASK) |
michael@0 | 99 | xcr0 = (uint32)(_xgetbv(_XCR_XFEATURE_ENABLED_MASK)); |
michael@0 | 100 | #elif defined(_MSC_VER) && defined(_M_IX86) |
michael@0 | 101 | __asm { |
michael@0 | 102 | xor ecx, ecx // xcr 0 |
michael@0 | 103 | _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0 // For VS2010 and earlier. |
michael@0 | 104 | mov xcr0, eax |
michael@0 | 105 | } |
michael@0 | 106 | #elif defined(__i386__) || defined(__x86_64__) |
michael@0 | 107 | asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcr0) : "c" (0) : "%edx"); |
michael@0 | 108 | #endif // defined(_MSC_VER) |
michael@0 | 109 | return((xcr0 & 6) == 6); // Is ymm saved? |
michael@0 | 110 | } |
michael@0 | 111 | #endif // !defined(__native_client__) |
michael@0 | 112 | #else |
michael@0 | 113 | LIBYUV_API |
michael@0 | 114 | void CpuId(uint32 eax, uint32 ecx, uint32* cpu_info) { |
michael@0 | 115 | cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; |
michael@0 | 116 | } |
michael@0 | 117 | #endif |
michael@0 | 118 | |
michael@0 | 119 | // based on libvpx arm_cpudetect.c |
michael@0 | 120 | // For Arm, but public to allow testing on any CPU |
michael@0 | 121 | LIBYUV_API SAFEBUFFERS |
michael@0 | 122 | int ArmCpuCaps(const char* cpuinfo_name) { |
michael@0 | 123 | FILE* f = fopen(cpuinfo_name, "r"); |
michael@0 | 124 | if (f) { |
michael@0 | 125 | char cpuinfo_line[512]; |
michael@0 | 126 | while (fgets(cpuinfo_line, sizeof(cpuinfo_line) - 1, f)) { |
michael@0 | 127 | if (memcmp(cpuinfo_line, "Features", 8) == 0) { |
michael@0 | 128 | char* p = strstr(cpuinfo_line, " neon"); |
michael@0 | 129 | if (p && (p[5] == ' ' || p[5] == '\n')) { |
michael@0 | 130 | fclose(f); |
michael@0 | 131 | return kCpuHasNEON; |
michael@0 | 132 | } |
michael@0 | 133 | } |
michael@0 | 134 | } |
michael@0 | 135 | fclose(f); |
michael@0 | 136 | } |
michael@0 | 137 | return 0; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | #if defined(__mips__) && defined(__linux__) |
michael@0 | 141 | static int MipsCpuCaps(const char* search_string) { |
michael@0 | 142 | const char* file_name = "/proc/cpuinfo"; |
michael@0 | 143 | char cpuinfo_line[256]; |
michael@0 | 144 | FILE* f = NULL; |
michael@0 | 145 | if ((f = fopen(file_name, "r")) != NULL) { |
michael@0 | 146 | while (fgets(cpuinfo_line, sizeof(cpuinfo_line) - 1, f) != NULL) { |
michael@0 | 147 | if (strstr(cpuinfo_line, search_string) != NULL) { |
michael@0 | 148 | fclose(f); |
michael@0 | 149 | return kCpuHasMIPS_DSP; |
michael@0 | 150 | } |
michael@0 | 151 | } |
michael@0 | 152 | fclose(f); |
michael@0 | 153 | } |
michael@0 | 154 | /* Did not find string in the proc file, or not Linux ELF. */ |
michael@0 | 155 | return 0; |
michael@0 | 156 | } |
michael@0 | 157 | #endif |
michael@0 | 158 | |
michael@0 | 159 | // CPU detect function for SIMD instruction sets. |
michael@0 | 160 | LIBYUV_API |
michael@0 | 161 | int cpu_info_ = kCpuInit; // cpu_info is not initialized yet. |
michael@0 | 162 | |
michael@0 | 163 | // Test environment variable for disabling CPU features. Any non-zero value |
michael@0 | 164 | // to disable. Zero ignored to make it easy to set the variable on/off. |
michael@0 | 165 | #if !defined(__native_client__) && !defined(_M_ARM) |
michael@0 | 166 | |
michael@0 | 167 | static LIBYUV_BOOL TestEnv(const char* name) { |
michael@0 | 168 | const char* var = getenv(name); |
michael@0 | 169 | if (var) { |
michael@0 | 170 | if (var[0] != '0') { |
michael@0 | 171 | return LIBYUV_TRUE; |
michael@0 | 172 | } |
michael@0 | 173 | } |
michael@0 | 174 | return LIBYUV_FALSE; |
michael@0 | 175 | } |
michael@0 | 176 | #else // nacl does not support getenv(). |
michael@0 | 177 | static LIBYUV_BOOL TestEnv(const char*) { |
michael@0 | 178 | return LIBYUV_FALSE; |
michael@0 | 179 | } |
michael@0 | 180 | #endif |
michael@0 | 181 | |
michael@0 | 182 | LIBYUV_API SAFEBUFFERS |
michael@0 | 183 | int InitCpuFlags(void) { |
michael@0 | 184 | #if !defined(__pnacl__) && !defined(__CLR_VER) && defined(CPU_X86) |
michael@0 | 185 | |
michael@0 | 186 | uint32 cpu_info1[4] = { 0, 0, 0, 0 }; |
michael@0 | 187 | uint32 cpu_info7[4] = { 0, 0, 0, 0 }; |
michael@0 | 188 | CpuId(1, 0, cpu_info1); |
michael@0 | 189 | CpuId(7, 0, cpu_info7); |
michael@0 | 190 | cpu_info_ = ((cpu_info1[3] & 0x04000000) ? kCpuHasSSE2 : 0) | |
michael@0 | 191 | ((cpu_info1[2] & 0x00000200) ? kCpuHasSSSE3 : 0) | |
michael@0 | 192 | ((cpu_info1[2] & 0x00080000) ? kCpuHasSSE41 : 0) | |
michael@0 | 193 | ((cpu_info1[2] & 0x00100000) ? kCpuHasSSE42 : 0) | |
michael@0 | 194 | ((cpu_info7[1] & 0x00000200) ? kCpuHasERMS : 0) | |
michael@0 | 195 | ((cpu_info1[2] & 0x00001000) ? kCpuHasFMA3 : 0) | |
michael@0 | 196 | kCpuHasX86; |
michael@0 | 197 | #ifdef HAS_XGETBV |
michael@0 | 198 | if ((cpu_info1[2] & 0x18000000) == 0x18000000 && // AVX and OSSave |
michael@0 | 199 | TestOsSaveYmm()) { // Saves YMM. |
michael@0 | 200 | cpu_info_ |= ((cpu_info7[1] & 0x00000020) ? kCpuHasAVX2 : 0) | |
michael@0 | 201 | kCpuHasAVX; |
michael@0 | 202 | } |
michael@0 | 203 | #endif |
michael@0 | 204 | // Environment variable overrides for testing. |
michael@0 | 205 | if (TestEnv("LIBYUV_DISABLE_X86")) { |
michael@0 | 206 | cpu_info_ &= ~kCpuHasX86; |
michael@0 | 207 | } |
michael@0 | 208 | if (TestEnv("LIBYUV_DISABLE_SSE2")) { |
michael@0 | 209 | cpu_info_ &= ~kCpuHasSSE2; |
michael@0 | 210 | } |
michael@0 | 211 | if (TestEnv("LIBYUV_DISABLE_SSSE3")) { |
michael@0 | 212 | cpu_info_ &= ~kCpuHasSSSE3; |
michael@0 | 213 | } |
michael@0 | 214 | if (TestEnv("LIBYUV_DISABLE_SSE41")) { |
michael@0 | 215 | cpu_info_ &= ~kCpuHasSSE41; |
michael@0 | 216 | } |
michael@0 | 217 | if (TestEnv("LIBYUV_DISABLE_SSE42")) { |
michael@0 | 218 | cpu_info_ &= ~kCpuHasSSE42; |
michael@0 | 219 | } |
michael@0 | 220 | if (TestEnv("LIBYUV_DISABLE_AVX")) { |
michael@0 | 221 | cpu_info_ &= ~kCpuHasAVX; |
michael@0 | 222 | } |
michael@0 | 223 | if (TestEnv("LIBYUV_DISABLE_AVX2")) { |
michael@0 | 224 | cpu_info_ &= ~kCpuHasAVX2; |
michael@0 | 225 | } |
michael@0 | 226 | if (TestEnv("LIBYUV_DISABLE_ERMS")) { |
michael@0 | 227 | cpu_info_ &= ~kCpuHasERMS; |
michael@0 | 228 | } |
michael@0 | 229 | if (TestEnv("LIBYUV_DISABLE_FMA3")) { |
michael@0 | 230 | cpu_info_ &= ~kCpuHasFMA3; |
michael@0 | 231 | } |
michael@0 | 232 | #elif defined(__mips__) && defined(__linux__) |
michael@0 | 233 | // Linux mips parse text file for dsp detect. |
michael@0 | 234 | cpu_info_ = MipsCpuCaps("dsp"); // set kCpuHasMIPS_DSP. |
michael@0 | 235 | #if defined(__mips_dspr2) |
michael@0 | 236 | cpu_info_ |= kCpuHasMIPS_DSPR2; |
michael@0 | 237 | #endif |
michael@0 | 238 | cpu_info_ |= kCpuHasMIPS; |
michael@0 | 239 | |
michael@0 | 240 | if (getenv("LIBYUV_DISABLE_MIPS")) { |
michael@0 | 241 | cpu_info_ &= ~kCpuHasMIPS; |
michael@0 | 242 | } |
michael@0 | 243 | if (getenv("LIBYUV_DISABLE_MIPS_DSP")) { |
michael@0 | 244 | cpu_info_ &= ~kCpuHasMIPS_DSP; |
michael@0 | 245 | } |
michael@0 | 246 | if (getenv("LIBYUV_DISABLE_MIPS_DSPR2")) { |
michael@0 | 247 | cpu_info_ &= ~kCpuHasMIPS_DSPR2; |
michael@0 | 248 | } |
michael@0 | 249 | #elif defined(__arm__) |
michael@0 | 250 | #if defined(__linux__) && (defined(__ARM_NEON__) || defined(LIBYUV_NEON)) && \ |
michael@0 | 251 | !defined(__native_client__) |
michael@0 | 252 | // Linux arm parse text file for neon detect. |
michael@0 | 253 | cpu_info_ = ArmCpuCaps("/proc/cpuinfo"); |
michael@0 | 254 | #elif defined(__ARM_NEON__) || defined(__native_client__) |
michael@0 | 255 | // gcc -mfpu=neon defines __ARM_NEON__ |
michael@0 | 256 | // Enable Neon if you want support for Neon and Arm, and use MaskCpuFlags |
michael@0 | 257 | // to disable Neon on devices that do not have it. |
michael@0 | 258 | cpu_info_ = kCpuHasNEON; |
michael@0 | 259 | #endif |
michael@0 | 260 | cpu_info_ |= kCpuHasARM; |
michael@0 | 261 | if (TestEnv("LIBYUV_DISABLE_NEON")) { |
michael@0 | 262 | cpu_info_ &= ~kCpuHasNEON; |
michael@0 | 263 | } |
michael@0 | 264 | #endif // __arm__ |
michael@0 | 265 | if (TestEnv("LIBYUV_DISABLE_ASM")) { |
michael@0 | 266 | cpu_info_ = 0; |
michael@0 | 267 | } |
michael@0 | 268 | return cpu_info_; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | LIBYUV_API |
michael@0 | 272 | void MaskCpuFlags(int enable_flags) { |
michael@0 | 273 | cpu_info_ = InitCpuFlags() & enable_flags; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | #ifdef __cplusplus |
michael@0 | 277 | } // extern "C" |
michael@0 | 278 | } // namespace libyuv |
michael@0 | 279 | #endif |