Add runtime detection of POWER8 and POWER9
[flac.git] / src / libFLAC / cpu.c
blob64da9cbcb1cccbfe6bf3c120b80ebdd8b3682b87
1 /* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001-2009 Josh Coalson
3 * Copyright (C) 2011-2016 Xiph.Org Foundation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
37 #include "private/cpu.h"
38 #include "share/compat.h"
39 #include <stdlib.h>
40 #include <string.h>
42 #if defined _MSC_VER
43 #include <intrin.h> /* for __cpuid() and _xgetbv() */
44 #elif defined __GNUC__ && defined HAVE_CPUID_H
45 #include <cpuid.h> /* for __get_cpuid() and __get_cpuid_max() */
46 #endif
48 #ifndef NDEBUG
49 #include <stdio.h>
50 #define dfprintf fprintf
51 #else
52 /* This is bad practice, it should be a static void empty function */
53 #define dfprintf(file, format, ...)
54 #endif
56 #if defined FLAC__CPU_PPC
57 #include <sys/auxv.h>
58 #endif
60 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
62 /* these are flags in EDX of CPUID AX=00000001 */
63 static const uint32_t FLAC__CPUINFO_X86_CPUID_CMOV = 0x00008000;
64 static const uint32_t FLAC__CPUINFO_X86_CPUID_MMX = 0x00800000;
65 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE = 0x02000000;
66 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE2 = 0x04000000;
68 /* these are flags in ECX of CPUID AX=00000001 */
69 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE3 = 0x00000001;
70 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSSE3 = 0x00000200;
71 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE41 = 0x00080000;
72 static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE42 = 0x00100000;
73 static const uint32_t FLAC__CPUINFO_X86_CPUID_OSXSAVE = 0x08000000;
74 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX = 0x10000000;
75 static const uint32_t FLAC__CPUINFO_X86_CPUID_FMA = 0x00001000;
77 /* these are flags in EBX of CPUID AX=00000007 */
78 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX2 = 0x00000020;
80 static uint32_t
81 cpu_xgetbv_x86(void)
83 #if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__AVX_SUPPORTED
84 return (uint32_t)_xgetbv(0);
85 #elif defined __GNUC__
86 uint32_t lo, hi;
87 __asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
88 return lo;
89 #else
90 return 0;
91 #endif
94 static uint32_t
95 cpu_have_cpuid(void)
97 #if defined FLAC__CPU_X86_64 || defined __i686__ || defined __SSE__ || (defined _M_IX86_FP && _M_IX86_FP > 0)
98 /* target CPU does have CPUID instruction */
99 return 1;
100 #elif defined FLAC__HAS_NASM
101 return FLAC__cpu_have_cpuid_asm_ia32();
102 #elif defined __GNUC__ && defined HAVE_CPUID_H
103 if (__get_cpuid_max(0, 0) != 0)
104 return 1;
105 else
106 return 0;
107 #elif defined _MSC_VER
108 FLAC__uint32 flags1, flags2;
109 __asm {
110 pushfd
111 pushfd
112 pop eax
113 mov flags1, eax
114 xor eax, 0x200000
115 push eax
116 popfd
117 pushfd
118 pop eax
119 mov flags2, eax
120 popfd
122 if (((flags1^flags2) & 0x200000) != 0)
123 return 1;
124 else
125 return 0;
126 #else
127 return 0;
128 #endif
131 static void
132 cpuinfo_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
134 #if defined _MSC_VER
135 int cpuinfo[4];
136 int ext = level & 0x80000000;
137 __cpuid(cpuinfo, ext);
138 if ((uint32_t)cpuinfo[0] >= level) {
139 #if FLAC__AVX_SUPPORTED
140 __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
141 #else
142 __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
143 #endif
144 *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
145 return;
147 #elif defined __GNUC__ && defined HAVE_CPUID_H
148 FLAC__uint32 ext = level & 0x80000000;
149 __cpuid(ext, *eax, *ebx, *ecx, *edx);
150 if (*eax >= level) {
151 __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
152 return;
154 #elif defined FLAC__HAS_NASM && defined FLAC__CPU_IA32
155 FLAC__cpu_info_asm_ia32(level, eax, ebx, ecx, edx);
156 return;
157 #endif
158 *eax = *ebx = *ecx = *edx = 0;
161 #endif
163 static void
164 x86_cpu_info (FLAC__CPUInfo *info)
166 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) && !defined FLAC__NO_ASM
167 FLAC__bool x86_osxsave = false;
168 FLAC__bool os_avx = false;
169 FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
171 info->use_asm = true; /* we assume a minimum of 80386 */
172 if (!cpu_have_cpuid())
173 return;
175 cpuinfo_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
176 info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */
177 cpuinfo_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
179 info->x86.cmov = (flags_edx & FLAC__CPUINFO_X86_CPUID_CMOV ) ? true : false;
180 info->x86.mmx = (flags_edx & FLAC__CPUINFO_X86_CPUID_MMX ) ? true : false;
181 info->x86.sse = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE ) ? true : false;
182 info->x86.sse2 = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE2 ) ? true : false;
183 info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE3 ) ? true : false;
184 info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSSE3) ? true : false;
185 info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE41) ? true : false;
186 info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE42) ? true : false;
188 if (FLAC__AVX_SUPPORTED) {
189 x86_osxsave = (flags_ecx & FLAC__CPUINFO_X86_CPUID_OSXSAVE) ? true : false;
190 info->x86.avx = (flags_ecx & FLAC__CPUINFO_X86_CPUID_AVX ) ? true : false;
191 info->x86.fma = (flags_ecx & FLAC__CPUINFO_X86_CPUID_FMA ) ? true : false;
192 cpuinfo_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
193 info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_AVX2 ) ? true : false;
196 #if defined FLAC__CPU_IA32
197 dfprintf(stderr, "CPU info (IA-32):\n");
198 #else
199 dfprintf(stderr, "CPU info (x86-64):\n");
200 #endif
201 dfprintf(stderr, " CMOV ....... %c\n", info->x86.cmov ? 'Y' : 'n');
202 dfprintf(stderr, " MMX ........ %c\n", info->x86.mmx ? 'Y' : 'n');
203 dfprintf(stderr, " SSE ........ %c\n", info->x86.sse ? 'Y' : 'n');
204 dfprintf(stderr, " SSE2 ....... %c\n", info->x86.sse2 ? 'Y' : 'n');
205 dfprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n');
206 dfprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
207 dfprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
208 dfprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
210 if (FLAC__AVX_SUPPORTED) {
211 dfprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n');
212 dfprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n');
213 dfprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n');
217 * now have to check for OS support of AVX instructions
219 if (FLAC__AVX_SUPPORTED && info->x86.avx && x86_osxsave && (cpu_xgetbv_x86() & 0x6) == 0x6) {
220 os_avx = true;
222 if (os_avx) {
223 dfprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
225 if (!os_avx) {
226 /* no OS AVX support */
227 info->x86.avx = false;
228 info->x86.avx2 = false;
229 info->x86.fma = false;
231 #else
232 info->use_asm = false;
233 #endif
236 static void
237 ppc_cpu_info (FLAC__CPUInfo *info)
239 #if defined FLAC__CPU_PPC
240 #ifndef PPC_FEATURE2_ARCH_3_00
241 #define PPC_FEATURE2_ARCH_3_00 0x00800000
242 #endif
244 #ifndef PPC_FEATURE2_ARCH_2_07
245 #define PPC_FEATURE2_ARCH_2_07 0x80000000
246 #endif
248 if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_3_00) {
249 info->ppc.arch_3_00 = true;
250 } else if (getauxval(AT_HWCAP2) & PPC_FEATURE2_ARCH_2_07) {
251 info->ppc.arch_2_07 = true;
253 #else
254 info->ppc.arch_2_07 = false;
255 info->ppc.arch_3_00 = false;
256 #endif
259 void FLAC__cpu_info (FLAC__CPUInfo *info)
261 memset(info, 0, sizeof(*info));
263 #ifdef FLAC__CPU_IA32
264 info->type = FLAC__CPUINFO_TYPE_IA32;
265 #elif defined FLAC__CPU_X86_64
266 info->type = FLAC__CPUINFO_TYPE_X86_64;
267 #elif defined FLAC__CPU_PPC
268 info->type = FLAC__CPUINFO_TYPE_PPC;
269 #else
270 info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
271 #endif
273 switch (info->type) {
274 case FLAC__CPUINFO_TYPE_IA32: /* fallthrough */
275 case FLAC__CPUINFO_TYPE_X86_64:
276 x86_cpu_info (info);
277 break;
278 case FLAC__CPUINFO_TYPE_PPC:
279 ppc_cpu_info (info);
280 break;
281 default:
282 info->use_asm = false;
283 break;