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