mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-26 23:44:58 -07:00
adb4692ce8
Someone ran into this on Discord, so adopt the same workaround that chrome did, by checking both OSXSAVE and XSAVE before calling XGETBV. The old way of detecting AVX is correct per Intel, but such is life.
136 lines
3.9 KiB
Odin
136 lines
3.9 KiB
Odin
//+build i386, amd64
|
|
package sysinfo
|
|
|
|
import "core:intrinsics"
|
|
|
|
// cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
|
|
cpuid :: intrinsics.x86_cpuid
|
|
|
|
// xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
|
xgetbv :: intrinsics.x86_xgetbv
|
|
|
|
CPU_Feature :: enum u64 {
|
|
aes, // AES hardware implementation (AES NI)
|
|
adx, // Multi-precision add-carry instruction extensions
|
|
avx, // Advanced vector extension
|
|
avx2, // Advanced vector extension 2
|
|
bmi1, // Bit manipulation instruction set 1
|
|
bmi2, // Bit manipulation instruction set 2
|
|
erms, // Enhanced REP for MOVSB and STOSB
|
|
fma, // Fused-multiply-add instructions
|
|
os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
|
pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM
|
|
popcnt, // Hamming weight instruction POPCNT.
|
|
rdrand, // RDRAND instruction (on-chip random number generator)
|
|
rdseed, // RDSEED instruction (on-chip random number generator)
|
|
sse2, // Streaming SIMD extension 2 (always available on amd64)
|
|
sse3, // Streaming SIMD extension 3
|
|
ssse3, // Supplemental streaming SIMD extension 3
|
|
sse41, // Streaming SIMD extension 4 and 4.1
|
|
sse42, // Streaming SIMD extension 4 and 4.2
|
|
}
|
|
|
|
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
|
|
|
cpu_features: Maybe(CPU_Features)
|
|
cpu_name: Maybe(string)
|
|
|
|
@(init, private)
|
|
init_cpu_features :: proc "c" () {
|
|
is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
|
|
return hwc&value != 0
|
|
}
|
|
try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
|
|
if is_set(hwc, value) {
|
|
set^ += {feature}
|
|
}
|
|
}
|
|
|
|
max_id, _, _, _ := cpuid(0, 0)
|
|
if max_id < 1 {
|
|
return
|
|
}
|
|
|
|
set: CPU_Features
|
|
|
|
_, _, ecx1, edx1 := cpuid(1, 0)
|
|
|
|
try_set(&set, .sse2, 26, edx1)
|
|
try_set(&set, .sse3, 0, ecx1)
|
|
try_set(&set, .pclmulqdq, 1, ecx1)
|
|
try_set(&set, .ssse3, 9, ecx1)
|
|
try_set(&set, .fma, 12, ecx1)
|
|
try_set(&set, .sse41, 19, ecx1)
|
|
try_set(&set, .sse42, 20, ecx1)
|
|
try_set(&set, .popcnt, 23, ecx1)
|
|
try_set(&set, .aes, 25, ecx1)
|
|
try_set(&set, .os_xsave, 27, ecx1)
|
|
try_set(&set, .rdrand, 30, ecx1)
|
|
|
|
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
|
// xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1
|
|
// return before probing further
|
|
cpu_features = set
|
|
return
|
|
}
|
|
|
|
// In certain rare cases (reason unknown), XGETBV generates an
|
|
// illegal instruction, even if OSXSAVE is set per CPUID.
|
|
//
|
|
// When Chrome ran into this problem, the problem went away
|
|
// after they started checking both OSXSAVE and XSAVE.
|
|
//
|
|
// See: crbug.com/375968
|
|
os_supports_avx := false
|
|
if .os_xsave in set && is_set(26, ecx1) {
|
|
eax, _ := xgetbv(0)
|
|
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
|
}
|
|
if os_supports_avx {
|
|
try_set(&set, .avx, 28, ecx1)
|
|
}
|
|
|
|
if max_id < 7 {
|
|
return
|
|
}
|
|
|
|
_, ebx7, _, _ := cpuid(7, 0)
|
|
try_set(&set, .bmi1, 3, ebx7)
|
|
if os_supports_avx {
|
|
try_set(&set, .avx2, 5, ebx7)
|
|
}
|
|
try_set(&set, .bmi2, 8, ebx7)
|
|
try_set(&set, .erms, 9, ebx7)
|
|
try_set(&set, .rdseed, 18, ebx7)
|
|
try_set(&set, .adx, 19, ebx7)
|
|
|
|
cpu_features = set
|
|
}
|
|
|
|
@(private)
|
|
_cpu_name_buf: [72]u8
|
|
|
|
@(init, private)
|
|
init_cpu_name :: proc "c" () {
|
|
number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
|
|
if number_of_extended_ids < 0x8000_0004 {
|
|
return
|
|
}
|
|
|
|
_buf := transmute(^[0x12]u32)&_cpu_name_buf
|
|
_buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
|
|
_buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
|
|
_buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
|
|
|
|
// Some CPUs like may include leading or trailing spaces. Trim them.
|
|
// e.g. ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz`
|
|
|
|
brand := string(_cpu_name_buf[:])
|
|
for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' {
|
|
brand = brand[1:]
|
|
}
|
|
for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
|
|
brand = brand[:len(brand) - 1]
|
|
}
|
|
cpu_name = brand
|
|
} |