diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index 4f07b9779..4e2bc9403 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -15,6 +15,7 @@ typedef struct FFCPUResult { FFstrbuf name; FFstrbuf vendor; + const char* march; // Microarchitecture uint16_t packages; uint16_t coresPhysical; @@ -37,22 +38,66 @@ const char* ffCPUQualcommCodeToName(uint32_t code); #include -// WARNING: CPUID may report frequencies of efficient cores -inline static const char* ffCPUDetectSpeedByCpuid(FFCPUResult* cpu) +inline static void ffCPUDetectByCpuid(FFCPUResult* cpu) { - uint32_t base = 0, max = 0, bus = 0, unused = 0; - if (!__get_cpuid(0x16, &base, &max, &bus, &unused)) - return "Unsupported instruction"; + 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; + } - // cpuid returns 0 MHz when hyper-v is enabled - if (base) cpu->frequencyBase = base; - if (max) cpu->frequencyMax = max; - return NULL; + 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 const char* ffCPUDetectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) +inline static void ffCPUDetectByCpuid(FFCPUResult* cpu) { return "Unsupported platform"; } diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index 1226131e5..b6f2a9036 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -72,6 +72,7 @@ static const char* detectFrequency(FFCPUResult* cpu) #else static const char* detectFrequency(FFCPUResult* cpu) { + ffCPUDetectByCpuid(cpu); cpu->frequencyBase = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency", 0) / 1000 / 1000); cpu->frequencyMax = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency_max", 0) / 1000 / 1000); if(cpu->frequencyBase == 0) diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c index 7eed83bea..9d8329aa0 100644 --- a/src/detection/cpu/cpu_bsd.c +++ b/src/detection/cpu/cpu_bsd.c @@ -70,7 +70,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), ¤tCPU); #endif - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); uint32_t clockrate = (uint32_t) ffSysctlGetInt("hw.clockrate", 0); if (clockrate > cpu->frequencyBase) cpu->frequencyBase = clockrate; diff --git a/src/detection/cpu/cpu_haiku.c b/src/detection/cpu/cpu_haiku.c index 17dc1385c..2c604d4be 100644 --- a/src/detection/cpu/cpu_haiku.c +++ b/src/detection/cpu/cpu_haiku.c @@ -55,7 +55,7 @@ const char* ffDetectCPUImpl(FF_MAYBE_UNUSED const FFCPUOptions* options, FFCPURe ffStrbufSetF(&cpu->name, "(Unknown %" B_PRIx32 ")", cpuModel); ffStrbufSetS(&cpu->vendor, get_cpu_vendor_string(cpuVendor)); - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); if (cpu->frequencyBase < frequency) cpu->frequencyBase = frequency; cpu->packages = packages; cpu->coresPhysical = cores; diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 411c1f0d5..c5c22148c 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -456,7 +456,7 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC cpu->coresPhysical *= cpu->packages; // Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252 - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0) cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0); diff --git a/src/detection/cpu/cpu_nbsd.c b/src/detection/cpu/cpu_nbsd.c index 16ad00505..4ab134d38 100644 --- a/src/detection/cpu/cpu_nbsd.c +++ b/src/detection/cpu/cpu_nbsd.c @@ -64,7 +64,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresLogical = cpu->coresPhysical; cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.ncpuonline", cpu->coresLogical); - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); uint32_t freq = (uint32_t) ffSysctlGetInt("machdep.cpu.frequency.target", 0); if (freq == 0) freq = (uint32_t) (ffSysctlGetInt64("hw.cpu0.clock_frequency", 0) / 1000000); diff --git a/src/detection/cpu/cpu_obsd.c b/src/detection/cpu/cpu_obsd.c index 51d12a297..ea2d27934 100644 --- a/src/detection/cpu/cpu_obsd.c +++ b/src/detection/cpu/cpu_obsd.c @@ -56,7 +56,7 @@ const char *ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresLogical = cpu->coresPhysical; cpu->coresOnline = (uint16_t) ffSysctlGetInt(CTL_HW, HW_NCPUONLINE, cpu->coresLogical); - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); uint32_t cpuspeed = (uint32_t) ffSysctlGetInt(CTL_HW, HW_CPUSPEED, 0); if (cpuspeed > cpu->frequencyBase) cpu->frequencyBase = cpuspeed; diff --git a/src/detection/cpu/cpu_sunos.c b/src/detection/cpu/cpu_sunos.c index 6f9f668bb..3185919b9 100644 --- a/src/detection/cpu/cpu_sunos.c +++ b/src/detection/cpu/cpu_sunos.c @@ -122,7 +122,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id"); if (kn) ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn)); } - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); { kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz"); if (kn && kn->value.ui32 > cpu->frequencyBase) diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index f27ec806a..79ed7a660 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -287,7 +287,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (error) return error; - ffCPUDetectSpeedByCpuid(cpu); + ffCPUDetectByCpuid(cpu); if (options->showPeCoreCount) detectCoreTypes(cpu); if (cpu->frequencyMax == 0) diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 69e0f3476..9a9911e33 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -110,6 +110,7 @@ bool ffPrintCPU(FFCPUOptions* options) FF_FORMAT_ARG(tempStr, "temperature"), FF_FORMAT_ARG(coreTypes, "core-types"), FF_FORMAT_ARG(cpu.packages, "packages"), + FF_FORMAT_ARG(cpu.march, "march"), })); } success = true; @@ -211,6 +212,11 @@ bool ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_ else yyjson_mut_obj_add_null(doc, obj, "temperature"); + if (cpu.march) + yyjson_mut_obj_add_str(doc, obj, "march", cpu.march); + else + yyjson_mut_obj_add_null(doc, obj, "march"); + success = true; } @@ -253,5 +259,6 @@ FFModuleBaseInfo ffCPUModuleInfo = { {"Temperature (formatted)", "temperature"}, {"Logical core count grouped by frequency", "core-types"}, {"Processor package count", "packages"}, + {"X86-64 CPU microarchitecture", "march"}, })) };