From dd1a799abb468ef8a78e19c9b4eb24cf09b54fd2 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Mon, 25 Aug 2025 14:44:32 +0800 Subject: [PATCH] CPU (Linux): detects march on aarch64 --- src/detection/cpu/cpu.c | 249 ++++++++++++++++++++++++++++++++++ src/detection/cpu/cpu.h | 71 +--------- src/detection/cpu/cpu_linux.c | 6 + 3 files changed, 256 insertions(+), 70 deletions(-) diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c index 40ae66ac9..cb916a2d6 100644 --- a/src/detection/cpu/cpu.c +++ b/src/detection/cpu/cpu.c @@ -60,3 +60,252 @@ const char* ffCPUQualcommCodeToName(uint32_t code) default: return NULL; } } + +#if defined(__x86_64__) || defined(__i386__) + +#include + +void ffCPUDetectByCpuid(FFCPUResult* cpu) +{ + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx)) + { + // WARNING: CPUID may report frequencies of efficient cores + // cpuid returns 0 MHz when hypervisor is enabled + if (eax) cpu->frequencyBase = eax; + if (ebx) cpu->frequencyMax = ebx; + } + + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) + { + // Feature tests (leaf1.ecx, leaf7.ebx) + bool sse2 = (ecx & bit_SSE2) != 0; + bool sse4_2 = (ecx & bit_SSE4_2) != 0; + bool pclmul = (ecx & bit_PCLMUL) != 0; + bool popcnt = (ecx & bit_POPCNT) != 0; + bool fma = (ecx & bit_FMA) != 0; + bool osxsave = (ecx & bit_OSXSAVE) != 0; + + unsigned int eax7 = 0, ebx7 = 0, ecx7 = 0, edx7 = 0; + __get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7); + + bool avx2 = (ebx7 & bit_AVX2) != 0; + bool bmi2 = (ebx7 & bit_BMI2) != 0; + bool avx512f = (ebx7 & bit_AVX512F) != 0; + bool avx512bw = (ebx7 & bit_AVX512BW) != 0; + bool avx512dq = (ebx7 & bit_AVX512DQ) != 0; + + // OS support for AVX/AVX512: check XGETBV (requires OSXSAVE) + bool avx_os = false; + bool avx512_os = false; + if (osxsave) + { + __asm__ __volatile__( + "xgetbv" + : "=a"(eax), "=d"(edx) + : "c"(0) + : + ); + uint64_t xcr0 = ((uint64_t)edx << 32) | eax; + + // AVX requires XCR0[1:2] == 11b (XMM and YMM state) + avx_os = (xcr0 & 0x6ULL) == 0x6ULL; + // AVX512 requires XCR0[7,5,6] etc. common mask 0xE6 (bits 1,2,5,6,7) + avx512_os = (xcr0 & 0xE6ULL) == 0xE6ULL; + } + + cpu->march = "unknown"; + if (avx512f && avx512bw && avx512dq && avx512_os) cpu->march = "x86_64-v4"; + else if (avx2 && fma && bmi2 && avx_os) cpu->march = "x86_64-v3"; + else if (sse4_2 && popcnt && pclmul) cpu->march = "x86_64-v2"; + else if (sse2) cpu->march = "x86_64-v1"; + } +} + +#elif defined(__aarch64__) + +// This is not accurate because a lot of flags are optional from old versions +// https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions?lang=en +// https://en.wikipedia.org/wiki/AArch64#ARM-A_(application_architecture) +// Worth noting: Apple M1 is marked as ARMv8.5-A on Wikipedia, but it lacks BTI (mandatory in v8.5) + +#ifdef __linux__ +#include "common/io/io.h" +#include +#include + +#ifndef HWCAP2_SME +#define HWCAP2_SME (1UL << 23) +#endif +#ifndef HWCAP2_SME2 +#define HWCAP2_SME2 (1UL << 37) +#endif +#ifndef HWCAP2_CSSC +#define HWCAP2_CSSC (1UL << 34) +#endif +#ifndef HWCAP2_SME2P1 +#define HWCAP2_SME2P1 (1UL << 38) +#endif +#ifndef HWCAP2_MOPS +#define HWCAP2_MOPS (1UL << 43) +#endif +#ifndef HWCAP2_F8E4M3 +#define HWCAP2_F8E4M3 (1UL << 55) +#endif +#ifndef HWCAP2_F8E5M2 +#define HWCAP2_F8E5M2 (1UL << 56) +#endif +#ifndef HWCAP_CMPBR +#define HWCAP_CMPBR (1UL << 33) +#endif +#ifndef HWCAP_FPRCVT +#define HWCAP_FPRCVT (1UL << 34) +#endif + +void ffCPUDetectByCpuid(FFCPUResult* cpu) +{ + char buf[PROC_FILE_BUFFSIZ]; + ssize_t nRead = ffReadFileData("/proc/self/auxv", ARRAY_SIZE(buf), buf); + + if (nRead < (ssize_t) sizeof(Elf64_auxv_t)) return; + + uint64_t hwcap = 0, hwcap2 = 0; + + for (Elf64_auxv_t* auxv = (Elf64_auxv_t*)buf; (char*)auxv < buf + nRead; ++auxv) + { + if (auxv->a_type == AT_HWCAP) + { + hwcap = auxv->a_un.a_val; + } + else if (auxv->a_type == AT_HWCAP2) + { + hwcap2 = auxv->a_un.a_val; + } + } + + if (!hwcap) return; + + cpu->march = "unknown"; + + // ARMv8-A + bool has_fp = (hwcap & HWCAP_FP) != 0; + bool has_asimd = (hwcap & HWCAP_ASIMD) != 0; + + // ARMv8.1-A + bool has_atomics = (hwcap & HWCAP_ATOMICS) != 0; // optional from v8.0 + bool has_crc32 = (hwcap & HWCAP_CRC32) != 0; // optional from v8.0 + bool has_asimdrdm = (hwcap & HWCAP_ASIMDRDM) != 0; // optional from v8.0 + + // ARMv8.2-A + bool has_fphp = (hwcap & HWCAP_FPHP) != 0; // optional + bool has_dcpop = (hwcap & HWCAP_DCPOP) != 0; // DC CVAP, optional from v8.1 + + // ARMv8.3-A + bool has_paca = (hwcap & HWCAP_PACA) != 0; // optional from v8.2 + bool has_lrcpc = (hwcap & HWCAP_LRCPC) != 0; // optional from v8.2 + bool has_fcma = (hwcap & HWCAP_FCMA) != 0; // optional from v8.2 + bool has_jscvt = (hwcap & HWCAP_JSCVT) != 0; // optional from v8.2 + + // ARMv8.4-A + bool has_dit = (hwcap & HWCAP_DIT) != 0; // optional from v8.3 + bool has_flagm = (hwcap & HWCAP_FLAGM) != 0; // optional from v8.1 + bool has_ilrcpc = (hwcap & HWCAP_ILRCPC) != 0; // optional from v8.2 + + // ARMv8.5-A + bool has_bti = (hwcap2 & HWCAP2_BTI) != 0; // optional from v8.4 + bool has_sb = (hwcap & HWCAP_SB) != 0; // optional from v8.0 + bool has_dcpodp = (hwcap2 & HWCAP2_DCPODP) != 0; // optional from v8.1 + bool has_flagm2 = (hwcap2 & HWCAP2_FLAGM2) != 0; // optional from v8.4 + bool has_frint = (hwcap2 & HWCAP2_FRINT) != 0; // optional from v8.4 + + // ARMv9.0-A + bool has_sve2 = (hwcap2 & HWCAP2_SVE2) != 0; + + // ARMv9.1-A + // ARMv8.6-A + bool has_bf16 = (hwcap2 & HWCAP2_BF16) != 0; // optional from v8.2 + bool has_i8mm = (hwcap2 & HWCAP2_I8MM) != 0; // optional from v8.1 + + // ARMv8.7-A + bool has_afp = (hwcap2 & HWCAP2_AFP) != 0; // optional from v8.6 + + // ARMv9.2-A + bool has_sme = (hwcap2 & HWCAP2_SME) != 0; + + // ARMv9.3-A + bool has_sme2 = (hwcap2 & HWCAP2_SME2) != 0; // optional from v9.2 + + // ARMv8.8-A + bool has_mops = (hwcap2 & HWCAP2_MOPS) != 0; // optional from v8.7 + + // ARMv8.9-A + bool has_cssc = (hwcap2 & HWCAP2_CSSC) != 0; // optional from v8.7 + + // ARMv9.4-A + bool has_sme2p1 = (hwcap2 & HWCAP2_SME2P1) != 0; // optional from v9.2 + + // ARMv9.5-A + bool has_f8e4m3 = (hwcap2 & HWCAP2_F8E4M3) != 0; // optional from v9.2 + bool has_f8e5m2 = (hwcap2 & HWCAP2_F8E5M2) != 0; // optional from v9.2 + + // ARMv9.6-A + bool has_cmpbr = (hwcap & HWCAP_CMPBR) != 0; // optional from v9.5 + bool has_fprcvt = (hwcap & HWCAP_FPRCVT) != 0; // optional from v9.5 + + if (has_sve2 || has_sme) { + // ARMv9 + if (has_cmpbr && has_fprcvt) { + cpu->march = "ARMv9.6-A"; + } else if (has_f8e5m2 && has_f8e4m3) { + cpu->march = "ARMv9.5-A"; + } else if (has_sme2p1) { + cpu->march = "ARMv9.4-A"; + } else if (has_sme2) { + cpu->march = "ARMv9.3-A"; + } else if (has_sme) { + cpu->march = "ARMv9.2-A"; + } else if (has_i8mm && has_bf16) { + cpu->march = "ARMv9.1-A"; + } else { + cpu->march = "ARMv9.0-A"; + } + } else { + // ARMv8 + if (has_cssc) { + cpu->march = "ARMv8.9-A"; + } else if (has_mops) { + cpu->march = "ARMv8.8-A"; + } else if (has_afp) { + cpu->march = "ARMv8.7-A"; + } else if (has_i8mm && has_bf16) { + cpu->march = "ARMv8.6-A"; + } else if (has_bti && has_sb && has_dcpodp && has_flagm2 && has_frint) { + cpu->march = "ARMv8.5-A"; + } else if (has_dit && has_flagm && has_ilrcpc) { + cpu->march = "ARMv8.4-A"; + } else if (has_paca && has_lrcpc && has_fcma && has_jscvt) { + cpu->march = "ARMv8.3-A"; + } else if (has_fphp && has_dcpop) { + cpu->march = "ARMv8.2-A"; + } else if (has_atomics && has_crc32 && has_asimdrdm) { + cpu->march = "ARMv8.1-A"; + } else if (has_asimd && has_fp) { + cpu->march = "ARMv8-A"; + } + } +} +#else +void ffCPUDetectByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) +{ + // Unsupported platform +} +#endif // __linux__ + +#else + +void ffCPUDetectByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) +{ + // Unsupported platform +} + +#endif diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index 9c4d8d5ba..bac4aa3e5 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -33,73 +33,4 @@ typedef struct FFCPUResult const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); const char* ffCPUAppleCodeToName(uint32_t code); const char* ffCPUQualcommCodeToName(uint32_t code); - -#if defined(__x86_64__) || defined(__i386__) - -#include - -inline static void ffCPUDetectByCpuid(FFCPUResult* cpu) -{ - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; - if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx)) - { - // WARNING: CPUID may report frequencies of efficient cores - // cpuid returns 0 MHz when hypervisor is enabled - if (eax) cpu->frequencyBase = eax; - if (ebx) cpu->frequencyMax = ebx; - } - - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) - { - // Feature tests (leaf1.ecx, leaf7.ebx) - bool sse2 = (ecx & bit_SSE2) != 0; - bool sse4_2 = (ecx & bit_SSE4_2) != 0; - bool pclmul = (ecx & bit_PCLMUL) != 0; - bool popcnt = (ecx & bit_POPCNT) != 0; - bool fma = (ecx & bit_FMA) != 0; - bool osxsave = (ecx & bit_OSXSAVE) != 0; - - unsigned int eax7 = 0, ebx7 = 0, ecx7 = 0, edx7 = 0; - __get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7); - - bool avx2 = (ebx7 & bit_AVX2) != 0; - bool bmi2 = (ebx7 & bit_BMI2) != 0; - bool avx512f = (ebx7 & bit_AVX512F) != 0; - bool avx512bw = (ebx7 & bit_AVX512BW) != 0; - bool avx512dq = (ebx7 & bit_AVX512DQ) != 0; - - // OS support for AVX/AVX512: check XGETBV (requires OSXSAVE) - bool avx_os = false; - bool avx512_os = false; - if (osxsave) - { - __asm__ __volatile__( - "xgetbv" - : "=a"(eax), "=d"(edx) - : "c"(0) - : - ); - uint64_t xcr0 = ((uint64_t)edx << 32) | eax; - - // AVX requires XCR0[1:2] == 11b (XMM and YMM state) - avx_os = (xcr0 & 0x6ULL) == 0x6ULL; - // AVX512 requires XCR0[7,5,6] etc. common mask 0xE6 (bits 1,2,5,6,7) - avx512_os = (xcr0 & 0xE6ULL) == 0xE6ULL; - } - - cpu->march = "unknown"; - if (avx512f && avx512bw && avx512dq && avx512_os) cpu->march = "x86_64-v4"; - else if (avx2 && fma && bmi2 && avx_os) cpu->march = "x86_64-v3"; - else if (sse4_2 && popcnt && pclmul) cpu->march = "x86_64-v2"; - else if (sse2) cpu->march = "x86_64-v1"; - } -} - -#else - -inline static void ffCPUDetectByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) -{ - // Unsupported platform -} - -#endif +void ffCPUDetectByCpuid(FFCPUResult* cpu); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index c5c22148c..6b7046c3b 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -46,9 +46,13 @@ static double parseHwmonDir(FFstrbuf* dir, FFstrbuf* buffer) if( ffStrbufContainS(buffer, "cpu") || + #if __x86_64__ || __i386__ ffStrbufEqualS(buffer, "k10temp") || // AMD ffStrbufEqualS(buffer, "fam15h_power") || // AMD ffStrbufEqualS(buffer, "coretemp") // Intel + #else + ffStrbufEqualS(buffer, "temp") // Asahi + #endif ) return value / 1000.; return FF_CPU_TEMP_UNSET; @@ -734,6 +738,8 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options, if (cpu->coresPhysical == 0) detectPhysicalCores(cpu); + ffCPUDetectByCpuid(cpu); + return NULL; } #endif