cpu_detect.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // Copyright 2022 The Abseil Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "absl/crc/internal/cpu_detect.h"
  15. #include <cstdint>
  16. #include <string>
  17. #include "absl/base/config.h"
  18. #if defined(__aarch64__) && defined(__linux__)
  19. #include <asm/hwcap.h>
  20. #include <sys/auxv.h>
  21. #endif
  22. #if defined(_WIN32) || defined(_WIN64)
  23. #include <intrin.h>
  24. #endif
  25. #if defined(__x86_64__) || defined(_M_X64)
  26. #if ABSL_HAVE_BUILTIN(__cpuid)
  27. // MSVC-equivalent __cpuid intrinsic declaration for clang-like compilers
  28. // for non-Windows build environments.
  29. extern void __cpuid(int[4], int);
  30. #elif !defined(_WIN32) && !defined(_WIN64)
  31. // MSVC defines this function for us.
  32. // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
  33. static void __cpuid(int cpu_info[4], int info_type) {
  34. __asm__ volatile("cpuid \n\t"
  35. : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
  36. "=d"(cpu_info[3])
  37. : "a"(info_type), "c"(0));
  38. }
  39. #endif // !defined(_WIN32) && !defined(_WIN64)
  40. #endif // defined(__x86_64__) || defined(_M_X64)
  41. namespace absl {
  42. ABSL_NAMESPACE_BEGIN
  43. namespace crc_internal {
  44. #if defined(__x86_64__) || defined(_M_X64)
  45. namespace {
  46. enum class Vendor {
  47. kUnknown,
  48. kIntel,
  49. kAmd,
  50. };
  51. Vendor GetVendor() {
  52. // Get the vendor string (issue CPUID with eax = 0).
  53. int cpu_info[4];
  54. __cpuid(cpu_info, 0);
  55. std::string vendor;
  56. vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4);
  57. vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4);
  58. vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4);
  59. if (vendor == "GenuineIntel") {
  60. return Vendor::kIntel;
  61. } else if (vendor == "AuthenticAMD") {
  62. return Vendor::kAmd;
  63. } else {
  64. return Vendor::kUnknown;
  65. }
  66. }
  67. CpuType GetIntelCpuType() {
  68. // To get general information and extended features we send eax = 1 and
  69. // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
  70. // (See Intel 64 and IA-32 Architectures Software Developer's Manual
  71. // Volume 2A: Instruction Set Reference, A-M CPUID).
  72. // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
  73. // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
  74. int cpu_info[4];
  75. __cpuid(cpu_info, 1);
  76. // Response in eax bits as follows:
  77. // 0-3 (stepping id)
  78. // 4-7 (model number),
  79. // 8-11 (family code),
  80. // 12-13 (processor type),
  81. // 16-19 (extended model)
  82. // 20-27 (extended family)
  83. int family = (cpu_info[0] >> 8) & 0x0f;
  84. int model_num = (cpu_info[0] >> 4) & 0x0f;
  85. int ext_family = (cpu_info[0] >> 20) & 0xff;
  86. int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
  87. int brand_id = cpu_info[1] & 0xff;
  88. // Process the extended family and model info if necessary
  89. if (family == 0x0f) {
  90. family += ext_family;
  91. }
  92. if (family == 0x0f || family == 0x6) {
  93. model_num += (ext_model_num << 4);
  94. }
  95. switch (brand_id) {
  96. case 0: // no brand ID, so parse CPU family/model
  97. switch (family) {
  98. case 6: // Most PentiumIII processors are in this category
  99. switch (model_num) {
  100. case 0x2c: // Westmere: Gulftown
  101. return CpuType::kIntelWestmere;
  102. case 0x2d: // Sandybridge
  103. return CpuType::kIntelSandybridge;
  104. case 0x3e: // Ivybridge
  105. return CpuType::kIntelIvybridge;
  106. case 0x3c: // Haswell (client)
  107. case 0x3f: // Haswell
  108. return CpuType::kIntelHaswell;
  109. case 0x4f: // Broadwell
  110. case 0x56: // BroadwellDE
  111. return CpuType::kIntelBroadwell;
  112. case 0x55: // Skylake Xeon
  113. if ((cpu_info[0] & 0x0f) < 5) { // stepping < 5 is skylake
  114. return CpuType::kIntelSkylakeXeon;
  115. } else { // stepping >= 5 is cascadelake
  116. return CpuType::kIntelCascadelakeXeon;
  117. }
  118. case 0x5e: // Skylake (client)
  119. return CpuType::kIntelSkylake;
  120. default:
  121. return CpuType::kUnknown;
  122. }
  123. default:
  124. return CpuType::kUnknown;
  125. }
  126. default:
  127. return CpuType::kUnknown;
  128. }
  129. }
  130. CpuType GetAmdCpuType() {
  131. // To get general information and extended features we send eax = 1 and
  132. // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
  133. // (See Intel 64 and IA-32 Architectures Software Developer's Manual
  134. // Volume 2A: Instruction Set Reference, A-M CPUID).
  135. // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
  136. int cpu_info[4];
  137. __cpuid(cpu_info, 1);
  138. // Response in eax bits as follows:
  139. // 0-3 (stepping id)
  140. // 4-7 (model number),
  141. // 8-11 (family code),
  142. // 12-13 (processor type),
  143. // 16-19 (extended model)
  144. // 20-27 (extended family)
  145. int family = (cpu_info[0] >> 8) & 0x0f;
  146. int model_num = (cpu_info[0] >> 4) & 0x0f;
  147. int ext_family = (cpu_info[0] >> 20) & 0xff;
  148. int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
  149. if (family == 0x0f) {
  150. family += ext_family;
  151. model_num += (ext_model_num << 4);
  152. }
  153. switch (family) {
  154. case 0x17:
  155. switch (model_num) {
  156. case 0x0: // Stepping Ax
  157. case 0x1: // Stepping Bx
  158. return CpuType::kAmdNaples;
  159. case 0x30: // Stepping Ax
  160. case 0x31: // Stepping Bx
  161. return CpuType::kAmdRome;
  162. default:
  163. return CpuType::kUnknown;
  164. }
  165. break;
  166. case 0x19:
  167. switch (model_num) {
  168. case 0x0: // Stepping Ax
  169. case 0x1: // Stepping B0
  170. return CpuType::kAmdMilan;
  171. case 0x10: // Stepping A0
  172. case 0x11: // Stepping B0
  173. return CpuType::kAmdGenoa;
  174. case 0x44: // Stepping A0
  175. return CpuType::kAmdRyzenV3000;
  176. default:
  177. return CpuType::kUnknown;
  178. }
  179. break;
  180. default:
  181. return CpuType::kUnknown;
  182. }
  183. }
  184. } // namespace
  185. CpuType GetCpuType() {
  186. switch (GetVendor()) {
  187. case Vendor::kIntel:
  188. return GetIntelCpuType();
  189. case Vendor::kAmd:
  190. return GetAmdCpuType();
  191. default:
  192. return CpuType::kUnknown;
  193. }
  194. }
  195. bool SupportsArmCRC32PMULL() { return false; }
  196. #elif defined(__aarch64__) && defined(__linux__)
  197. #ifndef HWCAP_CPUID
  198. #define HWCAP_CPUID (1 << 11)
  199. #endif
  200. #define ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \
  201. asm("mrs %0, " #id : "=r"(val))
  202. CpuType GetCpuType() {
  203. // MIDR_EL1 is not visible to EL0, however the access will be emulated by
  204. // linux if AT_HWCAP has HWCAP_CPUID set.
  205. //
  206. // This method will be unreliable on heterogeneous computing systems (ex:
  207. // big.LITTLE) since the value of MIDR_EL1 will change based on the calling
  208. // thread.
  209. uint64_t hwcaps = getauxval(AT_HWCAP);
  210. if (hwcaps & HWCAP_CPUID) {
  211. uint64_t midr = 0;
  212. ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
  213. uint32_t implementer = (midr >> 24) & 0xff;
  214. uint32_t part_number = (midr >> 4) & 0xfff;
  215. switch (implementer) {
  216. case 0x41:
  217. switch (part_number) {
  218. case 0xd0c: return CpuType::kArmNeoverseN1;
  219. case 0xd40: return CpuType::kArmNeoverseV1;
  220. case 0xd49: return CpuType::kArmNeoverseN2;
  221. case 0xd4f: return CpuType::kArmNeoverseV2;
  222. default:
  223. return CpuType::kUnknown;
  224. }
  225. break;
  226. case 0xc0:
  227. switch (part_number) {
  228. case 0xac3: return CpuType::kAmpereSiryn;
  229. default:
  230. return CpuType::kUnknown;
  231. }
  232. break;
  233. default:
  234. return CpuType::kUnknown;
  235. }
  236. }
  237. return CpuType::kUnknown;
  238. }
  239. bool SupportsArmCRC32PMULL() {
  240. uint64_t hwcaps = getauxval(AT_HWCAP);
  241. return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL);
  242. }
  243. #else
  244. CpuType GetCpuType() { return CpuType::kUnknown; }
  245. bool SupportsArmCRC32PMULL() { return false; }
  246. #endif
  247. } // namespace crc_internal
  248. ABSL_NAMESPACE_END
  249. } // namespace absl