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