1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001-2009  Josh Coalson
3  * Copyright (C) 2011-2014  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 <stdlib.h>
39 #include <memory.h>
40 #ifdef DEBUG
41 # include <stdio.h>
42 #endif
43 
44 #if defined FLAC__CPU_IA32
45 # include <signal.h>
46 
disable_sse(FLAC__CPUInfo * info)47 static void disable_sse(FLAC__CPUInfo *info)
48 {
49 	info->ia32.sse   = false;
50 	info->ia32.sse2  = false;
51 	info->ia32.sse3  = false;
52 	info->ia32.ssse3 = false;
53 	info->ia32.sse41 = false;
54 	info->ia32.sse42 = false;
55 }
56 
disable_avx(FLAC__CPUInfo * info)57 static void disable_avx(FLAC__CPUInfo *info)
58 {
59 	info->ia32.avx     = false;
60 	info->ia32.avx2    = false;
61 	info->ia32.fma     = false;
62 }
63 
64 #elif defined FLAC__CPU_X86_64
65 
disable_avx(FLAC__CPUInfo * info)66 static void disable_avx(FLAC__CPUInfo *info)
67 {
68 	info->x86.avx     = false;
69 	info->x86.avx2    = false;
70 	info->x86.fma     = false;
71 }
72 #endif
73 
74 #if defined (__NetBSD__) || defined(__OpenBSD__)
75 #include <sys/param.h>
76 #include <sys/sysctl.h>
77 #include <machine/cpu.h>
78 #endif
79 
80 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
81 #include <sys/types.h>
82 #include <sys/sysctl.h>
83 #endif
84 
85 #if defined(__APPLE__)
86 /* how to get sysctlbyname()? */
87 #endif
88 
89 #ifdef FLAC__CPU_IA32
90 /* these are flags in EDX of CPUID AX=00000001 */
91 static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000;
92 static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000;
93 static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000;
94 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000;
95 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000;
96 #endif
97 
98 /* these are flags in ECX of CPUID AX=00000001 */
99 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001;
100 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200;
101 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000;
102 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE42 = 0x00100000;
103 
104 #if defined FLAC__AVX_SUPPORTED
105 /* these are flags in ECX of CPUID AX=00000001 */
106 static const unsigned FLAC__CPUINFO_IA32_CPUID_OSXSAVE = 0x08000000;
107 static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX = 0x10000000;
108 static const unsigned FLAC__CPUINFO_IA32_CPUID_FMA = 0x00001000;
109 /* these are flags in EBX of CPUID AX=00000007 */
110 static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX2 = 0x00000020;
111 #endif
112 
113 /*
114  * Extra stuff needed for detection of OS support for SSE on IA-32
115  */
116 #if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN) && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS
117 # if defined(__linux__)
118 /*
119  * If the OS doesn't support SSE, we will get here with a SIGILL.  We
120  * modify the return address to jump over the offending SSE instruction
121  * and also the operation following it that indicates the instruction
122  * executed successfully.  In this way we use no global variables and
123  * stay thread-safe.
124  *
125  * 3 + 3 + 6:
126  *   3 bytes for "xorps xmm0,xmm0"
127  *   3 bytes for estimate of how long the follwing "inc var" instruction is
128  *   6 bytes extra in case our estimate is wrong
129  * 12 bytes puts us in the NOP "landing zone"
130  */
131 #   include <sys/ucontext.h>
sigill_handler_sse_os(int signal,siginfo_t * si,void * uc)132 	static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc)
133 	{
134 		(void)signal, (void)si;
135 		((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6;
136 	}
137 # elif defined(_MSC_VER)
138 #  include <windows.h>
139 # endif
140 #endif
141 
142 
FLAC__cpu_info(FLAC__CPUInfo * info)143 void FLAC__cpu_info(FLAC__CPUInfo *info)
144 {
145 /*
146  * IA32-specific
147  */
148 #ifdef FLAC__CPU_IA32
149 	FLAC__bool ia32_fxsr = false;
150 	FLAC__bool ia32_osxsave = false;
151 	(void) ia32_fxsr; (void) ia32_osxsave; /* to avoid warnings about unused variables */
152 	memset(info, 0, sizeof(*info));
153 	info->type = FLAC__CPUINFO_TYPE_IA32;
154 #if !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN)
155 	info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */
156 #ifdef FLAC__HAS_X86INTRIN
157 	if(!FLAC__cpu_have_cpuid_x86())
158 		return;
159 #else
160 	if(!FLAC__cpu_have_cpuid_asm_ia32())
161 		return;
162 #endif
163 	{
164 		/* http://www.sandpile.org/x86/cpuid.htm */
165 #ifdef FLAC__HAS_X86INTRIN
166 		FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
167 		FLAC__cpu_info_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
168 		info->ia32.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E)? true : false; /* GenuineIntel */
169 		FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
170 #else
171 		FLAC__uint32 flags_ecx, flags_edx;
172 		FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx);
173 #endif
174 		info->ia32.cmov  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false;
175 		info->ia32.mmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX  )? true : false;
176 		      ia32_fxsr  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false;
177 		info->ia32.sse   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE  )? true : false;
178 		info->ia32.sse2  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false;
179 		info->ia32.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
180 		info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
181 		info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
182 		info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
183 #if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED
184 		    ia32_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false;
185 		info->ia32.avx   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX    )? true : false;
186 		info->ia32.fma   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA    )? true : false;
187 		FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
188 		info->ia32.avx2  = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2   )? true : false;
189 #endif
190 	}
191 
192 #ifdef DEBUG
193 	fprintf(stderr, "CPU info (IA-32):\n");
194 	fprintf(stderr, "  CMOV ....... %c\n", info->ia32.cmov    ? 'Y' : 'n');
195 	fprintf(stderr, "  MMX ........ %c\n", info->ia32.mmx     ? 'Y' : 'n');
196 	fprintf(stderr, "  SSE ........ %c\n", info->ia32.sse     ? 'Y' : 'n');
197 	fprintf(stderr, "  SSE2 ....... %c\n", info->ia32.sse2    ? 'Y' : 'n');
198 	fprintf(stderr, "  SSE3 ....... %c\n", info->ia32.sse3    ? 'Y' : 'n');
199 	fprintf(stderr, "  SSSE3 ...... %c\n", info->ia32.ssse3   ? 'Y' : 'n');
200 	fprintf(stderr, "  SSE41 ...... %c\n", info->ia32.sse41   ? 'Y' : 'n');
201 	fprintf(stderr, "  SSE42 ...... %c\n", info->ia32.sse42   ? 'Y' : 'n');
202 # if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED
203 	fprintf(stderr, "  AVX ........ %c\n", info->ia32.avx     ? 'Y' : 'n');
204 	fprintf(stderr, "  FMA ........ %c\n", info->ia32.fma     ? 'Y' : 'n');
205 	fprintf(stderr, "  AVX2 ....... %c\n", info->ia32.avx2    ? 'Y' : 'n');
206 # endif
207 #endif
208 
209 	/*
210 	 * now have to check for OS support of SSE instructions
211 	 */
212 	if(info->ia32.sse) {
213 #if defined FLAC__NO_SSE_OS
214 		/* assume user knows better than us; turn it off */
215 		disable_sse(info);
216 #elif defined FLAC__SSE_OS
217 		/* assume user knows better than us; leave as detected above */
218 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__)
219 		int sse = 0;
220 		size_t len;
221 		/* at least one of these must work: */
222 		len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse);
223 		len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse"   , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */
224 		if(!sse)
225 			disable_sse(info);
226 #elif defined(__NetBSD__) || defined (__OpenBSD__)
227 # if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__)
228 		int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE };
229 		size_t len = sizeof(val);
230 		if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
231 			disable_sse(info);
232 		else { /* double-check SSE2 */
233 			mib[1] = CPU_SSE2;
234 			len = sizeof(val);
235 			if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) {
236 				disable_sse(info);
237 				info->ia32.sse = true;
238 			}
239 		}
240 # else
241 		disable_sse(info);
242 # endif
243 #elif defined(__linux__)
244 		int sse = 0;
245 		struct sigaction sigill_save;
246 		struct sigaction sigill_sse;
247 		sigill_sse.sa_sigaction = sigill_handler_sse_os;
248 		__sigemptyset(&sigill_sse.sa_mask);
249 		sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */
250 		if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save))
251 		{
252 			/* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */
253 			/* see sigill_handler_sse_os() for an explanation of the following: */
254 			asm volatile (
255 				"xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */
256 				"incl %0\n\t"             /* SIGILL handler will jump over this */
257 				/* landing zone */
258 				"nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */
259 				"nop\n\t"
260 				"nop\n\t"
261 				"nop\n\t"
262 				"nop\n\t"
263 				"nop\n\t"
264 				"nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */
265 				"nop\n\t"
266 				"nop"     /* SIGILL jump lands here if "inc" is 1 byte */
267 				: "=r"(sse)
268 				: "0"(sse)
269 			);
270 
271 			sigaction(SIGILL, &sigill_save, NULL);
272 		}
273 
274 		if(!sse)
275 			disable_sse(info);
276 #elif defined(_MSC_VER)
277 		__try {
278 			__asm {
279 				xorps xmm0,xmm0
280 			}
281 		}
282 		__except(EXCEPTION_EXECUTE_HANDLER) {
283 			if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
284 				disable_sse(info);
285 		}
286 #elif defined(__GNUC__) /* MinGW goes here */
287 		int sse = 0;
288 		/* Based on the idea described in Agner Fog's manual "Optimizing subroutines in assembly language" */
289 		/* In theory, not guaranteed to detect lack of OS SSE support on some future Intel CPUs, but in practice works (see the aforementioned manual) */
290 		if (ia32_fxsr) {
291 			struct {
292 				FLAC__uint32 buff[128];
293 			} __attribute__((aligned(16))) fxsr;
294 			FLAC__uint32 old_val, new_val;
295 
296 			asm volatile ("fxsave %0"  : "=m" (fxsr) : "m" (fxsr));
297 			old_val = fxsr.buff[50];
298 			fxsr.buff[50] ^= 0x0013c0de;                             /* change value in the buffer */
299 			asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* try to change SSE register */
300 			fxsr.buff[50] = old_val;                                 /* restore old value in the buffer */
301 			asm volatile ("fxsave %0 " : "=m" (fxsr) : "m" (fxsr));  /* old value will be overwritten if SSE register was changed */
302 			new_val = fxsr.buff[50];                                 /* == old_val if FXRSTOR didn't change SSE register and (old_val ^ 0x0013c0de) otherwise */
303 			fxsr.buff[50] = old_val;                                 /* again restore old value in the buffer */
304 			asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* restore old values of registers */
305 
306 			if ((old_val^new_val) == 0x0013c0de)
307 				sse = 1;
308 		}
309 		if(!sse)
310 			disable_sse(info);
311 #else
312 		/* no way to test, disable to be safe */
313 		disable_sse(info);
314 #endif
315 #ifdef DEBUG
316 		fprintf(stderr, "  SSE OS sup . %c\n", info->ia32.sse ? 'Y' : 'n');
317 #endif
318 	}
319 	else /* info->ia32.sse == false */
320 		disable_sse(info);
321 
322 	/*
323 	 * now have to check for OS support of AVX instructions
324 	 */
325 	if(info->ia32.avx && ia32_osxsave) {
326 		FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
327 		if ((ecr & 0x6) != 0x6)
328 			disable_avx(info);
329 #ifdef DEBUG
330 		fprintf(stderr, "  AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n');
331 #endif
332 	}
333 	else /* no OS AVX support*/
334 		disable_avx(info);
335 #else
336 	info->use_asm = false;
337 #endif
338 
339 /*
340  * x86-64-specific
341  */
342 #elif defined FLAC__CPU_X86_64
343 	FLAC__bool x86_osxsave = false;
344 	(void) x86_osxsave; /* to avoid warnings about unused variables */
345 	memset(info, 0, sizeof(*info));
346 	info->type = FLAC__CPUINFO_TYPE_X86_64;
347 #if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN
348 	info->use_asm = true;
349 	{
350 		/* http://www.sandpile.org/x86/cpuid.htm */
351 		FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
352 		FLAC__cpu_info_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
353 		info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E)? true : false; /* GenuineIntel */
354 		FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
355 		info->x86.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
356 		info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
357 		info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
358 		info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
359 #if defined FLAC__AVX_SUPPORTED
360 		    x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false;
361 		info->x86.avx   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX    )? true : false;
362 		info->x86.fma   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA    )? true : false;
363 		FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
364 		info->x86.avx2  = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2   )? true : false;
365 #endif
366 	}
367 #ifdef DEBUG
368 	fprintf(stderr, "CPU info (x86-64):\n");
369 	fprintf(stderr, "  SSE3 ....... %c\n", info->x86.sse3  ? 'Y' : 'n');
370 	fprintf(stderr, "  SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
371 	fprintf(stderr, "  SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
372 	fprintf(stderr, "  SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
373 # if defined FLAC__AVX_SUPPORTED
374 	fprintf(stderr, "  AVX ........ %c\n", info->x86.avx   ? 'Y' : 'n');
375 	fprintf(stderr, "  FMA ........ %c\n", info->x86.fma   ? 'Y' : 'n');
376 	fprintf(stderr, "  AVX2 ....... %c\n", info->x86.avx2  ? 'Y' : 'n');
377 # endif
378 #endif
379 
380 	/*
381 	 * now have to check for OS support of AVX instructions
382 	 */
383 	if(info->x86.avx && x86_osxsave) {
384 		FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
385 		if ((ecr & 0x6) != 0x6)
386 			disable_avx(info);
387 #ifdef DEBUG
388 		fprintf(stderr, "  AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
389 #endif
390 	}
391 	else /* no OS AVX support*/
392 		disable_avx(info);
393 #else
394 	info->use_asm = false;
395 #endif
396 
397 /*
398  * unknown CPU
399  */
400 #else
401 	info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
402 	info->use_asm = false;
403 #endif
404 }
405 
406 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN
407 
408 #if defined _MSC_VER
409 #include <intrin.h> /* for __cpuid() and _xgetbv() */
410 #elif defined __GNUC__ && defined HAVE_CPUID_H
411 #include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
412 #endif
413 
FLAC__cpu_have_cpuid_x86(void)414 FLAC__uint32 FLAC__cpu_have_cpuid_x86(void)
415 {
416 #ifdef FLAC__CPU_X86_64
417 	return 1;
418 #else
419 # if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */
420 	FLAC__uint32 flags1, flags2;
421 	__asm {
422 		pushfd
423 		pushfd
424 		pop		eax
425 		mov		flags1, eax
426 		xor		eax, 0x200000
427 		push	eax
428 		popfd
429 		pushfd
430 		pop		eax
431 		mov		flags2, eax
432 		popfd
433 	}
434 	if (((flags1^flags2) & 0x200000) != 0)
435 		return 1;
436 	else
437 		return 0;
438 # elif defined __GNUC__ && defined HAVE_CPUID_H
439 	if (__get_cpuid_max(0, 0) != 0)
440 		return 1;
441 	else
442 		return 0;
443 # else
444 	return 0;
445 # endif
446 #endif
447 }
448 
FLAC__cpu_info_x86(FLAC__uint32 level,FLAC__uint32 * eax,FLAC__uint32 * ebx,FLAC__uint32 * ecx,FLAC__uint32 * edx)449 void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
450 {
451 #if defined _MSC_VER || defined __INTEL_COMPILER
452 	int cpuinfo[4];
453 	int ext = level & 0x80000000;
454 	__cpuid(cpuinfo, ext);
455 	if((unsigned)cpuinfo[0] < level) {
456 		*eax = *ebx = *ecx = *edx = 0;
457 		return;
458 	}
459 #if defined FLAC__AVX_SUPPORTED
460 	__cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
461 #else
462 	__cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
463 #endif
464 	*eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
465 #elif defined __GNUC__ && defined HAVE_CPUID_H
466 	FLAC__uint32 ext = level & 0x80000000;
467 	__cpuid(ext, *eax, *ebx, *ecx, *edx);
468 	if (*eax < level) {
469 		*eax = *ebx = *ecx = *edx = 0;
470 		return;
471 	}
472 	__cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
473 #else
474 	*eax = *ebx = *ecx = *edx = 0;
475 #endif
476 }
477 
FLAC__cpu_xgetbv_x86(void)478 FLAC__uint32 FLAC__cpu_xgetbv_x86(void)
479 {
480 #if (defined _MSC_VER || defined __INTEL_COMPILER) && defined FLAC__AVX_SUPPORTED
481 	return (FLAC__uint32)_xgetbv(0);
482 #elif defined __GNUC__
483 	FLAC__uint32 lo, hi;
484 	asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
485 	return lo;
486 #else
487 	return 0;
488 #endif
489 }
490 
491 #endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */
492