src/libFLAC/ : CPU feature detection improvements.
[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 #if HAVE_CONFIG_H
34 #  include <config.h>
35 #endif
36
37 #include "private/cpu.h"
38 #include <stdlib.h>
39 #include <stdio.h>
40
41 #if defined FLAC__CPU_IA32
42 # include <signal.h>
43 #elif defined FLAC__CPU_PPC
44 # if !defined FLAC__NO_ASM
45 #  if defined FLAC__SYS_DARWIN
46 #   include <sys/sysctl.h>
47 #   include <mach/mach.h>
48 #   include <mach/mach_host.h>
49 #   include <mach/host_info.h>
50 #   include <mach/machine.h>
51 #   ifndef CPU_SUBTYPE_POWERPC_970
52 #    define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100)
53 #   endif
54 #  else /* FLAC__SYS_DARWIN */
55
56 #   include <signal.h>
57 #   include <setjmp.h>
58
59 static sigjmp_buf jmpbuf;
60 static volatile sig_atomic_t canjump = 0;
61
62 static void sigill_handler (int sig)
63 {
64         if (!canjump) {
65                 signal (sig, SIG_DFL);
66                 raise (sig);
67         }
68         canjump = 0;
69         siglongjmp (jmpbuf, 1);
70 }
71 #  endif /* FLAC__SYS_DARWIN */
72 # endif /* FLAC__NO_ASM */
73 #endif /* FLAC__CPU_PPC */
74
75 #if defined (__NetBSD__) || defined(__OpenBSD__)
76 #include <sys/param.h>
77 #include <sys/sysctl.h>
78 #include <machine/cpu.h>
79 #endif
80
81 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
82 #include <sys/types.h>
83 #include <sys/sysctl.h>
84 #endif
85
86 #if defined(__APPLE__)
87 /* how to get sysctlbyname()? */
88 #endif
89
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 /* these are flags in ECX of CPUID AX=00000001 */
97 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001;
98 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200;
99 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000;
100 static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE42 = 0x00100000;
101 /* these are flags in EDX of CPUID AX=80000001 */
102 static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000;
103 static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000;
104 static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000;
105
106
107 /*
108  * Extra stuff needed for detection of OS support for SSE on IA-32
109  */
110 #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
111 # if defined(__linux__)
112 /*
113  * If the OS doesn't support SSE, we will get here with a SIGILL.  We
114  * modify the return address to jump over the offending SSE instruction
115  * and also the operation following it that indicates the instruction
116  * executed successfully.  In this way we use no global variables and
117  * stay thread-safe.
118  *
119  * 3 + 3 + 6:
120  *   3 bytes for "xorps xmm0,xmm0"
121  *   3 bytes for estimate of how long the follwing "inc var" instruction is
122  *   6 bytes extra in case our estimate is wrong
123  * 12 bytes puts us in the NOP "landing zone"
124  */
125 #  undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */
126 #  ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR
127         static void sigill_handler_sse_os(int signal, struct sigcontext sc)
128         {
129                 (void)signal;
130                 sc.eip += 3 + 3 + 6;
131         }
132 #  else
133 #   include <sys/ucontext.h>
134         static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc)
135         {
136                 (void)signal, (void)si;
137                 ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6;
138         }
139 #  endif
140 # elif defined(_MSC_VER)
141 #  include <windows.h>
142 #  define USE_TRY_CATCH_FLAVOR /* sigill_handler flavor resulted in several crash reports on win32 */
143 #  ifdef USE_TRY_CATCH_FLAVOR
144 #  else
145         LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep)
146         {
147                 if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
148                         ep->ContextRecord->Eip += 3 + 3 + 6;
149                         return EXCEPTION_CONTINUE_EXECUTION;
150                 }
151                 return EXCEPTION_CONTINUE_SEARCH;
152         }
153 #  endif
154 # endif
155 #endif
156
157
158 void FLAC__cpu_info(FLAC__CPUInfo *info)
159 {
160 /*
161  * IA32-specific
162  */
163 #ifdef FLAC__CPU_IA32
164         info->type = FLAC__CPUINFO_TYPE_IA32;
165 #if !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN)
166         info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */
167 #ifdef FLAC__HAS_NASM
168         info->ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false;
169 #else
170         info->ia32.cpuid = FLAC__cpu_have_cpuid_x86()? true : false;
171 #endif
172         info->ia32.bswap = info->ia32.cpuid; /* CPUID => BSWAP since it came after */
173         info->ia32.cmov = false;
174         info->ia32.mmx = false;
175         info->ia32.fxsr = false;
176         info->ia32.sse = false;
177         info->ia32.sse2 = false;
178         info->ia32.sse3 = false;
179         info->ia32.ssse3 = false;
180         info->ia32.sse41 = false;
181         info->ia32.sse42 = false;
182         info->ia32._3dnow = false;
183         info->ia32.ext3dnow = false;
184         info->ia32.extmmx = false;
185         if(info->ia32.cpuid) {
186                 /* http://www.sandpile.org/x86/cpuid.htm */
187                 FLAC__uint32 flags_edx, flags_ecx;
188 #ifdef FLAC__HAS_NASM
189                 FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx);
190 #else
191                 FLAC__cpu_info_x86(&flags_edx, &flags_ecx);
192 #endif
193                 info->ia32.cmov  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false;
194                 info->ia32.mmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX  )? true : false;
195                 info->ia32.fxsr  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false;
196                 info->ia32.sse   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE  )? true : false;
197                 info->ia32.sse2  = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false;
198                 info->ia32.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
199                 info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
200                 info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
201                 info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
202
203 #if defined FLAC__HAS_NASM && defined FLAC__USE_3DNOW
204                 flags_edx = FLAC__cpu_info_extended_amd_asm_ia32();
205                 info->ia32._3dnow   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW   )? true : false;
206                 info->ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false;
207                 info->ia32.extmmx   = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX  )? true : false;
208 #else
209                 info->ia32._3dnow = info->ia32.ext3dnow = info->ia32.extmmx = false;
210 #endif
211
212 #ifdef DEBUG
213                 fprintf(stderr, "CPU info (IA-32):\n");
214                 fprintf(stderr, "  CPUID ...... %c\n", info->ia32.cpuid   ? 'Y' : 'n');
215                 fprintf(stderr, "  BSWAP ...... %c\n", info->ia32.bswap   ? 'Y' : 'n');
216                 fprintf(stderr, "  CMOV ....... %c\n", info->ia32.cmov    ? 'Y' : 'n');
217                 fprintf(stderr, "  MMX ........ %c\n", info->ia32.mmx     ? 'Y' : 'n');
218                 fprintf(stderr, "  FXSR ....... %c\n", info->ia32.fxsr    ? 'Y' : 'n');
219                 fprintf(stderr, "  SSE ........ %c\n", info->ia32.sse     ? 'Y' : 'n');
220                 fprintf(stderr, "  SSE2 ....... %c\n", info->ia32.sse2    ? 'Y' : 'n');
221                 fprintf(stderr, "  SSE3 ....... %c\n", info->ia32.sse3    ? 'Y' : 'n');
222                 fprintf(stderr, "  SSSE3 ...... %c\n", info->ia32.ssse3   ? 'Y' : 'n');
223                 fprintf(stderr, "  SSE41 ...... %c\n", info->ia32.sse41   ? 'Y' : 'n');
224                 fprintf(stderr, "  SSE42 ...... %c\n", info->ia32.sse42   ? 'Y' : 'n');
225                 fprintf(stderr, "  3DNow! ..... %c\n", info->ia32._3dnow  ? 'Y' : 'n');
226                 fprintf(stderr, "  3DNow!-ext . %c\n", info->ia32.ext3dnow? 'Y' : 'n');
227                 fprintf(stderr, "  3DNow!-MMX . %c\n", info->ia32.extmmx  ? 'Y' : 'n');
228 #endif
229
230                 /*
231                  * now have to check for OS support of SSE/SSE2
232                  */
233                 if(info->ia32.fxsr || info->ia32.sse || info->ia32.sse2) {
234 #if defined FLAC__NO_SSE_OS
235                         /* assume user knows better than us; turn it off */
236                         info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
237 #elif defined FLAC__SSE_OS
238                         /* assume user knows better than us; leave as detected above */
239 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__)
240                         int sse = 0;
241                         size_t len;
242                         /* at least one of these must work: */
243                         len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse);
244                         len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse"   , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */
245                         if(!sse)
246                                 info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
247 #elif defined(__NetBSD__) || defined (__OpenBSD__)
248 # if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__)
249                         int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE };
250                         size_t len = sizeof(val);
251                         if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
252                                 info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
253                         else { /* double-check SSE2 */
254                                 mib[1] = CPU_SSE2;
255                                 len = sizeof(val);
256                                 if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val)
257                                         info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
258                         }
259 # else
260                         info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
261 # endif
262 #elif defined(__linux__)
263                         int sse = 0;
264                         struct sigaction sigill_save;
265 #ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR
266                         if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR)
267 #else
268                         struct sigaction sigill_sse;
269                         sigill_sse.sa_sigaction = sigill_handler_sse_os;
270                         __sigemptyset(&sigill_sse.sa_mask);
271                         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 */
272                         if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save))
273 #endif
274                         {
275                                 /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */
276                                 /* see sigill_handler_sse_os() for an explanation of the following: */
277                                 asm volatile (
278                                         "xorl %0,%0\n\t"          /* for some reason, still need to do this to clear 'sse' var */
279                                         "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */
280                                         "incl %0\n\t"             /* SIGILL handler will jump over this */
281                                         /* landing zone */
282                                         "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */
283                                         "nop\n\t"
284                                         "nop\n\t"
285                                         "nop\n\t"
286                                         "nop\n\t"
287                                         "nop\n\t"
288                                         "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */
289                                         "nop\n\t"
290                                         "nop"     /* SIGILL jump lands here if "inc" is 1 byte */
291                                         : "=r"(sse)
292                                         : "r"(sse)
293                                 );
294
295                                 sigaction(SIGILL, &sigill_save, NULL);
296                         }
297
298                         if(!sse)
299                                 info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
300 #elif defined(_MSC_VER)
301 # ifdef USE_TRY_CATCH_FLAVOR
302                         __try {
303                                 __asm {
304                                         xorps xmm0,xmm0
305                                 }
306                         }
307                         __except(EXCEPTION_EXECUTE_HANDLER) {
308                                 if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
309                                         info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
310                         }
311 # else
312                         int sse = 0;
313                         LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os);
314                         /* see GCC version above for explanation */
315                         /*  http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */
316                         /*  http://www.codeproject.com/cpp/gccasm.asp */
317                         /*  http://www.hick.org/~mmiller/msvc_inline_asm.html */
318                         __asm {
319                                 xorps xmm0,xmm0
320                                 inc sse
321                                 nop
322                                 nop
323                                 nop
324                                 nop
325                                 nop
326                                 nop
327                                 nop
328                                 nop
329                                 nop
330                         }
331                         SetUnhandledExceptionFilter(save);
332                         if(!sse)
333                                 info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
334 # endif
335 #else
336                         /* no way to test, disable to be safe */
337                         info->ia32.fxsr = info->ia32.sse = info->ia32.sse2 = info->ia32.sse3 = info->ia32.ssse3 = info->ia32.sse41 = info->ia32.sse42 = false;
338 #endif
339 #ifdef DEBUG
340                 fprintf(stderr, "  SSE OS sup . %c\n", info->ia32.sse     ? 'Y' : 'n');
341 #endif
342
343                 }
344         }
345 #else
346         info->use_asm = false;
347 #endif
348
349 /*
350  * x86-64-specific
351  */
352 #elif defined FLAC__CPU_X86_64
353         info->type = FLAC__CPUINFO_TYPE_X86_64;
354 #if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN
355         info->use_asm = true;
356         {
357                 /* http://www.sandpile.org/x86/cpuid.htm */
358                 FLAC__uint32 flags_edx, flags_ecx;
359                 FLAC__cpu_info_x86(&flags_edx, &flags_ecx);
360                 info->x86_64.sse3  = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false;
361                 info->x86_64.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false;
362                 info->x86_64.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false;
363                 info->x86_64.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false;
364         }
365 #ifdef DEBUG
366         fprintf(stderr, "CPU info (x86-64):\n");
367         fprintf(stderr, "  SSE3 ....... %c\n", info->x86_64.sse3    ? 'Y' : 'n');
368         fprintf(stderr, "  SSSE3 ...... %c\n", info->x86_64.ssse3   ? 'Y' : 'n');
369         fprintf(stderr, "  SSE41 ...... %c\n", info->x86_64.sse41   ? 'Y' : 'n');
370         fprintf(stderr, "  SSE42 ...... %c\n", info->x86_64.sse42   ? 'Y' : 'n');
371 #endif
372
373 #else
374         info->use_asm = false;
375 #endif
376
377 /*
378  * PPC-specific
379  */
380 #elif defined FLAC__CPU_PPC
381         info->type = FLAC__CPUINFO_TYPE_PPC;
382 # if !defined FLAC__NO_ASM
383         info->use_asm = true;
384 #  ifdef FLAC__USE_ALTIVEC
385 #   if defined FLAC__SYS_DARWIN
386         {
387                 int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT };
388                 size_t len = sizeof(val);
389                 info->ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val);
390         }
391         {
392                 host_basic_info_data_t hostInfo;
393                 mach_msg_type_number_t infoCount;
394
395                 infoCount = HOST_BASIC_INFO_COUNT;
396                 host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount);
397
398                 info->ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970);
399         }
400 #   else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */
401         {
402                 /* no Darwin, do it the brute-force way */
403                 /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */
404                 info->ppc.altivec = 0;
405                 info->ppc.ppc64 = 0;
406
407                 signal (SIGILL, sigill_handler);
408                 canjump = 0;
409                 if (!sigsetjmp (jmpbuf, 1)) {
410                         canjump = 1;
411
412                         asm volatile (
413                                 "mtspr 256, %0\n\t"
414                                 "vand %%v0, %%v0, %%v0"
415                                 :
416                                 : "r" (-1)
417                         );
418
419                         info->ppc.altivec = 1;
420                 }
421                 canjump = 0;
422                 if (!sigsetjmp (jmpbuf, 1)) {
423                         int x = 0;
424                         canjump = 1;
425
426                         /* PPC64 hardware implements the cntlzd instruction */
427                         asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) );
428
429                         info->ppc.ppc64 = 1;
430                 }
431                 signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */
432         }
433 #   endif
434 #  else /* !FLAC__USE_ALTIVEC */
435         info->ppc.altivec = 0;
436         info->ppc.ppc64 = 0;
437 #  endif
438 # else
439         info->use_asm = false;
440 # endif
441
442 /*
443  * unknown CPU
444  */
445 #else
446         info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
447         info->use_asm = false;
448 #endif
449 }
450
451 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN
452
453 #if defined _MSC_VER
454 #include <intrin.h> /* for __cpuid() */
455 #elif defined __GNUC__
456 #include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
457 #endif
458
459 FLAC__uint32 FLAC__cpu_have_cpuid_x86(void)
460 {
461 #ifdef FLAC__CPU_X86_64
462         return 1;
463 #else
464 # if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */
465         FLAC__uint32 flags1, flags2;
466         __asm {
467                 pushfd
468                 pushfd
469                 pop             eax
470                 mov             flags1, eax
471                 xor             eax, 0x200000
472                 push    eax
473                 popfd
474                 pushfd
475                 pop             eax
476                 mov             flags2, eax
477                 popfd
478         }
479         if (((flags1^flags2) & 0x200000) != 0)
480                 return 1;
481         else
482                 return 0;
483 # elif defined __GNUC__
484         if (__get_cpuid_max(0, 0) != 0)
485                 return 1;
486         else
487                 return 0;
488 # else
489         return 0;
490 # endif
491 #endif
492 }
493
494 void FLAC__cpu_info_x86(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx)
495 {
496 #if defined _MSC_VER || defined __INTEL_COMPILER
497         int cpuinfo[4];
498         __cpuid(cpuinfo, 1);
499         *flags_ecx = cpuinfo[2];
500         *flags_edx = cpuinfo[3];
501 #elif defined __GNUC__
502         FLAC__uint32 flags_eax, flags_ebx;
503         if (0 == __get_cpuid(1, &flags_eax, &flags_ebx, flags_ecx, flags_edx))
504                 *flags_ecx = *flags_edx = 0;
505 #else
506         *flags_ecx = *flags_edx = 0;
507 #endif
508 }
509
510 #endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */