1 //
2 // CPURuntime.cpp
3 // MNN
4 //
5 // Created by MNN on 2018/08/31.
6 // Copyright © 2018, Alibaba Group Holding Limited
7 //
8
9 /**
10 Ref from:
11 https://github.com/Tencent/ncnn/blob/master/src/cpu.cpp
12 https://github.com/pytorch/cpuinfo
13 */
14 #ifdef __ANDROID__
15 #include <stdint.h>
16 #include <sys/syscall.h>
17 #include <unistd.h>
18 #endif
19
20 #include "core/Macro.h"
21 #ifdef MNN_USE_ARMV82
22
23 #ifdef __ANDROID__
24 #include <fcntl.h>
25 #include <sys/auxv.h>
26 #include <sys/system_properties.h>
27 #endif // __ANDROID__
28
29 #endif // MNN_USE_ARMV82
30
31 #if __APPLE__
32 #include "TargetConditionals.h"
33 #if __aarch64__
34 #include <sys/sysctl.h>
35 #endif
36 #if TARGET_OS_IPHONE
37 #include <mach/machine.h>
38 #include <sys/types.h>
39 #define __IOS__ 1
40 #endif // TARGET_OS_IPHONE
41 #endif // __APPLE__
42
43 #ifdef _OPENMP
44 #include <omp.h>
45 #endif // _OPENMP
46
47 #include <MNN/MNNDefine.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <algorithm>
51 #include <vector>
52 #include "backend/cpu/CPURuntime.hpp"
53
54 #ifdef __ANDROID__
55
56 /* As per include/sys/system_properties.h in Android NDK */
57 #define CPUINFO_HARDWARE_VALUE_MAX 64
58 #define CPUINFO_BUILD_PROP_VALUE_MAX 92
59
60 struct cpuinfo_android_properties {
61 char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
62 char ro_product_board[CPUINFO_BUILD_PROP_VALUE_MAX];
63 char ro_board_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
64 char ro_mediatek_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
65 char ro_arch[CPUINFO_BUILD_PROP_VALUE_MAX];
66 char ro_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
67 char ro_hardware_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
68 };
69
70 enum cpuinfo_android_chipset_property {
71 cpuinfo_android_chipset_property_proc_cpuinfo_hardware = 0,
72 cpuinfo_android_chipset_property_ro_product_board,
73 cpuinfo_android_chipset_property_ro_board_platform,
74 cpuinfo_android_chipset_property_ro_mediatek_platform,
75 cpuinfo_android_chipset_property_ro_arch,
76 cpuinfo_android_chipset_property_ro_chipname,
77 cpuinfo_android_chipset_property_ro_hardware_chipname,
78 cpuinfo_android_chipset_property_max,
79 };
80
81 enum cpuinfo_arm_chipset_vendor {
82 cpuinfo_arm_chipset_vendor_unknown = 0,
83 cpuinfo_arm_chipset_vendor_qualcomm,
84 cpuinfo_arm_chipset_vendor_mediatek,
85 cpuinfo_arm_chipset_vendor_samsung,
86 cpuinfo_arm_chipset_vendor_hisilicon,
87 cpuinfo_arm_chipset_vendor_actions,
88 cpuinfo_arm_chipset_vendor_allwinner,
89 cpuinfo_arm_chipset_vendor_amlogic,
90 cpuinfo_arm_chipset_vendor_broadcom,
91 cpuinfo_arm_chipset_vendor_lg,
92 cpuinfo_arm_chipset_vendor_leadcore,
93 cpuinfo_arm_chipset_vendor_marvell,
94 cpuinfo_arm_chipset_vendor_mstar,
95 cpuinfo_arm_chipset_vendor_novathor,
96 cpuinfo_arm_chipset_vendor_nvidia,
97 cpuinfo_arm_chipset_vendor_pinecone,
98 cpuinfo_arm_chipset_vendor_renesas,
99 cpuinfo_arm_chipset_vendor_rockchip,
100 cpuinfo_arm_chipset_vendor_spreadtrum,
101 cpuinfo_arm_chipset_vendor_telechips,
102 cpuinfo_arm_chipset_vendor_texas_instruments,
103 cpuinfo_arm_chipset_vendor_wondermedia,
104 cpuinfo_arm_chipset_vendor_max,
105 };
106
107 enum cpuinfo_arm_chipset_series {
108 cpuinfo_arm_chipset_series_unknown = 0,
109 cpuinfo_arm_chipset_series_qualcomm_qsd,
110 cpuinfo_arm_chipset_series_qualcomm_msm,
111 cpuinfo_arm_chipset_series_qualcomm_apq,
112 cpuinfo_arm_chipset_series_qualcomm_snapdragon,
113 cpuinfo_arm_chipset_series_mediatek_mt,
114 cpuinfo_arm_chipset_series_samsung_exynos,
115 cpuinfo_arm_chipset_series_hisilicon_k3v,
116 cpuinfo_arm_chipset_series_hisilicon_hi,
117 cpuinfo_arm_chipset_series_hisilicon_kirin,
118 cpuinfo_arm_chipset_series_actions_atm,
119 cpuinfo_arm_chipset_series_allwinner_a,
120 cpuinfo_arm_chipset_series_amlogic_aml,
121 cpuinfo_arm_chipset_series_amlogic_s,
122 cpuinfo_arm_chipset_series_broadcom_bcm,
123 cpuinfo_arm_chipset_series_lg_nuclun,
124 cpuinfo_arm_chipset_series_leadcore_lc,
125 cpuinfo_arm_chipset_series_marvell_pxa,
126 cpuinfo_arm_chipset_series_mstar_6a,
127 cpuinfo_arm_chipset_series_novathor_u,
128 cpuinfo_arm_chipset_series_nvidia_tegra_t,
129 cpuinfo_arm_chipset_series_nvidia_tegra_ap,
130 cpuinfo_arm_chipset_series_nvidia_tegra_sl,
131 cpuinfo_arm_chipset_series_pinecone_surge_s,
132 cpuinfo_arm_chipset_series_renesas_mp,
133 cpuinfo_arm_chipset_series_rockchip_rk,
134 cpuinfo_arm_chipset_series_spreadtrum_sc,
135 cpuinfo_arm_chipset_series_telechips_tcc,
136 cpuinfo_arm_chipset_series_texas_instruments_omap,
137 cpuinfo_arm_chipset_series_wondermedia_wm,
138 cpuinfo_arm_chipset_series_max,
139 };
140
141 struct cpuinfo_arm_chipset {
142 enum cpuinfo_arm_chipset_vendor vendor;
143 enum cpuinfo_arm_chipset_series series;
144 uint32_t model;
145 char suffix[8];
146 };
147
148 #define BUFFER_SIZE 1024
149
getNumberOfCPU()150 static uint32_t getNumberOfCPU() {
151 FILE* fp = fopen("/proc/cpuinfo", "rb");
152 if (!fp) {
153 return 1;
154 }
155 uint32_t number = 0;
156 char buffer[BUFFER_SIZE];
157 while (!feof(fp)) {
158 char* str = fgets(buffer, BUFFER_SIZE, fp);
159 if (!str) {
160 break;
161 }
162 if (memcmp(buffer, "processor", 9) == 0) {
163 number++;
164 }
165 }
166 fclose(fp);
167 if (number < 1) {
168 number = 1;
169 }
170 return number;
171 }
172
getCPUMaxFreqKHz(int cpuID)173 static int getCPUMaxFreqKHz(int cpuID) {
174 char path[256];
175 sprintf(path, "/sys/devices/system/cpu/cpufreq/stats/cpu%d/time_in_state", cpuID);
176 FILE* fp = fopen(path, "rb");
177 if (!fp) {
178 sprintf(path, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpuID);
179 fp = fopen(path, "rb");
180 if (!fp) {
181 sprintf(path, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpuID);
182 fp = fopen(path, "rb");
183 if (!fp) {
184 return -1;
185 }
186 int maxfrequency = -1;
187 fscanf(fp, "%d", &maxfrequency);
188 fclose(fp);
189 return maxfrequency;
190 }
191 }
192 int maxfrequency = 0;
193 while (!feof(fp)) {
194 int frequency = 0;
195 int history = fscanf(fp, "%d %*d", &frequency);
196 if (history != 1) {
197 break;
198 }
199 if (frequency > maxfrequency) {
200 maxfrequency = frequency;
201 }
202 }
203 fclose(fp);
204 return maxfrequency;
205 }
206
sortCPUIDByMaxFrequency(std::vector<int> & cpuIDs,int * littleClusterOffset)207 static int sortCPUIDByMaxFrequency(std::vector<int>& cpuIDs, int* littleClusterOffset) {
208 const int cpuNumbers = cpuIDs.size();
209 *littleClusterOffset = 0;
210 if (cpuNumbers == 0) {
211 return 0;
212 }
213 std::vector<int> cpusFrequency;
214 cpusFrequency.resize(cpuNumbers);
215 for (int i = 0; i < cpuNumbers; ++i) {
216 int frequency = getCPUMaxFreqKHz(i);
217 cpuIDs[i] = i;
218 cpusFrequency[i] = frequency;
219 // MNN_PRINT("cpu fre: %d, %d\n", i, frequency);
220 }
221 for (int i = 0; i < cpuNumbers; ++i) {
222 for (int j = i + 1; j < cpuNumbers; ++j) {
223 if (cpusFrequency[i] < cpusFrequency[j]) {
224 // id
225 int temp = cpuIDs[i];
226 cpuIDs[i] = cpuIDs[j];
227 cpuIDs[j] = temp;
228 // frequency
229 temp = cpusFrequency[i];
230 cpusFrequency[i] = cpusFrequency[j];
231 cpusFrequency[j] = temp;
232 }
233 }
234 }
235 int midMaxFrequency = (cpusFrequency.front() + cpusFrequency.back()) / 2;
236 if (midMaxFrequency == cpusFrequency.back()) {
237 return 0;
238 }
239 for (int i = 0; i < cpuNumbers; ++i) {
240 if (cpusFrequency[i] < midMaxFrequency) {
241 *littleClusterOffset = i;
242 break;
243 }
244 }
245 return 0;
246 }
247
setSchedAffinity(const std::vector<int> & cpuIDs)248 static int setSchedAffinity(const std::vector<int>& cpuIDs) {
249 #define CPU_SETSIZE 1024
250 #define __NCPUBITS (8 * sizeof(unsigned long))
251 typedef struct {
252 unsigned long __bits[CPU_SETSIZE / __NCPUBITS];
253 } cpu_set_t;
254
255 #define CPU_SET(cpu, cpusetp) ((cpusetp)->__bits[(cpu) / __NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS)))
256
257 #define CPU_ZERO(cpusetp) memset((cpusetp), 0, sizeof(cpu_set_t))
258
259 // set affinity for thread
260 #ifdef __GLIBC__
261 pid_t pid = syscall(SYS_gettid);
262 #else
263 #ifdef PI3
264 pid_t pid = getpid();
265 #else
266 pid_t pid = gettid();
267 #endif
268 #endif
269 cpu_set_t mask;
270 CPU_ZERO(&mask);
271 for (int i = 0; i < (int)cpuIDs.size(); i++) {
272 CPU_SET(cpuIDs[i], &mask);
273 }
274
275 int syscallret = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
276 if (syscallret) {
277 MNN_PRINT("syscall error %d\n", syscallret);
278 return -1;
279 }
280
281 return 0;
282 }
283
284 #endif // arch
285
MNNSetCPUThreadsMode(MNNCPUThreadsMode mode)286 int MNNSetCPUThreadsMode(MNNCPUThreadsMode mode) {
287 #ifdef __ANDROID__
288 auto numberOfCPUs = getNumberOfCPU();
289 if (mode == MNN_CPU_MODE_DEFAULT) {
290 return 0;
291 }
292 static std::vector<int> sortedCPUIDs;
293 static int littleClusterOffset = 0;
294 if (sortedCPUIDs.empty()) {
295 sortedCPUIDs.resize(numberOfCPUs);
296 for (int i = 0; i < numberOfCPUs; ++i) {
297 sortedCPUIDs[i] = i;
298 }
299 sortCPUIDByMaxFrequency(sortedCPUIDs, &littleClusterOffset);
300 }
301
302 if (littleClusterOffset == 0 && mode != MNN_CPU_MODE_POWER_FRI) {
303 MNN_PRINT("This CPU Arch Do NOT support for setting cpu thread mode\n");
304 }
305 std::vector<int> cpuAttachIDs;
306 switch (mode) {
307 case MNN_CPU_MODE_POWER_FRI:
308 cpuAttachIDs = sortedCPUIDs;
309 break;
310 case MNN_CPU_MODE_LITTLE:
311 cpuAttachIDs = std::vector<int>(sortedCPUIDs.begin() + littleClusterOffset, sortedCPUIDs.end());
312 break;
313 case MNN_CPU_MODE_BIG:
314 cpuAttachIDs = std::vector<int>(sortedCPUIDs.begin(), sortedCPUIDs.begin() + littleClusterOffset);
315 break;
316 default:
317 cpuAttachIDs = sortedCPUIDs;
318 break;
319 }
320
321 #ifdef _OPENMP
322 const int threadsNumber = cpuAttachIDs.size();
323 omp_set_num_threads(threadsNumber);
324 std::vector<int> result(threadsNumber, 0);
325 #pragma omp parallel for
326 for (int i = 0; i < threadsNumber; ++i) {
327 result[i] = setSchedAffinity(cpuAttachIDs);
328 }
329 for (int i = 0; i < threadsNumber; ++i) {
330 if (result[i] != 0) {
331 return -1;
332 }
333 }
334 #else
335 int res = setSchedAffinity(cpuAttachIDs);
336 if (res != 0) {
337 return -1;
338 }
339 #endif // _OPENMP
340 return 0;
341 #elif __IOS__
342 return -1;
343 #else
344 return -1;
345 #endif // arch
346 }
MNNGetCPUFlops(uint32_t number)347 float MNNGetCPUFlops(uint32_t number) {
348 float flops = 2048.0f;
349 #ifdef __ANDROID__
350 auto numberOfCPUs = getNumberOfCPU();
351 if (0 == numberOfCPUs) {
352 return flops;
353 }
354 std::vector<int> freqs;
355 freqs.resize(numberOfCPUs);
356 for (int i = 0; i < numberOfCPUs; ++i) {
357 freqs[i] = getCPUMaxFreqKHz(i);
358 }
359 std::sort(freqs.rbegin(), freqs.rend());
360 number = std::min(number, numberOfCPUs);
361 flops = 0.0f;
362 for (uint32_t i = 0; i < number; ++i) {
363 flops += (float)freqs[i] / 1024.0f;
364 }
365 #endif
366 return flops;
367 }
368
369 // cpuinfo
370 // Reference from: https://github.com/pytorch/cpuinfo
371
372 #ifdef MNN_USE_ARMV82
373
374 #ifdef __ANDROID__
375
376 #define CPUINFO_ARM_MIDR_IMPLEMENTER_MASK UINT32_C(0xFF000000)
377 #define CPUINFO_ARM_MIDR_VARIANT_MASK UINT32_C(0x00F00000)
378 #define CPUINFO_ARM_MIDR_ARCHITECTURE_MASK UINT32_C(0x000F0000)
379 #define CPUINFO_ARM_MIDR_PART_MASK UINT32_C(0x0000FFF0)
380 #define CPUINFO_ARM_MIDR_REVISION_MASK UINT32_C(0x0000000F)
381
382 #define CPUINFO_ARM_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000)
383 #define CPUINFO_ARM_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000)
384 #define CPUINFO_ARM_LINUX_VALID_VARIANT UINT32_C(0x00040000)
385 #define CPUINFO_LINUX_FLAG_VALID UINT32_C(0x00001000)
386 #define CPUINFO_ARM_LINUX_VALID_MIDR UINT32_C(0x003F0000)
387 #define CPUINFO_ARM_LINUX_VALID_PART UINT32_C(0x00080000)
388 #define CPUINFO_ARM_LINUX_VALID_PROCESSOR UINT32_C(0x00200000)
389 #define CPUINFO_ARM_LINUX_VALID_REVISION UINT32_C(0x00100000)
390
391 #define CPUINFO_ARM_MIDR_IMPLEMENTER_OFFSET 24
392 #define CPUINFO_ARM_MIDR_VARIANT_OFFSET 20
393 #define CPUINFO_ARM_MIDR_ARCHITECTURE_OFFSET 16
394 #define CPUINFO_ARM_MIDR_PART_OFFSET 4
395 #define CPUINFO_ARM_MIDR_REVISION_OFFSET 0
396
397 #ifdef __aarch64__
398 #define CPUINFO_ARM_LINUX_FEATURE_FPHP UINT32_C(0x00000200)
399 #define CPUINFO_ARM_LINUX_FEATURE_ASIMDHP UINT32_C(0x00000400)
400 #define CPUINFO_ARM_LINUX_FEATURE_ASIMDDP UINT32_C(0x00100000)
401 #else
402 #define CPUINFO_ARM_LINUX_FEATURE_HALF UINT32_C(0x00000002)
403 #define CPUINFO_ARM_LINUX_FEATURE_NEON UINT32_C(0x00001000)
404 #endif
405
406 struct cpuinfo_arm_linux_processor {
407 uint32_t architecture_version;
408 // Main ID Register value
409 uint32_t midr;
410
411 uint32_t max_frequency;
412 uint32_t min_frequency;
413
414 uint32_t system_processor_id;
415 uint32_t flags;
416 };
417
418 struct proc_cpuinfo_parser_state {
419 char* hardware;
420 uint32_t processor_index;
421 uint32_t max_processors_count;
422 struct cpuinfo_arm_linux_processor* processors;
423 struct cpuinfo_arm_linux_processor dummy_processor;
424 };
425
426 typedef bool (*cpuinfo_line_callback)(const char*, const char*, void*, uint64_t);
427
midr_set_implementer(uint32_t midr,uint32_t implementer)428 inline static uint32_t midr_set_implementer(uint32_t midr, uint32_t implementer) {
429 return (midr & ~CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) |
430 ((implementer << CPUINFO_ARM_MIDR_IMPLEMENTER_OFFSET) & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK);
431 }
432
midr_set_architecture(uint32_t midr,uint32_t architecture)433 inline static uint32_t midr_set_architecture(uint32_t midr, uint32_t architecture) {
434 return (midr & ~CPUINFO_ARM_MIDR_ARCHITECTURE_MASK) |
435 ((architecture << CPUINFO_ARM_MIDR_ARCHITECTURE_OFFSET) & CPUINFO_ARM_MIDR_ARCHITECTURE_MASK);
436 }
437
midr_set_part(uint32_t midr,uint32_t part)438 inline static uint32_t midr_set_part(uint32_t midr, uint32_t part) {
439 return (midr & ~CPUINFO_ARM_MIDR_PART_MASK) | ((part << CPUINFO_ARM_MIDR_PART_OFFSET) & CPUINFO_ARM_MIDR_PART_MASK);
440 }
441
midr_set_revision(uint32_t midr,uint32_t revision)442 inline static uint32_t midr_set_revision(uint32_t midr, uint32_t revision) {
443 return (midr & ~CPUINFO_ARM_MIDR_REVISION_MASK) |
444 ((revision << CPUINFO_ARM_MIDR_REVISION_OFFSET) & CPUINFO_ARM_MIDR_REVISION_MASK);
445 }
446
midr_set_variant(uint32_t midr,uint32_t variant)447 inline static uint32_t midr_set_variant(uint32_t midr, uint32_t variant) {
448 return (midr & ~CPUINFO_ARM_MIDR_VARIANT_MASK) |
449 ((variant << CPUINFO_ARM_MIDR_VARIANT_OFFSET) & CPUINFO_ARM_MIDR_VARIANT_MASK);
450 }
451
midr_get_variant(uint32_t midr)452 inline static uint32_t midr_get_variant(uint32_t midr) {
453 return (midr & CPUINFO_ARM_MIDR_VARIANT_MASK) >> CPUINFO_ARM_MIDR_VARIANT_OFFSET;
454 }
455
cpuinfo_arm_linux_hwcap_from_getauxval(void)456 uint32_t cpuinfo_arm_linux_hwcap_from_getauxval(void) {
457 return (uint32_t)getauxval(AT_HWCAP);
458 }
459
bitmask_all(uint32_t bitfield,uint32_t mask)460 static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
461 return (bitfield & mask) == mask;
462 }
463
parse_cpu_part(const char * cpu_part_start,const char * cpu_part_end,struct cpuinfo_arm_linux_processor * processor)464 static void parse_cpu_part(const char* cpu_part_start, const char* cpu_part_end,
465 struct cpuinfo_arm_linux_processor* processor) {
466 const size_t cpu_part_length = (size_t)(cpu_part_end - cpu_part_start);
467
468 /*
469 * CPU part should contain hex prefix (0x) and one to three hex digits.
470 * I have never seen less than three digits as a value of this field,
471 * but I don't think it is impossible to see such values in future.
472 * Value can not contain more than three hex digits since
473 * Main ID Register (MIDR) assigns only a 12-bit value for CPU part.
474 */
475 if (cpu_part_length < 3 || cpu_part_length > 5) {
476 MNN_PRINT("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)\n", (int)cpu_part_length,
477 cpu_part_start, cpu_part_length);
478 return;
479 }
480
481 /* Verify the presence of hex prefix */
482 if (cpu_part_start[0] != '0' || cpu_part_start[1] != 'x') {
483 MNN_PRINT("CPU part %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix\n", (int)cpu_part_length,
484 cpu_part_start);
485 return;
486 }
487
488 /* Verify that characters after hex prefix are hexadecimal digits and decode them */
489 uint32_t cpu_part = 0;
490 for (const char* digit_ptr = cpu_part_start + 2; digit_ptr != cpu_part_end; digit_ptr++) {
491 const char digit_char = *digit_ptr;
492 uint32_t digit;
493 if (digit_char >= '0' && digit_char <= '9') {
494 digit = digit_char - '0';
495 } else if ((uint32_t)(digit_char - 'A') < 6) {
496 digit = 10 + (digit_char - 'A');
497 } else if ((uint32_t)(digit_char - 'a') < 6) {
498 digit = 10 + (digit_char - 'a');
499 } else {
500 MNN_PRINT("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character %c at offset %zu\n",
501 (int)cpu_part_length, cpu_part_start, digit_char, (size_t)(digit_ptr - cpu_part_start));
502 return;
503 }
504 cpu_part = cpu_part * 16 + digit;
505 }
506
507 processor->midr = midr_set_part(processor->midr, cpu_part);
508 processor->flags |= CPUINFO_ARM_LINUX_VALID_PART | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
509 }
510
parse_cpu_revision(const char * cpu_revision_start,const char * cpu_revision_end,struct cpuinfo_arm_linux_processor * processor)511 static void parse_cpu_revision(const char* cpu_revision_start, const char* cpu_revision_end,
512 struct cpuinfo_arm_linux_processor* processor) {
513 uint32_t cpu_revision = 0;
514 for (const char* digit_ptr = cpu_revision_start; digit_ptr != cpu_revision_end; digit_ptr++) {
515 const uint32_t digit = (uint32_t)(*digit_ptr - '0');
516
517 /* Verify that the character in CPU revision is a decimal digit */
518 if (digit >= 10) {
519 MNN_PRINT(
520 "CPU revision %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset "
521 "%zu\n",
522 (int)(cpu_revision_end - cpu_revision_start), cpu_revision_start, *digit_ptr,
523 (size_t)(digit_ptr - cpu_revision_start));
524 return;
525 }
526
527 cpu_revision = cpu_revision * 10 + digit;
528 }
529
530 processor->midr = midr_set_revision(processor->midr, cpu_revision);
531 processor->flags |= CPUINFO_ARM_LINUX_VALID_REVISION | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
532 }
533
parse_cpu_architecture(const char * cpu_architecture_start,const char * cpu_architecture_end,struct cpuinfo_arm_linux_processor * processor)534 static void parse_cpu_architecture(const char* cpu_architecture_start, const char* cpu_architecture_end,
535 struct cpuinfo_arm_linux_processor* processor) {
536 const size_t cpu_architecture_length = (size_t)(cpu_architecture_end - cpu_architecture_start);
537 /* Early AArch64 kernels report "CPU architecture: AArch64" instead of a numeric value 8 */
538 if (cpu_architecture_length == 7) {
539 if (memcmp(cpu_architecture_start, "AArch64", cpu_architecture_length) == 0) {
540 processor->midr = midr_set_architecture(processor->midr, UINT32_C(0xF));
541 processor->architecture_version = 8;
542 processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
543 return;
544 }
545 }
546
547 uint32_t architecture = 0;
548 const char* cpu_architecture_ptr = cpu_architecture_start;
549 for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
550 const uint32_t digit = (*cpu_architecture_ptr) - '0';
551
552 /* Verify that CPU architecture is a decimal number */
553 if (digit >= 10) {
554 break;
555 }
556
557 architecture = architecture * 10 + digit;
558 }
559
560 if (cpu_architecture_ptr == cpu_architecture_start) {
561 MNN_PRINT("CPU architecture %.*s in /proc/cpuinfo is ignored due to non-digit at the beginning of the string\n",
562 (int)cpu_architecture_length, cpu_architecture_start);
563 } else {
564 if (architecture != 0) {
565 processor->architecture_version = architecture;
566 processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
567
568 for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
569 const char feature = *cpu_architecture_ptr;
570 switch (feature) {
571 case ' ':
572 case '\t':
573 /* Ignore whitespace at the end */
574 break;
575 default:
576 MNN_PRINT("skipped unknown architectural feature '%c' for ARMv%u\n", feature, architecture);
577 break;
578 }
579 }
580 } else {
581 MNN_PRINT("CPU architecture %.*s in /proc/cpuinfo is ignored due to invalid value (0)\n",
582 (int)cpu_architecture_length, cpu_architecture_start);
583 }
584 }
585
586 uint32_t midr_architecture = UINT32_C(0xF);
587 processor->midr = midr_set_architecture(processor->midr, midr_architecture);
588 }
589
parse_processor_number(const char * processor_start,const char * processor_end)590 static uint32_t parse_processor_number(const char* processor_start, const char* processor_end) {
591 const size_t processor_length = (size_t)(processor_end - processor_start);
592
593 if (processor_length == 0) {
594 MNN_PRINT("Processor number in /proc/cpuinfo is ignored: string is empty\n");
595 return 0;
596 }
597
598 uint32_t processor_number = 0;
599 for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
600 const uint32_t digit = (uint32_t)(*digit_ptr - '0');
601 if (digit > 10) {
602 MNN_PRINT("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored\n",
603 (int)(processor_end - digit_ptr), digit_ptr);
604 break;
605 }
606
607 processor_number = processor_number * 10 + digit;
608 }
609
610 return processor_number;
611 }
612
parse_cpu_variant(const char * cpu_variant_start,const char * cpu_variant_end,struct cpuinfo_arm_linux_processor * processor)613 static void parse_cpu_variant(const char* cpu_variant_start, const char* cpu_variant_end,
614 struct cpuinfo_arm_linux_processor* processor) {
615 const size_t cpu_variant_length = cpu_variant_end - cpu_variant_start;
616
617 /*
618 * Value should contain hex prefix (0x) and one hex digit.
619 * Value can not contain more than one hex digits since
620 * Main ID Register (MIDR) assigns only a 4-bit value for CPU variant.
621 */
622 if (cpu_variant_length != 3) {
623 MNN_PRINT("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)\n",
624 (int)cpu_variant_length, cpu_variant_start, cpu_variant_length);
625 return;
626 }
627
628 /* Skip if there is no hex prefix (0x) */
629 if (cpu_variant_start[0] != '0' || cpu_variant_start[1] != 'x') {
630 MNN_PRINT("CPU variant %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix\n", (int)cpu_variant_length,
631 cpu_variant_start);
632 return;
633 }
634
635 /* Check if the value after hex prefix is indeed a hex digit and decode it. */
636 const char digit_char = cpu_variant_start[2];
637 uint32_t cpu_variant;
638 if ((uint32_t)(digit_char - '0') < 10) {
639 cpu_variant = (uint32_t)(digit_char - '0');
640 } else if ((uint32_t)(digit_char - 'A') < 6) {
641 cpu_variant = 10 + (uint32_t)(digit_char - 'A');
642 } else if ((uint32_t)(digit_char - 'a') < 6) {
643 cpu_variant = 10 + (uint32_t)(digit_char - 'a');
644 } else {
645 MNN_PRINT("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c'\n",
646 (int)cpu_variant_length, cpu_variant_start, digit_char);
647 return;
648 }
649
650 processor->midr = midr_set_variant(processor->midr, cpu_variant);
651 processor->flags |= CPUINFO_ARM_LINUX_VALID_VARIANT | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
652 }
653
parse_cpu_implementer(const char * cpu_implementer_start,const char * cpu_implementer_end,struct cpuinfo_arm_linux_processor * processor)654 static void parse_cpu_implementer(const char* cpu_implementer_start, const char* cpu_implementer_end,
655 struct cpuinfo_arm_linux_processor* processor) {
656 const size_t cpu_implementer_length = cpu_implementer_end - cpu_implementer_start;
657
658 /*
659 * Value should contain hex prefix (0x) and one or two hex digits.
660 * I have never seen single hex digit as a value of this field,
661 * but I don't think it is impossible in future.
662 * Value can not contain more than two hex digits since
663 * Main ID Register (MIDR) assigns only an 8-bit value for CPU implementer.
664 */
665 switch (cpu_implementer_length) {
666 case 3:
667 case 4:
668 break;
669 default:
670 MNN_PRINT("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)\n",
671 (int)cpu_implementer_length, cpu_implementer_start, cpu_implementer_length);
672 return;
673 }
674
675 /* Verify the presence of hex prefix */
676 if (cpu_implementer_start[0] != '0' || cpu_implementer_start[1] != 'x') {
677 MNN_PRINT("CPU implementer %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix\n",
678 (int)cpu_implementer_length, cpu_implementer_start);
679 return;
680 }
681
682 /* Verify that characters after hex prefix are hexadecimal digits and decode them */
683 uint32_t cpu_implementer = 0;
684 for (const char* digit_ptr = cpu_implementer_start + 2; digit_ptr != cpu_implementer_end; digit_ptr++) {
685 const char digit_char = *digit_ptr;
686 uint32_t digit;
687 if (digit_char >= '0' && digit_char <= '9') {
688 digit = digit_char - '0';
689 } else if ((uint32_t)(digit_char - 'A') < 6) {
690 digit = 10 + (digit_char - 'A');
691 } else if ((uint32_t)(digit_char - 'a') < 6) {
692 digit = 10 + (digit_char - 'a');
693 } else {
694 MNN_PRINT(
695 "CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c' at offset "
696 "%zu\n",
697 (int)cpu_implementer_length, cpu_implementer_start, digit_char,
698 (size_t)(digit_ptr - cpu_implementer_start));
699 return;
700 }
701 cpu_implementer = cpu_implementer * 16 + digit;
702 }
703
704 processor->midr = midr_set_implementer(processor->midr, cpu_implementer);
705 processor->flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
706 }
707
parse_line(const char * line_start,const char * line_end,struct proc_cpuinfo_parser_state * state,uint64_t line_number)708 static bool parse_line(const char* line_start, const char* line_end, struct proc_cpuinfo_parser_state* state,
709 uint64_t line_number) {
710 /* Empty line. Skip. */
711 if (line_start == line_end) {
712 return true;
713 }
714
715 /* Search for ':' on the line. */
716 const char* separator = line_start;
717 for (; separator != line_end; separator++) {
718 if (*separator == ':') {
719 break;
720 }
721 }
722 /* Skip line if no ':' separator was found. */
723 if (separator == line_end) {
724 MNN_PRINT("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found\n",
725 (int)(line_end - line_start), line_start);
726 return true;
727 }
728
729 /* Skip trailing spaces in key part. */
730 const char* key_end = separator;
731 for (; key_end != line_start; key_end--) {
732 if (key_end[-1] != ' ' && key_end[-1] != '\t') {
733 break;
734 }
735 }
736 /* Skip line if key contains nothing but spaces. */
737 if (key_end == line_start) {
738 MNN_PRINT("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces\n", (int)(line_end - line_start),
739 line_start);
740 return true;
741 }
742
743 /* Skip leading spaces in value part. */
744 const char* value_start = separator + 1;
745 for (; value_start != line_end; value_start++) {
746 if (*value_start != ' ') {
747 break;
748 }
749 }
750 /* Value part contains nothing but spaces. Skip line. */
751 if (value_start == line_end) {
752 MNN_PRINT("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces\n", (int)(line_end - line_start),
753 line_start);
754 return true;
755 }
756
757 /* Skip trailing spaces in value part (if any) */
758 const char* value_end = line_end;
759 for (; value_end != value_start; value_end--) {
760 if (value_end[-1] != ' ') {
761 break;
762 }
763 }
764
765 const uint32_t processor_index = state->processor_index;
766 const uint32_t max_processors_count = state->max_processors_count;
767 struct cpuinfo_arm_linux_processor* processors = state->processors;
768 struct cpuinfo_arm_linux_processor* processor = &state->dummy_processor;
769 if (processor_index < max_processors_count) {
770 processor = &processors[processor_index];
771 }
772
773 const size_t key_length = key_end - line_start;
774 switch (key_length) {
775 case 6:
776 break;
777 case 8:
778 if (memcmp(line_start, "CPU part", key_length) == 0) {
779 parse_cpu_part(value_start, value_end, processor);
780 } else if (memcmp(line_start, "Features", key_length) == 0) {
781 /* parse_features(value_start, value_end, processor); */
782 } else if (memcmp(line_start, "BogoMIPS", key_length) == 0) {
783 /* BogoMIPS is useless, don't parse */
784 } else if (memcmp(line_start, "Hardware", key_length) == 0) {
785 size_t value_length = value_end - value_start;
786 if (value_length > CPUINFO_HARDWARE_VALUE_MAX) {
787 MNN_PRINT(
788 "length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the "
789 "limit\n",
790 (int)value_length, value_start, CPUINFO_HARDWARE_VALUE_MAX);
791 value_length = CPUINFO_HARDWARE_VALUE_MAX;
792 } else {
793 state->hardware[value_length] = '\0';
794 }
795 memcpy(state->hardware, value_start, value_length);
796 MNN_PRINT("parsed /proc/cpuinfo Hardware = \"%.*s\"\n", (int)value_length, value_start);
797 } else if (memcmp(line_start, "Revision", key_length) == 0) {
798 /* Board revision, no use for now */
799 }
800 break;
801 case 9:
802 if (memcmp(line_start, "processor", key_length) == 0) {
803 const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
804 if (new_processor_index < processor_index) {
805 /* Strange: decreasing processor number */
806 MNN_PRINT("unexpectedly low processor number %u following processor %u in /proc/cpuinfo\n",
807 new_processor_index, processor_index);
808 } else if (new_processor_index > processor_index + 1) {
809 /* Strange, but common: skipped processor $(processor_index + 1) */
810 MNN_PRINT("unexpectedly high processor number %u following processor %u in /proc/cpuinfo\n",
811 new_processor_index, processor_index);
812 }
813 if (new_processor_index < max_processors_count) {
814 /* Record that the processor was mentioned in /proc/cpuinfo */
815 processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR;
816 } else {
817 /* Log and ignore processor */
818 MNN_PRINT("processor %u in /proc/cpuinfo is ignored: index exceeds system limit %u\n",
819 new_processor_index, max_processors_count - 1);
820 }
821 state->processor_index = new_processor_index;
822 return true;
823 } else if (memcmp(line_start, "Processor", key_length) == 0) {
824 /* TODO: parse to fix misreported architecture, similar to Android's cpufeatures */
825 }
826 break;
827 case 11:
828 if (memcmp(line_start, "CPU variant", key_length) == 0) {
829 parse_cpu_variant(value_start, value_end, processor);
830 }
831 break;
832 case 12:
833 if (memcmp(line_start, "CPU revision", key_length) == 0) {
834 parse_cpu_revision(value_start, value_end, processor);
835 }
836 break;
837 case 15:
838 if (memcmp(line_start, "CPU implementer", key_length) == 0) {
839 parse_cpu_implementer(value_start, value_end, processor);
840 } else if (memcmp(line_start, "CPU implementor", key_length) == 0) {
841 parse_cpu_implementer(value_start, value_end, processor);
842 }
843 break;
844 case 16:
845 if (memcmp(line_start, "CPU architecture", key_length) == 0) {
846 parse_cpu_architecture(value_start, value_end, processor);
847 }
848 break;
849 default:
850 break;
851 }
852 return true;
853 }
854
cpuinfo_linux_parse_multiline_file(const char * filename,size_t buffer_size,cpuinfo_line_callback callback,void * context)855 bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size, cpuinfo_line_callback callback,
856 void* context) {
857 #define RETIEMENT \
858 if (file != -1) { \
859 close(file); \
860 file = -1; \
861 } \
862 return false;
863
864 int file = -1;
865 bool status = false;
866 char* buffer = (char*)alloca(buffer_size);
867
868 file = open(filename, O_RDONLY);
869 if (file == -1) {
870 MNN_PRINT("failed to open %s\n", filename);
871 RETIEMENT
872 }
873
874 /* Only used for error reporting */
875 size_t position = 0;
876 uint64_t line_number = 1;
877 const char* buffer_end = &buffer[buffer_size];
878 char* data_start = buffer;
879 ssize_t bytes_read;
880 do {
881 bytes_read = read(file, data_start, (size_t)(buffer_end - data_start));
882 if (bytes_read < 0) {
883 MNN_PRINT("failed to read file %s at position %zu\n", filename, position);
884 RETIEMENT
885 }
886
887 position += (size_t)bytes_read;
888 const char* data_end = data_start + (size_t)bytes_read;
889 const char* line_start = buffer;
890
891 if (bytes_read == 0) {
892 /* No more data in the file: process the remaining text in the buffer as a single entry */
893 const char* line_end = data_end;
894 if (!callback(line_start, line_end, context, line_number)) {
895 RETIEMENT
896 }
897 } else {
898 const char* line_end;
899 do {
900 /* Find the end of the entry, as indicated by newline character ('\n') */
901 for (line_end = line_start; line_end != data_end; line_end++) {
902 if (*line_end == '\n') {
903 break;
904 }
905 }
906
907 /*
908 * If we located separator at the end of the entry, parse it.
909 * Otherwise, there may be more data at the end; read the file once again.
910 */
911 if (line_end != data_end) {
912 if (!callback(line_start, line_end, context, line_number++)) {
913 RETIEMENT
914 }
915 line_start = line_end + 1;
916 }
917 } while (line_end != data_end);
918
919 /* Move remaining partial line data at the end to the beginning of the buffer */
920 const size_t line_length = (size_t)(line_end - line_start);
921 memmove(buffer, line_start, line_length);
922 data_start = &buffer[line_length];
923 }
924 } while (bytes_read != 0);
925
926 /* Commit */
927 status = true;
928
929 if (file != -1) {
930 close(file);
931 file = -1;
932 }
933 return status;
934 }
935
cpuinfo_arm_linux_parse_proc_cpuinfo(char * hardware,uint32_t max_processors_count,struct cpuinfo_arm_linux_processor * processors)936 bool cpuinfo_arm_linux_parse_proc_cpuinfo(char* hardware, uint32_t max_processors_count,
937 struct cpuinfo_arm_linux_processor* processors) {
938 struct proc_cpuinfo_parser_state state = {
939 .hardware = hardware,
940 .processor_index = 0,
941 .max_processors_count = max_processors_count,
942 .processors = processors,
943 };
944
945 return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state);
946 }
947
cpuinfo_android_property_get(const char * key,char * value)948 static inline int cpuinfo_android_property_get(const char* key, char* value) {
949 return __system_property_get(key, value);
950 }
951
cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties * properties)952 void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties* properties) {
953 cpuinfo_android_property_get("ro.product.board", properties->ro_product_board);
954 cpuinfo_android_property_get("ro.board.platform", properties->ro_board_platform);
955 cpuinfo_android_property_get("ro.mediatek.platform", properties->ro_mediatek_platform);
956 cpuinfo_android_property_get("ro.arch", properties->ro_arch);
957 cpuinfo_android_property_get("ro.chipname", properties->ro_chipname);
958 cpuinfo_android_property_get("ro.hardware.chipname", properties->ro_hardware_chipname);
959 }
960
961 /*
962 * Map from ARM chipset series ID to ARM chipset vendor ID.
963 * This map is used to avoid storing vendor IDs in tables.
964 */
965 static enum cpuinfo_arm_chipset_vendor chipset_series_vendor[cpuinfo_arm_chipset_series_max] = {
966 [cpuinfo_arm_chipset_series_unknown] = cpuinfo_arm_chipset_vendor_unknown,
967 [cpuinfo_arm_chipset_series_qualcomm_qsd] = cpuinfo_arm_chipset_vendor_qualcomm,
968 [cpuinfo_arm_chipset_series_qualcomm_msm] = cpuinfo_arm_chipset_vendor_qualcomm,
969 [cpuinfo_arm_chipset_series_qualcomm_apq] = cpuinfo_arm_chipset_vendor_qualcomm,
970 [cpuinfo_arm_chipset_series_qualcomm_snapdragon] = cpuinfo_arm_chipset_vendor_qualcomm,
971 [cpuinfo_arm_chipset_series_mediatek_mt] = cpuinfo_arm_chipset_vendor_mediatek,
972 [cpuinfo_arm_chipset_series_samsung_exynos] = cpuinfo_arm_chipset_vendor_samsung,
973 [cpuinfo_arm_chipset_series_hisilicon_k3v] = cpuinfo_arm_chipset_vendor_hisilicon,
974 [cpuinfo_arm_chipset_series_hisilicon_hi] = cpuinfo_arm_chipset_vendor_hisilicon,
975 [cpuinfo_arm_chipset_series_hisilicon_kirin] = cpuinfo_arm_chipset_vendor_hisilicon,
976 [cpuinfo_arm_chipset_series_actions_atm] = cpuinfo_arm_chipset_vendor_actions,
977 [cpuinfo_arm_chipset_series_allwinner_a] = cpuinfo_arm_chipset_vendor_allwinner,
978 [cpuinfo_arm_chipset_series_amlogic_aml] = cpuinfo_arm_chipset_vendor_amlogic,
979 [cpuinfo_arm_chipset_series_amlogic_s] = cpuinfo_arm_chipset_vendor_amlogic,
980 [cpuinfo_arm_chipset_series_broadcom_bcm] = cpuinfo_arm_chipset_vendor_broadcom,
981 [cpuinfo_arm_chipset_series_lg_nuclun] = cpuinfo_arm_chipset_vendor_lg,
982 [cpuinfo_arm_chipset_series_leadcore_lc] = cpuinfo_arm_chipset_vendor_leadcore,
983 [cpuinfo_arm_chipset_series_marvell_pxa] = cpuinfo_arm_chipset_vendor_marvell,
984 [cpuinfo_arm_chipset_series_mstar_6a] = cpuinfo_arm_chipset_vendor_mstar,
985 [cpuinfo_arm_chipset_series_novathor_u] = cpuinfo_arm_chipset_vendor_novathor,
986 [cpuinfo_arm_chipset_series_nvidia_tegra_t] = cpuinfo_arm_chipset_vendor_nvidia,
987 [cpuinfo_arm_chipset_series_nvidia_tegra_ap] = cpuinfo_arm_chipset_vendor_nvidia,
988 [cpuinfo_arm_chipset_series_nvidia_tegra_sl] = cpuinfo_arm_chipset_vendor_nvidia,
989 [cpuinfo_arm_chipset_series_pinecone_surge_s] = cpuinfo_arm_chipset_vendor_pinecone,
990 [cpuinfo_arm_chipset_series_renesas_mp] = cpuinfo_arm_chipset_vendor_renesas,
991 [cpuinfo_arm_chipset_series_rockchip_rk] = cpuinfo_arm_chipset_vendor_rockchip,
992 [cpuinfo_arm_chipset_series_spreadtrum_sc] = cpuinfo_arm_chipset_vendor_spreadtrum,
993 [cpuinfo_arm_chipset_series_telechips_tcc] = cpuinfo_arm_chipset_vendor_telechips,
994 [cpuinfo_arm_chipset_series_texas_instruments_omap] = cpuinfo_arm_chipset_vendor_texas_instruments,
995 [cpuinfo_arm_chipset_series_wondermedia_wm] = cpuinfo_arm_chipset_vendor_wondermedia,
996 };
997
load_u16le(const void * ptr)998 static inline uint16_t load_u16le(const void* ptr) {
999 return *((const uint16_t*)ptr);
1000 }
1001
load_u32le(const void * ptr)1002 static inline uint32_t load_u32le(const void* ptr) {
1003 return *((const uint32_t*)ptr);
1004 }
1005
1006 /**
1007 * Tries to match /Samsung Exynos\d{4}$/ signature (case-insensitive) for Samsung Exynos chipsets.
1008 * If match successful, extracts model information into \p chipset argument.
1009 *
1010 * @param start - start of the /proc/cpuinfo Hardware string to match.
1011 * @param end - end of the /proc/cpuinfo Hardware string to match.
1012 * @param[out] chipset - location where chipset information will be stored upon a successful match.
1013 *
1014 * @returns true if signature matched, false otherwise.
1015 */
match_samsung_exynos(const char * start,const char * end,struct cpuinfo_arm_chipset * chipset)1016 static bool match_samsung_exynos(const char* start, const char* end, struct cpuinfo_arm_chipset* chipset) {
1017 /*
1018 * Expect at 18-19 symbols:
1019 * - "Samsung" (7 symbols) + space + "Exynos" (6 symbols) + optional space 4-digit model number
1020 */
1021 const size_t length = end - start;
1022 switch (length) {
1023 case 18:
1024 case 19:
1025 break;
1026 default:
1027 return false;
1028 }
1029
1030 /*
1031 * Check that the string starts with "samsung exynos", case-insensitive.
1032 * Blocks of 4 characters are loaded and compared as little-endian 32-bit word.
1033 * Case-insensitive characters are binary ORed with 0x20 to convert them to lowercase.
1034 */
1035 const uint32_t expected_sams = UINT32_C(0x20202000) | load_u32le(start);
1036 if (expected_sams != UINT32_C(0x736D6153) /* "smaS" = reverse("Sams") */) {
1037 return false;
1038 }
1039 const uint32_t expected_ung = UINT32_C(0x00202020) | load_u32le(start + 4);
1040 if (expected_ung != UINT32_C(0x20676E75) /* " ung" = reverse("ung ") */) {
1041 return false;
1042 }
1043 const uint32_t expected_exyn = UINT32_C(0x20202000) | load_u32le(start + 8);
1044 if (expected_exyn != UINT32_C(0x6E797845) /* "nyxE" = reverse("Exyn") */) {
1045 return false;
1046 }
1047 const uint16_t expected_os = UINT16_C(0x2020) | load_u16le(start + 12);
1048 if (expected_os != UINT16_C(0x736F) /* "so" = reverse("os") */) {
1049 return false;
1050 }
1051
1052 const char* pos = start + 14;
1053
1054 /* There can be a space ' ' following the "Exynos" string */
1055 if (*pos == ' ') {
1056 pos++;
1057
1058 /* If optional space if present, we expect exactly 19 characters */
1059 if (length != 19) {
1060 return false;
1061 }
1062 }
1063
1064 /* Validate and parse 4-digit model number */
1065 uint32_t model = 0;
1066 for (uint32_t i = 0; i < 4; i++) {
1067 const uint32_t digit = (uint32_t)(uint8_t)(*pos++) - '0';
1068 if (digit >= 10) {
1069 /* Not really a digit */
1070 return false;
1071 }
1072 model = model * 10 + digit;
1073 }
1074
1075 /* Return parsed chipset */
1076 *chipset = (struct cpuinfo_arm_chipset){
1077 .vendor = cpuinfo_arm_chipset_vendor_samsung,
1078 .series = cpuinfo_arm_chipset_series_samsung_exynos,
1079 .model = model,
1080 };
1081 return true;
1082 }
1083
1084 /**
1085 * Tries to match /exynos\d{4}$/ signature for Samsung Exynos chipsets.
1086 * If match successful, extracts model information into \p chipset argument.
1087 *
1088 * @param start - start of the platform identifier (ro.board.platform or ro.chipname) to match.
1089 * @param end - end of the platform identifier (ro.board.platform or ro.chipname) to match.
1090 * @param[out] chipset - location where chipset information will be stored upon a successful match.
1091 *
1092 * @returns true if signature matched, false otherwise.
1093 */
match_exynos(const char * start,const char * end,struct cpuinfo_arm_chipset * chipset)1094 static bool match_exynos(const char* start, const char* end, struct cpuinfo_arm_chipset* chipset) {
1095 /* Expect exactly 10 symbols: "exynos" (6 symbols) + 4-digit model number */
1096 if (start + 10 != end) {
1097 return false;
1098 }
1099
1100 /* Load first 4 bytes as little endian 32-bit word */
1101 const uint32_t expected_exyn = load_u32le(start);
1102 if (expected_exyn != UINT32_C(0x6E797865) /* "nyxe" = reverse("exyn") */) {
1103 return false;
1104 }
1105
1106 /* Load next 2 bytes as little endian 16-bit word */
1107 const uint16_t expected_os = load_u16le(start + 4);
1108 if (expected_os != UINT16_C(0x736F) /* "so" = reverse("os") */) {
1109 return false;
1110 }
1111
1112 /* Check and parse 4-digit model number */
1113 uint32_t model = 0;
1114 for (uint32_t i = 6; i < 10; i++) {
1115 const uint32_t digit = (uint32_t)(uint8_t)start[i] - '0';
1116 if (digit >= 10) {
1117 /* Not really a digit */
1118 return false;
1119 }
1120 model = model * 10 + digit;
1121 }
1122
1123 /* Return parsed chipset. */
1124 *chipset = (struct cpuinfo_arm_chipset){
1125 .vendor = cpuinfo_arm_chipset_vendor_samsung,
1126 .series = cpuinfo_arm_chipset_series_samsung_exynos,
1127 .model = model,
1128 };
1129 return true;
1130 }
1131
1132 /**
1133 * Tries to match /universal\d{4}$/ signature for Samsung Exynos chipsets.
1134 * If match successful, extracts model information into \p chipset argument.
1135 *
1136 * @param start - start of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board or ro.chipname)
1137 * to match.
1138 * @param end - end of the platform identifier (/proc/cpuinfo Hardware string, ro.product.board or ro.chipname)
1139 * to match.
1140 * @param[out] chipset - location where chipset information will be stored upon a successful match.
1141 *
1142 * @returns true if signature matched, false otherwise.
1143 */
match_universal(const char * start,const char * end,struct cpuinfo_arm_chipset * chipset)1144 static bool match_universal(const char* start, const char* end, struct cpuinfo_arm_chipset* chipset) {
1145 /* Expect exactly 13 symbols: "universal" (9 symbols) + 4-digit model number */
1146 if (start + 13 != end) {
1147 return false;
1148 }
1149
1150 /*
1151 * Check that the string starts with "universal".
1152 * Blocks of 4 characters are loaded and compared as little-endian 32-bit word.
1153 * Case-insensitive characters are binary ORed with 0x20 to convert them to lowercase.
1154 */
1155 const uint8_t expected_u = UINT8_C(0x20) | (uint8_t)start[0];
1156 if (expected_u != UINT8_C(0x75) /* "u" */) {
1157 return false;
1158 }
1159 const uint32_t expected_nive = UINT32_C(0x20202020) | load_u32le(start + 1);
1160 if (expected_nive != UINT32_C(0x6576696E) /* "evin" = reverse("nive") */) {
1161 return false;
1162 }
1163 const uint32_t expected_ersa = UINT32_C(0x20202020) | load_u32le(start + 5);
1164 if (expected_ersa != UINT32_C(0x6C617372) /* "lasr" = reverse("rsal") */) {
1165 return false;
1166 }
1167
1168 /* Validate and parse 4-digit model number */
1169 uint32_t model = 0;
1170 for (uint32_t i = 9; i < 13; i++) {
1171 const uint32_t digit = (uint32_t)(uint8_t)start[i] - '0';
1172 if (digit >= 10) {
1173 /* Not really a digit */
1174 return false;
1175 }
1176 model = model * 10 + digit;
1177 }
1178
1179 /* Return parsed chipset. */
1180 *chipset = (struct cpuinfo_arm_chipset){
1181 .vendor = cpuinfo_arm_chipset_vendor_samsung,
1182 .series = cpuinfo_arm_chipset_series_samsung_exynos,
1183 .model = model,
1184 };
1185 return true;
1186 }
1187
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(const char * hardware,uint32_t cores,uint32_t max_cpu_freq_max)1188 struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(const char* hardware,
1189 uint32_t cores,
1190 uint32_t max_cpu_freq_max) {
1191 struct cpuinfo_arm_chipset chipset;
1192 const size_t hardware_length = strnlen(hardware, CPUINFO_HARDWARE_VALUE_MAX);
1193 const char* hardware_end = hardware + hardware_length;
1194
1195 if (match_samsung_exynos(hardware, hardware_end, &chipset)) {
1196 return chipset;
1197 }
1198
1199 if (match_universal(hardware, hardware_end, &chipset)) {
1200 return chipset;
1201 }
1202 return (struct cpuinfo_arm_chipset){
1203 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1204 .series = cpuinfo_arm_chipset_series_unknown,
1205 };
1206 }
1207
cpuinfo_arm_android_decode_chipset_from_ro_product_board(const char * ro_product_board,uint32_t cores,uint32_t max_cpu_freq_max)1208 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_product_board(const char* ro_product_board,
1209 uint32_t cores,
1210 uint32_t max_cpu_freq_max) {
1211 struct cpuinfo_arm_chipset chipset;
1212 const char* board = ro_product_board;
1213 const size_t board_length = strnlen(ro_product_board, CPUINFO_BUILD_PROP_VALUE_MAX);
1214 const char* board_end = ro_product_board + board_length;
1215
1216 if (match_universal(board, board_end, &chipset)) {
1217 return chipset;
1218 }
1219
1220 return (struct cpuinfo_arm_chipset){
1221 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1222 .series = cpuinfo_arm_chipset_series_unknown,
1223 };
1224 }
1225
cpuinfo_arm_android_decode_chipset_from_ro_board_platform(const char * platform,uint32_t cores,uint32_t max_cpu_freq_max)1226 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_board_platform(const char* platform,
1227 uint32_t cores,
1228 uint32_t max_cpu_freq_max) {
1229 struct cpuinfo_arm_chipset chipset;
1230 const size_t platform_length = strnlen(platform, CPUINFO_BUILD_PROP_VALUE_MAX);
1231 const char* platform_end = platform + platform_length;
1232
1233 if (match_exynos(platform, platform_end, &chipset)) {
1234 return chipset;
1235 }
1236
1237 return (struct cpuinfo_arm_chipset){
1238 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1239 .series = cpuinfo_arm_chipset_series_unknown,
1240 };
1241 }
1242
cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(const char * platform)1243 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(const char* platform) {
1244 return (struct cpuinfo_arm_chipset){
1245 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1246 .series = cpuinfo_arm_chipset_series_unknown,
1247 };
1248 }
1249
cpuinfo_arm_android_decode_chipset_from_ro_arch(const char * arch)1250 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_arch(const char* arch) {
1251 struct cpuinfo_arm_chipset chipset;
1252 const char* arch_end = arch + strnlen(arch, CPUINFO_BUILD_PROP_VALUE_MAX);
1253
1254 /* Check Samsung exynosXXXX signature */
1255 if (match_exynos(arch, arch_end, &chipset)) {
1256 return chipset;
1257 }
1258
1259 return (struct cpuinfo_arm_chipset){
1260 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1261 .series = cpuinfo_arm_chipset_series_unknown,
1262 };
1263 }
1264
cpuinfo_arm_android_decode_chipset_from_ro_chipname(const char * chipname)1265 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_chipname(const char* chipname) {
1266 struct cpuinfo_arm_chipset chipset;
1267 const size_t chipname_length = strnlen(chipname, CPUINFO_BUILD_PROP_VALUE_MAX);
1268 const char* chipname_end = chipname + chipname_length;
1269
1270 if (match_exynos(chipname, chipname_end, &chipset)) {
1271 return chipset;
1272 }
1273 if (match_universal(chipname, chipname_end, &chipset)) {
1274 return chipset;
1275 }
1276
1277 return (struct cpuinfo_arm_chipset){
1278 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1279 .series = cpuinfo_arm_chipset_series_unknown,
1280 };
1281 }
1282
cpuinfo_arm_android_decode_chipset(const struct cpuinfo_android_properties * properties,uint32_t cores,uint32_t max_cpu_freq_max)1283 struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset(const struct cpuinfo_android_properties* properties,
1284 uint32_t cores, uint32_t max_cpu_freq_max) {
1285 // this function is used to decode chipset, which is only used to detect Samsung Exynos chipsets
1286 // so chipesets now only have TWO classes, one is cpuinfo_arm_chipset_vendor_samsung, the other is
1287 // cpuinfo_arm_chipset_vendor_unknown
1288
1289 struct cpuinfo_arm_chipset chipset = {
1290 .vendor = cpuinfo_arm_chipset_vendor_unknown,
1291 .series = cpuinfo_arm_chipset_series_unknown,
1292 };
1293
1294 struct cpuinfo_arm_chipset chipsets[cpuinfo_android_chipset_property_max] = {
1295 [cpuinfo_android_chipset_property_proc_cpuinfo_hardware] =
1296 cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(properties->proc_cpuinfo_hardware, cores,
1297 max_cpu_freq_max),
1298 [cpuinfo_android_chipset_property_ro_product_board] = cpuinfo_arm_android_decode_chipset_from_ro_product_board(
1299 properties->ro_product_board, cores, max_cpu_freq_max),
1300 [cpuinfo_android_chipset_property_ro_board_platform] =
1301 cpuinfo_arm_android_decode_chipset_from_ro_board_platform(properties->ro_board_platform, cores,
1302 max_cpu_freq_max),
1303 [cpuinfo_android_chipset_property_ro_mediatek_platform] =
1304 cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(properties->ro_mediatek_platform),
1305 [cpuinfo_android_chipset_property_ro_arch] =
1306 cpuinfo_arm_android_decode_chipset_from_ro_arch(properties->ro_arch),
1307 [cpuinfo_android_chipset_property_ro_chipname] =
1308 cpuinfo_arm_android_decode_chipset_from_ro_chipname(properties->ro_chipname),
1309 [cpuinfo_android_chipset_property_ro_hardware_chipname] =
1310 cpuinfo_arm_android_decode_chipset_from_ro_chipname(properties->ro_hardware_chipname),
1311 };
1312
1313 enum cpuinfo_arm_chipset_vendor vendor = cpuinfo_arm_chipset_vendor_unknown;
1314 for (size_t i = 0; i < cpuinfo_android_chipset_property_max; ++i) {
1315 const enum cpuinfo_arm_chipset_vendor decoded_vendor = chipsets[i].vendor;
1316 if (decoded_vendor != cpuinfo_arm_chipset_vendor_unknown) {
1317 if (vendor == cpuinfo_arm_chipset_vendor_unknown) {
1318 vendor = decoded_vendor;
1319 } else if (vendor != decoded_vendor) {
1320 // MNN_PRINT(
1321 // "[MNN WARNING] chipset detection failed: different chipset vendors reported in different system "
1322 // "properties\n");
1323 return chipset;
1324 }
1325 }
1326 }
1327 if (vendor == cpuinfo_arm_chipset_vendor_unknown) {
1328 // MNN_PRINT("[MNN WARNING] chipset detection failed: none of the system properties matched known signatures\n");
1329 return chipset;
1330 }
1331
1332 for (size_t i = 0; i < cpuinfo_android_chipset_property_max; ++i) {
1333 if (chipsets[i].series != cpuinfo_arm_chipset_series_unknown) {
1334 chipset = chipsets[i];
1335 break;
1336 }
1337 }
1338
1339 // MNN_PRINT("chipset vendor, series, model is: %d, %d, %d\n", chipset.vendor, chipset.series, chipset.model);
1340 return chipset;
1341 }
1342
1343 #endif // __ANDROID__
1344
1345 #if defined(__APPLE__) && defined(__aarch64__)
1346
get_sys_info_by_name(const char * type_specifier)1347 static uint32_t get_sys_info_by_name(const char* type_specifier) {
1348 size_t size = 0;
1349 uint32_t result = 0;
1350 if (sysctlbyname(type_specifier, NULL, &size, NULL, 0) != 0) {
1351 MNN_PRINT("sysctlbyname(\"%s\") failed\n", type_specifier);
1352 } else if (size == sizeof(uint32_t)) {
1353 sysctlbyname(type_specifier, &result, &size, NULL, 0);
1354 MNN_PRINT("%s: %u , size = %lu\n", type_specifier, result, size);
1355 } else {
1356 MNN_PRINT("sysctl does not support non-integer lookup for (\"%s\")\n", type_specifier);
1357 }
1358 return result;
1359 }
1360
1361 #endif // iOS
1362
cpuinfo_arm_init(struct cpuinfo_arm_isa * cpuinfo_isa)1363 void cpuinfo_arm_init(struct cpuinfo_arm_isa* cpuinfo_isa) {
1364 memset(cpuinfo_isa, 0, sizeof(struct cpuinfo_arm_isa));
1365
1366 // android
1367 #ifdef __ANDROID__
1368 struct cpuinfo_arm_linux_processor* arm_linux_processors = NULL;
1369 const uint32_t processors_count = getNumberOfCPU();
1370
1371 char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX] = {0};
1372
1373 arm_linux_processors = static_cast<struct cpuinfo_arm_linux_processor*>(
1374 calloc(processors_count, sizeof(struct cpuinfo_arm_linux_processor)));
1375 if (arm_linux_processors == NULL) {
1376 MNN_PRINT("failed to allocate %zu bytes for descriptions of %u ARM logical processors\n",
1377 processors_count * sizeof(struct cpuinfo_arm_linux_processor), processors_count);
1378 return;
1379 }
1380
1381 if (!cpuinfo_arm_linux_parse_proc_cpuinfo(proc_cpuinfo_hardware, processors_count, arm_linux_processors)) {
1382 MNN_PRINT("failed to parse processor information from /proc/cpuinfo\n");
1383 return;
1384 }
1385
1386 uint32_t valid_processor_mask = 0;
1387 for (uint32_t i = 0; i < processors_count; i++) {
1388 if (bitmask_all(arm_linux_processors[i].flags, valid_processor_mask)) {
1389 arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_VALID;
1390 }
1391 }
1392
1393 uint32_t valid_processors = 0, last_midr = 0;
1394 for (uint32_t i = 0; i < processors_count; i++) {
1395 arm_linux_processors[i].system_processor_id = i;
1396 if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
1397 valid_processors += 1;
1398 if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
1399 last_midr = arm_linux_processors[i].midr;
1400 }
1401 }
1402 }
1403
1404 const uint32_t isa_features = cpuinfo_arm_linux_hwcap_from_getauxval();
1405
1406 struct cpuinfo_android_properties android_properties;
1407 cpuinfo_arm_android_parse_properties(&android_properties);
1408 const struct cpuinfo_arm_chipset chipset =
1409 cpuinfo_arm_android_decode_chipset(&android_properties, valid_processors, 0);
1410
1411 switch (last_midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
1412 case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
1413 cpuinfo_isa->dot = true;
1414 break;
1415 default:
1416 #ifdef __aarch64__
1417 if (isa_features & CPUINFO_ARM_LINUX_FEATURE_ASIMDDP) {
1418 cpuinfo_isa->dot = true;
1419 }
1420 #endif
1421 // TODO, whitelist, ex: hisilicon_kirin 980...
1422 break;
1423 }
1424 #ifdef __aarch64__
1425 const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
1426 if ((isa_features & fp16arith_mask) == fp16arith_mask) {
1427 if (chipset.series == cpuinfo_arm_chipset_series_samsung_exynos && chipset.model == 9810) {
1428 cpuinfo_isa->fp16arith = false;
1429 } else {
1430 cpuinfo_isa->fp16arith = true;
1431 }
1432 }
1433 #else
1434 // pytorch/cpuinfo: src/arm/linux/aarch32-isa.c
1435 uint32_t architecture_version = 0;
1436 if (processors_count > 0) {
1437 architecture_version = arm_linux_processors[0].architecture_version;
1438 }
1439 if (architecture_version >= 8) {
1440 /*
1441 * NEON FP16 compute extension and VQRDMLAH/VQRDMLSH instructions are not indicated in /proc/cpuinfo.
1442 * Use a MIDR-based heuristic to whitelist processors known to support it:
1443 * - Processors with Cortex-A55 cores
1444 * - Processors with Cortex-A65 cores
1445 * - Processors with Cortex-A75 cores
1446 * - Processors with Cortex-A76 cores
1447 * - Processors with Cortex-A77 cores
1448 * - Processors with Exynos M4 cores
1449 * - Processors with Exynos M5 cores
1450 * - Neoverse N1 cores
1451 */
1452 if (chipset.series == cpuinfo_arm_chipset_series_samsung_exynos && chipset.model == 9810) {
1453 /* Only little cores of Exynos 9810 support FP16 & RDM */
1454 MNN_PRINT("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
1455 } else {
1456 switch (last_midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
1457 case UINT32_C(0x4100D050): /* Cortex-A55 */
1458 case UINT32_C(0x4100D060): /* Cortex-A65 */
1459 case UINT32_C(0x4100D0B0): /* Cortex-A76 */
1460 case UINT32_C(0x4100D0C0): /* Neoverse N1 */
1461 case UINT32_C(0x4100D0D0): /* Cortex-A77 */
1462 case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
1463 case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
1464 case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
1465 case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
1466 case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
1467 case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
1468 case UINT32_C(0x53000030): /* Exynos M4 */
1469 case UINT32_C(0x53000040): /* Exynos M5 */
1470 cpuinfo_isa->fp16arith = true;
1471 break;
1472 }
1473 }
1474 /*
1475 * NEON VDOT instructions are not indicated in /proc/cpuinfo.
1476 * Use a MIDR-based heuristic to whitelist processors known to support it.
1477 */
1478 switch (last_midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
1479 case UINT32_C(0x4100D0B0): /* Cortex-A76 */
1480 case UINT32_C(0x4100D0D0): /* Cortex-A77 */
1481 case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
1482 case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
1483 case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
1484 case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
1485 case UINT32_C(0x53000030): /* Exynos-M4 */
1486 case UINT32_C(0x53000040): /* Exynos-M5 */
1487 cpuinfo_isa->dot = true;
1488 break;
1489 case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */
1490 cpuinfo_isa->dot = (midr_get_variant(last_midr) >= 1);
1491 break;
1492 case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */
1493 cpuinfo_isa->dot = (midr_get_variant(last_midr) >= 2);
1494 break;
1495 }
1496 }
1497 #endif
1498
1499 #endif // #ifdef __ANDROID__
1500
1501 // iOS
1502 #if defined(__IOS__) && defined(__aarch64__)
1503
1504 // A11
1505 #ifndef CPUFAMILY_ARM_MONSOON_MISTRAL
1506 #define CPUFAMILY_ARM_MONSOON_MISTRAL 0xe81e7ef6
1507 #endif
1508 // A12
1509 #ifndef CPUFAMILY_ARM_VORTEX_TEMPEST
1510 #define CPUFAMILY_ARM_VORTEX_TEMPEST 0x07d34b9f
1511 #endif
1512 // A13
1513 #ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER
1514 #define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504d2
1515 #endif
1516 // A14
1517 #ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM
1518 #define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1b588bb3
1519 #endif
1520
1521 const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily");
1522 // const uint32_t cpu_type = get_sys_info_by_name("hw.cputype");
1523 // const uint32_t cpu_subtype = get_sys_info_by_name("hw.cpusubtype");
1524
1525 cpuinfo_isa->fp16arith = cpu_family == CPUFAMILY_ARM_MONSOON_MISTRAL ||
1526 cpu_family == CPUFAMILY_ARM_VORTEX_TEMPEST ||
1527 cpu_family == CPUFAMILY_ARM_LIGHTNING_THUNDER ||
1528 cpu_family == CPUFAMILY_ARM_FIRESTORM_ICESTORM;
1529
1530 cpuinfo_isa->dot = cpu_family == CPUFAMILY_ARM_LIGHTNING_THUNDER ||
1531 cpu_family == CPUFAMILY_ARM_FIRESTORM_ICESTORM;
1532
1533 #endif // iOS
1534
1535 // arm64-osx
1536 #if defined(__APPLE__) && defined(__aarch64__) && !defined(__IOS__)
1537 #ifndef CPUFAMILY_AARCH64_FIRESTORM_ICESTORM
1538 #define CPUFAMILY_AARCH64_FIRESTORM_ICESTORM 458787763
1539 #endif
1540 const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily");
1541 cpuinfo_isa->fp16arith = cpu_family == CPUFAMILY_AARCH64_FIRESTORM_ICESTORM;
1542 cpuinfo_isa->dot = cpu_family == CPUFAMILY_AARCH64_FIRESTORM_ICESTORM;
1543 #endif
1544 MNN_PRINT("The device support dot:%d, support fp16:%d\n", cpuinfo_isa->dot, cpuinfo_isa->fp16arith);
1545 }
1546
1547 #endif // MNN_USE_ARMV82
1548