cpu.c 15.9 KB
Newer Older
Josh Coalson's avatar
Josh Coalson committed
1
/* libFLAC - Free Lossless Audio Codec library
2
3
 * Copyright (C) 2001-2009  Josh Coalson
 * Copyright (C) 2011-2013  Xiph.Org Foundation
Josh Coalson's avatar
Josh Coalson committed
4
 *
5
6
7
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
Josh Coalson's avatar
Josh Coalson committed
8
 *
9
10
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
Josh Coalson's avatar
Josh Coalson committed
11
 *
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Xiph.org Foundation nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Josh Coalson's avatar
Josh Coalson committed
31
32
 */

Josh Coalson's avatar
Josh Coalson committed
33
34
35
36
#if HAVE_CONFIG_H
#  include <config.h>
#endif

Josh Coalson's avatar
Josh Coalson committed
37
#include "private/cpu.h"
38
39
#include <stdlib.h>
#include <stdio.h>
Josh Coalson's avatar
Josh Coalson committed
40

41
42
43
#if defined FLAC__CPU_IA32
# include <signal.h>
#elif defined FLAC__CPU_PPC
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# if !defined FLAC__NO_ASM
#  if defined FLAC__SYS_DARWIN
#   include <sys/sysctl.h>
#   include <mach/mach.h>
#   include <mach/mach_host.h>
#   include <mach/host_info.h>
#   include <mach/machine.h>
#   ifndef CPU_SUBTYPE_POWERPC_970
#    define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100)
#   endif
#  else /* FLAC__SYS_DARWIN */

#   include <signal.h>
#   include <setjmp.h>
58
59
60
61
62
63
64
65
66
67
68
69
70

static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump = 0;

static void sigill_handler (int sig)
{
	if (!canjump) {
		signal (sig, SIG_DFL);
		raise (sig);
	}
	canjump = 0;
	siglongjmp (jmpbuf, 1);
}
71
72
#  endif /* FLAC__SYS_DARWIN */
# endif /* FLAC__NO_ASM */
73
74
#endif /* FLAC__CPU_PPC */

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#if defined (__NetBSD__) || defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#endif

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif

#if defined(__APPLE__)
/* how to get sysctlbyname()? */
#endif

90
91
92
93
94
95
96
97
98
99
100
101
102
/* these are flags in EDX of CPUID AX=00000001 */
static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000;
/* these are flags in ECX of CPUID AX=00000001 */
static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001;
static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200;
/* these are flags in EDX of CPUID AX=80000001 */
static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000;
static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000;
Josh Coalson's avatar
Josh Coalson committed
103

Josh Coalson's avatar
Josh Coalson committed
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Extra stuff needed for detection of OS support for SSE on IA-32
 */
#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS
# if defined(__linux__)
/*
 * If the OS doesn't support SSE, we will get here with a SIGILL.  We
 * modify the return address to jump over the offending SSE instruction
 * and also the operation following it that indicates the instruction
 * executed successfully.  In this way we use no global variables and
 * stay thread-safe.
 *
 * 3 + 3 + 6:
 *   3 bytes for "xorps xmm0,xmm0"
 *   3 bytes for estimate of how long the follwing "inc var" instruction is
 *   6 bytes extra in case our estimate is wrong
 * 12 bytes puts us in the NOP "landing zone"
 */
123
#  undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#  ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR
	static void sigill_handler_sse_os(int signal, struct sigcontext sc)
	{
		(void)signal;
		sc.eip += 3 + 3 + 6;
	}
#  else
#   include <sys/ucontext.h>
	static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc)
	{
		(void)signal, (void)si;
		((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6;
	}
#  endif
# elif defined(_MSC_VER)
#  include <windows.h>
140
#  define USE_TRY_CATCH_FLAVOR /* sigill_handler flavor resulted in several crash reports on win32 */
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#  ifdef USE_TRY_CATCH_FLAVOR
#  else
	LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep)
	{
		if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
			ep->ContextRecord->Eip += 3 + 3 + 6;
			return EXCEPTION_CONTINUE_EXECUTION;
		}
		return EXCEPTION_CONTINUE_SEARCH;
	}
#  endif
# endif
#endif


Josh Coalson's avatar
Josh Coalson committed
156
157
void FLAC__cpu_info(FLAC__CPUInfo *info)
{
158
159
160
/*
 * IA32-specific
 */
Josh Coalson's avatar
Josh Coalson committed
161
162
163
#ifdef FLAC__CPU_IA32
	info->type = FLAC__CPUINFO_TYPE_IA32;
#if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM
164
165
166
167
168
169
170
171
172
173
174
175
176
177
	info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */
	info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false;
	info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */
	info->data.ia32.cmov = false;
	info->data.ia32.mmx = false;
	info->data.ia32.fxsr = false;
	info->data.ia32.sse = false;
	info->data.ia32.sse2 = false;
	info->data.ia32.sse3 = false;
	info->data.ia32.ssse3 = false;
	info->data.ia32._3dnow = false;
	info->data.ia32.ext3dnow = false;
	info->data.ia32.extmmx = false;
	if(info->data.ia32.cpuid) {
178
		/* http://www.sandpile.org/x86/cpuid.htm */
179
180
181
182
183
184
185
186
187
		FLAC__uint32 flags_edx, flags_ecx;
		FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx);
		info->data.ia32.cmov  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false;
		info->data.ia32.mmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX  )? true : false;
		info->data.ia32.fxsr  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false;
		info->data.ia32.sse   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE  )? true : false;
		info->data.ia32.sse2  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false;
		info->data.ia32.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
		info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
188

189
#ifdef FLAC__USE_3DNOW
190
191
192
193
		flags_edx = FLAC__cpu_info_extended_amd_asm_ia32();
		info->data.ia32._3dnow   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW   )? true : false;
		info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false;
		info->data.ia32.extmmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX  )? true : false;
194
195
196
#else
		info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false;
#endif
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#ifdef DEBUG
		fprintf(stderr, "CPU info (IA-32):\n");
		fprintf(stderr, "  CPUID ...... %c\n", info->data.ia32.cpuid   ? 'Y' : 'n');
		fprintf(stderr, "  BSWAP ...... %c\n", info->data.ia32.bswap   ? 'Y' : 'n');
		fprintf(stderr, "  CMOV ....... %c\n", info->data.ia32.cmov    ? 'Y' : 'n');
		fprintf(stderr, "  MMX ........ %c\n", info->data.ia32.mmx     ? 'Y' : 'n');
		fprintf(stderr, "  FXSR ....... %c\n", info->data.ia32.fxsr    ? 'Y' : 'n');
		fprintf(stderr, "  SSE ........ %c\n", info->data.ia32.sse     ? 'Y' : 'n');
		fprintf(stderr, "  SSE2 ....... %c\n", info->data.ia32.sse2    ? 'Y' : 'n');
		fprintf(stderr, "  SSE3 ....... %c\n", info->data.ia32.sse3    ? 'Y' : 'n');
		fprintf(stderr, "  SSSE3 ...... %c\n", info->data.ia32.ssse3   ? 'Y' : 'n');
		fprintf(stderr, "  3DNow! ..... %c\n", info->data.ia32._3dnow  ? 'Y' : 'n');
		fprintf(stderr, "  3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n');
		fprintf(stderr, "  3DNow!-MMX . %c\n", info->data.ia32.extmmx  ? 'Y' : 'n');
#endif

214
215
216
217
218
219
		/*
		 * now have to check for OS support of SSE/SSE2
		 */
		if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) {
#if defined FLAC__NO_SSE_OS
			/* assume user knows better than us; turn it off */
220
			info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
221
222
223
224
225
226
227
228
229
#elif defined FLAC__SSE_OS
			/* assume user knows better than us; leave as detected above */
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__)
			int sse = 0;
			size_t len;
			/* at least one of these must work: */
			len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse);
			len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse"   , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */
			if(!sse)
230
				info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
231
232
233
234
235
#elif defined(__NetBSD__) || defined (__OpenBSD__)
# if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__)
			int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE };
			size_t len = sizeof(val);
			if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
236
				info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
237
238
239
240
			else { /* double-check SSE2 */
				mib[1] = CPU_SSE2;
				len = sizeof(val);
				if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
241
					info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
242
243
			}
# else
244
			info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# endif
#elif defined(__linux__)
			int sse = 0;
			struct sigaction sigill_save;
#ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR
			if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR)
#else
			struct sigaction sigill_sse;
			sigill_sse.sa_sigaction = sigill_handler_sse_os;
			__sigemptyset(&sigill_sse.sa_mask);
			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 */
			if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save))
#endif
			{
				/* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */
				/* see sigill_handler_sse_os() for an explanation of the following: */
				asm volatile (
					"xorl %0,%0\n\t"          /* for some reason, still need to do this to clear 'sse' var */
					"xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */
					"incl %0\n\t"             /* SIGILL handler will jump over this */
					/* landing zone */
					"nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */
					"nop\n\t"
					"nop\n\t"
					"nop\n\t"
					"nop\n\t"
					"nop\n\t"
					"nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */
					"nop\n\t"
					"nop"     /* SIGILL jump lands here if "inc" is 1 byte */
					: "=r"(sse)
					: "r"(sse)
				);

				sigaction(SIGILL, &sigill_save, NULL);
			}

			if(!sse)
283
				info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
284
285
#elif defined(_MSC_VER)
# ifdef USE_TRY_CATCH_FLAVOR
286
			__try {
287
288
289
				__asm {
					xorps xmm0,xmm0
				}
290
			}
291
			__except(EXCEPTION_EXECUTE_HANDLER) {
292
				if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
293
					info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
294
295
296
297
298
			}
# else
			int sse = 0;
			LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os);
			/* see GCC version above for explanation */
Josh Coalson's avatar
Josh Coalson committed
299
300
301
			/*  http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */
			/*  http://www.codeproject.com/cpp/gccasm.asp */
			/*  http://www.hick.org/~mmiller/msvc_inline_asm.html */
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
			__asm {
				xorps xmm0,xmm0
				inc sse
				nop
				nop
				nop
				nop
				nop
				nop
				nop
				nop
				nop
			}
			SetUnhandledExceptionFilter(save);
			if(!sse)
317
				info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
318
319
320
# endif
#else
			/* no way to test, disable to be safe */
321
322
323
324
			info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false;
#endif
#ifdef DEBUG
		fprintf(stderr, "  SSE OS sup . %c\n", info->data.ia32.sse     ? 'Y' : 'n');
325
#endif
326

327
		}
Josh Coalson's avatar
Josh Coalson committed
328
329
330
331
	}
#else
	info->use_asm = false;
#endif
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*
 * x86-64-specific
 */
#elif defined FLAC__CPU_X86_64
	info->type = FLAC__CPUINFO_TYPE_X86_64;
#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN
	info->use_asm = true;
	info->data.x86_64.sse3 = false;
	info->data.x86_64.ssse3 = false;
	{
		/* http://www.sandpile.org/x86/cpuid.htm */
		FLAC__uint32 flags_edx, flags_ecx;
		FLAC__cpu_info_x86(&flags_edx, &flags_ecx);
		info->data.x86_64.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
		info->data.x86_64.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
	}
#ifdef DEBUG
	fprintf(stderr, "CPU info (x86-64):\n");
	fprintf(stderr, "  SSE3 ....... %c\n", info->data.x86_64.sse3    ? 'Y' : 'n');
	fprintf(stderr, "  SSSE3 ...... %c\n", info->data.x86_64.ssse3   ? 'Y' : 'n');
#endif

#else
	info->use_asm = false;
#endif

359
360
361
/*
 * PPC-specific
 */
362
363
#elif defined FLAC__CPU_PPC
	info->type = FLAC__CPUINFO_TYPE_PPC;
364
# if !defined FLAC__NO_ASM
365
	info->use_asm = true;
366
367
#  ifdef FLAC__USE_ALTIVEC
#   if defined FLAC__SYS_DARWIN
368
	{
369
370
371
		int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT };
		size_t len = sizeof(val);
		info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val);
372
	}
373
374
375
376
377
378
379
380
381
	{
		host_basic_info_data_t hostInfo;
		mach_msg_type_number_t infoCount;

		infoCount = HOST_BASIC_INFO_COUNT;
		host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount);

		info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970);
	}
382
#   else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */
383
384
	{
		/* no Darwin, do it the brute-force way */
385
		/* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */
386
387
388
389
		info->data.ppc.altivec = 0;
		info->data.ppc.ppc64 = 0;

		signal (SIGILL, sigill_handler);
390
		canjump = 0;
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
		if (!sigsetjmp (jmpbuf, 1)) {
			canjump = 1;

			asm volatile (
				"mtspr 256, %0\n\t"
				"vand %%v0, %%v0, %%v0"
				:
				: "r" (-1)
			);

			info->data.ppc.altivec = 1;
		}
		canjump = 0;
		if (!sigsetjmp (jmpbuf, 1)) {
			int x = 0;
			canjump = 1;

			/* PPC64 hardware implements the cntlzd instruction */
			asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) );

			info->data.ppc.ppc64 = 1;
		}
413
		signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */
414
	}
415
416
#   endif
#  else /* !FLAC__USE_ALTIVEC */
417
	info->data.ppc.altivec = 0;
418
	info->data.ppc.ppc64 = 0;
419
420
#  endif
# else
421
	info->use_asm = false;
422
423
424
# endif

/*
425
 * unknown CPU
426
 */
Josh Coalson's avatar
Josh Coalson committed
427
428
429
430
431
#else
	info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
	info->use_asm = false;
#endif
}
432

433
#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458

#if defined _MSC_VER && (_MSC_VER >= 1400)
#include <intrin.h> /* for __cpuid() */
#endif

void FLAC__cpu_info_x86(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx)
{
#if defined _MSC_VER && (_MSC_VER >= 1400)
		int cpuinfo[4];
		__cpuid(cpuinfo, 1);
		*flags_ecx = cpuinfo[2];
		*flags_edx = cpuinfo[3];
#elif defined __GNUC__ && __GNUC__
		FLAC__uint32 info = 1, flags_eax, flags_ebx;
		__asm__ __volatile__ (
			"xchg %%ebx, %%edi;"
			"cpuid;"
			"xchg %%edi, %%ebx;"
			:"=a" (flags_eax), "=D" (flags_ebx), "=c" (*flags_ecx), "=d" (*flags_edx)
			:"a" (info)
		);
#else
		*flags_ecx = *flags_edx = 0;
#endif
}
459
#endif /* (FLAC__CPU_IA32 || FLAC__HAS_X86INTRIN) && FLAC__CPU_X86_64 */