cpu_intel.odin 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //+build i386, amd64
  2. package sysinfo
  3. import "core:intrinsics"
  4. // cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
  5. cpuid :: intrinsics.x86_cpuid
  6. // xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
  7. xgetbv :: intrinsics.x86_xgetbv
  8. CPU_Feature :: enum u64 {
  9. aes, // AES hardware implementation (AES NI)
  10. adx, // Multi-precision add-carry instruction extensions
  11. avx, // Advanced vector extension
  12. avx2, // Advanced vector extension 2
  13. bmi1, // Bit manipulation instruction set 1
  14. bmi2, // Bit manipulation instruction set 2
  15. erms, // Enhanced REP for MOVSB and STOSB
  16. fma, // Fused-multiply-add instructions
  17. os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
  18. pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM
  19. popcnt, // Hamming weight instruction POPCNT.
  20. rdrand, // RDRAND instruction (on-chip random number generator)
  21. rdseed, // RDSEED instruction (on-chip random number generator)
  22. sse2, // Streaming SIMD extension 2 (always available on amd64)
  23. sse3, // Streaming SIMD extension 3
  24. ssse3, // Supplemental streaming SIMD extension 3
  25. sse41, // Streaming SIMD extension 4 and 4.1
  26. sse42, // Streaming SIMD extension 4 and 4.2
  27. }
  28. CPU_Features :: distinct bit_set[CPU_Feature; u64]
  29. cpu_features: Maybe(CPU_Features)
  30. cpu_name: Maybe(string)
  31. @(init, private)
  32. init_cpu_features :: proc "c" () {
  33. is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
  34. return hwc&(1 << value) != 0
  35. }
  36. try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
  37. if is_set(hwc, value) {
  38. set^ += {feature}
  39. }
  40. }
  41. max_id, _, _, _ := cpuid(0, 0)
  42. if max_id < 1 {
  43. return
  44. }
  45. set: CPU_Features
  46. _, _, ecx1, edx1 := cpuid(1, 0)
  47. try_set(&set, .sse2, 26, edx1)
  48. try_set(&set, .sse3, 0, ecx1)
  49. try_set(&set, .pclmulqdq, 1, ecx1)
  50. try_set(&set, .ssse3, 9, ecx1)
  51. try_set(&set, .fma, 12, ecx1)
  52. try_set(&set, .sse41, 19, ecx1)
  53. try_set(&set, .sse42, 20, ecx1)
  54. try_set(&set, .popcnt, 23, ecx1)
  55. try_set(&set, .aes, 25, ecx1)
  56. try_set(&set, .os_xsave, 27, ecx1)
  57. try_set(&set, .rdrand, 30, ecx1)
  58. when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
  59. // xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1
  60. // return before probing further
  61. cpu_features = set
  62. return
  63. }
  64. // In certain rare cases (reason unknown), XGETBV generates an
  65. // illegal instruction, even if OSXSAVE is set per CPUID.
  66. //
  67. // When Chrome ran into this problem, the problem went away
  68. // after they started checking both OSXSAVE and XSAVE.
  69. //
  70. // See: crbug.com/375968
  71. os_supports_avx := false
  72. if .os_xsave in set && is_set(26, ecx1) {
  73. eax, _ := xgetbv(0)
  74. os_supports_avx = is_set(1, eax) && is_set(2, eax)
  75. }
  76. if os_supports_avx {
  77. try_set(&set, .avx, 28, ecx1)
  78. }
  79. if max_id < 7 {
  80. return
  81. }
  82. _, ebx7, _, _ := cpuid(7, 0)
  83. try_set(&set, .bmi1, 3, ebx7)
  84. if os_supports_avx {
  85. try_set(&set, .avx2, 5, ebx7)
  86. }
  87. try_set(&set, .bmi2, 8, ebx7)
  88. try_set(&set, .erms, 9, ebx7)
  89. try_set(&set, .rdseed, 18, ebx7)
  90. try_set(&set, .adx, 19, ebx7)
  91. cpu_features = set
  92. }
  93. @(private)
  94. _cpu_name_buf: [72]u8
  95. @(init, private)
  96. init_cpu_name :: proc "c" () {
  97. number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
  98. if number_of_extended_ids < 0x8000_0004 {
  99. return
  100. }
  101. _buf := transmute(^[0x12]u32)&_cpu_name_buf
  102. _buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
  103. _buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
  104. _buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
  105. // Some CPUs like may include leading or trailing spaces. Trim them.
  106. // e.g. ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz`
  107. brand := string(_cpu_name_buf[:])
  108. for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' {
  109. brand = brand[1:]
  110. }
  111. for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
  112. brand = brand[:len(brand) - 1]
  113. }
  114. cpu_name = brand
  115. }