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