Add AVX/AVX2/FMA support to CPU detection code.
[flac.git] / src / libFLAC / cpu.c
1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001-2009  Josh Coalson
3  * Copyright (C) 2011-2013  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
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
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
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>
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
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(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
168 #else
169                 FLAC__uint32 flags_ecx, flags_edx;
170                 FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx);
171 #endif
172                 info->ia32.cmov  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false;
173                 info->ia32.mmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX  )? true : false;
174                       ia32_fxsr  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false;
175                 info->ia32.sse   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE  )? true : false;
176                 info->ia32.sse2  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false;
177                 info->ia32.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
178                 info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
179                 info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
180                 info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
181 #if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED
182                     ia32_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false;
183                 info->ia32.avx   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX    )? true : false;
184                 info->ia32.fma   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA    )? true : false;
185                 FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
186                 info->ia32.avx2  = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2   )? true : false;
187 #endif
188         }
189
190 #ifdef DEBUG
191         fprintf(stderr, "CPU info (IA-32):\n");
192         fprintf(stderr, "  CMOV ....... %c\n", info->ia32.cmov    ? 'Y' : 'n');
193         fprintf(stderr, "  MMX ........ %c\n", info->ia32.mmx     ? 'Y' : 'n');
194         fprintf(stderr, "  SSE ........ %c\n", info->ia32.sse     ? 'Y' : 'n');
195         fprintf(stderr, "  SSE2 ....... %c\n", info->ia32.sse2    ? 'Y' : 'n');
196         fprintf(stderr, "  SSE3 ....... %c\n", info->ia32.sse3    ? 'Y' : 'n');
197         fprintf(stderr, "  SSSE3 ...... %c\n", info->ia32.ssse3   ? 'Y' : 'n');
198         fprintf(stderr, "  SSE41 ...... %c\n", info->ia32.sse41   ? 'Y' : 'n');
199         fprintf(stderr, "  SSE42 ...... %c\n", info->ia32.sse42   ? 'Y' : 'n');
200 # if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED
201         fprintf(stderr, "  AVX ........ %c\n", info->ia32.avx     ? 'Y' : 'n');
202         fprintf(stderr, "  FMA ........ %c\n", info->ia32.fma     ? 'Y' : 'n');
203         fprintf(stderr, "  AVX2 ....... %c\n", info->ia32.avx2    ? 'Y' : 'n');
204 # endif
205 #endif
206
207         /*
208          * now have to check for OS support of SSE instructions
209          */
210         if(info->ia32.sse) {
211 #if defined FLAC__NO_SSE_OS
212                 /* assume user knows better than us; turn it off */
213                 disable_sse(info);
214 #elif defined FLAC__SSE_OS
215                 /* assume user knows better than us; leave as detected above */
216 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__)
217                 int sse = 0;
218                 size_t len;
219                 /* at least one of these must work: */
220                 len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse);
221                 len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse"   , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */
222                 if(!sse)
223                         disable_sse(info);
224 #elif defined(__NetBSD__) || defined (__OpenBSD__)
225 # if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__)
226                 int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE };
227                 size_t len = sizeof(val);
228                 if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
229                         disable_sse(info);
230                 else { /* double-check SSE2 */
231                         mib[1] = CPU_SSE2;
232                         len = sizeof(val);
233                         if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) {
234                                 disable_sse(info);
235                                 info->ia32.sse = true;
236                         }
237                 }
238 # else
239                 disable_sse(info);
240 # endif
241 #elif defined(__linux__)
242                 int sse = 0;
243                 struct sigaction sigill_save;
244                 struct sigaction sigill_sse;
245                 sigill_sse.sa_sigaction = sigill_handler_sse_os;
246                 __sigemptyset(&sigill_sse.sa_mask);
247                 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 */
248                 if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save))
249                 {
250                         /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */
251                         /* see sigill_handler_sse_os() for an explanation of the following: */
252                         asm volatile (
253                                 "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */
254                                 "incl %0\n\t"             /* SIGILL handler will jump over this */
255                                 /* landing zone */
256                                 "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */
257                                 "nop\n\t"
258                                 "nop\n\t"
259                                 "nop\n\t"
260                                 "nop\n\t"
261                                 "nop\n\t"
262                                 "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */
263                                 "nop\n\t"
264                                 "nop"     /* SIGILL jump lands here if "inc" is 1 byte */
265                                 : "=r"(sse)
266                                 : "0"(sse)
267                         );
268
269                         sigaction(SIGILL, &sigill_save, NULL);
270                 }
271
272                 if(!sse)
273                         disable_sse(info);
274 #elif defined(_MSC_VER)
275                 __try {
276                         __asm {
277                                 xorps xmm0,xmm0
278                         }
279                 }
280                 __except(EXCEPTION_EXECUTE_HANDLER) {
281                         if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
282                                 disable_sse(info);
283                 }
284 #elif defined(__GNUC__) /* MinGW goes here */
285                 int sse = 0;
286                 /* Based on the idea described in Agner Fog's manual "Optimizing subroutines in assembly language" */
287                 /* In theory, not guaranteed to detect lack of OS SSE support on some future Intel CPUs, but in practice works (see the aforementioned manual) */
288                 if (ia32_fxsr) {
289                         struct {
290                                 FLAC__uint32 buff[128];
291                         } __attribute__((aligned(16))) fxsr;
292                         FLAC__uint32 old_val, new_val;
293
294                         asm volatile ("fxsave %0"  : "=m" (fxsr) : "m" (fxsr));
295                         old_val = fxsr.buff[50];
296                         fxsr.buff[50] ^= 0x0013c0de;                             /* change value in the buffer */
297                         asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* try to change SSE register */
298                         fxsr.buff[50] = old_val;                                 /* restore old value in the buffer */
299                         asm volatile ("fxsave %0 " : "=m" (fxsr) : "m" (fxsr));  /* old value will be overwritten if SSE register was changed */
300                         new_val = fxsr.buff[50];                                 /* == old_val if FXRSTOR didn't change SSE register and (old_val ^ 0x0013c0de) otherwise */
301                         fxsr.buff[50] = old_val;                                 /* again restore old value in the buffer */
302                         asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr));  /* restore old values of registers */
303
304                         if ((old_val^new_val) == 0x0013c0de)
305                                 sse = 1;
306                 }
307                 if(!sse)
308                         disable_sse(info);
309 #else
310                 /* no way to test, disable to be safe */
311                 disable_sse(info);
312 #endif
313 #ifdef DEBUG
314                 fprintf(stderr, "  SSE OS sup . %c\n", info->ia32.sse ? 'Y' : 'n');
315 #endif
316         }
317         else /* info->ia32.sse == false */
318                 disable_sse(info);
319
320         /*
321          * now have to check for OS support of AVX instructions
322          */
323         if(info->ia32.avx && ia32_osxsave) {
324                 FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
325                 if ((ecr & 0x6) != 0x6)
326                         disable_avx(info);
327 #ifdef DEBUG
328                 fprintf(stderr, "  AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n');
329 #endif
330         }
331         else /* no OS AVX support*/
332                 disable_avx(info);
333 #else
334         info->use_asm = false;
335 #endif
336
337 /*
338  * x86-64-specific
339  */
340 #elif defined FLAC__CPU_X86_64
341         FLAC__bool x86_osxsave = false;
342         (void) x86_osxsave; /* to avoid warnings about unused variables */
343         memset(info, 0, sizeof(*info));
344         info->type = FLAC__CPUINFO_TYPE_X86_64;
345 #if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN
346         info->use_asm = true;
347         {
348                 /* http://www.sandpile.org/x86/cpuid.htm */
349                 FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
350                 FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
351                 info->x86.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
352                 info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
353                 info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
354                 info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
355 #if defined FLAC__AVX_SUPPORTED
356                     x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false;
357                 info->x86.avx   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX    )? true : false;
358                 info->x86.fma   = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA    )? true : false;
359                 FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
360                 info->x86.avx2  = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2   )? true : false;
361 #endif
362         }
363 #ifdef DEBUG
364         fprintf(stderr, "CPU info (x86-64):\n");
365         fprintf(stderr, "  SSE3 ....... %c\n", info->x86.sse3  ? 'Y' : 'n');
366         fprintf(stderr, "  SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
367         fprintf(stderr, "  SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
368         fprintf(stderr, "  SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
369 # if defined FLAC__AVX_SUPPORTED
370         fprintf(stderr, "  AVX ........ %c\n", info->x86.avx   ? 'Y' : 'n');
371         fprintf(stderr, "  FMA ........ %c\n", info->x86.fma   ? 'Y' : 'n');
372         fprintf(stderr, "  AVX2 ....... %c\n", info->x86.avx2  ? 'Y' : 'n');
373 # endif
374 #endif
375
376         /*
377          * now have to check for OS support of AVX instructions
378          */
379         if(info->x86.avx && x86_osxsave) {
380                 FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86();
381                 if ((ecr & 0x6) != 0x6)
382                         disable_avx(info);
383 #ifdef DEBUG
384                 fprintf(stderr, "  AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
385 #endif
386         }
387         else /* no OS AVX support*/
388                 disable_avx(info);
389 #else
390         info->use_asm = false;
391 #endif
392
393 /*
394  * unknown CPU
395  */
396 #else
397         info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
398         info->use_asm = false;
399 #endif
400 }
401
402 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN
403
404 #if defined _MSC_VER
405 #include <intrin.h> /* for __cpuid() and _xgetbv() */
406 #elif defined __GNUC__ && defined HAVE_CPUID_H
407 #include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
408 #endif
409
410 FLAC__uint32 FLAC__cpu_have_cpuid_x86(void)
411 {
412 #ifdef FLAC__CPU_X86_64
413         return 1;
414 #else
415 # if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */
416         FLAC__uint32 flags1, flags2;
417         __asm {
418                 pushfd
419                 pushfd
420                 pop             eax
421                 mov             flags1, eax
422                 xor             eax, 0x200000
423                 push    eax
424                 popfd
425                 pushfd
426                 pop             eax
427                 mov             flags2, eax
428                 popfd
429         }
430         if (((flags1^flags2) & 0x200000) != 0)
431                 return 1;
432         else
433                 return 0;
434 # elif defined __GNUC__ && defined HAVE_CPUID_H
435         if (__get_cpuid_max(0, 0) != 0)
436                 return 1;
437         else
438                 return 0;
439 # else
440         return 0;
441 # endif
442 #endif
443 }
444
445 void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
446 {
447 #if defined _MSC_VER || defined __INTEL_COMPILER
448         int cpuinfo[4];
449         int ext = level & 0x80000000;
450         __cpuid(cpuinfo, ext);
451         if((unsigned)cpuinfo[0] < level) {
452                 *eax = *ebx = *ecx = *edx = 0;
453                 return;
454         }
455 #if defined FLAC__AVX_SUPPORTED
456         __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
457 #else
458         __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
459 #endif
460         *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
461 #elif defined __GNUC__ && defined HAVE_CPUID_H
462         FLAC__uint32 ext = level & 0x80000000;
463         __cpuid(ext, *eax, *ebx, *ecx, *edx);
464         if (*eax < level) {
465                 *eax = *ebx = *ecx = *edx = 0;
466                 return;
467         }
468         __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
469 #else
470         *eax = *ebx = *ecx = *edx = 0;
471 #endif
472 }
473
474 FLAC__uint32 FLAC__cpu_xgetbv_x86(void)
475 {
476 #if (defined _MSC_VER || defined __INTEL_COMPILER) && defined FLAC__AVX_SUPPORTED
477         return (FLAC__uint32)_xgetbv(0);
478 #elif defined __GNUC__
479         FLAC__uint32 lo, hi;
480         asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
481         return lo;
482 #else
483         return 0;
484 #endif
485 }
486
487 #endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */