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
47static 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
57static 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
66static 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 */
91static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000;
92static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000;
93static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000;
94static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000;
95static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000;
96#endif
97
98/* these are flags in ECX of CPUID AX=00000001 */
99static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001;
100static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200;
101static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000;
102static 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 */
106static const unsigned FLAC__CPUINFO_IA32_CPUID_OSXSAVE = 0x08000000;
107static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX = 0x10000000;
108static const unsigned FLAC__CPUINFO_IA32_CPUID_FMA = 0x00001000;
109/* these are flags in EBX of CPUID AX=00000007 */
110static 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>
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
143void 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(__ANDROID__) || defined(ANDROID)
244		/* no need to check OS SSE support */
245#elif defined(__linux__)
246		int sse = 0;
247		struct sigaction sigill_save;
248		struct sigaction sigill_sse;
249		sigill_sse.sa_sigaction = sigill_handler_sse_os;
250		__sigemptyset(&sigill_sse.sa_mask);
251		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 */
252		if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save))
253		{
254			/* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */
255			/* see sigill_handler_sse_os() for an explanation of the following: */
256			asm volatile (
257				"xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */
258				"incl %0\n\t"             /* SIGILL handler will jump over this */
259				/* landing zone */
260				"nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */
261				"nop\n\t"
262				"nop\n\t"
263				"nop\n\t"
264				"nop\n\t"
265				"nop\n\t"
266				"nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */
267				"nop\n\t"
268				"nop"     /* SIGILL jump lands here if "inc" is 1 byte */
269				: "=r"(sse)
270				: "0"(sse)
271			);
272
273			sigaction(SIGILL, &sigill_save, NULL);
274		}
275
276		if(!sse)
277			disable_sse(info);
278#elif defined(_MSC_VER)
279		__try {
280			__asm {
281				xorps xmm0,xmm0
282			}
283		}
284		__except(EXCEPTION_EXECUTE_HANDLER) {
285			if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
286				disable_sse(info);
287		}
288#elif defined(__GNUC__) /* MinGW goes here */
289		int sse = 0;
290		/* Based on the idea described in Agner Fog's manual "Optimizing subroutines in assembly language" */
291		/* In theory, not guaranteed to detect lack of OS SSE support on some future Intel CPUs, but in practice works (see the aforementioned manual) */
292		if (ia32_fxsr) {
293			struct {
294				FLAC__uint32 buff[128];
295			} __attribute__((aligned(16))) fxsr;
296			FLAC__uint32 old_val, new_val;
297
298			asm volatile ("fxsave %0"  : "=m" (fxsr) : "m" (fxsr));
299			old_val = fxsr.buff[50];
300			fxsr.buff[50] ^= 0x0013c0de;                             /* change value in the buffer */
301			asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* try to change SSE register */
302			fxsr.buff[50] = old_val;                                 /* restore old value in the buffer */
303			asm volatile ("fxsave %0 " : "=m" (fxsr) : "m" (fxsr));  /* old value will be overwritten if SSE register was changed */
304			new_val = fxsr.buff[50];                                 /* == old_val if FXRSTOR didn't change SSE register and (old_val ^ 0x0013c0de) otherwise */
305			fxsr.buff[50] = old_val;                                 /* again restore old value in the buffer */
306			asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* restore old values of registers */
307
308			if ((old_val^new_val) == 0x0013c0de)
309				sse = 1;
310		}
311		if(!sse)
312			disable_sse(info);
313#else
314		/* no way to test, disable to be safe */
315		disable_sse(info);
316#endif
317#ifdef DEBUG
318		fprintf(stderr, "  SSE OS sup . %c\n", info->ia32.sse ? 'Y' : 'n');
319#endif
320	}
321	else /* info->ia32.sse == false */
322		disable_sse(info);
323
324	/*
325	 * now have to check for OS support of AVX instructions
326	 */
327	if(info->ia32.avx && ia32_osxsave) {
328		FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
329		if ((ecr & 0x6) != 0x6)
330			disable_avx(info);
331#ifdef DEBUG
332		fprintf(stderr, "  AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n');
333#endif
334	}
335	else /* no OS AVX support*/
336		disable_avx(info);
337#else
338	info->use_asm = false;
339#endif
340
341/*
342 * x86-64-specific
343 */
344#elif defined FLAC__CPU_X86_64
345	FLAC__bool x86_osxsave = false;
346	(void) x86_osxsave; /* to avoid warnings about unused variables */
347	memset(info, 0, sizeof(*info));
348	info->type = FLAC__CPUINFO_TYPE_X86_64;
349#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN
350	info->use_asm = true;
351	{
352		/* http://www.sandpile.org/x86/cpuid.htm */
353		FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
354		FLAC__cpu_info_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
355		info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E)? true : false; /* GenuineIntel */
356		FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
357		info->x86.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
358		info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
359		info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
360		info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
361#if defined FLAC__AVX_SUPPORTED
362		    x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false;
363		info->x86.avx   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX    )? true : false;
364		info->x86.fma   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA    )? true : false;
365		FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
366		info->x86.avx2  = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2   )? true : false;
367#endif
368	}
369#ifdef DEBUG
370	fprintf(stderr, "CPU info (x86-64):\n");
371	fprintf(stderr, "  SSE3 ....... %c\n", info->x86.sse3  ? 'Y' : 'n');
372	fprintf(stderr, "  SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
373	fprintf(stderr, "  SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
374	fprintf(stderr, "  SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
375# if defined FLAC__AVX_SUPPORTED
376	fprintf(stderr, "  AVX ........ %c\n", info->x86.avx   ? 'Y' : 'n');
377	fprintf(stderr, "  FMA ........ %c\n", info->x86.fma   ? 'Y' : 'n');
378	fprintf(stderr, "  AVX2 ....... %c\n", info->x86.avx2  ? 'Y' : 'n');
379# endif
380#endif
381
382	/*
383	 * now have to check for OS support of AVX instructions
384	 */
385	if(info->x86.avx && x86_osxsave) {
386		FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
387		if ((ecr & 0x6) != 0x6)
388			disable_avx(info);
389#ifdef DEBUG
390		fprintf(stderr, "  AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
391#endif
392	}
393	else /* no OS AVX support*/
394		disable_avx(info);
395#else
396	info->use_asm = false;
397#endif
398
399/*
400 * unknown CPU
401 */
402#else
403	info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
404	info->use_asm = false;
405#endif
406}
407
408#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN
409
410#if defined _MSC_VER
411#include <intrin.h> /* for __cpuid() and _xgetbv() */
412#elif defined __GNUC__ && defined HAVE_CPUID_H
413#include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
414#endif
415
416FLAC__uint32 FLAC__cpu_have_cpuid_x86(void)
417{
418#ifdef FLAC__CPU_X86_64
419	return 1;
420#else
421# if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */
422	FLAC__uint32 flags1, flags2;
423	__asm {
424		pushfd
425		pushfd
426		pop		eax
427		mov		flags1, eax
428		xor		eax, 0x200000
429		push	eax
430		popfd
431		pushfd
432		pop		eax
433		mov		flags2, eax
434		popfd
435	}
436	if (((flags1^flags2) & 0x200000) != 0)
437		return 1;
438	else
439		return 0;
440# elif defined __GNUC__ && defined HAVE_CPUID_H
441	if (__get_cpuid_max(0, 0) != 0)
442		return 1;
443	else
444		return 0;
445# else
446	return 0;
447# endif
448#endif
449}
450
451void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
452{
453#if defined _MSC_VER || defined __INTEL_COMPILER
454	int cpuinfo[4];
455	int ext = level & 0x80000000;
456	__cpuid(cpuinfo, ext);
457	if((unsigned)cpuinfo[0] < level) {
458		*eax = *ebx = *ecx = *edx = 0;
459		return;
460	}
461#if defined FLAC__AVX_SUPPORTED
462	__cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
463#else
464	__cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
465#endif
466	*eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
467#elif defined __GNUC__ && defined HAVE_CPUID_H
468	FLAC__uint32 ext = level & 0x80000000;
469	__cpuid(ext, *eax, *ebx, *ecx, *edx);
470	if (*eax < level) {
471		*eax = *ebx = *ecx = *edx = 0;
472		return;
473	}
474	__cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
475#else
476	*eax = *ebx = *ecx = *edx = 0;
477#endif
478}
479
480FLAC__uint32 FLAC__cpu_xgetbv_x86(void)
481{
482#if (defined _MSC_VER || defined __INTEL_COMPILER) && defined FLAC__AVX_SUPPORTED
483	return (FLAC__uint32)_xgetbv(0);
484#elif defined __GNUC__
485	FLAC__uint32 lo, hi;
486	asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
487	return lo;
488#else
489	return 0;
490#endif
491}
492
493#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */
494