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(__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
414FLAC__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
449void 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
478FLAC__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