xref: /aosp_15_r20/external/flac/src/libFLAC/cpu.c (revision 600f14f40d737144c998e2ec7a483122d3776fbc)
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