Skip to content

Commit 9ae968f

Browse files
committed
CPU: adds CPU microarchitecture detection for x64 and reporting
Fixes fastfetch-cli#1928
1 parent 8f6608d commit 9ae968f

File tree

10 files changed

+71
-18
lines changed

10 files changed

+71
-18
lines changed

src/detection/cpu/cpu.h

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ typedef struct FFCPUResult
1515
{
1616
FFstrbuf name;
1717
FFstrbuf vendor;
18+
const char* march; // Microarchitecture
1819

1920
uint16_t packages;
2021
uint16_t coresPhysical;
@@ -37,22 +38,66 @@ const char* ffCPUQualcommCodeToName(uint32_t code);
3738

3839
#include <cpuid.h>
3940

40-
// WARNING: CPUID may report frequencies of efficient cores
41-
inline static const char* ffCPUDetectSpeedByCpuid(FFCPUResult* cpu)
41+
inline static void ffCPUDetectByCpuid(FFCPUResult* cpu)
4242
{
43-
uint32_t base = 0, max = 0, bus = 0, unused = 0;
44-
if (!__get_cpuid(0x16, &base, &max, &bus, &unused))
45-
return "Unsupported instruction";
46-
47-
// cpuid returns 0 MHz when hyper-v is enabled
48-
if (base) cpu->frequencyBase = base;
49-
if (max) cpu->frequencyMax = max;
50-
return NULL;
43+
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
44+
if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx))
45+
{
46+
// WARNING: CPUID may report frequencies of efficient cores
47+
// cpuid returns 0 MHz when hypervisor is enabled
48+
if (eax) cpu->frequencyBase = eax;
49+
if (ebx) cpu->frequencyMax = ebx;
50+
}
51+
52+
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx))
53+
{
54+
// Feature tests (leaf1.ecx, leaf7.ebx)
55+
bool sse2 = (ecx & bit_SSE2) != 0;
56+
bool sse4_2 = (ecx & bit_SSE4_2) != 0;
57+
bool pclmul = (ecx & bit_PCLMUL) != 0;
58+
bool popcnt = (ecx & bit_POPCNT) != 0;
59+
bool fma = (ecx & bit_FMA) != 0;
60+
bool osxsave = (ecx & bit_OSXSAVE) != 0;
61+
62+
unsigned int eax7 = 0, ebx7 = 0, ecx7 = 0, edx7 = 0;
63+
__get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7);
64+
65+
bool avx2 = (ebx7 & bit_AVX2) != 0;
66+
bool bmi2 = (ebx7 & bit_BMI2) != 0;
67+
bool avx512f = (ebx7 & bit_AVX512F) != 0;
68+
bool avx512bw = (ebx7 & bit_AVX512BW) != 0;
69+
bool avx512dq = (ebx7 & bit_AVX512DQ) != 0;
70+
71+
// OS support for AVX/AVX512: check XGETBV (requires OSXSAVE)
72+
bool avx_os = false;
73+
bool avx512_os = false;
74+
if (osxsave)
75+
{
76+
__asm__ __volatile__(
77+
"xgetbv"
78+
: "=a"(eax), "=d"(edx)
79+
: "c"(0)
80+
:
81+
);
82+
uint64_t xcr0 = ((uint64_t)edx << 32) | eax;
83+
84+
// AVX requires XCR0[1:2] == 11b (XMM and YMM state)
85+
avx_os = (xcr0 & 0x6ULL) == 0x6ULL;
86+
// AVX512 requires XCR0[7,5,6] etc. common mask 0xE6 (bits 1,2,5,6,7)
87+
avx512_os = (xcr0 & 0xE6ULL) == 0xE6ULL;
88+
}
89+
90+
cpu->march = "unknown";
91+
if (avx512f && avx512bw && avx512dq && avx512_os) cpu->march = "x86_64-v4";
92+
else if (avx2 && fma && bmi2 && avx_os) cpu->march = "x86_64-v3";
93+
else if (sse4_2 && popcnt && pclmul) cpu->march = "x86_64-v2";
94+
else if (sse2) cpu->march = "x86_64-v1";
95+
}
5196
}
5297

5398
#else
5499

55-
inline static const char* ffCPUDetectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu)
100+
inline static void ffCPUDetectByCpuid(FFCPUResult* cpu)
56101
{
57102
return "Unsupported platform";
58103
}

src/detection/cpu/cpu_apple.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static const char* detectFrequency(FFCPUResult* cpu)
7272
#else
7373
static const char* detectFrequency(FFCPUResult* cpu)
7474
{
75+
ffCPUDetectByCpuid(cpu);
7576
cpu->frequencyBase = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency", 0) / 1000 / 1000);
7677
cpu->frequencyMax = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency_max", 0) / 1000 / 1000);
7778
if(cpu->frequencyBase == 0)

src/detection/cpu/cpu_bsd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
7070
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &currentCPU);
7171
#endif
7272

73-
ffCPUDetectSpeedByCpuid(cpu);
73+
ffCPUDetectByCpuid(cpu);
7474

7575
uint32_t clockrate = (uint32_t) ffSysctlGetInt("hw.clockrate", 0);
7676
if (clockrate > cpu->frequencyBase) cpu->frequencyBase = clockrate;

src/detection/cpu/cpu_haiku.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const char* ffDetectCPUImpl(FF_MAYBE_UNUSED const FFCPUOptions* options, FFCPURe
5555
ffStrbufSetF(&cpu->name, "(Unknown %" B_PRIx32 ")", cpuModel);
5656
ffStrbufSetS(&cpu->vendor, get_cpu_vendor_string(cpuVendor));
5757

58-
ffCPUDetectSpeedByCpuid(cpu);
58+
ffCPUDetectByCpuid(cpu);
5959
if (cpu->frequencyBase < frequency) cpu->frequencyBase = frequency;
6060
cpu->packages = packages;
6161
cpu->coresPhysical = cores;

src/detection/cpu/cpu_linux.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
456456
cpu->coresPhysical *= cpu->packages;
457457

458458
// Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252
459-
ffCPUDetectSpeedByCpuid(cpu);
459+
ffCPUDetectByCpuid(cpu);
460460
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0)
461461
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
462462

src/detection/cpu/cpu_nbsd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
6464
cpu->coresLogical = cpu->coresPhysical;
6565
cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.ncpuonline", cpu->coresLogical);
6666

67-
ffCPUDetectSpeedByCpuid(cpu);
67+
ffCPUDetectByCpuid(cpu);
6868

6969
uint32_t freq = (uint32_t) ffSysctlGetInt("machdep.cpu.frequency.target", 0);
7070
if (freq == 0) freq = (uint32_t) (ffSysctlGetInt64("hw.cpu0.clock_frequency", 0) / 1000000);

src/detection/cpu/cpu_obsd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const char *ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
5656
cpu->coresLogical = cpu->coresPhysical;
5757
cpu->coresOnline = (uint16_t) ffSysctlGetInt(CTL_HW, HW_NCPUONLINE, cpu->coresLogical);
5858

59-
ffCPUDetectSpeedByCpuid(cpu);
59+
ffCPUDetectByCpuid(cpu);
6060

6161
uint32_t cpuspeed = (uint32_t) ffSysctlGetInt(CTL_HW, HW_CPUSPEED, 0);
6262
if (cpuspeed > cpu->frequencyBase) cpu->frequencyBase = cpuspeed;

src/detection/cpu/cpu_sunos.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
122122
kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id");
123123
if (kn) ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
124124
}
125-
ffCPUDetectSpeedByCpuid(cpu);
125+
ffCPUDetectByCpuid(cpu);
126126
{
127127
kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz");
128128
if (kn && kn->value.ui32 > cpu->frequencyBase)

src/detection/cpu/cpu_windows.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
287287
if (error)
288288
return error;
289289

290-
ffCPUDetectSpeedByCpuid(cpu);
290+
ffCPUDetectByCpuid(cpu);
291291
if (options->showPeCoreCount) detectCoreTypes(cpu);
292292

293293
if (cpu->frequencyMax == 0)

src/modules/cpu/cpu.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ bool ffPrintCPU(FFCPUOptions* options)
110110
FF_FORMAT_ARG(tempStr, "temperature"),
111111
FF_FORMAT_ARG(coreTypes, "core-types"),
112112
FF_FORMAT_ARG(cpu.packages, "packages"),
113+
FF_FORMAT_ARG(cpu.march, "march"),
113114
}));
114115
}
115116
success = true;
@@ -211,6 +212,11 @@ bool ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_
211212
else
212213
yyjson_mut_obj_add_null(doc, obj, "temperature");
213214

215+
if (cpu.march)
216+
yyjson_mut_obj_add_str(doc, obj, "march", cpu.march);
217+
else
218+
yyjson_mut_obj_add_null(doc, obj, "march");
219+
214220
success = true;
215221
}
216222

@@ -253,5 +259,6 @@ FFModuleBaseInfo ffCPUModuleInfo = {
253259
{"Temperature (formatted)", "temperature"},
254260
{"Logical core count grouped by frequency", "core-types"},
255261
{"Processor package count", "packages"},
262+
{"X86-64 CPU microarchitecture", "march"},
256263
}))
257264
};

0 commit comments

Comments
 (0)