master ohmu/fastfetch-openbsd / src / detection / cpu / cpu.c
ohmu  ·  2026-10-15
  1#include "cpu.h"
  2
  3const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu);
  4
  5const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu) {
  6    const char* error = ffDetectCPUImpl(options, cpu);
  7    if (error) {
  8        return error;
  9    }
 10
 11    const char* removeStrings[] = {
 12        " CPU", " FPU", " APU", " Processor", " Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core", " 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core"
 13    };
 14    ffStrbufRemoveStrings(&cpu->name, ARRAY_SIZE(removeStrings), removeStrings);
 15    uint32_t radeonGraphics = ffStrbufFirstIndexS(&cpu->name, " w/ Radeon "); // w/ Radeon 780M Graphics
 16    if (radeonGraphics >= cpu->name.length) {
 17        radeonGraphics = ffStrbufFirstIndexS(&cpu->name, " with Radeon ");
 18    }
 19    if (radeonGraphics < cpu->name.length) {
 20        ffStrbufSubstrBefore(&cpu->name, radeonGraphics);
 21    }
 22    ffStrbufSubstrBeforeFirstC(&cpu->name, '@'); // Cut the speed output in the name as we append our own
 23    ffStrbufTrimRight(&cpu->name, ' ');          // If we removed the @ in previous step there was most likely a space before it
 24    ffStrbufRemoveDupWhitespaces(&cpu->name);
 25
 26#if __i386__ || __x86_64__
 27    ffCPUDetectX86Specific(cpu);
 28#endif
 29
 30    return nullptr;
 31}
 32
 33const char* ffCPUAppleCodeToName(uint32_t code) {
 34    // https://github.com/AsahiLinux/docs/blob/main/docs/hw/soc/soc-codenames.md
 35    switch (code) {
 36        case 8103:
 37            return "Apple M1";
 38        case 6000:
 39            return "Apple M1 Pro";
 40        case 6001:
 41            return "Apple M1 Max";
 42        case 6002:
 43            return "Apple M1 Ultra";
 44        case 8112:
 45            return "Apple M2";
 46        case 6020:
 47            return "Apple M2 Pro";
 48        case 6021:
 49            return "Apple M2 Max";
 50        case 6022:
 51            return "Apple M2 Ultra";
 52        case 8122:
 53            return "Apple M3";
 54        case 6030:
 55            return "Apple M3 Pro";
 56        case 6031:
 57        case 6034:
 58            return "Apple M3 Max";
 59        case 8132:
 60            return "Apple M4";
 61        case 6040:
 62            return "Apple M4 Pro";
 63        case 6041:
 64            return "Apple M4 Max";
 65        case 8140:
 66            return "Apple A18 Pro";
 67        case 8142:
 68            return "Apple M5";
 69        case 6050:
 70            return "Apple M5 Pro / Max";
 71        default:
 72            return nullptr;
 73    }
 74}
 75
 76const char* ffCPUQualcommCodeToName(uint32_t code) {
 77    // https://github.com/AsahiLinux/docs/wiki/Codenames
 78    switch (code) {
 79        case 7180:
 80            return "Qualcomm Snapdragon 7c";
 81        case 7280:
 82            return "Qualcomm Snapdragon 7c+ Gen 3";
 83        case 8180:
 84            return "Qualcomm Snapdragon 8cx Gen 2 5G";
 85        case 8280:
 86            return "Qualcomm Snapdragon 8cx Gen 3";
 87        default:
 88            return nullptr;
 89    }
 90}
 91
 92#if defined(__x86_64__) || defined(__i386__)
 93
 94    #include <cpuid.h>
 95
 96void ffCPUDetectByCpuid(FFCPUResult* cpu) {
 97    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
 98    if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx)) {
 99        // WARNING: CPUID may report frequencies of efficient cores
100        // cpuid returns 0 MHz when hypervisor is enabled
101        if (eax) {
102            cpu->frequencyBase = eax;
103        }
104        if (ebx) {
105            cpu->frequencyMax = ebx;
106        }
107    }
108
109    if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
110        // Feature tests (leaf1.ecx, leaf7.ebx)
111        bool sse2 = (ecx & bit_SSE2) != 0;
112        bool sse4_2 = (ecx & bit_SSE4_2) != 0;
113        bool pclmul = (ecx & bit_PCLMUL) != 0;
114        bool popcnt = (ecx & bit_POPCNT) != 0;
115        bool fma = (ecx & bit_FMA) != 0;
116        bool osxsave = (ecx & bit_OSXSAVE) != 0;
117
118        unsigned int eax7 = 0, ebx7 = 0, ecx7 = 0, edx7 = 0;
119        __get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7);
120
121        bool avx2 = (ebx7 & bit_AVX2) != 0;
122        bool bmi2 = (ebx7 & bit_BMI2) != 0;
123        bool avx512f = (ebx7 & bit_AVX512F) != 0;
124        bool avx512bw = (ebx7 & bit_AVX512BW) != 0;
125        bool avx512dq = (ebx7 & bit_AVX512DQ) != 0;
126
127        // OS support for AVX/AVX512: check XGETBV (requires OSXSAVE)
128        bool avx_os = false;
129        bool avx512_os = false;
130        if (osxsave) {
131            __asm__ __volatile__(
132                "xgetbv"
133                : "=a"(eax), "=d"(edx)
134                : "c"(0)
135                :);
136            uint64_t xcr0 = ((uint64_t) edx << 32) | eax;
137
138            // AVX requires XCR0[1:2] == 11b (XMM and YMM state)
139            avx_os = (xcr0 & 0x6ULL) == 0x6ULL;
140            // AVX512 requires XCR0[7,5,6] etc. common mask 0xE6 (bits 1,2,5,6,7)
141            avx512_os = (xcr0 & 0xE6ULL) == 0xE6ULL;
142        }
143
144        cpu->march = "unknown";
145        if (avx512f && avx512bw && avx512dq && avx512_os) {
146            cpu->march = "x86_64-v4";
147        } else if (avx2 && fma && bmi2 && avx_os) {
148            cpu->march = "x86_64-v3";
149        } else if (sse4_2 && popcnt && pclmul) {
150            cpu->march = "x86_64-v2";
151        } else if (sse2) {
152            cpu->march = "x86_64-v1";
153        }
154    }
155}
156
157#elif defined(__aarch64__)
158
159// This is not accurate because a lot of flags are optional from old versions
160// https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions?lang=en
161// https://en.wikipedia.org/wiki/AArch64#ARM-A_(application_architecture)
162// Worth noting: Apple M1 is marked as ARMv8.5-A on Wikipedia, but it lacks BTI (mandatory in v8.5)
163
164    #ifdef __linux__
165        #include "common/io.h"
166        #include <elf.h>
167// #include <asm/hwcap.h>
168
169void ffCPUDetectByCpuid(FFCPUResult* cpu) {
170    char buf[PROC_FILE_BUFFSIZ];
171    ssize_t nRead = ffReadFileData("/proc/self/auxv", ARRAY_SIZE(buf), buf);
172
173    if (nRead < (ssize_t) sizeof(Elf64_auxv_t)) {
174        return;
175    }
176
177    uint64_t hwcap = 0, hwcap2 = 0;
178
179    for (Elf64_auxv_t* auxv = (Elf64_auxv_t*) buf; (char*) auxv < buf + nRead; ++auxv) {
180        if (auxv->a_type == AT_HWCAP) {
181            hwcap = auxv->a_un.a_val;
182        } else if (auxv->a_type == AT_HWCAP2) {
183            hwcap2 = auxv->a_un.a_val;
184        }
185    }
186
187    if (!hwcap) {
188        return;
189    }
190
191    cpu->march = "unknown";
192
193    // ARMv8-A
194    bool has_fp = (hwcap & (1 << 0) /* HWCAP_FP */) != 0;
195    bool has_asimd = (hwcap & (1 << 1) /* HWCAP_ASIMD */) != 0;
196
197    // ARMv8.1-A
198    bool has_atomics = (hwcap & (1 << 8) /* HWCAP_ATOMICS */) != 0;    // optional from v8.0
199    bool has_crc32 = (hwcap & (1 << 7) /* HWCAP_CRC32 */) != 0;        // optional from v8.0
200    bool has_asimdrdm = (hwcap & (1 << 12) /* HWCAP_ASIMDRDM */) != 0; // optional from v8.0
201
202    // ARMv8.2-A
203    bool has_fphp = (hwcap & (1 << 9) /* HWCAP_FPHP */) != 0;    // optional
204    bool has_dcpop = (hwcap & (1 << 16) /* HWCAP_DCPOP */) != 0; // DC CVAP, optional from v8.1
205
206    // ARMv8.3-A
207    bool has_paca = (hwcap & (1 << 30) /* HWCAP_PACA */) != 0;   // optional from v8.2
208    bool has_lrcpc = (hwcap & (1 << 15) /* HWCAP_LRCPC */) != 0; // optional from v8.2
209    bool has_fcma = (hwcap & (1 << 14) /* HWCAP_FCMA */) != 0;   // optional from v8.2
210    bool has_jscvt = (hwcap & (1 << 13) /* HWCAP_JSCVT */) != 0; // optional from v8.2
211
212    // ARMv8.4-A
213    bool has_dit = (hwcap & (1 << 24) /* HWCAP_DIT */) != 0;       // optional from v8.3
214    bool has_flagm = (hwcap & (1 << 27) /* HWCAP_FLAGM */) != 0;   // optional from v8.1
215    bool has_ilrcpc = (hwcap & (1 << 26) /* HWCAP_ILRCPC */) != 0; // optional from v8.2
216
217    // ARMv8.5-A
218    bool has_bti = (hwcap2 & (1 << 17) /* HWCAP2_BTI */) != 0;      // optional from v8.4
219    bool has_sb = (hwcap & (1 << 29) /* HWCAP_SB */) != 0;          // optional from v8.0
220    bool has_dcpodp = (hwcap2 & (1 << 0) /* HWCAP2_DCPODP */) != 0; // optional from v8.1
221    bool has_flagm2 = (hwcap2 & (1 << 7) /* HWCAP2_FLAGM2 */) != 0; // optional from v8.4
222    bool has_frint = (hwcap2 & (1 << 8) /* HWCAP2_FRINT */) != 0;   // optional from v8.4
223
224    // ARMv9.0-A
225    bool has_sve2 = (hwcap2 & (1 << 1) /* HWCAP2_SVE2 */) != 0;
226
227    // ARMv9.1-A
228    // ARMv8.6-A
229    bool has_bf16 = (hwcap2 & (1 << 14) /* HWCAP2_BF16 */) != 0; // optional from v8.2
230    bool has_i8mm = (hwcap2 & (1 << 13) /* HWCAP2_I8MM */) != 0; // optional from v8.1
231
232    // ARMv8.7-A
233    bool has_afp = (hwcap2 & (1 << 20) /* HWCAP2_AFP */) != 0; // optional from v8.6
234
235    // ARMv9.2-A
236    bool has_sme = (hwcap2 & (1 << 23) /* HWCAP2_SME */) != 0;
237
238    // ARMv9.3-A
239    bool has_sme2 = (hwcap2 & (1UL << 37) /* HWCAP2_SME2 */) != 0; // optional from v9.2
240
241    // ARMv8.8-A
242    bool has_mops = (hwcap2 & (1UL << 43) /* HWCAP2_MOPS */) != 0; // optional from v8.7
243
244    // ARMv8.9-A
245    bool has_cssc = (hwcap2 & (1UL << 34) /* HWCAP2_CSSC */) != 0; // optional from v8.7
246
247    // ARMv9.4-A
248    bool has_sme2p1 = (hwcap2 & (1UL << 38) /* HWCAP2_SME2P1 */) != 0; // optional from v9.2
249
250    // ARMv9.5-A
251    bool has_f8e4m3 = (hwcap2 & (1UL << 55) /* HWCAP2_F8E4M3 */) != 0; // optional from v9.2
252    bool has_f8e5m2 = (hwcap2 & (1UL << 56) /* HWCAP2_F8E5M2 */) != 0; // optional from v9.2
253
254    // ARMv9.6-A
255    bool has_cmpbr = (hwcap & (1UL << 33) /* HWCAP_CMPBR */) != 0;   // optional from v9.5
256    bool has_fprcvt = (hwcap & (1UL << 34) /* HWCAP_FPRCVT */) != 0; // optional from v9.5
257
258    if (has_sve2 || has_sme) {
259        // ARMv9
260        if (has_cmpbr && has_fprcvt) {
261            cpu->march = "ARMv9.6-A";
262        } else if (has_f8e5m2 && has_f8e4m3) {
263            cpu->march = "ARMv9.5-A";
264        } else if (has_sme2p1) {
265            cpu->march = "ARMv9.4-A";
266        } else if (has_sme2) {
267            cpu->march = "ARMv9.3-A";
268        } else if (has_sme) {
269            cpu->march = "ARMv9.2-A";
270        } else if (has_i8mm && has_bf16) {
271            cpu->march = "ARMv9.1-A";
272        } else {
273            cpu->march = "ARMv9.0-A";
274        }
275    } else {
276        // ARMv8
277        if (has_cssc) {
278            cpu->march = "ARMv8.9-A";
279        } else if (has_mops) {
280            cpu->march = "ARMv8.8-A";
281        } else if (has_afp) {
282            cpu->march = "ARMv8.7-A";
283        } else if (has_i8mm && has_bf16) {
284            cpu->march = "ARMv8.6-A";
285        } else if (has_bti && has_sb && has_dcpodp && has_flagm2 && has_frint) {
286            cpu->march = "ARMv8.5-A";
287        } else if (has_dit && has_flagm && has_ilrcpc) {
288            cpu->march = "ARMv8.4-A";
289        } else if (has_paca && has_lrcpc && has_fcma && has_jscvt) {
290            cpu->march = "ARMv8.3-A";
291        } else if (has_fphp && has_dcpop) {
292            cpu->march = "ARMv8.2-A";
293        } else if (has_atomics && has_crc32 && has_asimdrdm) {
294            cpu->march = "ARMv8.1-A";
295        } else if (has_asimd && has_fp) {
296            cpu->march = "ARMv8-A";
297        }
298    }
299}
300    #elif __APPLE__
301        #include <sys/sysctl.h>
302// #include <arm/cpu_capabilities_public.h> // Not available in macOS 14-
303
304void ffCPUDetectByCpuid(FFCPUResult* cpu) {
305    uint64_t caps[2] = { 0 }; // 80-bit capability mask, split into two 64-bit values
306    size_t size = sizeof(caps);
307
308    if (sysctlbyname("hw.optional.arm.caps", caps, &size, nullptr, 0) != 0) {
309        return;
310    }
311
312        // Helper macro to test bit in 80-bit capability mask
313        #define FF_HAS_CAP(bit) \
314            (((bit) < 64) ? ((caps[0] >> (bit)) & 1ULL) : ((caps[1] >> ((bit) - 64U)) & 1ULL))
315
316    cpu->march = "unknown";
317
318    // ARMv8-A
319    bool has_fp = FF_HAS_CAP(50); /* CAP_BIT_AdvSIMD_HPFPCvt */ // Full FP16 support (implies FP/ASIMD)
320    bool has_asimd = FF_HAS_CAP(49); /* CAP_BIT_AdvSIMD */      // Advanced SIMD (NEON)
321
322    // ARMv8.1-A
323    bool has_lse = FF_HAS_CAP(6); /* CAP_BIT_FEAT_LSE */      // Large System Extensions, optional in v8.0
324    bool has_crc32 = FF_HAS_CAP(51); /* CAP_BIT_FEAT_CRC32 */ // CRC32 instructions, optional in v8.0
325    bool has_rdm = FF_HAS_CAP(5); /* CAP_BIT_FEAT_RDM */      // AdvSIMD rounding double multiply accumulate, optional in v8.0
326
327    // ARMv8.2-A
328    bool has_fp16 = FF_HAS_CAP(34); /* CAP_BIT_FEAT_FP16 */ // Half-precision FP support, optional
329    bool has_dpb = FF_HAS_CAP(22); /* CAP_BIT_FEAT_DPB */   // DC CVAP, optional from v8.1
330
331    // ARMv8.3-A
332    bool has_pauth = FF_HAS_CAP(19); /* CAP_BIT_FEAT_PAuth */ // Pointer Authentication (PAC), optional from v8.2
333    bool has_lrcpc = FF_HAS_CAP(15); /* CAP_BIT_FEAT_LRCPC */ // LDAPR/LR with RCPC semantics, optional from v8.2
334    bool has_fcma = FF_HAS_CAP(17); /* CAP_BIT_FEAT_FCMA */   // Complex number multiply-add, optional from v8.2
335    bool has_jscvt = FF_HAS_CAP(18); /* CAP_BIT_FEAT_JSCVT */ // JavaScript-style conversion (FJCVTZS), optional from v8.2
336
337    // ARMv8.4-A
338    bool has_lse2 = FF_HAS_CAP(30); /* CAP_BIT_FEAT_LSE2 */     // Large System Extensions version 2, optional from v8.2
339    bool has_dit = FF_HAS_CAP(33); /* CAP_BIT_FEAT_DIT */       // Data Independent Timing, optional from v8.3
340    bool has_flagm = FF_HAS_CAP(0); /* CAP_BIT_FEAT_FlagM */    // Flag manipulation (FMOV/FCVT), optional from v8.1
341    bool has_lrcpc2 = FF_HAS_CAP(16); /* CAP_BIT_FEAT_LRCPC2 */ // Enhanced RCPC (LDAPUR/LDAPST), optional from v8.2
342
343    // ARMv8.5-A
344    bool has_bti = FF_HAS_CAP(36); /* CAP_BIT_FEAT_BTI */         // Branch Target Identification, optional from v8.4
345    bool has_sb = FF_HAS_CAP(13); /* CAP_BIT_FEAT_SB */           // Speculative Barrier, optional from v8.0
346    bool has_dpb2 = FF_HAS_CAP(23); /* CAP_BIT_FEAT_DPB2 */       // DC CVADP (DPB2), optional from v8.1
347    bool has_flagm2 = FF_HAS_CAP(1); /* CAP_BIT_FEAT_FlagM2 */    // Enhanced FlagM, optional from v8.4
348    bool has_frintts = FF_HAS_CAP(14); /* CAP_BIT_FEAT_FRINTTS */ // Floating-point to integer instructions, optional from v8.4
349
350    // ARMv9.0-A
351    bool has_sve2 = false; // Not exposed and not supported by Apple M4
352
353    // ARMv9.1-A
354    // ARMv8.6-A
355    bool has_bf16 = FF_HAS_CAP(24); /* CAP_BIT_FEAT_BF16 */ // Brain float16, optional from v8.2
356    bool has_i8mm = FF_HAS_CAP(25); /* CAP_BIT_FEAT_I8MM */ // Int8 Matrix Multiply, optional from v8.1
357
358    // ARMv8.7-A
359    bool has_afp = FF_HAS_CAP(29); /* CAP_BIT_FEAT_AFP */ // Alternate FP16 (FEXPA), optional from v8.6
360
361    // ARMv9.2-A
362    bool has_sme = FF_HAS_CAP(40); /* CAP_BIT_FEAT_SME */ // Scalable Matrix Extension, optional from v9.2
363
364    // ARMv9.3-A
365    bool has_sme2 = FF_HAS_CAP(41); /* CAP_BIT_FEAT_SME2 */ // SME2, optional from v9.2
366
367    // ARMv8.8-A
368    bool has_hbc = FF_HAS_CAP(64); /* CAP_BIT_FEAT_HBC */ // Hinted conditional branches, optional from v8.7
369
370    // ARMv8.9-A
371    bool has_cssc = FF_HAS_CAP(67); /* CAP_BIT_FEAT_CSSC */ // Common Short String Compare, optional from v8.7
372
373    // ARMv9.4-A+ are not exposed yet
374
375    if (has_sve2 || has_sme) {
376        // ARMv9 family
377        if (has_sme2) {
378            cpu->march = "ARMv9.3-A";
379        } else if (has_sme) {
380            cpu->march = "ARMv9.2-A";
381        } else if (has_i8mm && has_bf16) {
382            cpu->march = "ARMv9.1-A";
383        } else {
384            cpu->march = "ARMv9.0-A";
385        }
386    } else {
387        // ARMv8 family
388        if (has_cssc) {
389            cpu->march = "ARMv8.9-A";
390        } else if (has_hbc) {
391            cpu->march = "ARMv8.8-A";
392        } else if (has_afp) {
393            cpu->march = "ARMv8.7-A";
394        } else if (has_i8mm && has_bf16) {
395            cpu->march = "ARMv8.6-A";
396        } else if (has_bti && has_sb && has_dpb2 && has_flagm2 && has_frintts) {
397            cpu->march = "ARMv8.5-A";
398        } else if (has_lse2 && has_dit && has_flagm && has_lrcpc2) {
399            cpu->march = "ARMv8.4-A";
400        } else if (has_pauth && has_lrcpc && has_fcma && has_jscvt) {
401            cpu->march = "ARMv8.3-A";
402        } else if (has_fp16 && has_dpb) {
403            cpu->march = "ARMv8.2-A";
404        } else if (has_lse && has_crc32 && has_rdm) {
405            cpu->march = "ARMv8.1-A";
406        } else if (has_asimd && has_fp) {
407            cpu->march = "ARMv8-A";
408        }
409    }
410
411        #undef HAS_CAP
412}
413    #elif _WIN32
414        #include <processthreadsapi.h>
415
416        // Missing from winnt.h of MinGW-w64
417        #define PF_ARM_LSE2_AVAILABLE 62
418        #define PF_RESERVED_FEATURE 63
419        #define PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE 64
420        #define PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE 65
421        #define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 66
422        #define PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE 67
423        #define PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE 68
424        #define PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE 69
425        #define PF_ARM_SME_INSTRUCTIONS_AVAILABLE 70
426        #define PF_ARM_SME2_INSTRUCTIONS_AVAILABLE 71
427        #define PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE 72
428        #define PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE 73
429        #define PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE 74
430        #define PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE 75
431        #define PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE 76
432        #define PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE 77
433        #define PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE 78
434        #define PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE 79
435        #define PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE 80
436        #define PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE 81
437        #define PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE 82
438        #define PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE 83
439        #define PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE 84
440        #define PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE 85
441        #define PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE 86
442        #define PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE 87
443        #define PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE 88
444
445void ffCPUDetectByCpuid(FFCPUResult* cpu) {
446    // ARMv8-A
447    bool has_vfp = IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE);   // Implies basic FP support
448    bool has_neon = IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE); // NEON (ASIMD)
449
450    // ARMv8.1-A
451    bool has_atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE); // LSE atomics
452    bool has_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE);     // CRC32
453
454    // ARMv8.2-A
455    bool has_fp16 = IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE); // Half-precision FP
456
457    // ARMv8.3-A
458    bool has_lrcpc = IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE); // LDAPR/LR with RCPC semantics
459    bool has_jscvt = IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE); // FJCVTZS
460
461    // ARMv8.4-A
462    // My CPU (Apple M1 Pro in VM) does support LSE2, but Windows doesn't detect it for some reason
463    bool has_lse2 = IsProcessorFeaturePresent(PF_ARM_LSE2_AVAILABLE);              // Large System Extensions version 2, optional from v8.2
464    bool has_dp = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE); // DotProd, optional from v8.1 (*)
465
466    // ARMv9.0-A
467    bool has_sve2 = IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE); // SVE2
468
469    // ARMv9.1-A
470    // ARMv8.6-A
471    bool has_bf16 = IsProcessorFeaturePresent(PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE); // BF16, optional from v8.2
472    bool has_i8mm = IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE); // Int8 matrix multiply, optional from v8.2
473
474    // ARMv8.7-A
475    bool has_ebf16 = IsProcessorFeaturePresent(PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE); // Extended BFloat16 behaviors, optional from v8.2
476
477    // ARMv9.2-A
478    bool has_sme = IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE); // SME
479
480    // ARMv9.3-A
481    bool has_sme2 = IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE); // SME2
482
483    // ARMv9.4-A
484    bool has_sme2p1 = IsProcessorFeaturePresent(PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE); // SME2.1
485
486    if (has_sve2 || has_sme) {
487        // ARMv9 family
488        if (has_sme2p1) {
489            cpu->march = "ARMv9.4-A";
490        } else if (has_sme2) {
491            cpu->march = "ARMv9.3-A";
492        } else if (has_sme) {
493            cpu->march = "ARMv9.2-A";
494        } else if (has_i8mm && has_bf16) {
495            cpu->march = "ARMv9.1-A";
496        } else {
497            cpu->march = "ARMv9.0-A";
498        }
499    } else {
500        // ARMv8 family
501        if (has_ebf16) {
502            cpu->march = "ARMv8.7-A";
503        } else if (has_i8mm && has_bf16) {
504            cpu->march = "ARMv8.6-A";
505        } else if (has_dp && has_lse2) {
506            cpu->march = "ARMv8.4-A";
507        } else if (has_lrcpc && has_jscvt) {
508            cpu->march = "ARMv8.3-A";
509        } else if (has_fp16) {
510            cpu->march = "ARMv8.2-A";
511        } else if (has_atomics && has_crc32) {
512            cpu->march = "ARMv8.1-A";
513        } else if (has_neon && has_vfp) {
514            cpu->march = "ARMv8-A";
515        }
516    }
517}
518    #else
519void ffCPUDetectByCpuid([[maybe_unused]] FFCPUResult* cpu) {
520    // Unsupported system
521}
522    #endif
523
524#else
525
526void ffCPUDetectByCpuid([[maybe_unused]] FFCPUResult* cpu) {
527    // Unsupported architecture
528}
529
530#endif