1 /* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001-2009 Josh Coalson
3 * Copyright (C) 2011-2016 Xiph.Org Foundation
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Xiph.org Foundation nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
36
37 #include "private/cpu.h"
38 #include "share/compat.h"
39 #include <stdlib.h>
40 #include <string.h>
41
42 #if defined _MSC_VER
43 #include <intrin.h> /* for __cpuid() and _xgetbv() */
44 #elif defined __GNUC__ && defined HAVE_CPUID_H
45 #include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
46 #endif
47
48 #ifndef NDEBUG
49 #include <stdio.h>
50 #define dfprintf fprintf
51 #else
52 /* This is bad practice, it should be a static void empty function */
53 #define dfprintf(file, format, ...)
54 #endif
55
56 #if defined FLAC__CPU_PPC
57 #if defined(__linux__) || (defined(__FreeBSD__) && (__FreeBSD__ >= 12))
58 #include <sys/auxv.h>
59 #endif
60 #endif
61
62 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
63
64 /* these are flags in EDX of CPUID AX=00000001 */
65 static const uint32_t FLAC__CPUINFO_X86_CPUID_CMOV = 0x00008000;
66 static const uint32_t FLAC__CPUINFO_X86_CPUID_MMX = 0x00800000;
67 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE = 0x02000000;
68 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE2 = 0x04000000;
69
70 /* these are flags in ECX of CPUID AX=00000001 */
71 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE3 = 0x00000001;
72 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSSE3 = 0x00000200;
73 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE41 = 0x00080000;
74 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE42 = 0x00100000;
75 static const uint32_t FLAC__CPUINFO_X86_CPUID_OSXSAVE = 0x08000000;
76 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX = 0x10000000;
77 static const uint32_t FLAC__CPUINFO_X86_CPUID_FMA = 0x00001000;
78
79 /* these are flags in EBX of CPUID AX=00000007 */
80 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX2 = 0x00000020;
81
82 static uint32_t
cpu_xgetbv_x86(void)83 cpu_xgetbv_x86(void)
84 {
85 #if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__AVX_SUPPORTED
86 return (uint32_t)_xgetbv(0);
87 #elif defined __GNUC__
88 uint32_t lo, hi;
89 __asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
90 return lo;
91 #else
92 return 0;
93 #endif
94 }
95
96 static uint32_t
cpu_have_cpuid(void)97 cpu_have_cpuid(void)
98 {
99 #if defined FLAC__CPU_X86_64 || defined __i686__ || defined __SSE__ || (defined _M_IX86_FP && _M_IX86_FP > 0)
100 /* target CPU does have CPUID instruction */
101 return 1;
102 #elif defined FLAC__HAS_NASM
103 return FLAC__cpu_have_cpuid_asm_ia32();
104 #elif defined __GNUC__ && defined HAVE_CPUID_H
105 if (__get_cpuid_max(0, 0) != 0)
106 return 1;
107 else
108 return 0;
109 #elif defined _MSC_VER
110 FLAC__uint32 flags1, flags2;
111 __asm {
112 pushfd
113 pushfd
114 pop eax
115 mov flags1, eax
116 xor eax, 0x200000
117 push eax
118 popfd
119 pushfd
120 pop eax
121 mov flags2, eax
122 popfd
123 }
124 if (((flags1^flags2) & 0x200000) != 0)
125 return 1;
126 else
127 return 0;
128 #else
129 return 0;
130 #endif
131 }
132
133 static void
cpuinfo_x86(FLAC__uint32 level,FLAC__uint32 * eax,FLAC__uint32 * ebx,FLAC__uint32 * ecx,FLAC__uint32 * edx)134 cpuinfo_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
135 {
136 #if defined _MSC_VER
137 int cpuinfo[4];
138 int ext = level & 0x80000000;
139 __cpuid(cpuinfo, ext);
140 if ((uint32_t)cpuinfo[0] >= level) {
141 #if FLAC__AVX_SUPPORTED
142 __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
143 #else
144 __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
145 #endif
146 *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
147 return;
148 }
149 #elif defined __GNUC__ && defined HAVE_CPUID_H
150 FLAC__uint32 ext = level & 0x80000000;
151 __cpuid(ext, *eax, *ebx, *ecx, *edx);
152 if (*eax >= level) {
153 __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
154 return;
155 }
156 #elif defined FLAC__HAS_NASM && defined FLAC__CPU_IA32
157 FLAC__cpu_info_asm_ia32(level, eax, ebx, ecx, edx);
158 return;
159 #endif
160 *eax = *ebx = *ecx = *edx = 0;
161 }
162
163 #endif
164
165 static void
x86_cpu_info(FLAC__CPUInfo * info)166 x86_cpu_info (FLAC__CPUInfo *info)
167 {
168 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
169 FLAC__bool x86_osxsave = false;
170 FLAC__bool os_avx = false;
171 FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
172
173 info->use_asm = true; /* we assume a minimum of 80386 */
174 if (!cpu_have_cpuid())
175 return;
176
177 cpuinfo_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
178 info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */
179 cpuinfo_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
180
181 info->x86.cmov = (flags_edx & FLAC__CPUINFO_X86_CPUID_CMOV ) ? true : false;
182 info->x86.mmx = (flags_edx & FLAC__CPUINFO_X86_CPUID_MMX ) ? true : false;
183 info->x86.sse = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE ) ? true : false;
184 info->x86.sse2 = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE2 ) ? true : false;
185 info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE3 ) ? true : false;
186 info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSSE3) ? true : false;
187 info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE41) ? true : false;
188 info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE42) ? true : false;
189
190 if (FLAC__AVX_SUPPORTED) {
191 x86_osxsave = (flags_ecx & FLAC__CPUINFO_X86_CPUID_OSXSAVE) ? true : false;
192 info->x86.avx = (flags_ecx & FLAC__CPUINFO_X86_CPUID_AVX ) ? true : false;
193 info->x86.fma = (flags_ecx & FLAC__CPUINFO_X86_CPUID_FMA ) ? true : false;
194 cpuinfo_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
195 info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_AVX2 ) ? true : false;
196 }
197
198 #if defined FLAC__CPU_IA32
199 dfprintf(stderr, "CPU info (IA-32):\n");
200 #else
201 dfprintf(stderr, "CPU info (x86-64):\n");
202 #endif
203 dfprintf(stderr, " CMOV ....... %c\n", info->x86.cmov ? 'Y' : 'n');
204 dfprintf(stderr, " MMX ........ %c\n", info->x86.mmx ? 'Y' : 'n');
205 dfprintf(stderr, " SSE ........ %c\n", info->x86.sse ? 'Y' : 'n');
206 dfprintf(stderr, " SSE2 ....... %c\n", info->x86.sse2 ? 'Y' : 'n');
207 dfprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n');
208 dfprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
209 dfprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
210 dfprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
211
212 if (FLAC__AVX_SUPPORTED) {
213 dfprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n');
214 dfprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n');
215 dfprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n');
216 }
217
218 /*
219 * now have to check for OS support of AVX instructions
220 */
221 if (FLAC__AVX_SUPPORTED && info->x86.avx && x86_osxsave && (cpu_xgetbv_x86() & 0x6) == 0x6) {
222 os_avx = true;
223 }
224 if (os_avx) {
225 dfprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
226 }
227 if (!os_avx) {
228 /* no OS AVX support */
229 info->x86.avx = false;
230 info->x86.avx2 = false;
231 info->x86.fma = false;
232 }
233 #else
234 info->use_asm = false;
235 #endif
236 }
237
238 static void
ppc_cpu_info(FLAC__CPUInfo * info)239 ppc_cpu_info (FLAC__CPUInfo *info)
240 {
241 #if defined FLAC__CPU_PPC
242 #ifndef PPC_FEATURE2_ARCH_3_00
243 #define PPC_FEATURE2_ARCH_3_00 0x00800000
244 #endif
245
246 #ifndef PPC_FEATURE2_ARCH_2_07
247 #define PPC_FEATURE2_ARCH_2_07 0x80000000
248 #endif
249
250 #ifdef __linux__
251 if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_3_00) {
252 info->ppc.arch_3_00 = true;
253 } else if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_2_07) {
254 info->ppc.arch_2_07 = true;
255 }
256 #elif defined(__FreeBSD__) && (__FreeBSD__ >= 12)
257 long hwcaps;
258 /* elf_aux_info() appeared in FreeBSD 12.0 */
259 elf_aux_info(AT_HWCAP2, &hwcaps, sizeof(hwcaps));
260 if (hwcaps & PPC_FEATURE2_ARCH_3_00) {
261 info->ppc.arch_3_00 = true;
262 } else if (hwcaps & PPC_FEATURE2_ARCH_2_07) {
263 info->ppc.arch_2_07 = true;
264 }
265 #elif defined(__APPLE__)
266 /* no Mac OS X version supports CPU with Power AVI v2.07 or better */
267 info->ppc.arch_2_07 = false;
268 info->ppc.arch_3_00 = false;
269 #else
270 #error Unsupported platform! Please add support for reading ppc hwcaps.
271 #endif
272
273 #else
274 info->ppc.arch_2_07 = false;
275 info->ppc.arch_3_00 = false;
276 #endif
277 }
278
FLAC__cpu_info(FLAC__CPUInfo * info)279 void FLAC__cpu_info (FLAC__CPUInfo *info)
280 {
281 memset(info, 0, sizeof(*info));
282
283 #ifdef FLAC__CPU_IA32
284 info->type = FLAC__CPUINFO_TYPE_IA32;
285 #elif defined FLAC__CPU_X86_64
286 info->type = FLAC__CPUINFO_TYPE_X86_64;
287 #elif defined FLAC__CPU_PPC
288 info->type = FLAC__CPUINFO_TYPE_PPC;
289 #else
290 info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
291 #endif
292
293 switch (info->type) {
294 case FLAC__CPUINFO_TYPE_IA32: /* fallthrough */
295 case FLAC__CPUINFO_TYPE_X86_64:
296 x86_cpu_info (info);
297 break;
298 case FLAC__CPUINFO_TYPE_PPC:
299 ppc_cpu_info (info);
300 break;
301 default:
302 info->use_asm = false;
303 break;
304 }
305 }
306