Line data Source code
1 : /*
2 : * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3 : *
4 : * This source code is subject to the terms of the BSD 2 Clause License and
5 : * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 : * was not distributed with this source code in the LICENSE file, you can
7 : * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 : * Media Patent License 1.0 was not distributed with this source code in the
9 : * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 : */
11 :
12 : #ifndef AOM_PORTS_X86_H_
13 : #define AOM_PORTS_X86_H_
14 : #include <stdlib.h>
15 :
16 : #if defined(_MSC_VER)
17 : #include <intrin.h> /* For __cpuidex, __rdtsc */
18 : #endif
19 :
20 : #include "aom_config.h"
21 : #include "aom/aom_integer.h"
22 :
23 : #ifdef __cplusplus
24 : extern "C" {
25 : #endif
26 :
27 : typedef enum {
28 : AOM_CPU_UNKNOWN = -1,
29 : AOM_CPU_AMD,
30 : AOM_CPU_AMD_OLD,
31 : AOM_CPU_CENTAUR,
32 : AOM_CPU_CYRIX,
33 : AOM_CPU_INTEL,
34 : AOM_CPU_NEXGEN,
35 : AOM_CPU_NSC,
36 : AOM_CPU_RISE,
37 : AOM_CPU_SIS,
38 : AOM_CPU_TRANSMETA,
39 : AOM_CPU_TRANSMETA_OLD,
40 : AOM_CPU_UMC,
41 : AOM_CPU_VIA,
42 :
43 : AOM_CPU_LAST
44 : } aom_cpu_t;
45 :
46 : #if defined(__GNUC__) && __GNUC__ || defined(__ANDROID__)
47 : #if ARCH_X86_64
48 : #define cpuid(func, func2, ax, bx, cx, dx) \
49 : __asm__ __volatile__("cpuid \n\t" \
50 : : "=a"(ax), "=b"(bx), "=c"(cx), "=d"(dx) \
51 : : "a"(func), "c"(func2));
52 : #else
53 : #define cpuid(func, func2, ax, bx, cx, dx) \
54 : __asm__ __volatile__( \
55 : "mov %%ebx, %%edi \n\t" \
56 : "cpuid \n\t" \
57 : "xchg %%edi, %%ebx \n\t" \
58 : : "=a"(ax), "=D"(bx), "=c"(cx), "=d"(dx) \
59 : : "a"(func), "c"(func2));
60 : #endif
61 : #elif defined(__SUNPRO_C) || \
62 : defined(__SUNPRO_CC) /* end __GNUC__ or __ANDROID__*/
63 : #if ARCH_X86_64
64 : #define cpuid(func, func2, ax, bx, cx, dx) \
65 : asm volatile( \
66 : "xchg %rsi, %rbx \n\t" \
67 : "cpuid \n\t" \
68 : "movl %ebx, %edi \n\t" \
69 : "xchg %rsi, %rbx \n\t" \
70 : : "=a"(ax), "=D"(bx), "=c"(cx), "=d"(dx) \
71 : : "a"(func), "c"(func2));
72 : #else
73 : #define cpuid(func, func2, ax, bx, cx, dx) \
74 : asm volatile( \
75 : "pushl %ebx \n\t" \
76 : "cpuid \n\t" \
77 : "movl %ebx, %edi \n\t" \
78 : "popl %ebx \n\t" \
79 : : "=a"(ax), "=D"(bx), "=c"(cx), "=d"(dx) \
80 : : "a"(func), "c"(func2));
81 : #endif
82 : #else /* end __SUNPRO__ */
83 : #if ARCH_X86_64
84 : #if defined(_MSC_VER) && _MSC_VER > 1500
85 : #define cpuid(func, func2, a, b, c, d) \
86 : do { \
87 : int regs[4]; \
88 : __cpuidex(regs, func, func2); \
89 : a = regs[0]; \
90 : b = regs[1]; \
91 : c = regs[2]; \
92 : d = regs[3]; \
93 : } while (0)
94 : #else
95 : #define cpuid(func, func2, a, b, c, d) \
96 : do { \
97 : int regs[4]; \
98 : __cpuid(regs, func); \
99 : a = regs[0]; \
100 : b = regs[1]; \
101 : c = regs[2]; \
102 : d = regs[3]; \
103 : } while (0)
104 : #endif
105 : #else
106 : /* clang-format off */
107 : #define cpuid(func, func2, a, b, c, d) \
108 : __asm mov eax, func \
109 : __asm mov ecx, func2 \
110 : __asm cpuid \
111 : __asm mov a, eax \
112 : __asm mov b, ebx \
113 : __asm mov c, ecx \
114 : __asm mov d, edx
115 : #endif
116 : /* clang-format on */
117 : #endif /* end others */
118 :
119 : // NaCl has no support for xgetbv or the raw opcode.
120 : #if !defined(__native_client__) && (defined(__i386__) || defined(__x86_64__))
121 0 : static INLINE uint64_t xgetbv(void) {
122 0 : const uint32_t ecx = 0;
123 : uint32_t eax, edx;
124 : // Use the raw opcode for xgetbv for compatibility with older toolchains.
125 0 : __asm__ volatile(".byte 0x0f, 0x01, 0xd0\n"
126 : : "=a"(eax), "=d"(edx)
127 : : "c"(ecx));
128 0 : return ((uint64_t)edx << 32) | eax;
129 : }
130 : #elif (defined(_M_X64) || defined(_M_IX86)) && defined(_MSC_FULL_VER) && \
131 : _MSC_FULL_VER >= 160040219 // >= VS2010 SP1
132 : #include <immintrin.h>
133 : #define xgetbv() _xgetbv(0)
134 : #elif defined(_MSC_VER) && defined(_M_IX86)
135 : static INLINE uint64_t xgetbv(void) {
136 : uint32_t eax_, edx_;
137 : __asm {
138 : xor ecx, ecx // ecx = 0
139 : // Use the raw opcode for xgetbv for compatibility with older toolchains.
140 : __asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0
141 : mov eax_, eax
142 : mov edx_, edx
143 : }
144 : return ((uint64_t)edx_ << 32) | eax_;
145 : }
146 : #else
147 : #define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains.
148 : #endif
149 :
150 : #if defined(_MSC_VER) && _MSC_VER >= 1700
151 : #include <windows.h>
152 : #if WINAPI_FAMILY_PARTITION(WINAPI_FAMILY_APP)
153 : #define getenv(x) NULL
154 : #endif
155 : #endif
156 :
157 : #define HAS_MMX 0x01
158 : #define HAS_SSE 0x02
159 : #define HAS_SSE2 0x04
160 : #define HAS_SSE3 0x08
161 : #define HAS_SSSE3 0x10
162 : #define HAS_SSE4_1 0x20
163 : #define HAS_AVX 0x40
164 : #define HAS_AVX2 0x80
165 : #ifndef BIT
166 : #define BIT(n) (1 << n)
167 : #endif
168 :
169 0 : static INLINE int x86_simd_caps(void) {
170 0 : unsigned int flags = 0;
171 0 : unsigned int mask = ~0;
172 : unsigned int max_cpuid_val, reg_eax, reg_ebx, reg_ecx, reg_edx;
173 : char *env;
174 : (void)reg_ebx;
175 :
176 : /* See if the CPU capabilities are being overridden by the environment */
177 0 : env = getenv("AOM_SIMD_CAPS");
178 :
179 0 : if (env && *env) return (int)strtol(env, NULL, 0);
180 :
181 0 : env = getenv("AOM_SIMD_CAPS_MASK");
182 :
183 0 : if (env && *env) mask = (unsigned int)strtoul(env, NULL, 0);
184 :
185 : /* Ensure that the CPUID instruction supports extended features */
186 0 : cpuid(0, 0, max_cpuid_val, reg_ebx, reg_ecx, reg_edx);
187 :
188 0 : if (max_cpuid_val < 1) return 0;
189 :
190 : /* Get the standard feature flags */
191 0 : cpuid(1, 0, reg_eax, reg_ebx, reg_ecx, reg_edx);
192 :
193 0 : if (reg_edx & BIT(23)) flags |= HAS_MMX;
194 :
195 0 : if (reg_edx & BIT(25)) flags |= HAS_SSE; /* aka xmm */
196 :
197 0 : if (reg_edx & BIT(26)) flags |= HAS_SSE2; /* aka wmt */
198 :
199 0 : if (reg_ecx & BIT(0)) flags |= HAS_SSE3;
200 :
201 0 : if (reg_ecx & BIT(9)) flags |= HAS_SSSE3;
202 :
203 0 : if (reg_ecx & BIT(19)) flags |= HAS_SSE4_1;
204 :
205 : // bits 27 (OSXSAVE) & 28 (256-bit AVX)
206 0 : if ((reg_ecx & (BIT(27) | BIT(28))) == (BIT(27) | BIT(28))) {
207 0 : if ((xgetbv() & 0x6) == 0x6) {
208 0 : flags |= HAS_AVX;
209 :
210 0 : if (max_cpuid_val >= 7) {
211 : /* Get the leaf 7 feature flags. Needed to check for AVX2 support */
212 0 : cpuid(7, 0, reg_eax, reg_ebx, reg_ecx, reg_edx);
213 :
214 0 : if (reg_ebx & BIT(5)) flags |= HAS_AVX2;
215 : }
216 : }
217 : }
218 :
219 0 : return flags & mask;
220 : }
221 :
222 : // Note:
223 : // 32-bit CPU cycle counter is light-weighted for most function performance
224 : // measurement. For large function (CPU time > a couple of seconds), 64-bit
225 : // counter should be used.
226 : // 32-bit CPU cycle counter
227 : static INLINE unsigned int x86_readtsc(void) {
228 : #if defined(__GNUC__) && __GNUC__
229 : unsigned int tsc;
230 : __asm__ __volatile__("rdtsc\n\t" : "=a"(tsc) :);
231 : return tsc;
232 : #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
233 : unsigned int tsc;
234 : asm volatile("rdtsc\n\t" : "=a"(tsc) :);
235 : return tsc;
236 : #else
237 : #if ARCH_X86_64
238 : return (unsigned int)__rdtsc();
239 : #else
240 : __asm rdtsc;
241 : #endif
242 : #endif
243 : }
244 : // 64-bit CPU cycle counter
245 : static INLINE uint64_t x86_readtsc64(void) {
246 : #if defined(__GNUC__) && __GNUC__
247 : uint32_t hi, lo;
248 : __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
249 : return ((uint64_t)hi << 32) | lo;
250 : #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
251 : uint_t hi, lo;
252 : asm volatile("rdtsc\n\t" : "=a"(lo), "=d"(hi));
253 : return ((uint64_t)hi << 32) | lo;
254 : #else
255 : #if ARCH_X86_64
256 : return (uint64_t)__rdtsc();
257 : #else
258 : __asm rdtsc;
259 : #endif
260 : #endif
261 : }
262 :
263 : #if defined(__GNUC__) && __GNUC__
264 : #define x86_pause_hint() __asm__ __volatile__("pause \n\t")
265 : #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
266 : #define x86_pause_hint() asm volatile("pause \n\t")
267 : #else
268 : #if ARCH_X86_64
269 : #define x86_pause_hint() _mm_pause();
270 : #else
271 : #define x86_pause_hint() __asm pause
272 : #endif
273 : #endif
274 :
275 : #if defined(__GNUC__) && __GNUC__
276 0 : static void x87_set_control_word(unsigned short mode) {
277 0 : __asm__ __volatile__("fldcw %0" : : "m"(*&mode));
278 0 : }
279 0 : static unsigned short x87_get_control_word(void) {
280 : unsigned short mode;
281 0 : __asm__ __volatile__("fstcw %0\n\t" : "=m"(*&mode) :);
282 0 : return mode;
283 : }
284 : #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
285 : static void x87_set_control_word(unsigned short mode) {
286 : asm volatile("fldcw %0" : : "m"(*&mode));
287 : }
288 : static unsigned short x87_get_control_word(void) {
289 : unsigned short mode;
290 : asm volatile("fstcw %0\n\t" : "=m"(*&mode) :);
291 : return mode;
292 : }
293 : #elif ARCH_X86_64
294 : /* No fldcw intrinsics on Windows x64, punt to external asm */
295 : extern void aom_winx64_fldcw(unsigned short mode);
296 : extern unsigned short aom_winx64_fstcw(void);
297 : #define x87_set_control_word aom_winx64_fldcw
298 : #define x87_get_control_word aom_winx64_fstcw
299 : #else
300 : static void x87_set_control_word(unsigned short mode) {
301 : __asm { fldcw mode }
302 : }
303 : static unsigned short x87_get_control_word(void) {
304 : unsigned short mode;
305 : __asm { fstcw mode }
306 : return mode;
307 : }
308 : #endif
309 :
310 0 : static INLINE unsigned int x87_set_double_precision(void) {
311 0 : unsigned int mode = x87_get_control_word();
312 0 : x87_set_control_word((mode & ~0x300) | 0x200);
313 0 : return mode;
314 : }
315 :
316 : extern void aom_reset_mmx_state(void);
317 :
318 : #ifdef __cplusplus
319 : } // extern "C"
320 : #endif
321 :
322 : #endif // AOM_PORTS_X86_H_
|