Home | History | Annotate | Download | only in libFLAC
      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 
     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(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 
    416 FLAC__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 
    451 void 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 
    480 FLAC__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