FFmpeg
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 
48 static 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 
66 int 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 
84 int 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 
120 static checkasm_checked_call_func checked_call_ptr;
121 
123 {
124  return checked_call_ptr;
125 }
126 
127 COLD 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 
135 int 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 
153 int 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))
167 struct arm_core {
168  unsigned id;
169  const char *name;
170 };
171 
172 struct arm_implementer {
173  unsigned id;
174  const struct arm_core *parts;
175  const char *name;
176 };
177 
178  #include "cores.h"
179 
180 struct arm_core_id {
181  unsigned implementer;
182  unsigned part;
183 };
184 
185 COLD 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 
203 COLD 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__
222 COLD 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
304 COLD 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
COLD
#define COLD
Definition: internal.h:45
checkasm_config.h
name
const char * name
Definition: cpu.c:41
cores.h
checkasm_getauxval
COLD unsigned long checkasm_getauxval(unsigned long type)
Definition: cpu.c:51
checkasm_checked_call_ptr
CHECKASM_API checkasm_checked_call_func checkasm_checked_call_ptr(void)
key
const char * key
Definition: hwcontext_opencl.c:189
AT_HWCAP
#define AT_HWCAP
Definition: cpu.c:50
NULL
#define NULL
Definition: coverity.c:32
index
int index
Definition: gxfenc.c:90
f
f
Definition: af_crystalizer.c:122
ARRAY_SIZE
#define ARRAY_SIZE(a)
Definition: internal.h:81
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
size
int size
Definition: twinvq_data.h:10344
checkasm_checked_call_func
void(* checkasm_checked_call_func)(void *func, int dummy,...)
Definition: arm.h:37
line
Definition: graph2dot.c:48
AT_HWCAP2
#define AT_HWCAP2
Definition: cpu.c:53
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
len
int len
Definition: vorbis_enc_data.h:426
arm_implementers
static const struct arm_implementer arm_implementers[]
Definition: cores.h:284
pos
unsigned int pos
Definition: spdifenc.c:414
id
enum AVCodecID id
Definition: dts2pts.c:607
snprintf
#define snprintf
Definition: snprintf.h:34