Browse Source

sys/info: add feature detection for Darwin and Linux ARM

Laytan Laats 1 year ago
parent
commit
8660718ebe
3 changed files with 206 additions and 14 deletions
  1. 43 14
      core/sys/info/cpu_arm.odin
  2. 98 0
      core/sys/info/cpu_darwin_arm64.odin
  3. 65 0
      core/sys/info/cpu_linux_arm.odin

+ 43 - 14
core/sys/info/cpu_arm.odin

@@ -1,26 +1,55 @@
 //+build arm32, arm64
 //+build arm32, arm64
 package sysinfo
 package sysinfo
 
 
-// TODO: Set up an enum with the ARM equivalent of the above.
-CPU_Feature :: enum u64 {}
+CPU_Feature :: enum u64 {
+	// Advanced SIMD & floating-point capabilities:
+	asimd,         // General support for Advanced SIMD instructions/neon.
+	floatingpoint, // General support for floating-point instructions.
+	asimdhp,       // Advanced SIMD half-precision conversion instructions.
+	bf16,          // Storage and arithmetic instructions of the Brain Floating Point (BFloat16) data type.
+	fcma,          // Floating-point complex number instructions.
+	fhm,           // Floating-point half-precision multiplication instructions.
+	fp16,          // General half-precision floating-point data processing instructions.
+	frint,         // Floating-point to integral valued floating-point number rounding instructions.
+	i8mm,          // Advanced SIMD int8 matrix multiplication instructions.
+	jscvt,         // JavaScript conversion instruction.
+	rdm,           // Advanced SIMD rounding double multiply accumulate instructions.
 
 
-cpu_features: Maybe(CPU_Feature)
-cpu_name:     Maybe(string)
+	flagm,  // Condition flag manipulation instructions.
+	flagm2, // Enhancements to condition flag manipulation instructions.
+	crc32,  // CRC32 instructions.
 
 
-@(init, private)
-init_cpu_features :: proc "c" () {
+	lse,    // Atomic instructions to support large systems.
+	lse2,   // Changes to single-copy atomicity and alignment requirements for loads and stores for large systems.
+	lrcpc,  // Load-acquire Release Consistency processor consistent (RCpc) instructions.
+	lrcpc2, // Load-acquire Release Consistency processor consistent (RCpc) instructions version 2.
+
+	aes,
+	pmull,
+	sha1,
+	sha256,
+	sha512,
+	sha3,
+
+	sb,   // Barrier instruction to control speculation.
+	ssbs, // Instructions to control speculation of loads and stores.
 }
 }
 
 
+CPU_Features :: distinct bit_set[CPU_Feature; u64]
+
+cpu_features: Maybe(CPU_Features)
+cpu_name: Maybe(string)
+
 @(private)
 @(private)
-_cpu_name_buf: [72]u8
+cpu_name_buf: [128]byte
 
 
 @(init, private)
 @(init, private)
-init_cpu_name :: proc "c" () {
-	when ODIN_ARCH == .arm32 {
-		copy(_cpu_name_buf[:], "ARM")
-		cpu_name = string(_cpu_name_buf[:3])
+init_cpu_name :: proc "contextless" () {
+	when ODIN_ARCH == .arm64 {
+		copy(cpu_name_buf[:], "ARM64")
+		cpu_name = string(cpu_name_buf[:len("ARM64")])
 	} else {
 	} else {
-		copy(_cpu_name_buf[:], "ARM64")
-		cpu_name = string(_cpu_name_buf[:5])
+		copy(cpu_name_buf[:], "ARM")
+		cpu_name = string(cpu_name_buf[:len("ARM")])
 	}
 	}
-}
+}

+ 98 - 0
core/sys/info/cpu_darwin_arm64.odin

@@ -0,0 +1,98 @@
+package sysinfo
+
+import "core:sys/unix"
+
+@(init, private)
+init_cpu_features :: proc "contextless" () {
+	@(static) features: CPU_Features
+	defer cpu_features = features
+
+	try_set :: proc "contextless" (name: string, feature: CPU_Feature) -> (ok: bool) {
+		support: b32
+		if ok = unix.sysctlbyname(name, &support); ok && support {
+			features += { feature }
+		}
+		return
+	}
+
+	// Docs from Apple: https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
+	// Features from there that do not have (or I didn't find) an equivalent on Linux are commented out below.
+
+	// Advanced SIMD & floating-point capabilities:
+	{
+		if !try_set("hw.optional.AdvSIMD", .asimd) {
+			try_set("hw.optional.neon", .asimd)
+		}
+
+		try_set("hw.optional.floatingpoint", .floatingpoint)
+
+		if !try_set("hw.optional.AdvSIMD_HPFPCvt", .asimdhp) {
+			try_set("hw.optional.neon_hpfp", .asimdhp)
+		}
+
+		try_set("hw.optional.arm.FEAT_BF16", .bf16)
+		// try_set("hw.optional.arm.FEAT_DotProd", .dotprod)
+
+		if !try_set("hw.optional.arm.FEAT_FCMA", .fcma) {
+			try_set("hw.optional.armv8_3_compnum", .fcma)
+		}
+
+		if !try_set("hw.optional.arm.FEAT_FHM", .fhm) {
+			try_set("hw.optional.armv8_2_fhm", .fhm)
+		}
+
+		if !try_set("hw.optional.arm.FEAT_FP16", .fp16) {
+			try_set("hw.optional.neon_fp16", .fp16)
+		}
+
+		try_set("hw.optional.arm.FEAT_FRINTTS", .frint)
+		try_set("hw.optional.arm.FEAT_I8MM", .i8mm)
+		try_set("hw.optional.arm.FEAT_JSCVT", .jscvt)
+		try_set("hw.optional.arm.FEAT_RDM", .rdm)
+	}
+
+	// Integer capabilities:
+	{
+		try_set("hw.optional.arm.FEAT_FlagM", .flagm)
+		try_set("hw.optional.arm.FEAT_FlagM2", .flagm2)
+		try_set("hw.optional.armv8_crc32", .crc32)
+	}
+
+	// Atomic and memory ordering instruction capabilities:
+	{
+		try_set("hw.optional.arm.FEAT_LRCPC", .lrcpc)
+		try_set("hw.optional.arm.FEAT_LRCPC2", .lrcpc2)
+
+		if !try_set("hw.optional.arm.FEAT_LSE", .lse) {
+			try_set("hw.optional.armv8_1_atomics", .lse)
+		}
+
+		// try_set("hw.optional.arm.FEAT_LSE2", .lse2)
+	}
+
+	// Encryption capabilities:
+	{
+		try_set("hw.optional.arm.FEAT_AES", .aes)
+		try_set("hw.optional.arm.FEAT_PMULL", .pmull)
+		try_set("hw.optional.arm.FEAT_SHA1", .sha1)
+		try_set("hw.optional.arm.FEAT_SHA256", .sha256)
+
+		if !try_set("hw.optional.arm.FEAT_SHA512", .sha512) {
+			try_set("hw.optional.armv8_2_sha512", .sha512)
+		}
+
+		if !try_set("hw.optional.arm.FEAT_SHA3", .sha3) {
+			try_set("hw.optional.armv8_2_sha3", .sha3)
+		}
+	}
+
+	// General capabilities:
+	{
+		// try_set("hw.optional.arm.FEAT_BTI", .bti)
+		// try_set("hw.optional.arm.FEAT_DPB", .dpb)
+		// try_set("hw.optional.arm.FEAT_DPB2", .dpb2)
+		// try_set("hw.optional.arm.FEAT_ECV", .ecv)
+		try_set("hw.optional.arm.FEAT_SB", .sb)
+		try_set("hw.optional.arm.FEAT_SSBS", .ssbs)
+	}
+}

+ 65 - 0
core/sys/info/cpu_linux_arm.odin

@@ -0,0 +1,65 @@
+//+build arm32, arm64
+//+build linux
+package sysinfo
+
+import "core:sys/linux"
+import "core:strings"
+
+@(init, private)
+init_cpu_features :: proc() {
+	fd, err := linux.open("/proc/cpuinfo", {})
+	if err != .NONE { return }
+	defer linux.close(fd)
+
+	// This is probably enough right?
+	buf: [4096]byte
+	n, rerr := linux.read(fd, buf[:])
+	if rerr != .NONE || n == 0 { return }
+
+	features: CPU_Features
+	defer cpu_features = features
+
+	str := string(buf[:n])
+	for line in strings.split_lines_iterator(&str) {
+		key, _, value := strings.partition(line, ":")
+		key   = strings.trim_space(key)
+		value = strings.trim_space(value)
+
+		if key != "Features" { continue }
+
+		for feature in strings.split_by_byte_iterator(&value, ' ') {
+			switch feature {
+			case "asimd", "neon": features += { .asimd }
+			case "fp":            features += { .floatingpoint }
+			case "asimdhp":       features += { .asimdhp }
+			case "asimdbf16":     features += { .bf16 }
+			case "fcma":          features += { .fcma }
+			case "asimdfhm":      features += { .fhm }
+			case "fphp", "half":  features += { .fp16 }
+			case "frint":         features += { .frint }
+			case "i8mm":          features += { .i8mm }
+			case "jscvt":         features += { .jscvt }
+			case "asimdrdm":      features += { .rdm }
+
+			case "flagm":  features += { .flagm }
+			case "flagm2": features += { .flagm2 }
+			case "crc32":  features += { .crc32 }
+
+			case "atomics": features += { .lse }
+			case "lrcpc":   features += { .lrcpc }
+			case "ilrcpc":  features += { .lrcpc2 }
+
+			case "aes":    features += { .aes }
+			case "pmull":  features += { .pmull }
+			case "sha1":   features += { .sha1 }
+			case "sha2":   features += { .sha256 }
+			case "sha3":   features += { .sha3 }
+			case "sha512": features += { .sha512 }
+
+			case "sb":   features += { .sb }
+			case "ssbs": features += { .ssbs }
+			}
+		}
+		break
+	}
+}