FFmpeg
Loading...
Searching...
No Matches
cpu.c
Go to the documentation of this file.
1/*
2 * Copyright © 2025, Martin Storsjo
3 * Copyright © 2018, VideoLAN and dav1d authors
4 * Copyright © 2018, Two Orioles, LLC
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdint.h>
30
31#include "checkasm_config.h"
32#include "cpu.h"
33#include "internal.h"
34
35#ifdef _WIN32
36 #include <windows.h>
37#endif
38#include <ctype.h>
39#include <string.h>
40
41#if ARCH_AARCH64
42
43 #ifdef _WIN32
44 #include <windows.h>
45 #elif defined(__APPLE__)
46 #include <sys/sysctl.h>
47
48static int have_feature(const char *feature)
49{
50 int supported = 0;
51 size_t size = sizeof(supported);
52 if (sysctlbyname(feature, &supported, &size, NULL, 0) != 0) {
53 return 0;
54 }
55 return supported;
56}
57 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
58
59 #include <sys/auxv.h>
60
61 #define HWCAP_AARCH64_SVE (1 << 22)
62 #define HWCAP2_AARCH64_SME (1 << 23)
63
64 #endif
65
66int checkasm_has_sve(void)
67{
68 #ifdef _WIN32
69 #ifdef PF_ARM_SVE_INSTRUCTIONS_AVAILABLE
70 return IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE);
71 #else
72 return 0;
73 #endif
74 #elif defined(__APPLE__)
75 /* No sysctlbyname feature defined for SVE yet */
76 return 0;
77 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
78 return checkasm_getauxval(AT_HWCAP) & HWCAP_AARCH64_SVE;
79 #else
80 return 0;
81 #endif
82}
83
84int checkasm_has_sme(void)
85{
86 #ifdef _WIN32
87 #ifdef PF_ARM_SME_INSTRUCTIONS_AVAILABLE
88 return IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE);
89 #else
90 return 0;
91 #endif
92 #elif defined(__APPLE__)
93 return have_feature("hw.optional.arm.FEAT_SME");
94 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
95 return checkasm_getauxval(AT_HWCAP2) & HWCAP2_AARCH64_SME;
96 #else
97 return 0;
98 #endif
99}
100
101#elif ARCH_ARM
102
103 #ifdef _WIN32
104 #include <windows.h>
105 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
106 #include <sys/auxv.h>
107
108 #ifndef HWCAP_ARM_VFP
109 #define HWCAP_ARM_VFP (1 << 6)
110 #endif
111 #ifndef HWCAP_ARM_NEON
112 #define HWCAP_ARM_NEON (1 << 12)
113 #endif
114 #ifndef HWCAP_ARM_VFPD32
115 #define HWCAP_ARM_VFPD32 (1 << 19)
116 #endif
117
118 #endif
119
120static checkasm_checked_call_func checked_call_ptr;
121
123{
124 return checked_call_ptr;
125}
126
127COLD void checkasm_init_arm(void)
128{
129 if (checkasm_has_vfp())
130 checked_call_ptr = checkasm_checked_call_vfp;
131 else
132 checked_call_ptr = checkasm_checked_call_novfp;
133}
134
135int checkasm_has_vfpd32(void)
136{
137 #ifdef _WIN32
138 #ifdef PF_ARM_VFP_32_REGISTERS_AVAILABLE
139 return IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE);
140 #else
141 return 0;
142 #endif
143 #elif (defined(__APPLE__) && __ARM_ARCH >= 7)
144 return 1;
145 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
146 /* HWCAP_ARM_VFPD32 was introduced in Linux 3.7, alternatively check for NEON as a fallback */
147 return checkasm_getauxval(AT_HWCAP) & (HWCAP_ARM_NEON | HWCAP_ARM_VFPD32);
148 #else
149 return 0;
150 #endif
151}
152
153int checkasm_has_vfp(void)
154{
155 #if defined(__APPLE__) || defined(_WIN32)
156 return 1;
157 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
158 return checkasm_getauxval(AT_HWCAP) & HWCAP_ARM_VFP;
159 #else
160 return 0;
161 #endif
162}
163
164#endif // ARCH_ARM
165
166#if (ARCH_ARM || ARCH_AARCH64) && (defined(__linux__) || defined(_WIN32))
167struct arm_core {
168 unsigned id;
169 const char *name;
170};
171
172struct arm_implementer {
173 unsigned id;
174 const struct arm_core *parts;
175 const char *name;
176};
177
178 #include "cores.h"
179
180struct arm_core_id {
181 unsigned implementer;
182 unsigned part;
183};
184
185COLD static void find_implementer_part(const struct arm_core_id *core,
186 const char **implementer, const char **part)
187{
188 for (int i = 0; arm_implementers[i].name; i++) {
189 if (arm_implementers[i].id == core->implementer) {
190 *implementer = arm_implementers[i].name;
191 const struct arm_core *cores = arm_implementers[i].parts;
192 for (int j = 0; cores[j].name; j++) {
193 if (cores[j].id == core->part) {
194 *part = cores[j].name;
195 return;
196 }
197 }
198 return;
199 }
200 }
201}
202
203COLD static size_t print_cores(char *buf, size_t buflen, const struct arm_core_id *cores,
204 unsigned nb_cores)
205{
206 size_t pos = 0;
207 for (unsigned i = 0; i < nb_cores; i++) {
208 if (pos >= buflen)
209 break;
210 char buf1[20], buf2[20];
211 snprintf(buf1, sizeof(buf1), "Implementer 0x%02x", cores[i].implementer);
212 snprintf(buf2, sizeof(buf2), "Part 0x%03x", cores[i].part);
213 const char *implementer = buf1, *part = buf2;
214 find_implementer_part(&cores[i], &implementer, &part);
215 pos += snprintf(buf + pos, buflen - pos, "%s%s %s", i > 0 ? ", " : "",
216 implementer, part);
217 }
218 return pos;
219}
220
221 #ifdef __linux__
222COLD const char *checkasm_get_arm_cpuinfo(char *buf, size_t buflen, int affinity)
223{
224 FILE *f = fopen("/proc/cpuinfo", "r");
225 if (!f)
226 return NULL;
227
228 int implementer = -1, part = -1, processor = -1;
229 char model[100] = "";
230 struct arm_core_id cores[5];
231 unsigned nb_cores = 0;
232
233 char line[1024];
234 while (fgets(line, sizeof(line), f)) {
235 size_t len = strlen(line);
236 /* Trim out the trailing \n that fgets includes */
237 if (len > 0 && line[len - 1] == '\n')
238 line[--len] = '\0';
239 else
240 continue;
241
242 char *ptr;
243 if ((ptr = strchr(line, ':')) != NULL) {
244 /* Interpret a line like "processor : 0". Split the
245 * string at the ':' character. */
246 *ptr = '\0';
247 const char *value = ptr + 1;
248 /* Skip past leading space after ':' */
249 while (isspace(*value))
250 value++;
251 /* Trim out trailing spaces before the ':' */
252 while (ptr > line && isspace(ptr[-1]))
253 *--ptr = '\0';
254 /* Handle the different keys */
255 if (!strcmp(line, "CPU implementer")) {
256 implementer = (int) strtol(value, NULL, 0);
257 } else if (!strcmp(line, "CPU part")) {
258 part = (int) strtol(value, NULL, 0);
259 } else if (!strcmp(line, "processor")) {
260 processor = (int) strtol(value, NULL, 0);
261 } else if (!strcmp(line, "Model")) {
262 snprintf(model, sizeof(model), "%s", value);
263 }
264 } else if (line[0] == '\0') {
265 /* We got an empty line; interpret that as terminating the
266 * current processor. */
267 if (implementer != -1 && part != -1
268 && (affinity < 0 || affinity == processor)) {
269 struct arm_core_id core = { implementer, part };
270 unsigned i;
271 for (i = 0; i < nb_cores; i++)
272 if (memcmp(&cores[i], &core, sizeof(core)) == 0)
273 break;
274 /* If the new core differs from all of the earlier seen ones,
275 * store the new one. */
276 if (i == nb_cores && nb_cores < ARRAY_SIZE(cores))
277 cores[nb_cores++] = core;
278 }
279 /* Reset variables for the next processor. */
280 implementer = part = processor = -1;
281 }
282 }
283
284 fclose(f);
285
286 if (model[0] == '\0' && nb_cores == 0)
287 return NULL;
288
289 if (nb_cores == 0) {
290 snprintf(buf, buflen, "%s", model);
291 return buf;
292 }
293
294 size_t pos = print_cores(buf, buflen, cores, nb_cores);
295
296 if (pos < buflen && model[0] != '\0')
297 pos += snprintf(buf + pos, buflen - pos, " (%s)", model);
298
299 return buf;
300}
301 #endif
302
303 #ifdef _WIN32
304COLD const char *checkasm_get_arm_win32_reg(char *buf, size_t buflen, int affinity)
305{
306 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
307 HKEY key;
308 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
309 "Hardware\\Description\\System\\CentralProcessor", 0, KEY_READ,
310 &key)
311 != ERROR_SUCCESS)
312 return NULL;
313 DWORD index = 0;
314
315 char cpu_name[100] = "";
316 struct arm_core_id cores[5];
317 unsigned nb_cores = 0;
318
319 while (1) {
320 /* Iterate over all subkeys. They are named 0, 1, 2, etc, one for each
321 * processor. */
322 char name[10];
323 DWORD name_size = sizeof(name);
324 if (RegEnumKeyExA(key, index, name, &name_size, NULL, NULL, NULL, NULL)
325 != ERROR_SUCCESS)
326 break;
327 index++;
328 unsigned processor = strtoul(name, NULL, 0);
329
330 if (affinity >= 0 && (unsigned) affinity != processor)
331 continue;
332
333 DWORD64 midr_el1;
334 DWORD midr_el1_size = sizeof(midr_el1);
335 /* Read the register CP 4000 aka MIDR_EL1. */
336 if (RegGetValueA(key, name, "CP 4000", RRF_RT_QWORD, NULL, &midr_el1,
337 &midr_el1_size)
338 == ERROR_SUCCESS) {
339 unsigned implementer = (midr_el1 >> 24) & 0xff;
340 unsigned part = (midr_el1 >> 4) & 0xfff;
341
342 struct arm_core_id core = { implementer, part };
343 unsigned i;
344 for (i = 0; i < nb_cores; i++)
345 if (memcmp(&cores[i], &core, sizeof(core)) == 0)
346 break;
347 /* If the new core differs from all of the earlier seen ones,
348 * store the new one. */
349 if (i == nb_cores && nb_cores < ARRAY_SIZE(cores))
350 cores[nb_cores++] = core;
351 }
352 DWORD cpu_name_size = sizeof(cpu_name);
353 if (RegGetValueA(key, name, "ProcessorNameString", RRF_RT_REG_SZ, NULL, cpu_name,
354 &cpu_name_size)
355 != ERROR_SUCCESS)
356 cpu_name[0] = '\0';
357 /* Not including "Identifier", which usually has a nondescriptive string like
358 * "ARMv8 (64-bit) Family 8 Model 800 Revision A01".
359 * Not including "VendorIdentifier"; it doesn't add much extra value,
360 * and may contain a longer string like "Qualcomm Technologies Inc". */
361 }
362 RegCloseKey(key);
363
364 if (cpu_name[0] == '\0' && nb_cores == 0)
365 return NULL;
366
367 if (nb_cores == 0) {
368 snprintf(buf, buflen, "%s", cpu_name);
369 return buf;
370 }
371
372 size_t pos = print_cores(buf, buflen, cores, nb_cores);
373
374 if (pos < buflen && cpu_name[0] != '\0')
375 pos += snprintf(buf + pos, buflen - pos, " (%s)", cpu_name);
376
377 return buf;
378 #else
379 (void) print_cores;
380 return NULL;
381 #endif
382}
383 #endif
384#endif
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
static const struct arm_implementer arm_implementers[]
Definition cores.h:284
#define NULL
Definition coverity.c:32
enum AVCodecID id
Definition dts2pts.c:607
double value
Definition eval.c:102
const char * key
int index
Definition gxfenc.c:90
#define AT_HWCAP2
Definition cpu.c:53
#define AT_HWCAP
Definition cpu.c:50
void(* checkasm_checked_call_func)(void *func, int dummy,...)
Definition arm.h:37
CHECKASM_API checkasm_checked_call_func checkasm_checked_call_ptr(void)
const char * name
Definition qsvenc.c:142
unsigned int pos
Definition spdifenc.c:431
COLD unsigned long checkasm_getauxval(unsigned long type)
Definition cpu.c:51
#define ARRAY_SIZE(a)
Definition internal.h:81
#define COLD
Definition internal.h:45
int size
int len