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