CPURuntime.cpp 59 KB

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