1 /* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001-2009 Josh Coalson
3 * Copyright (C) 2011-2023 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 "share/compat.h"
39 #include <stdlib.h>
40 #include <string.h>
41
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
47
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
55
56 #if defined(HAVE_SYS_AUXV_H)
57 #include <sys/auxv.h>
58 #endif
59
60 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN && !defined FLAC__NO_ASM
61
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;
67
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;
76
77 /* these are flags in EBX of CPUID AX=00000007 */
78 static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX2 = 0x00000020;
79 static const uint32_t FLAC__CPUINFO_X86_CPUID_BMI2 = 0x00000100;
80
81 static uint32_t
cpu_xgetbv_x86(void)82 cpu_xgetbv_x86(void)
83 {
84 #if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__AVX_SUPPORTED
85 return (uint32_t)_xgetbv(0);
86 #elif defined __GNUC__
87 uint32_t lo, hi;
88 __asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0));
89 return lo;
90 #else
91 return 0;
92 #endif
93 }
94
95 static uint32_t
cpu_have_cpuid(void)96 cpu_have_cpuid(void)
97 {
98 #if defined FLAC__CPU_X86_64 || defined __i686__ || defined __SSE__ || (defined _M_IX86_FP && _M_IX86_FP > 0)
99 /* target CPU does have CPUID instruction */
100 return 1;
101 #elif defined __GNUC__ && defined HAVE_CPUID_H
102 if (__get_cpuid_max(0, 0) != 0)
103 return 1;
104 else
105 return 0;
106 #elif defined _MSC_VER
107 FLAC__uint32 flags1, flags2;
108 __asm {
109 pushfd
110 pushfd
111 pop eax
112 mov flags1, eax
113 xor eax, 0x200000
114 push eax
115 popfd
116 pushfd
117 pop eax
118 mov flags2, eax
119 popfd
120 }
121 if (((flags1^flags2) & 0x200000) != 0)
122 return 1;
123 else
124 return 0;
125 #else
126 return 0;
127 #endif
128 }
129
130 static void
cpuinfo_x86(FLAC__uint32 level,FLAC__uint32 * eax,FLAC__uint32 * ebx,FLAC__uint32 * ecx,FLAC__uint32 * edx)131 cpuinfo_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx)
132 {
133 #if defined _MSC_VER
134 int cpuinfo[4];
135 int ext = level & 0x80000000;
136 __cpuid(cpuinfo, ext);
137 if ((uint32_t)cpuinfo[0] >= level) {
138 #if FLAC__AVX_SUPPORTED
139 __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */
140 #else
141 __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */
142 #endif
143 *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3];
144 return;
145 }
146 #elif defined __GNUC__ && defined HAVE_CPUID_H
147 FLAC__uint32 ext = level & 0x80000000;
148 __cpuid(ext, *eax, *ebx, *ecx, *edx);
149 if (*eax >= level) {
150 __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx);
151 return;
152 }
153 #endif
154 *eax = *ebx = *ecx = *edx = 0;
155 }
156
157 #endif
158
159 static void
x86_cpu_info(FLAC__CPUInfo * info)160 x86_cpu_info (FLAC__CPUInfo *info)
161 {
162 #if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN && !defined FLAC__NO_ASM
163 FLAC__bool x86_osxsave = false;
164 FLAC__bool os_avx = false;
165 FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx;
166
167 info->use_asm = true; /* we assume a minimum of 80386 */
168 if (!cpu_have_cpuid())
169 return;
170
171 cpuinfo_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
172 info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */
173 cpuinfo_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
174
175 info->x86.cmov = (flags_edx & FLAC__CPUINFO_X86_CPUID_CMOV ) ? true : false;
176 info->x86.mmx = (flags_edx & FLAC__CPUINFO_X86_CPUID_MMX ) ? true : false;
177 info->x86.sse = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE ) ? true : false;
178 info->x86.sse2 = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE2 ) ? true : false;
179 info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE3 ) ? true : false;
180 info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSSE3) ? true : false;
181 info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE41) ? true : false;
182 info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE42) ? true : false;
183
184 if (FLAC__AVX_SUPPORTED) {
185 x86_osxsave = (flags_ecx & FLAC__CPUINFO_X86_CPUID_OSXSAVE) ? true : false;
186 info->x86.avx = (flags_ecx & FLAC__CPUINFO_X86_CPUID_AVX ) ? true : false;
187 info->x86.fma = (flags_ecx & FLAC__CPUINFO_X86_CPUID_FMA ) ? true : false;
188 cpuinfo_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx);
189 info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_AVX2 ) ? true : false;
190 info->x86.bmi2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_BMI2 ) ? true : false;
191 }
192
193 #if defined FLAC__CPU_IA32
194 dfprintf(stderr, "CPU info (IA-32):\n");
195 #else
196 dfprintf(stderr, "CPU info (x86-64):\n");
197 #endif
198 dfprintf(stderr, " CMOV ....... %c\n", info->x86.cmov ? 'Y' : 'n');
199 dfprintf(stderr, " MMX ........ %c\n", info->x86.mmx ? 'Y' : 'n');
200 dfprintf(stderr, " SSE ........ %c\n", info->x86.sse ? 'Y' : 'n');
201 dfprintf(stderr, " SSE2 ....... %c\n", info->x86.sse2 ? 'Y' : 'n');
202 dfprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n');
203 dfprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n');
204 dfprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n');
205 dfprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n');
206
207 if (FLAC__AVX_SUPPORTED) {
208 dfprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n');
209 dfprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n');
210 dfprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n');
211 dfprintf(stderr, " BMI2 ....... %c\n", info->x86.bmi2 ? 'Y' : 'n');
212 }
213
214 /*
215 * now have to check for OS support of AVX instructions
216 */
217 if (FLAC__AVX_SUPPORTED && info->x86.avx && x86_osxsave && (cpu_xgetbv_x86() & 0x6) == 0x6) {
218 os_avx = true;
219 }
220 if (os_avx) {
221 dfprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n');
222 }
223 if (!os_avx) {
224 /* no OS AVX support */
225 info->x86.avx = false;
226 info->x86.avx2 = false;
227 info->x86.fma = false;
228 }
229 #else
230 info->use_asm = false;
231 #endif
232 }
233
FLAC__cpu_info(FLAC__CPUInfo * info)234 void FLAC__cpu_info (FLAC__CPUInfo *info)
235 {
236 memset(info, 0, sizeof(*info));
237
238 #ifdef FLAC__CPU_IA32
239 info->type = FLAC__CPUINFO_TYPE_IA32;
240 #elif defined FLAC__CPU_X86_64
241 info->type = FLAC__CPUINFO_TYPE_X86_64;
242 #else
243 info->type = FLAC__CPUINFO_TYPE_UNKNOWN;
244 #endif
245
246 switch (info->type) {
247 case FLAC__CPUINFO_TYPE_IA32: /* fallthrough */
248 case FLAC__CPUINFO_TYPE_X86_64:
249 x86_cpu_info (info);
250 break;
251 default:
252 info->use_asm = false;
253 break;
254 }
255 }
256