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 #include <sys/auxv.h>
58 #endif
59 
60 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
61 
62 /* these are flags in EDX of CPUID AX=00000001 */
63 static const uint32_t FLAC__CPUINFO_X86_CPUID_CMOV    = 0x00008000;
64 static const uint32_t FLAC__CPUINFO_X86_CPUID_MMX     = 0x00800000;
65 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE     = 0x02000000;
66 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE2    = 0x04000000;
67 
68 /* these are flags in ECX of CPUID AX=00000001 */
69 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE3    = 0x00000001;
70 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSSE3   = 0x00000200;
71 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE41   = 0x00080000;
72 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE42   = 0x00100000;
73 static const uint32_t FLAC__CPUINFO_X86_CPUID_OSXSAVE = 0x08000000;
74 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX     = 0x10000000;
75 static const uint32_t FLAC__CPUINFO_X86_CPUID_FMA     = 0x00001000;
76 
77 /* these are flags in EBX of CPUID AX=00000007 */
78 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX2    = 0x00000020;
79 
80 static uint32_t
cpu_xgetbv_x86(void)81 cpu_xgetbv_x86(void)
82 {
83 #if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__AVX_SUPPORTED
84 	return (uint32_t)_xgetbv(0);
85 #elif defined __GNUC__
86 	uint32_t lo, hi;
87 	__asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
88 	return lo;
89 #else
90 	return 0;
91 #endif
92 }
93 
94 static uint32_t
cpu_have_cpuid(void)95 cpu_have_cpuid(void)
96 {
97 #if defined FLAC__CPU_X86_64 || defined __i686__ || defined __SSE__ || (defined _M_IX86_FP && _M_IX86_FP > 0)
98 	/* target CPU does have CPUID instruction */
99 	return 1;
100 #elif defined FLAC__HAS_NASM
101 	return FLAC__cpu_have_cpuid_asm_ia32();
102 #elif defined __GNUC__ && defined HAVE_CPUID_H
103 	if (__get_cpuid_max(0, 0) != 0)
104 		return 1;
105 	else
106 		return 0;
107 #elif defined _MSC_VER
108 	FLAC__uint32 flags1, flags2;
109 	__asm {
110 		pushfd
111 		pushfd
112 		pop		eax
113 		mov		flags1, eax
114 		xor		eax, 0x200000
115 		push	eax
116 		popfd
117 		pushfd
118 		pop		eax
119 		mov		flags2, eax
120 		popfd
121 	}
122 	if (((flags1^flags2) & 0x200000) != 0)
123 		return 1;
124 	else
125 		return 0;
126 #else
127 	return 0;
128 #endif
129 }
130 
131 static void
cpuinfo_x86(FLAC__uint32 level,FLAC__uint32 * eax,FLAC__uint32 * ebx,FLAC__uint32 * ecx,FLAC__uint32 * edx)132 cpuinfo_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
133 {
134 #if defined _MSC_VER
135 	int cpuinfo[4];
136 	int ext = level & 0x80000000;
137 	__cpuid(cpuinfo, ext);
138 	if ((uint32_t)cpuinfo[0] >= level) {
139 #if FLAC__AVX_SUPPORTED
140 		__cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
141 #else
142 		__cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
143 #endif
144 		*eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
145 		return;
146 	}
147 #elif defined __GNUC__ && defined HAVE_CPUID_H
148 	FLAC__uint32 ext = level & 0x80000000;
149 	__cpuid(ext, *eax, *ebx, *ecx, *edx);
150 	if (*eax >= level) {
151 		__cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
152 		return;
153 	}
154 #elif defined FLAC__HAS_NASM && defined FLAC__CPU_IA32
155 	FLAC__cpu_info_asm_ia32(level, eax, ebx, ecx, edx);
156 	return;
157 #endif
158 	*eax = *ebx = *ecx = *edx = 0;
159 }
160 
161 #endif
162 
163 static void
x86_cpu_info(FLAC__CPUInfo * info)164 x86_cpu_info (FLAC__CPUInfo *info)
165 {
166 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
167 	FLAC__bool x86_osxsave = false;
168 	FLAC__bool os_avx = false;
169 	FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
170 
171 	info->use_asm = true; /* we assume a minimum of 80386 */
172 	if (!cpu_have_cpuid())
173 		return;
174 
175 	cpuinfo_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
176 	info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */
177 	cpuinfo_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
178 
179 	info->x86.cmov  = (flags_edx & FLAC__CPUINFO_X86_CPUID_CMOV ) ? true : false;
180 	info->x86.mmx   = (flags_edx & FLAC__CPUINFO_X86_CPUID_MMX  ) ? true : false;
181 	info->x86.sse   = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE  ) ? true : false;
182 	info->x86.sse2  = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE2 ) ? true : false;
183 	info->x86.sse3  = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE3 ) ? true : false;
184 	info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSSE3) ? true : false;
185 	info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE41) ? true : false;
186 	info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE42) ? true : false;
187 
188 	if (FLAC__AVX_SUPPORTED) {
189 		x86_osxsave     = (flags_ecx & FLAC__CPUINFO_X86_CPUID_OSXSAVE) ? true : false;
190 		info->x86.avx   = (flags_ecx & FLAC__CPUINFO_X86_CPUID_AVX    ) ? true : false;
191 		info->x86.fma   = (flags_ecx & FLAC__CPUINFO_X86_CPUID_FMA    ) ? true : false;
192 		cpuinfo_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
193 		info->x86.avx2  = (flags_ebx & FLAC__CPUINFO_X86_CPUID_AVX2   ) ? true : false;
194 	}
195 
196 #if defined FLAC__CPU_IA32
197 	dfprintf(stderr, "CPU info (IA-32):\n");
198 #else
199 	dfprintf(stderr, "CPU info (x86-64):\n");
200 #endif
201 	dfprintf(stderr, "  CMOV ....... %c\n", info->x86.cmov    ? 'Y' : 'n');
202 	dfprintf(stderr, "  MMX ........ %c\n", info->x86.mmx     ? 'Y' : 'n');
203 	dfprintf(stderr, "  SSE ........ %c\n", info->x86.sse     ? 'Y' : 'n');
204 	dfprintf(stderr, "  SSE2 ....... %c\n", info->x86.sse2    ? 'Y' : 'n');
205 	dfprintf(stderr, "  SSE3 ....... %c\n", info->x86.sse3    ? 'Y' : 'n');
206 	dfprintf(stderr, "  SSSE3 ...... %c\n", info->x86.ssse3   ? 'Y' : 'n');
207 	dfprintf(stderr, "  SSE41 ...... %c\n", info->x86.sse41   ? 'Y' : 'n');
208 	dfprintf(stderr, "  SSE42 ...... %c\n", info->x86.sse42   ? 'Y' : 'n');
209 
210 	if (FLAC__AVX_SUPPORTED) {
211 		dfprintf(stderr, "  AVX ........ %c\n", info->x86.avx     ? 'Y' : 'n');
212 		dfprintf(stderr, "  FMA ........ %c\n", info->x86.fma     ? 'Y' : 'n');
213 		dfprintf(stderr, "  AVX2 ....... %c\n", info->x86.avx2    ? 'Y' : 'n');
214 	}
215 
216 	/*
217 	 * now have to check for OS support of AVX instructions
218 	 */
219 	if (FLAC__AVX_SUPPORTED && info->x86.avx && x86_osxsave && (cpu_xgetbv_x86() & 0x6) == 0x6) {
220 		os_avx = true;
221 	}
222 	if (os_avx) {
223 		dfprintf(stderr, "  AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
224 	}
225 	if (!os_avx) {
226 		/* no OS AVX support */
227 		info->x86.avx     = false;
228 		info->x86.avx2    = false;
229 		info->x86.fma     = false;
230 	}
231 #else
232 	info->use_asm = false;
233 #endif
234 }
235 
236 static void
ppc_cpu_info(FLAC__CPUInfo * info)237 ppc_cpu_info (FLAC__CPUInfo *info)
238 {
239 #if defined FLAC__CPU_PPC
240 #ifndef PPC_FEATURE2_ARCH_3_00
241 #define PPC_FEATURE2_ARCH_3_00		0x00800000
242 #endif
243 
244 #ifndef PPC_FEATURE2_ARCH_2_07
245 #define PPC_FEATURE2_ARCH_2_07		0x80000000
246 #endif
247 
248 	if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_3_00) {
249 		info->ppc.arch_3_00 = true;
250 	} else if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_2_07) {
251 		info->ppc.arch_2_07 = true;
252 	}
253 #else
254 	info->ppc.arch_2_07 = false;
255 	info->ppc.arch_3_00 = false;
256 #endif
257 }
258 
FLAC__cpu_info(FLAC__CPUInfo * info)259 void FLAC__cpu_info (FLAC__CPUInfo *info)
260 {
261 	memset(info, 0, sizeof(*info));
262 
263 #ifdef FLAC__CPU_IA32
264 	info->type = FLAC__CPUINFO_TYPE_IA32;
265 #elif defined FLAC__CPU_X86_64
266 	info->type = FLAC__CPUINFO_TYPE_X86_64;
267 #elif defined FLAC__CPU_PPC
268 	info->type = FLAC__CPUINFO_TYPE_PPC;
269 #else
270 	info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
271 #endif
272 
273 	switch (info->type) {
274 	case FLAC__CPUINFO_TYPE_IA32: /* fallthrough */
275 	case FLAC__CPUINFO_TYPE_X86_64:
276 		x86_cpu_info (info);
277 		break;
278 	case FLAC__CPUINFO_TYPE_PPC:
279 		ppc_cpu_info (info);
280 		break;
281 	default:
282 		info->use_asm = false;
283 		break;
284 	}
285 }
286