1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const CpuidResult = struct { eax: u32, ebx: u32, ecx: u32, edx: u32 };
fn cpuid(leaf: u32, subleaf: u32) CpuidResult {
var eax: u32 = undefined;
var ebx: u32 = undefined;
var ecx: u32 = undefined;
var edx: u32 = undefined;
asm volatile ("cpuid"
: [_] "={eax}" (eax),
[_] "={ebx}" (ebx),
[_] "={ecx}" (ecx),
[_] "={edx}" (edx),
: [_] "{eax}" (leaf),
[_] "{ecx}" (subleaf),
);
return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx };
}
inline fn bit(val: u32, comptime pos: u5) bool {
return (val >> pos) & 1 != 0;
}
pub fn detect() i64 {
var t: HwyTargets = .{};
// x86_64 always has SSE2.
if (comptime builtin.cpu.arch == .x86_64) {
t.sse2 = true;
}
const leaf0 = cpuid(0, 0);
const max_leaf = leaf0.eax;
if (max_leaf < 1) return @bitCast(t);
const leaf1 = cpuid(1, 0);
// -- SSE2 on 32-bit x86 -------------------------------------------------
if (comptime builtin.cpu.arch == .x86) {
if (bit(leaf1.edx, 25) and bit(leaf1.edx, 26)) {
t.sse2 = true;
}
}
// -- SSSE3 ---------------------------------------------------------------
if (bit(leaf1.ecx, 0) and // SSE3
bit(leaf1.ecx, 9)) // SSSE3
{
t.ssse3 = true;
}
// -- SSE4 ----------------------------------------------------------------
if (bit(leaf1.ecx, 19) and // SSE4.1
bit(leaf1.ecx, 20) and // SSE4.2
bit(leaf1.ecx, 1) and // PCLMUL
bit(leaf1.ecx, 25)) // AES
{
t.sse4 = true;
}
// Check XSAVE / AVX OS support before enabling any AVX-dependent target.
const has_xsave = bit(leaf1.ecx, 27);
const has_avx_bit = bit(leaf1.ecx, 28);
const xcr0: u32 = if (has_xsave and has_avx_bit) asm volatile ("xgetbv"
: [_] "={eax}" (-> u32),
: [_] "{ecx}" (@as(u32, 0)),
: .{ .edx = true }) else 0;
const has_avx_save = (xcr0 & 0x6) == 0x6; // SSE + AVX state
// Darwin lazily saves AVX-512 context on first use.
const has_avx512_save = if (comptime builtin.os.tag.isDarwin())
true
else
(xcr0 & 0xE0) == 0xE0; // opmask + zmm_hi256 + hi16_zmm
// -- AVX2 ----------------------------------------------------------------
if (has_avx_save and max_leaf >= 7) {
const leaf7 = cpuid(7, 0);
if (bit(leaf7.ebx, 5) and // AVX2
bit(leaf1.ecx, 12) and // FMA
bit(leaf1.ecx, 29)) // F16C
{
// Also need LZCNT (extended leaf), BMI, BMI2.
const leaf_ext = cpuid(0x80000001, 0);
if (bit(leaf_ext.ecx, 5) and // LZCNT
bit(leaf7.ebx, 3) and // BMI
bit(leaf7.ebx, 8)) // BMI2
{
t.avx2 = true;
}
}
// -- AVX-512 ---------------------------------------------------------
if (has_avx512_save) {
if (bit(leaf7.ebx, 16) and // AVX512F
bit(leaf7.ebx, 31) and // AVX512VL
bit(leaf7.ebx, 17) and // AVX512DQ
bit(leaf7.ebx, 30) and // AVX512BW
bit(leaf7.ebx, 28)) // AVX512CD
{
t.avx3 = true;
}
if (bit(leaf7.ecx, 11) and // AVX512VNNI
bit(leaf7.ecx, 10) and // VPCLMULQDQ (AVX save ok)
bit(leaf7.ecx, 1) and // AVX512VBMI
bit(leaf7.ecx, 6) and // AVX512VBMI2
bit(leaf7.ecx, 9) and // VAES (AVX save ok)
bit(leaf7.ecx, 14) and // AVX512VPOPCNTDQ
bit(leaf7.ecx, 12) and // AVX512BITALG
bit(leaf7.ecx, 8)) // GFNI
{
t.avx3_dl = true;
}
// AVX512BF16 is in leaf 7 sub-1.
if (t.avx3_dl and leaf7.eax >= 1) {
const leaf7_1 = cpuid(7, 1);
if (bit(leaf7_1.eax, 5)) { // AVX512BF16
if (isAMD()) {
t.avx3_zen4 = true;
}
}
if (bit(leaf7.edx, 23) and // AVX512FP16
bit(leaf7_1.eax, 5)) // AVX512BF16
{
t.avx3_spr = true;
}
} else if (bit(leaf7.edx, 23)) { // AVX512FP16 without sub-leaf
// Can't check BF16 without sub-leaf support, skip avx3_spr.
}
}
// -- AVX10 -----------------------------------------------------------
if (max_leaf >= 7 and cpuid(7, 0).eax >= 1) {
const leaf7_1 = cpuid(7, 1);
if (bit(leaf7_1.edx, 19)) { // AVX10.1-256
if (max_leaf >= 0x24) {
const leaf24 = cpuid(0x24, 0);
if (bit(leaf24.ebx, 18)) { // AVX10.1-512
t.avx3_spr = true;
t.avx3_dl = true;
t.avx3 = true;
}
}
// AVX10.2 detection would require a leaf we can't
// reliably check yet; leave for future.
}
}
}
return @bitCast(t);
}
fn isAMD() bool {
const leaf0 = cpuid(0, 0);
// "Auth" "enti" "cAMD"
return leaf0.ebx == 0x68747541 and
leaf0.ecx == 0x444d4163 and
leaf0.edx == 0x69746e65;
}
|