x86: cpu: Break out test for cpuid capabilities into separate function
[ffmpeg.git] / libavutil / x86 / cpu.c
1 /*
2  * CPU detection code, extracted from mmx.h
3  * (c)1997-99 by H. Dietz and R. Fisher
4  * Converted to C and improved by Fabrice Bellard.
5  *
6  * This file is part of Libav.
7  *
8  * Libav is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * Libav is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with Libav; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include "libavutil/x86/asm.h"
26 #include "libavutil/cpu.h"
27
28 #if HAVE_INLINE_ASM
29 /* ebx saving is necessary for PIC. gcc seems unable to see it alone */
30 #define cpuid(index, eax, ebx, ecx, edx)                        \
31     __asm__ volatile (                                          \
32         "mov    %%"REG_b", %%"REG_S" \n\t"                      \
33         "cpuid                       \n\t"                      \
34         "xchg   %%"REG_b", %%"REG_S                             \
35         : "=a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx)        \
36         : "0" (index))
37 #elif HAVE_CPUID
38 #include <intrin.h>
39
40 #define cpuid(index, eax, ebx, ecx, edx)        \
41     do {                                        \
42         int info[4];                            \
43         __cpuid(info, index);                   \
44         eax = info[0];                          \
45         ebx = info[1];                          \
46         ecx = info[2];                          \
47         edx = info[3];                          \
48     } while (0)
49 #endif /* HAVE_CPUID */
50
51 #if HAVE_INLINE_ASM
52 #define xgetbv(index, eax, edx)                                 \
53     __asm__ (".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c" (index))
54 #elif HAVE_XGETBV
55 #include <immintrin.h>
56
57 #define xgetbv(index, eax, edx)                 \
58     do {                                        \
59         uint64_t res = __xgetbv(index);         \
60         eax = res;                              \
61         edx = res >> 32;                        \
62     } while (0)
63 #endif /* HAVE_XGETBV */
64
65 #if HAVE_INLINE_ASM
66
67 #define get_eflags(x)                           \
68     __asm__ volatile ("pushfl     \n"           \
69                       "pop    %0  \n"           \
70                       : "=r"(x))
71
72 #define set_eflags(x)                           \
73     __asm__ volatile ("push    %0 \n"           \
74                       "popfl      \n"           \
75                       :: "r"(x))
76
77 #elif HAVE_RWEFLAGS
78
79 #include <intrin.h>
80
81 #define get_eflags(x)                           \
82     x = __readeflags()
83
84 #define set_eflags(x)                           \
85     __writeeflags(x)
86
87 #endif /* HAVE_INLINE_ASM */
88
89 #if ARCH_X86_64
90
91 #define cpuid_test() 1
92
93 #elif HAVE_INLINE_ASM || HAVE_RWEFLAGS
94
95 static int cpuid_test(void)
96 {
97     x86_reg a, c;
98
99     /* Check if CPUID is supported by attempting to toggle the ID bit in
100      * the EFLAGS register. */
101     get_eflags(a);
102     set_eflags(a ^ 0x200000);
103     get_eflags(c);
104
105     return a != c;
106 }
107 #endif
108
109 /* Function to test if multimedia instructions are supported...  */
110 int ff_get_cpu_flags_x86(void)
111 {
112     int rval = 0;
113     int eax, ebx, ecx, edx;
114     int max_std_level, max_ext_level, std_caps = 0, ext_caps = 0;
115     int family = 0, model = 0;
116     union { int i[3]; char c[12]; } vendor;
117
118     if (!cpuid_test())
119         return 0; /* CPUID not supported */
120
121     cpuid(0, max_std_level, vendor.i[0], vendor.i[2], vendor.i[1]);
122
123     if (max_std_level >= 1) {
124         cpuid(1, eax, ebx, ecx, std_caps);
125         family = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
126         model  = ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0);
127         if (std_caps & (1 << 15))
128             rval |= AV_CPU_FLAG_CMOV;
129         if (std_caps & (1 << 23))
130             rval |= AV_CPU_FLAG_MMX;
131         if (std_caps & (1 << 25))
132             rval |= AV_CPU_FLAG_MMXEXT;
133 #if HAVE_SSE
134         if (std_caps & (1 << 25))
135             rval |= AV_CPU_FLAG_SSE;
136         if (std_caps & (1 << 26))
137             rval |= AV_CPU_FLAG_SSE2;
138         if (ecx & 1)
139             rval |= AV_CPU_FLAG_SSE3;
140         if (ecx & 0x00000200 )
141             rval |= AV_CPU_FLAG_SSSE3;
142         if (ecx & 0x00080000 )
143             rval |= AV_CPU_FLAG_SSE4;
144         if (ecx & 0x00100000 )
145             rval |= AV_CPU_FLAG_SSE42;
146 #if HAVE_AVX
147         /* Check OXSAVE and AVX bits */
148         if ((ecx & 0x18000000) == 0x18000000) {
149             /* Check for OS support */
150             xgetbv(0, eax, edx);
151             if ((eax & 0x6) == 0x6)
152                 rval |= AV_CPU_FLAG_AVX;
153         }
154 #endif /* HAVE_AVX */
155 #endif /* HAVE_SSE */
156     }
157
158     cpuid(0x80000000, max_ext_level, ebx, ecx, edx);
159
160     if (max_ext_level >= 0x80000001) {
161         cpuid(0x80000001, eax, ebx, ecx, ext_caps);
162         if (ext_caps & (1U << 31))
163             rval |= AV_CPU_FLAG_3DNOW;
164         if (ext_caps & (1 << 30))
165             rval |= AV_CPU_FLAG_3DNOWEXT;
166         if (ext_caps & (1 << 23))
167             rval |= AV_CPU_FLAG_MMX;
168         if (ext_caps & (1 << 22))
169             rval |= AV_CPU_FLAG_MMXEXT;
170
171         /* Allow for selectively disabling SSE2 functions on AMD processors
172            with SSE2 support but not SSE4a. This includes Athlon64, some
173            Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster
174            than SSE2 often enough to utilize this special-case flag.
175            AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case
176            so that SSE2 is used unless explicitly disabled by checking
177            AV_CPU_FLAG_SSE2SLOW. */
178         if (!strncmp(vendor.c, "AuthenticAMD", 12) &&
179             rval & AV_CPU_FLAG_SSE2 && !(ecx & 0x00000040)) {
180             rval |= AV_CPU_FLAG_SSE2SLOW;
181         }
182
183         /* XOP and FMA4 use the AVX instruction coding scheme, so they can't be
184          * used unless the OS has AVX support. */
185         if (rval & AV_CPU_FLAG_AVX) {
186             if (ecx & 0x00000800)
187                 rval |= AV_CPU_FLAG_XOP;
188             if (ecx & 0x00010000)
189                 rval |= AV_CPU_FLAG_FMA4;
190         }
191     }
192
193     if (!strncmp(vendor.c, "GenuineIntel", 12)) {
194         if (family == 6 && (model == 9 || model == 13 || model == 14)) {
195             /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and
196              * 6/14 (core1 "yonah") theoretically support sse2, but it's
197              * usually slower than mmx, so let's just pretend they don't.
198              * AV_CPU_FLAG_SSE2 is disabled and AV_CPU_FLAG_SSE2SLOW is
199              * enabled so that SSE2 is not used unless explicitly enabled
200              * by checking AV_CPU_FLAG_SSE2SLOW. The same situation
201              * applies for AV_CPU_FLAG_SSE3 and AV_CPU_FLAG_SSE3SLOW. */
202             if (rval & AV_CPU_FLAG_SSE2)
203                 rval ^= AV_CPU_FLAG_SSE2SLOW | AV_CPU_FLAG_SSE2;
204             if (rval & AV_CPU_FLAG_SSE3)
205                 rval ^= AV_CPU_FLAG_SSE3SLOW | AV_CPU_FLAG_SSE3;
206         }
207         /* The Atom processor has SSSE3 support, which is useful in many cases,
208          * but sometimes the SSSE3 version is slower than the SSE2 equivalent
209          * on the Atom, but is generally faster on other processors supporting
210          * SSSE3. This flag allows for selectively disabling certain SSSE3
211          * functions on the Atom. */
212         if (family == 6 && model == 28)
213             rval |= AV_CPU_FLAG_ATOM;
214     }
215
216     return rval;
217 }