Browse Source

Add package sys/cpu - implements processor feature detection

gingerBill 4 years ago
parent
commit
7c5247f5fb
2 changed files with 102 additions and 0 deletions
  1. 35 0
      core/sys/cpu/cpu.odin
  2. 67 0
      core/sys/cpu/cpu_x86.odin

+ 35 - 0
core/sys/cpu/cpu.odin

@@ -0,0 +1,35 @@
+package sys_cpu
+
+#assert(ODIN_USE_LLVM_API);
+
+Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
+
+initialized: bool;
+
+x86: struct {
+	_: Cache_Line_Pad,
+	has_aes:       bool, // AES hardware implementation (AES NI)
+	has_adx:       bool, // Multi-precision add-carry instruction extensions
+	has_avx:       bool, // Advanced vector extension
+	has_avx2:      bool, // Advanced vector extension 2
+	has_bmi1:      bool, // Bit manipulation instruction set 1
+	has_bmi2:      bool, // Bit manipulation instruction set 2
+	has_erms:      bool, // Enhanced REP for MOVSB and STOSB
+	has_fma:       bool, // Fused-multiply-add instructions
+	has_os_xsave:  bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
+	has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
+	has_popcnt:    bool, // Hamming weight instruction POPCNT.
+	has_rdrand:    bool, // RDRAND instruction (on-chip random number generator)
+	has_rdseed:    bool, // RDSEED instruction (on-chip random number generator)
+	has_sse2:      bool, // Streaming SIMD extension 2 (always available on amd64)
+	has_sse3:      bool, // Streaming SIMD extension 3
+	has_ssse3:     bool, // Supplemental streaming SIMD extension 3
+	has_sse41:     bool, // Streaming SIMD extension 4 and 4.1
+	has_sse42:     bool, // Streaming SIMD extension 4 and 4.2
+	_: Cache_Line_Pad,
+};
+
+
+init :: proc() {
+	_init();
+}

+ 67 - 0
core/sys/cpu/cpu_x86.odin

@@ -0,0 +1,67 @@
+//+build 386, amd64
+package sys_cpu
+
+_cache_line_size :: 64;
+
+cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
+	return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
+		"cpuid",
+		"={ax},={bx},={cx},={dx},{ax},{cx}",
+	}(ax, cx));
+}
+
+xgetbv :: proc() -> (eax, edx: u32) {
+	return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
+		"xgetbv",
+		"={ax},={dx},{cx}",
+	}(0));
+}
+
+_init :: proc() {
+	is_set :: proc(hwc: u32, value: u32) -> bool {
+		return hwc&value != 0;
+	}
+
+	initialized = true;
+
+	max_id, _, _, _ := cpuid(0, 0);
+
+	if max_id < 1 {
+		return;
+	}
+
+	_, _, ecx1, edx1 := cpuid(1, 0);
+
+	x86.has_sse2 = is_set(26, edx1);
+
+	x86.has_sse3      = is_set(0, ecx1);
+	x86.has_pclmulqdq = is_set(1, ecx1);
+	x86.has_ssse3     = is_set(9, ecx1);
+	x86.has_fma       = is_set(12, ecx1);
+	x86.has_sse41     = is_set(19, ecx1);
+	x86.has_sse42     = is_set(20, ecx1);
+	x86.has_popcnt    = is_set(23, ecx1);
+	x86.has_aes       = is_set(25, ecx1);
+	x86.has_os_xsave  = is_set(27, ecx1);
+	x86.has_rdrand    = is_set(30, ecx1);
+
+	os_supports_avx := false;
+	if x86.has_os_xsave {
+		eax, _ := xgetbv();
+		os_supports_avx = is_set(1, eax) && is_set(2, eax);
+	}
+
+	x86.has_avx = is_set(28, ecx1) && os_supports_avx;
+
+	if max_id < 7 {
+		return;
+	}
+
+	_, ebx7, _, _ := cpuid(7, 0);
+	x86.has_bmi1   = is_set(3, ebx7);
+	x86.has_avx2   = is_set(5, ebx7) && os_supports_avx;
+	x86.has_bmi2   = is_set(8, ebx7);
+	x86.has_erms   = is_set(9, ebx7);
+	x86.has_rdseed = is_set(18, ebx7);
+	x86.has_adx    = is_set(19, ebx7);
+}