1 // Copyright 2017 Google LLC
2 // Copyright 2020 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "cpuinfo_x86.h"
17
18 #include <stdbool.h>
19 #include <string.h>
20
21 #include "internal/bit_utils.h"
22 #include "internal/cpuid_x86.h"
23
24 #if !defined(CPU_FEATURES_ARCH_X86)
25 #error "Cannot compile cpuinfo_x86 on a non x86 platform."
26 #endif
27
28 // Generation of feature's getters/setters functions and kGetters, kSetters,
29 // kCpuInfoFlags global tables.
30 #define DEFINE_TABLE_FEATURES \
31 FEATURE(X86_FPU, fpu, "fpu", 0, 0) \
32 FEATURE(X86_TSC, tsc, "tsc", 0, 0) \
33 FEATURE(X86_CX8, cx8, "cx8", 0, 0) \
34 FEATURE(X86_CLFSH, clfsh, "clfsh", 0, 0) \
35 FEATURE(X86_MMX, mmx, "mmx", 0, 0) \
36 FEATURE(X86_AES, aes, "aes", 0, 0) \
37 FEATURE(X86_ERMS, erms, "erms", 0, 0) \
38 FEATURE(X86_F16C, f16c, "f16c", 0, 0) \
39 FEATURE(X86_FMA4, fma4, "fma4", 0, 0) \
40 FEATURE(X86_FMA3, fma3, "fma3", 0, 0) \
41 FEATURE(X86_VAES, vaes, "vaes", 0, 0) \
42 FEATURE(X86_VPCLMULQDQ, vpclmulqdq, "vpclmulqdq", 0, 0) \
43 FEATURE(X86_BMI1, bmi1, "bmi1", 0, 0) \
44 FEATURE(X86_HLE, hle, "hle", 0, 0) \
45 FEATURE(X86_BMI2, bmi2, "bmi2", 0, 0) \
46 FEATURE(X86_RTM, rtm, "rtm", 0, 0) \
47 FEATURE(X86_RDSEED, rdseed, "rdseed", 0, 0) \
48 FEATURE(X86_CLFLUSHOPT, clflushopt, "clflushopt", 0, 0) \
49 FEATURE(X86_CLWB, clwb, "clwb", 0, 0) \
50 FEATURE(X86_SSE, sse, "sse", 0, 0) \
51 FEATURE(X86_SSE2, sse2, "sse2", 0, 0) \
52 FEATURE(X86_SSE3, sse3, "sse3", 0, 0) \
53 FEATURE(X86_SSSE3, ssse3, "ssse3", 0, 0) \
54 FEATURE(X86_SSE4_1, sse4_1, "sse4_1", 0, 0) \
55 FEATURE(X86_SSE4_2, sse4_2, "sse4_2", 0, 0) \
56 FEATURE(X86_SSE4A, sse4a, "sse4a", 0, 0) \
57 FEATURE(X86_AVX, avx, "avx", 0, 0) \
58 FEATURE(X86_AVX2, avx2, "avx2", 0, 0) \
59 FEATURE(X86_AVX512F, avx512f, "avx512f", 0, 0) \
60 FEATURE(X86_AVX512CD, avx512cd, "avx512cd", 0, 0) \
61 FEATURE(X86_AVX512ER, avx512er, "avx512er", 0, 0) \
62 FEATURE(X86_AVX512PF, avx512pf, "avx512pf", 0, 0) \
63 FEATURE(X86_AVX512BW, avx512bw, "avx512bw", 0, 0) \
64 FEATURE(X86_AVX512DQ, avx512dq, "avx512dq", 0, 0) \
65 FEATURE(X86_AVX512VL, avx512vl, "avx512vl", 0, 0) \
66 FEATURE(X86_AVX512IFMA, avx512ifma, "avx512ifma", 0, 0) \
67 FEATURE(X86_AVX512VBMI, avx512vbmi, "avx512vbmi", 0, 0) \
68 FEATURE(X86_AVX512VBMI2, avx512vbmi2, "avx512vbmi2", 0, 0) \
69 FEATURE(X86_AVX512VNNI, avx512vnni, "avx512vnni", 0, 0) \
70 FEATURE(X86_AVX512BITALG, avx512bitalg, "avx512bitalg", 0, 0) \
71 FEATURE(X86_AVX512VPOPCNTDQ, avx512vpopcntdq, "avx512vpopcntdq", 0, 0) \
72 FEATURE(X86_AVX512_4VNNIW, avx512_4vnniw, "avx512_4vnniw", 0, 0) \
73 FEATURE(X86_AVX512_4VBMI2, avx512_4vbmi2, "avx512_4vbmi2", 0, 0) \
74 FEATURE(X86_AVX512_SECOND_FMA, avx512_second_fma, "avx512_second_fma", 0, 0) \
75 FEATURE(X86_AVX512_4FMAPS, avx512_4fmaps, "avx512_4fmaps", 0, 0) \
76 FEATURE(X86_AVX512_BF16, avx512_bf16, "avx512_bf16", 0, 0) \
77 FEATURE(X86_AVX512_VP2INTERSECT, avx512_vp2intersect, "avx512_vp2intersect", \
78 0, 0) \
79 FEATURE(X86_AMX_BF16, amx_bf16, "amx_bf16", 0, 0) \
80 FEATURE(X86_AMX_TILE, amx_tile, "amx_tile", 0, 0) \
81 FEATURE(X86_AMX_INT8, amx_int8, "amx_int8", 0, 0) \
82 FEATURE(X86_PCLMULQDQ, pclmulqdq, "pclmulqdq", 0, 0) \
83 FEATURE(X86_SMX, smx, "smx", 0, 0) \
84 FEATURE(X86_SGX, sgx, "sgx", 0, 0) \
85 FEATURE(X86_CX16, cx16, "cx16", 0, 0) \
86 FEATURE(X86_SHA, sha, "sha", 0, 0) \
87 FEATURE(X86_POPCNT, popcnt, "popcnt", 0, 0) \
88 FEATURE(X86_MOVBE, movbe, "movbe", 0, 0) \
89 FEATURE(X86_RDRND, rdrnd, "rdrnd", 0, 0) \
90 FEATURE(X86_DCA, dca, "dca", 0, 0) \
91 FEATURE(X86_SS, ss, "ss", 0, 0)
92 #define DEFINE_TABLE_FEATURE_TYPE X86Features
93 #define DEFINE_TABLE_DONT_GENERATE_HWCAPS
94 #include "define_tables.h"
95
96 // The following includes are necessary to provide SSE detections on pre-AVX
97 // microarchitectures.
98 #if defined(CPU_FEATURES_OS_WINDOWS)
99 #include <windows.h> // IsProcessorFeaturePresent
100 #elif defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
101 #include "internal/filesystem.h" // Needed to parse /proc/cpuinfo
102 #include "internal/stack_line_reader.h" // Needed to parse /proc/cpuinfo
103 #include "internal/string_view.h" // Needed to parse /proc/cpuinfo
104 #elif defined(CPU_FEATURES_OS_DARWIN)
105 #if !defined(HAVE_SYSCTLBYNAME)
106 #error "Darwin needs support for sysctlbyname"
107 #endif
108 #include <sys/sysctl.h>
109 #else
110 #error "Unsupported OS"
111 #endif // CPU_FEATURES_OS
112
113 ////////////////////////////////////////////////////////////////////////////////
114 // Definitions for CpuId and GetXCR0Eax.
115 ////////////////////////////////////////////////////////////////////////////////
116
117 #if defined(CPU_FEATURES_MOCK_CPUID_X86)
118 // Implementation will be provided by test/cpuinfo_x86_test.cc.
119 #elif defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
120
121 #include <cpuid.h>
122
GetCpuidLeaf(uint32_t leaf_id,int ecx)123 Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
124 Leaf leaf;
125 __cpuid_count(leaf_id, ecx, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
126 return leaf;
127 }
128
GetXCR0Eax(void)129 uint32_t GetXCR0Eax(void) {
130 uint32_t eax, edx;
131 /* named form of xgetbv not supported on OSX, so must use byte form, see:
132 https://github.com/asmjit/asmjit/issues/78
133 */
134 __asm(".byte 0x0F, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0));
135 return eax;
136 }
137
138 #elif defined(CPU_FEATURES_COMPILER_MSC)
139
140 #include <immintrin.h>
141 #include <intrin.h> // For __cpuidex()
142
GetCpuidLeaf(uint32_t leaf_id,int ecx)143 Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
144 Leaf leaf;
145 int data[4];
146 __cpuidex(data, leaf_id, ecx);
147 leaf.eax = data[0];
148 leaf.ebx = data[1];
149 leaf.ecx = data[2];
150 leaf.edx = data[3];
151 return leaf;
152 }
153
GetXCR0Eax(void)154 uint32_t GetXCR0Eax(void) { return (uint32_t)_xgetbv(0); }
155
156 #else
157 #error "Unsupported compiler, x86 cpuid requires either GCC, Clang or MSVC."
158 #endif
159
CpuId(uint32_t leaf_id)160 static Leaf CpuId(uint32_t leaf_id) { return GetCpuidLeaf(leaf_id, 0); }
161
162 static const Leaf kEmptyLeaf;
163
SafeCpuIdEx(uint32_t max_cpuid_leaf,uint32_t leaf_id,int ecx)164 static Leaf SafeCpuIdEx(uint32_t max_cpuid_leaf, uint32_t leaf_id, int ecx) {
165 if (leaf_id <= max_cpuid_leaf) {
166 return GetCpuidLeaf(leaf_id, ecx);
167 } else {
168 return kEmptyLeaf;
169 }
170 }
171
SafeCpuId(uint32_t max_cpuid_leaf,uint32_t leaf_id)172 static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
173 return SafeCpuIdEx(max_cpuid_leaf, leaf_id, 0);
174 }
175
176 #define MASK_XMM 0x2
177 #define MASK_YMM 0x4
178 #define MASK_MASKREG 0x20
179 #define MASK_ZMM0_15 0x40
180 #define MASK_ZMM16_31 0x80
181 #define MASK_XTILECFG 0x20000
182 #define MASK_XTILEDATA 0x40000
183
HasMask(uint32_t value,uint32_t mask)184 static bool HasMask(uint32_t value, uint32_t mask) {
185 return (value & mask) == mask;
186 }
187
188 // Checks that operating system saves and restores xmm registers during context
189 // switches.
HasXmmOsXSave(uint32_t xcr0_eax)190 static bool HasXmmOsXSave(uint32_t xcr0_eax) {
191 return HasMask(xcr0_eax, MASK_XMM);
192 }
193
194 // Checks that operating system saves and restores ymm registers during context
195 // switches.
HasYmmOsXSave(uint32_t xcr0_eax)196 static bool HasYmmOsXSave(uint32_t xcr0_eax) {
197 return HasMask(xcr0_eax, MASK_XMM | MASK_YMM);
198 }
199
200 // Checks that operating system saves and restores zmm registers during context
201 // switches.
HasZmmOsXSave(uint32_t xcr0_eax)202 static bool HasZmmOsXSave(uint32_t xcr0_eax) {
203 return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
204 MASK_ZMM16_31);
205 }
206
207 // Checks that operating system saves and restores AMX/TMUL state during context
208 // switches.
HasTmmOsXSave(uint32_t xcr0_eax)209 static bool HasTmmOsXSave(uint32_t xcr0_eax) {
210 return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
211 MASK_ZMM16_31 | MASK_XTILECFG | MASK_XTILEDATA);
212 }
213
HasSecondFMA(uint32_t model)214 static bool HasSecondFMA(uint32_t model) {
215 // Skylake server
216 if (model == 0x55) {
217 char proc_name[49] = {0};
218 FillX86BrandString(proc_name);
219 // detect Xeon
220 if (proc_name[9] == 'X') {
221 // detect Silver or Bronze
222 if (proc_name[17] == 'S' || proc_name[17] == 'B') return false;
223 // detect Gold 5_20 and below, except for Gold 53__
224 if (proc_name[17] == 'G' && proc_name[22] == '5')
225 return ((proc_name[23] == '3') ||
226 (proc_name[24] == '2' && proc_name[25] == '2'));
227 // detect Xeon W 210x
228 if (proc_name[17] == 'W' && proc_name[21] == '0') return false;
229 // detect Xeon D 2xxx
230 if (proc_name[17] == 'D' && proc_name[19] == '2' && proc_name[20] == '1')
231 return false;
232 }
233 return true;
234 }
235 // Cannon Lake client
236 if (model == 0x66) return false;
237 // Ice Lake client
238 if (model == 0x7d || model == 0x7e) return false;
239 // This is the right default...
240 return true;
241 }
242
SetVendor(const Leaf leaf,char * const vendor)243 static void SetVendor(const Leaf leaf, char* const vendor) {
244 *(uint32_t*)(vendor) = leaf.ebx;
245 *(uint32_t*)(vendor + 4) = leaf.edx;
246 *(uint32_t*)(vendor + 8) = leaf.ecx;
247 vendor[12] = '\0';
248 }
249
IsVendor(const Leaf leaf,const char * const name)250 static int IsVendor(const Leaf leaf, const char* const name) {
251 const uint32_t ebx = *(const uint32_t*)(name);
252 const uint32_t edx = *(const uint32_t*)(name + 4);
253 const uint32_t ecx = *(const uint32_t*)(name + 8);
254 return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
255 }
256
257 static const CacheLevelInfo kEmptyCacheLevelInfo;
258
GetCacheLevelInfo(const uint32_t reg)259 static CacheLevelInfo GetCacheLevelInfo(const uint32_t reg) {
260 const int UNDEF = -1;
261 const int KiB = 1024;
262 const int MiB = 1024 * KiB;
263 switch (reg) {
264 case 0x01:
265 return (CacheLevelInfo){.level = UNDEF,
266 .cache_type = CPU_FEATURE_CACHE_TLB,
267 .cache_size = 4 * KiB,
268 .ways = 4,
269 .line_size = UNDEF,
270 .tlb_entries = 32,
271 .partitioning = 0};
272 case 0x02:
273 return (CacheLevelInfo){.level = UNDEF,
274 .cache_type = CPU_FEATURE_CACHE_TLB,
275 .cache_size = 4 * MiB,
276 .ways = 0xFF,
277 .line_size = UNDEF,
278 .tlb_entries = 2,
279 .partitioning = 0};
280 case 0x03:
281 return (CacheLevelInfo){.level = UNDEF,
282 .cache_type = CPU_FEATURE_CACHE_TLB,
283 .cache_size = 4 * KiB,
284 .ways = 4,
285 .line_size = UNDEF,
286 .tlb_entries = 64,
287 .partitioning = 0};
288 case 0x04:
289 return (CacheLevelInfo){.level = UNDEF,
290 .cache_type = CPU_FEATURE_CACHE_TLB,
291 .cache_size = 4 * MiB,
292 .ways = 4,
293 .line_size = UNDEF,
294 .tlb_entries = 8,
295 .partitioning = 0};
296 case 0x05:
297 return (CacheLevelInfo){.level = UNDEF,
298 .cache_type = CPU_FEATURE_CACHE_TLB,
299 .cache_size = 4 * MiB,
300 .ways = 4,
301 .line_size = UNDEF,
302 .tlb_entries = 32,
303 .partitioning = 0};
304 case 0x06:
305 return (CacheLevelInfo){.level = 1,
306 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
307 .cache_size = 8 * KiB,
308 .ways = 4,
309 .line_size = 32,
310 .tlb_entries = UNDEF,
311 .partitioning = 0};
312 case 0x08:
313 return (CacheLevelInfo){.level = 1,
314 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
315 .cache_size = 16 * KiB,
316 .ways = 4,
317 .line_size = 32,
318 .tlb_entries = UNDEF,
319 .partitioning = 0};
320 case 0x09:
321 return (CacheLevelInfo){.level = 1,
322 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
323 .cache_size = 32 * KiB,
324 .ways = 4,
325 .line_size = 64,
326 .tlb_entries = UNDEF,
327 .partitioning = 0};
328 case 0x0A:
329 return (CacheLevelInfo){.level = 1,
330 .cache_type = CPU_FEATURE_CACHE_DATA,
331 .cache_size = 8 * KiB,
332 .ways = 2,
333 .line_size = 32,
334 .tlb_entries = UNDEF,
335 .partitioning = 0};
336 case 0x0B:
337 return (CacheLevelInfo){.level = UNDEF,
338 .cache_type = CPU_FEATURE_CACHE_TLB,
339 .cache_size = 4 * MiB,
340 .ways = 4,
341 .line_size = UNDEF,
342 .tlb_entries = 4,
343 .partitioning = 0};
344 case 0x0C:
345 return (CacheLevelInfo){.level = 1,
346 .cache_type = CPU_FEATURE_CACHE_DATA,
347 .cache_size = 16 * KiB,
348 .ways = 4,
349 .line_size = 32,
350 .tlb_entries = UNDEF,
351 .partitioning = 0};
352 case 0x0D:
353 return (CacheLevelInfo){.level = 1,
354 .cache_type = CPU_FEATURE_CACHE_DATA,
355 .cache_size = 16 * KiB,
356 .ways = 4,
357 .line_size = 64,
358 .tlb_entries = UNDEF,
359 .partitioning = 0};
360 case 0x0E:
361 return (CacheLevelInfo){.level = 1,
362 .cache_type = CPU_FEATURE_CACHE_DATA,
363 .cache_size = 24 * KiB,
364 .ways = 6,
365 .line_size = 64,
366 .tlb_entries = UNDEF,
367 .partitioning = 0};
368 case 0x1D:
369 return (CacheLevelInfo){.level = 2,
370 .cache_type = CPU_FEATURE_CACHE_DATA,
371 .cache_size = 128 * KiB,
372 .ways = 2,
373 .line_size = 64,
374 .tlb_entries = UNDEF,
375 .partitioning = 0};
376 case 0x21:
377 return (CacheLevelInfo){.level = 2,
378 .cache_type = CPU_FEATURE_CACHE_DATA,
379 .cache_size = 256 * KiB,
380 .ways = 8,
381 .line_size = 64,
382 .tlb_entries = UNDEF,
383 .partitioning = 0};
384 case 0x22:
385 return (CacheLevelInfo){.level = 3,
386 .cache_type = CPU_FEATURE_CACHE_DATA,
387 .cache_size = 512 * KiB,
388 .ways = 4,
389 .line_size = 64,
390 .tlb_entries = UNDEF,
391 .partitioning = 2};
392 case 0x23:
393 return (CacheLevelInfo){.level = 3,
394 .cache_type = CPU_FEATURE_CACHE_DATA,
395 .cache_size = 1 * MiB,
396 .ways = 8,
397 .line_size = 64,
398 .tlb_entries = UNDEF,
399 .partitioning = 2};
400 case 0x24:
401 return (CacheLevelInfo){.level = 2,
402 .cache_type = CPU_FEATURE_CACHE_DATA,
403 .cache_size = 1 * MiB,
404 .ways = 16,
405 .line_size = 64,
406 .tlb_entries = UNDEF,
407 .partitioning = 0};
408 case 0x25:
409 return (CacheLevelInfo){.level = 3,
410 .cache_type = CPU_FEATURE_CACHE_DATA,
411 .cache_size = 2 * MiB,
412 .ways = 8,
413 .line_size = 64,
414 .tlb_entries = UNDEF,
415 .partitioning = 2};
416 case 0x29:
417 return (CacheLevelInfo){.level = 3,
418 .cache_type = CPU_FEATURE_CACHE_DATA,
419 .cache_size = 4 * MiB,
420 .ways = 8,
421 .line_size = 64,
422 .tlb_entries = UNDEF,
423 .partitioning = 2};
424 case 0x2C:
425 return (CacheLevelInfo){.level = 1,
426 .cache_type = CPU_FEATURE_CACHE_DATA,
427 .cache_size = 32 * KiB,
428 .ways = 8,
429 .line_size = 64,
430 .tlb_entries = UNDEF,
431 .partitioning = 0};
432 case 0x30:
433 return (CacheLevelInfo){.level = 1,
434 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
435 .cache_size = 32 * KiB,
436 .ways = 8,
437 .line_size = 64,
438 .tlb_entries = UNDEF,
439 .partitioning = 0};
440 case 0x40:
441 return (CacheLevelInfo){.level = UNDEF,
442 .cache_type = CPU_FEATURE_CACHE_DATA,
443 .cache_size = UNDEF,
444 .ways = UNDEF,
445 .line_size = UNDEF,
446 .tlb_entries = UNDEF,
447 .partitioning = 0};
448 case 0x41:
449 return (CacheLevelInfo){.level = 2,
450 .cache_type = CPU_FEATURE_CACHE_DATA,
451 .cache_size = 128 * KiB,
452 .ways = 4,
453 .line_size = 32,
454 .tlb_entries = UNDEF,
455 .partitioning = 0};
456 case 0x42:
457 return (CacheLevelInfo){.level = 2,
458 .cache_type = CPU_FEATURE_CACHE_DATA,
459 .cache_size = 256 * KiB,
460 .ways = 4,
461 .line_size = 32,
462 .tlb_entries = UNDEF,
463 .partitioning = 0};
464 case 0x43:
465 return (CacheLevelInfo){.level = 2,
466 .cache_type = CPU_FEATURE_CACHE_DATA,
467 .cache_size = 512 * KiB,
468 .ways = 4,
469 .line_size = 32,
470 .tlb_entries = UNDEF,
471 .partitioning = 0};
472 case 0x44:
473 return (CacheLevelInfo){.level = 2,
474 .cache_type = CPU_FEATURE_CACHE_DATA,
475 .cache_size = 1 * MiB,
476 .ways = 4,
477 .line_size = 32,
478 .tlb_entries = UNDEF,
479 .partitioning = 0};
480 case 0x45:
481 return (CacheLevelInfo){.level = 2,
482 .cache_type = CPU_FEATURE_CACHE_DATA,
483 .cache_size = 2 * MiB,
484 .ways = 4,
485 .line_size = 32,
486 .tlb_entries = UNDEF,
487 .partitioning = 0};
488 case 0x46:
489 return (CacheLevelInfo){.level = 3,
490 .cache_type = CPU_FEATURE_CACHE_DATA,
491 .cache_size = 4 * MiB,
492 .ways = 4,
493 .line_size = 64,
494 .tlb_entries = UNDEF,
495 .partitioning = 0};
496 case 0x47:
497 return (CacheLevelInfo){.level = 3,
498 .cache_type = CPU_FEATURE_CACHE_DATA,
499 .cache_size = 8 * MiB,
500 .ways = 8,
501 .line_size = 64,
502 .tlb_entries = UNDEF,
503 .partitioning = 0};
504 case 0x48:
505 return (CacheLevelInfo){.level = 2,
506 .cache_type = CPU_FEATURE_CACHE_DATA,
507 .cache_size = 3 * MiB,
508 .ways = 12,
509 .line_size = 64,
510 .tlb_entries = UNDEF,
511 .partitioning = 0};
512 case 0x49:
513 return (CacheLevelInfo){.level = 2,
514 .cache_type = CPU_FEATURE_CACHE_DATA,
515 .cache_size = 4 * MiB,
516 .ways = 16,
517 .line_size = 64,
518 .tlb_entries = UNDEF,
519 .partitioning = 0};
520 case (0x49 | (1 << 8)):
521 return (CacheLevelInfo){.level = 3,
522 .cache_type = CPU_FEATURE_CACHE_DATA,
523 .cache_size = 4 * MiB,
524 .ways = 16,
525 .line_size = 64,
526 .tlb_entries = UNDEF,
527 .partitioning = 0};
528 case 0x4A:
529 return (CacheLevelInfo){.level = 3,
530 .cache_type = CPU_FEATURE_CACHE_DATA,
531 .cache_size = 6 * MiB,
532 .ways = 12,
533 .line_size = 64,
534 .tlb_entries = UNDEF,
535 .partitioning = 0};
536 case 0x4B:
537 return (CacheLevelInfo){.level = 3,
538 .cache_type = CPU_FEATURE_CACHE_DATA,
539 .cache_size = 8 * MiB,
540 .ways = 16,
541 .line_size = 64,
542 .tlb_entries = UNDEF,
543 .partitioning = 0};
544 case 0x4C:
545 return (CacheLevelInfo){.level = 3,
546 .cache_type = CPU_FEATURE_CACHE_DATA,
547 .cache_size = 12 * MiB,
548 .ways = 12,
549 .line_size = 64,
550 .tlb_entries = UNDEF,
551 .partitioning = 0};
552 case 0x4D:
553 return (CacheLevelInfo){.level = 3,
554 .cache_type = CPU_FEATURE_CACHE_DATA,
555 .cache_size = 16 * MiB,
556 .ways = 16,
557 .line_size = 64,
558 .tlb_entries = UNDEF,
559 .partitioning = 0};
560 case 0x4E:
561 return (CacheLevelInfo){.level = 2,
562 .cache_type = CPU_FEATURE_CACHE_DATA,
563 .cache_size = 6 * MiB,
564 .ways = 24,
565 .line_size = 64,
566 .tlb_entries = UNDEF,
567 .partitioning = 0};
568 case 0x4F:
569 return (CacheLevelInfo){.level = UNDEF,
570 .cache_type = CPU_FEATURE_CACHE_TLB,
571 .cache_size = 4 * KiB,
572 .ways = UNDEF,
573 .line_size = UNDEF,
574 .tlb_entries = 32,
575 .partitioning = 0};
576 case 0x50:
577 return (CacheLevelInfo){.level = UNDEF,
578 .cache_type = CPU_FEATURE_CACHE_TLB,
579 .cache_size = 4 * KiB,
580 .ways = UNDEF,
581 .line_size = UNDEF,
582 .tlb_entries = 64,
583 .partitioning = 0};
584 case 0x51:
585 return (CacheLevelInfo){.level = UNDEF,
586 .cache_type = CPU_FEATURE_CACHE_TLB,
587 .cache_size = 4 * KiB,
588 .ways = UNDEF,
589 .line_size = UNDEF,
590 .tlb_entries = 128,
591 .partitioning = 0};
592 case 0x52:
593 return (CacheLevelInfo){.level = UNDEF,
594 .cache_type = CPU_FEATURE_CACHE_TLB,
595 .cache_size = 4 * KiB,
596 .ways = UNDEF,
597 .line_size = UNDEF,
598 .tlb_entries = 256,
599 .partitioning = 0};
600 case 0x55:
601 return (CacheLevelInfo){.level = UNDEF,
602 .cache_type = CPU_FEATURE_CACHE_TLB,
603 .cache_size = 2 * MiB,
604 .ways = 0xFF,
605 .line_size = UNDEF,
606 .tlb_entries = 7,
607 .partitioning = 0};
608 case 0x56:
609 return (CacheLevelInfo){.level = UNDEF,
610 .cache_type = CPU_FEATURE_CACHE_TLB,
611 .cache_size = 4 * MiB,
612 .ways = 4,
613 .line_size = UNDEF,
614 .tlb_entries = 16,
615 .partitioning = 0};
616 case 0x57:
617 return (CacheLevelInfo){.level = UNDEF,
618 .cache_type = CPU_FEATURE_CACHE_TLB,
619 .cache_size = 4 * KiB,
620 .ways = 4,
621 .line_size = UNDEF,
622 .tlb_entries = 16,
623 .partitioning = 0};
624 case 0x59:
625 return (CacheLevelInfo){.level = UNDEF,
626 .cache_type = CPU_FEATURE_CACHE_TLB,
627 .cache_size = 4 * KiB,
628 .ways = 0xFF,
629 .line_size = UNDEF,
630 .tlb_entries = 16,
631 .partitioning = 0};
632 case 0x5A:
633 return (CacheLevelInfo){.level = UNDEF,
634 .cache_type = CPU_FEATURE_CACHE_TLB,
635 .cache_size = 2 * MiB,
636 .ways = 4,
637 .line_size = UNDEF,
638 .tlb_entries = 32,
639 .partitioning = 0};
640 case 0x5B:
641 return (CacheLevelInfo){.level = UNDEF,
642 .cache_type = CPU_FEATURE_CACHE_TLB,
643 .cache_size = 4 * KiB,
644 .ways = UNDEF,
645 .line_size = UNDEF,
646 .tlb_entries = 64,
647 .partitioning = 0};
648 case 0x5C:
649 return (CacheLevelInfo){.level = UNDEF,
650 .cache_type = CPU_FEATURE_CACHE_TLB,
651 .cache_size = 4 * KiB,
652 .ways = UNDEF,
653 .line_size = UNDEF,
654 .tlb_entries = 128,
655 .partitioning = 0};
656 case 0x5D:
657 return (CacheLevelInfo){.level = UNDEF,
658 .cache_type = CPU_FEATURE_CACHE_TLB,
659 .cache_size = 4,
660 .ways = UNDEF,
661 .line_size = UNDEF,
662 .tlb_entries = 256,
663 .partitioning = 0};
664 case 0x60:
665 return (CacheLevelInfo){.level = 1,
666 .cache_type = CPU_FEATURE_CACHE_DATA,
667 .cache_size = 16 * KiB,
668 .ways = 8,
669 .line_size = 64,
670 .tlb_entries = UNDEF,
671 .partitioning = 0};
672 case 0x61:
673 return (CacheLevelInfo){.level = UNDEF,
674 .cache_type = CPU_FEATURE_CACHE_TLB,
675 .cache_size = 4 * KiB,
676 .ways = 0xFF,
677 .line_size = UNDEF,
678 .tlb_entries = 48,
679 .partitioning = 0};
680 case 0x63:
681 return (CacheLevelInfo){.level = UNDEF,
682 .cache_type = CPU_FEATURE_CACHE_TLB,
683 .cache_size = 2 * MiB,
684 .ways = 4,
685 .line_size = UNDEF,
686 .tlb_entries = 4,
687 .partitioning = 0};
688 case 0x66:
689 return (CacheLevelInfo){.level = 1,
690 .cache_type = CPU_FEATURE_CACHE_DATA,
691 .cache_size = 8 * KiB,
692 .ways = 4,
693 .line_size = 64,
694 .tlb_entries = UNDEF,
695 .partitioning = 0};
696 case 0x67:
697 return (CacheLevelInfo){.level = 1,
698 .cache_type = CPU_FEATURE_CACHE_DATA,
699 .cache_size = 16 * KiB,
700 .ways = 4,
701 .line_size = 64,
702 .tlb_entries = UNDEF,
703 .partitioning = 0};
704 case 0x68:
705 return (CacheLevelInfo){.level = 1,
706 .cache_type = CPU_FEATURE_CACHE_DATA,
707 .cache_size = 32 * KiB,
708 .ways = 4,
709 .line_size = 64,
710 .tlb_entries = UNDEF,
711 .partitioning = 0};
712 case 0x70:
713 return (CacheLevelInfo){.level = 1,
714 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
715 .cache_size = 12 * KiB,
716 .ways = 8,
717 .line_size = UNDEF,
718 .tlb_entries = UNDEF,
719 .partitioning = 0};
720 case 0x71:
721 return (CacheLevelInfo){.level = 1,
722 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
723 .cache_size = 16 * KiB,
724 .ways = 8,
725 .line_size = UNDEF,
726 .tlb_entries = UNDEF,
727 .partitioning = 0};
728 case 0x72:
729 return (CacheLevelInfo){.level = 1,
730 .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
731 .cache_size = 32 * KiB,
732 .ways = 8,
733 .line_size = UNDEF,
734 .tlb_entries = UNDEF,
735 .partitioning = 0};
736 case 0x76:
737 return (CacheLevelInfo){.level = UNDEF,
738 .cache_type = CPU_FEATURE_CACHE_TLB,
739 .cache_size = 2 * MiB,
740 .ways = 0xFF,
741 .line_size = UNDEF,
742 .tlb_entries = 8,
743 .partitioning = 0};
744 case 0x78:
745 return (CacheLevelInfo){.level = 2,
746 .cache_type = CPU_FEATURE_CACHE_DATA,
747 .cache_size = 1 * MiB,
748 .ways = 4,
749 .line_size = 64,
750 .tlb_entries = UNDEF,
751 .partitioning = 0};
752 case 0x79:
753 return (CacheLevelInfo){.level = 2,
754 .cache_type = CPU_FEATURE_CACHE_DATA,
755 .cache_size = 128 * KiB,
756 .ways = 8,
757 .line_size = 64,
758 .tlb_entries = UNDEF,
759 .partitioning = 2};
760 case 0x7A:
761 return (CacheLevelInfo){.level = 2,
762 .cache_type = CPU_FEATURE_CACHE_DATA,
763 .cache_size = 256 * KiB,
764 .ways = 8,
765 .line_size = 64,
766 .tlb_entries = UNDEF,
767 .partitioning = 2};
768 case 0x7B:
769 return (CacheLevelInfo){.level = 2,
770 .cache_type = CPU_FEATURE_CACHE_DATA,
771 .cache_size = 512 * KiB,
772 .ways = 8,
773 .line_size = 64,
774 .tlb_entries = UNDEF,
775 .partitioning = 2};
776 case 0x7C:
777 return (CacheLevelInfo){.level = 2,
778 .cache_type = CPU_FEATURE_CACHE_DATA,
779 .cache_size = 1 * MiB,
780 .ways = 8,
781 .line_size = 64,
782 .tlb_entries = UNDEF,
783 .partitioning = 2};
784 case 0x7D:
785 return (CacheLevelInfo){.level = 2,
786 .cache_type = CPU_FEATURE_CACHE_DATA,
787 .cache_size = 2 * MiB,
788 .ways = 8,
789 .line_size = 64,
790 .tlb_entries = UNDEF,
791 .partitioning = 0};
792 case 0x7F:
793 return (CacheLevelInfo){.level = 2,
794 .cache_type = CPU_FEATURE_CACHE_DATA,
795 .cache_size = 512 * KiB,
796 .ways = 2,
797 .line_size = 64,
798 .tlb_entries = UNDEF,
799 .partitioning = 0};
800 case 0x80:
801 return (CacheLevelInfo){.level = 2,
802 .cache_type = CPU_FEATURE_CACHE_DATA,
803 .cache_size = 512 * KiB,
804 .ways = 8,
805 .line_size = 64,
806 .tlb_entries = UNDEF,
807 .partitioning = 0};
808 case 0x82:
809 return (CacheLevelInfo){.level = 2,
810 .cache_type = CPU_FEATURE_CACHE_DATA,
811 .cache_size = 256 * KiB,
812 .ways = 8,
813 .line_size = 32,
814 .tlb_entries = UNDEF,
815 .partitioning = 0};
816 case 0x83:
817 return (CacheLevelInfo){.level = 2,
818 .cache_type = CPU_FEATURE_CACHE_DATA,
819 .cache_size = 512 * KiB,
820 .ways = 8,
821 .line_size = 32,
822 .tlb_entries = UNDEF,
823 .partitioning = 0};
824 case 0x84:
825 return (CacheLevelInfo){.level = 2,
826 .cache_type = CPU_FEATURE_CACHE_DATA,
827 .cache_size = 1 * MiB,
828 .ways = 8,
829 .line_size = 32,
830 .tlb_entries = UNDEF,
831 .partitioning = 0};
832 case 0x85:
833 return (CacheLevelInfo){.level = 2,
834 .cache_type = CPU_FEATURE_CACHE_DATA,
835 .cache_size = 2 * MiB,
836 .ways = 8,
837 .line_size = 32,
838 .tlb_entries = UNDEF,
839 .partitioning = 0};
840 case 0x86:
841 return (CacheLevelInfo){.level = 2,
842 .cache_type = CPU_FEATURE_CACHE_DATA,
843 .cache_size = 512 * KiB,
844 .ways = 4,
845 .line_size = 32,
846 .tlb_entries = UNDEF,
847 .partitioning = 0};
848 case 0x87:
849 return (CacheLevelInfo){.level = 2,
850 .cache_type = CPU_FEATURE_CACHE_DATA,
851 .cache_size = 1 * MiB,
852 .ways = 8,
853 .line_size = 64,
854 .tlb_entries = UNDEF,
855 .partitioning = 0};
856 case 0xA0:
857 return (CacheLevelInfo){.level = UNDEF,
858 .cache_type = CPU_FEATURE_CACHE_DTLB,
859 .cache_size = 4 * KiB,
860 .ways = 0xFF,
861 .line_size = UNDEF,
862 .tlb_entries = 32,
863 .partitioning = 0};
864 case 0xB0:
865 return (CacheLevelInfo){.level = UNDEF,
866 .cache_type = CPU_FEATURE_CACHE_TLB,
867 .cache_size = 4 * KiB,
868 .ways = 4,
869 .line_size = UNDEF,
870 .tlb_entries = 128,
871 .partitioning = 0};
872 case 0xB1:
873 return (CacheLevelInfo){.level = UNDEF,
874 .cache_type = CPU_FEATURE_CACHE_TLB,
875 .cache_size = 2 * MiB,
876 .ways = 4,
877 .line_size = UNDEF,
878 .tlb_entries = 8,
879 .partitioning = 0};
880 case 0xB2:
881 return (CacheLevelInfo){.level = UNDEF,
882 .cache_type = CPU_FEATURE_CACHE_TLB,
883 .cache_size = 4 * KiB,
884 .ways = 4,
885 .line_size = UNDEF,
886 .tlb_entries = 64,
887 .partitioning = 0};
888 case 0xB3:
889 return (CacheLevelInfo){.level = UNDEF,
890 .cache_type = CPU_FEATURE_CACHE_TLB,
891 .cache_size = 4 * KiB,
892 .ways = 4,
893 .line_size = UNDEF,
894 .tlb_entries = 128,
895 .partitioning = 0};
896 case 0xB4:
897 return (CacheLevelInfo){.level = UNDEF,
898 .cache_type = CPU_FEATURE_CACHE_TLB,
899 .cache_size = 4 * KiB,
900 .ways = 4,
901 .line_size = UNDEF,
902 .tlb_entries = 256,
903 .partitioning = 0};
904 case 0xB5:
905 return (CacheLevelInfo){.level = UNDEF,
906 .cache_type = CPU_FEATURE_CACHE_TLB,
907 .cache_size = 4 * KiB,
908 .ways = 8,
909 .line_size = UNDEF,
910 .tlb_entries = 64,
911 .partitioning = 0};
912 case 0xB6:
913 return (CacheLevelInfo){.level = UNDEF,
914 .cache_type = CPU_FEATURE_CACHE_TLB,
915 .cache_size = 4 * KiB,
916 .ways = 8,
917 .line_size = UNDEF,
918 .tlb_entries = 128,
919 .partitioning = 0};
920 case 0xBA:
921 return (CacheLevelInfo){.level = UNDEF,
922 .cache_type = CPU_FEATURE_CACHE_TLB,
923 .cache_size = 4 * KiB,
924 .ways = 4,
925 .line_size = UNDEF,
926 .tlb_entries = 64,
927 .partitioning = 0};
928 case 0xC0:
929 return (CacheLevelInfo){.level = UNDEF,
930 .cache_type = CPU_FEATURE_CACHE_TLB,
931 .cache_size = 4 * KiB,
932 .ways = 4,
933 .line_size = UNDEF,
934 .tlb_entries = 8,
935 .partitioning = 0};
936 case 0xC1:
937 return (CacheLevelInfo){.level = UNDEF,
938 .cache_type = CPU_FEATURE_CACHE_STLB,
939 .cache_size = 4 * KiB,
940 .ways = 8,
941 .line_size = UNDEF,
942 .tlb_entries = 1024,
943 .partitioning = 0};
944 case 0xC2:
945 return (CacheLevelInfo){.level = UNDEF,
946 .cache_type = CPU_FEATURE_CACHE_DTLB,
947 .cache_size = 4 * KiB,
948 .ways = 4,
949 .line_size = UNDEF,
950 .tlb_entries = 16,
951 .partitioning = 0};
952 case 0xC3:
953 return (CacheLevelInfo){.level = UNDEF,
954 .cache_type = CPU_FEATURE_CACHE_STLB,
955 .cache_size = 4 * KiB,
956 .ways = 6,
957 .line_size = UNDEF,
958 .tlb_entries = 1536,
959 .partitioning = 0};
960 case 0xCA:
961 return (CacheLevelInfo){.level = UNDEF,
962 .cache_type = CPU_FEATURE_CACHE_STLB,
963 .cache_size = 4 * KiB,
964 .ways = 4,
965 .line_size = UNDEF,
966 .tlb_entries = 512,
967 .partitioning = 0};
968 case 0xD0:
969 return (CacheLevelInfo){.level = 3,
970 .cache_type = CPU_FEATURE_CACHE_DATA,
971 .cache_size = 512 * KiB,
972 .ways = 4,
973 .line_size = 64,
974 .tlb_entries = UNDEF,
975 .partitioning = 0};
976 case 0xD1:
977 return (CacheLevelInfo){.level = 3,
978 .cache_type = CPU_FEATURE_CACHE_DATA,
979 .cache_size = 1 * MiB,
980 .ways = 4,
981 .line_size = 64,
982 .tlb_entries = UNDEF,
983 .partitioning = 0};
984 case 0xD2:
985 return (CacheLevelInfo){.level = 3,
986 .cache_type = CPU_FEATURE_CACHE_DATA,
987 .cache_size = 2 * MiB,
988 .ways = 4,
989 .line_size = 64,
990 .tlb_entries = UNDEF,
991 .partitioning = 0};
992 case 0xD6:
993 return (CacheLevelInfo){.level = 3,
994 .cache_type = CPU_FEATURE_CACHE_DATA,
995 .cache_size = 1 * MiB,
996 .ways = 8,
997 .line_size = 64,
998 .tlb_entries = UNDEF,
999 .partitioning = 0};
1000 case 0xD7:
1001 return (CacheLevelInfo){.level = 3,
1002 .cache_type = CPU_FEATURE_CACHE_DATA,
1003 .cache_size = 2 * MiB,
1004 .ways = 8,
1005 .line_size = 64,
1006 .tlb_entries = UNDEF,
1007 .partitioning = 0};
1008 case 0xD8:
1009 return (CacheLevelInfo){.level = 3,
1010 .cache_type = CPU_FEATURE_CACHE_DATA,
1011 .cache_size = 4 * MiB,
1012 .ways = 8,
1013 .line_size = 64,
1014 .tlb_entries = UNDEF,
1015 .partitioning = 0};
1016 case 0xDC:
1017 return (CacheLevelInfo){.level = 3,
1018 .cache_type = CPU_FEATURE_CACHE_DATA,
1019 .cache_size = 1 * 1536 * KiB,
1020 .ways = 12,
1021 .line_size = 64,
1022 .tlb_entries = UNDEF,
1023 .partitioning = 0};
1024 case 0xDD:
1025 return (CacheLevelInfo){.level = 3,
1026 .cache_type = CPU_FEATURE_CACHE_DATA,
1027 .cache_size = 3 * MiB,
1028 .ways = 12,
1029 .line_size = 64,
1030 .tlb_entries = UNDEF,
1031 .partitioning = 0};
1032 case 0xDE:
1033 return (CacheLevelInfo){.level = 3,
1034 .cache_type = CPU_FEATURE_CACHE_DATA,
1035 .cache_size = 6 * MiB,
1036 .ways = 12,
1037 .line_size = 64,
1038 .tlb_entries = UNDEF,
1039 .partitioning = 0};
1040 case 0xE2:
1041 return (CacheLevelInfo){.level = 3,
1042 .cache_type = CPU_FEATURE_CACHE_DATA,
1043 .cache_size = 2 * MiB,
1044 .ways = 16,
1045 .line_size = 64,
1046 .tlb_entries = UNDEF,
1047 .partitioning = 0};
1048 case 0xE3:
1049 return (CacheLevelInfo){.level = 3,
1050 .cache_type = CPU_FEATURE_CACHE_DATA,
1051 .cache_size = 4 * MiB,
1052 .ways = 16,
1053 .line_size = 64,
1054 .tlb_entries = UNDEF,
1055 .partitioning = 0};
1056 case 0xE4:
1057 return (CacheLevelInfo){.level = 3,
1058 .cache_type = CPU_FEATURE_CACHE_DATA,
1059 .cache_size = 8 * MiB,
1060 .ways = 16,
1061 .line_size = 64,
1062 .tlb_entries = UNDEF,
1063 .partitioning = 0};
1064 case 0xEA:
1065 return (CacheLevelInfo){.level = 3,
1066 .cache_type = CPU_FEATURE_CACHE_DATA,
1067 .cache_size = 12 * MiB,
1068 .ways = 24,
1069 .line_size = 64,
1070 .tlb_entries = UNDEF,
1071 .partitioning = 0};
1072 case 0xEB:
1073 return (CacheLevelInfo){.level = 3,
1074 .cache_type = CPU_FEATURE_CACHE_DATA,
1075 .cache_size = 18 * MiB,
1076 .ways = 24,
1077 .line_size = 64,
1078 .tlb_entries = UNDEF,
1079 .partitioning = 0};
1080 case 0xEC:
1081 return (CacheLevelInfo){.level = 3,
1082 .cache_type = CPU_FEATURE_CACHE_DATA,
1083 .cache_size = 24 * MiB,
1084 .ways = 24,
1085 .line_size = 64,
1086 .tlb_entries = UNDEF,
1087 .partitioning = 0};
1088 case 0xF0:
1089 return (CacheLevelInfo){.level = UNDEF,
1090 .cache_type = CPU_FEATURE_CACHE_PREFETCH,
1091 .cache_size = 64 * KiB,
1092 .ways = UNDEF,
1093 .line_size = UNDEF,
1094 .tlb_entries = UNDEF,
1095 .partitioning = 0};
1096 case 0xF1:
1097 return (CacheLevelInfo){.level = UNDEF,
1098 .cache_type = CPU_FEATURE_CACHE_PREFETCH,
1099 .cache_size = 128 * KiB,
1100 .ways = UNDEF,
1101 .line_size = UNDEF,
1102 .tlb_entries = UNDEF,
1103 .partitioning = 0};
1104 case 0xFF:
1105 return (CacheLevelInfo){.level = UNDEF,
1106 .cache_type = CPU_FEATURE_CACHE_NULL,
1107 .cache_size = UNDEF,
1108 .ways = UNDEF,
1109 .line_size = UNDEF,
1110 .tlb_entries = UNDEF,
1111 .partitioning = 0};
1112 default:
1113 return kEmptyCacheLevelInfo;
1114 }
1115 }
1116
GetByteArrayFromRegister(uint32_t result[4],const uint32_t reg)1117 static void GetByteArrayFromRegister(uint32_t result[4], const uint32_t reg) {
1118 for (int i = 0; i < 4; ++i) {
1119 result[i] = ExtractBitRange(reg, (i + 1) * 8, i * 8);
1120 }
1121 }
1122
ParseLeaf2(const int max_cpuid_leaf,CacheInfo * info)1123 static void ParseLeaf2(const int max_cpuid_leaf, CacheInfo* info) {
1124 Leaf leaf = SafeCpuId(max_cpuid_leaf, 2);
1125 uint32_t registers[] = {leaf.eax, leaf.ebx, leaf.ecx, leaf.edx};
1126 for (int i = 0; i < 4; ++i) {
1127 if (registers[i] & (1U << 31)) {
1128 continue; // register does not contains valid information
1129 }
1130 uint32_t bytes[4];
1131 GetByteArrayFromRegister(bytes, registers[i]);
1132 for (int j = 0; j < 4; ++j) {
1133 if (bytes[j] == 0xFF)
1134 break; // leaf 4 should be used to fetch cache information
1135 info->levels[info->size] = GetCacheLevelInfo(bytes[j]);
1136 }
1137 info->size++;
1138 }
1139 }
1140
ParseLeaf4(const int max_cpuid_leaf,CacheInfo * info)1141 static void ParseLeaf4(const int max_cpuid_leaf, CacheInfo* info) {
1142 info->size = 0;
1143 for (int cache_id = 0; cache_id < CPU_FEATURES_MAX_CACHE_LEVEL; cache_id++) {
1144 const Leaf leaf = SafeCpuIdEx(max_cpuid_leaf, 4, cache_id);
1145 CacheType cache_type = ExtractBitRange(leaf.eax, 4, 0);
1146 if (cache_type == CPU_FEATURE_CACHE_NULL) {
1147 info->levels[cache_id] = kEmptyCacheLevelInfo;
1148 continue;
1149 }
1150 int level = ExtractBitRange(leaf.eax, 7, 5);
1151 int line_size = ExtractBitRange(leaf.ebx, 11, 0) + 1;
1152 int partitioning = ExtractBitRange(leaf.ebx, 21, 12) + 1;
1153 int ways = ExtractBitRange(leaf.ebx, 31, 22) + 1;
1154 int tlb_entries = leaf.ecx + 1;
1155 int cache_size = (ways * partitioning * line_size * (tlb_entries));
1156 info->levels[cache_id] = (CacheLevelInfo){.level = level,
1157 .cache_type = cache_type,
1158 .cache_size = cache_size,
1159 .ways = ways,
1160 .line_size = line_size,
1161 .tlb_entries = tlb_entries,
1162 .partitioning = partitioning};
1163 info->size++;
1164 }
1165 }
1166
1167 // Internal structure to hold the OS support for vector operations.
1168 // Avoid to recompute them since each call to cpuid is ~100 cycles.
1169 typedef struct {
1170 bool have_sse_via_os;
1171 bool have_sse_via_cpuid;
1172 bool have_avx;
1173 bool have_avx512;
1174 bool have_amx;
1175 } OsSupport;
1176
1177 static const OsSupport kEmptyOsSupport;
1178
CheckOsSupport(const uint32_t max_cpuid_leaf)1179 static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) {
1180 const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
1181 const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
1182 const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
1183 const bool have_xcr0 = have_xsave && have_osxsave;
1184
1185 OsSupport os_support = kEmptyOsSupport;
1186
1187 if (have_xcr0) {
1188 // AVX capable cpu will expose XCR0.
1189 const uint32_t xcr0_eax = GetXCR0Eax();
1190 os_support.have_sse_via_cpuid = HasXmmOsXSave(xcr0_eax);
1191 os_support.have_avx = HasYmmOsXSave(xcr0_eax);
1192 os_support.have_avx512 = HasZmmOsXSave(xcr0_eax);
1193 os_support.have_amx = HasTmmOsXSave(xcr0_eax);
1194 } else {
1195 // Atom based or older cpus need to ask the OS for sse support.
1196 os_support.have_sse_via_os = true;
1197 }
1198
1199 return os_support;
1200 }
1201
1202 #if defined(CPU_FEATURES_OS_WINDOWS)
1203 #if defined(CPU_FEATURES_MOCK_CPUID_X86)
1204 extern bool GetWindowsIsProcessorFeaturePresent(DWORD);
1205 #else // CPU_FEATURES_MOCK_CPUID_X86
GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature)1206 static bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
1207 return IsProcessorFeaturePresent(ProcessorFeature);
1208 }
1209 #endif
1210 #endif // CPU_FEATURES_OS_WINDOWS
1211
1212 #if defined(CPU_FEATURES_OS_DARWIN)
1213 #if defined(CPU_FEATURES_MOCK_CPUID_X86)
1214 extern bool GetDarwinSysCtlByName(const char*);
1215 #else // CPU_FEATURES_MOCK_CPUID_X86
GetDarwinSysCtlByName(const char * name)1216 static bool GetDarwinSysCtlByName(const char* name) {
1217 int enabled;
1218 size_t enabled_len = sizeof(enabled);
1219 const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0);
1220 return failure ? false : enabled;
1221 }
1222 #endif
1223 #endif // CPU_FEATURES_OS_DARWIN
1224
DetectSseViaOs(X86Features * features)1225 static void DetectSseViaOs(X86Features* features) {
1226 #if defined(CPU_FEATURES_OS_WINDOWS)
1227 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
1228 features->sse =
1229 GetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
1230 features->sse2 =
1231 GetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
1232 features->sse3 =
1233 GetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
1234 #elif defined(CPU_FEATURES_OS_DARWIN)
1235 // Handling Darwin platform through sysctlbyname.
1236 features->sse = GetDarwinSysCtlByName("hw.optional.sse");
1237 features->sse2 = GetDarwinSysCtlByName("hw.optional.sse2");
1238 features->sse3 = GetDarwinSysCtlByName("hw.optional.sse3");
1239 features->ssse3 = GetDarwinSysCtlByName("hw.optional.supplementalsse3");
1240 features->sse4_1 = GetDarwinSysCtlByName("hw.optional.sse4_1");
1241 features->sse4_2 = GetDarwinSysCtlByName("hw.optional.sse4_2");
1242 #elif defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
1243 // Handling Linux platform through /proc/cpuinfo.
1244 const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
1245 if (fd >= 0) {
1246 StackLineReader reader;
1247 StackLineReader_Initialize(&reader, fd);
1248 for (;;) {
1249 const LineResult result = StackLineReader_NextLine(&reader);
1250 const StringView line = result.line;
1251 StringView key, value;
1252 if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
1253 if (CpuFeatures_StringView_IsEquals(key, str("flags"))) {
1254 features->sse = CpuFeatures_StringView_HasWord(value, "sse");
1255 features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2");
1256 features->sse3 = CpuFeatures_StringView_HasWord(value, "sse3");
1257 features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3");
1258 features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1");
1259 features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2");
1260 break;
1261 }
1262 }
1263 if (result.eof) break;
1264 }
1265 CpuFeatures_CloseFile(fd);
1266 }
1267 #else
1268 #error "Unsupported fallback detection of SSE OS support."
1269 #endif
1270 }
1271
1272 // Reference https://en.wikipedia.org/wiki/CPUID.
ParseCpuId(const uint32_t max_cpuid_leaf,const OsSupport os_support,X86Info * info)1273 static void ParseCpuId(const uint32_t max_cpuid_leaf,
1274 const OsSupport os_support, X86Info* info) {
1275 const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
1276 const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
1277 const Leaf leaf_7_1 = SafeCpuIdEx(max_cpuid_leaf, 7, 1);
1278
1279 const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
1280 const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
1281 const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
1282 const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
1283
1284 X86Features* const features = &info->features;
1285
1286 info->family = extended_family + family;
1287 info->model = (extended_model << 4) + model;
1288 info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
1289
1290 features->fpu = IsBitSet(leaf_1.edx, 0);
1291 features->tsc = IsBitSet(leaf_1.edx, 4);
1292 features->cx8 = IsBitSet(leaf_1.edx, 8);
1293 features->clfsh = IsBitSet(leaf_1.edx, 19);
1294 features->mmx = IsBitSet(leaf_1.edx, 23);
1295 features->ss = IsBitSet(leaf_1.edx, 27);
1296 features->pclmulqdq = IsBitSet(leaf_1.ecx, 1);
1297 features->smx = IsBitSet(leaf_1.ecx, 6);
1298 features->cx16 = IsBitSet(leaf_1.ecx, 13);
1299 features->dca = IsBitSet(leaf_1.ecx, 18);
1300 features->movbe = IsBitSet(leaf_1.ecx, 22);
1301 features->popcnt = IsBitSet(leaf_1.ecx, 23);
1302 features->aes = IsBitSet(leaf_1.ecx, 25);
1303 features->f16c = IsBitSet(leaf_1.ecx, 29);
1304 features->rdrnd = IsBitSet(leaf_1.ecx, 30);
1305 features->sgx = IsBitSet(leaf_7.ebx, 2);
1306 features->bmi1 = IsBitSet(leaf_7.ebx, 3);
1307 features->hle = IsBitSet(leaf_7.ebx, 4);
1308 features->bmi2 = IsBitSet(leaf_7.ebx, 8);
1309 features->erms = IsBitSet(leaf_7.ebx, 9);
1310 features->rtm = IsBitSet(leaf_7.ebx, 11);
1311 features->rdseed = IsBitSet(leaf_7.ebx, 18);
1312 features->clflushopt = IsBitSet(leaf_7.ebx, 23);
1313 features->clwb = IsBitSet(leaf_7.ebx, 24);
1314 features->sha = IsBitSet(leaf_7.ebx, 29);
1315 features->vaes = IsBitSet(leaf_7.ecx, 9);
1316 features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
1317
1318 if (os_support.have_sse_via_os) {
1319 DetectSseViaOs(features);
1320 } else if (os_support.have_sse_via_cpuid) {
1321 features->sse = IsBitSet(leaf_1.edx, 25);
1322 features->sse2 = IsBitSet(leaf_1.edx, 26);
1323 features->sse3 = IsBitSet(leaf_1.ecx, 0);
1324 features->ssse3 = IsBitSet(leaf_1.ecx, 9);
1325 features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
1326 features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
1327 }
1328
1329 if (os_support.have_avx) {
1330 features->fma3 = IsBitSet(leaf_1.ecx, 12);
1331 features->avx = IsBitSet(leaf_1.ecx, 28);
1332 features->avx2 = IsBitSet(leaf_7.ebx, 5);
1333 }
1334
1335 if (os_support.have_avx512) {
1336 features->avx512f = IsBitSet(leaf_7.ebx, 16);
1337 features->avx512cd = IsBitSet(leaf_7.ebx, 28);
1338 features->avx512er = IsBitSet(leaf_7.ebx, 27);
1339 features->avx512pf = IsBitSet(leaf_7.ebx, 26);
1340 features->avx512bw = IsBitSet(leaf_7.ebx, 30);
1341 features->avx512dq = IsBitSet(leaf_7.ebx, 17);
1342 features->avx512vl = IsBitSet(leaf_7.ebx, 31);
1343 features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
1344 features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
1345 features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
1346 features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
1347 features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
1348 features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
1349 features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
1350 features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
1351 features->avx512_second_fma = HasSecondFMA(info->model);
1352 features->avx512_4fmaps = IsBitSet(leaf_7.edx, 3);
1353 features->avx512_bf16 = IsBitSet(leaf_7_1.eax, 5);
1354 features->avx512_vp2intersect = IsBitSet(leaf_7.edx, 8);
1355 }
1356
1357 if (os_support.have_amx) {
1358 features->amx_bf16 = IsBitSet(leaf_7.edx, 22);
1359 features->amx_tile = IsBitSet(leaf_7.edx, 24);
1360 features->amx_int8 = IsBitSet(leaf_7.edx, 25);
1361 }
1362 }
1363
1364 // Reference
1365 // https://en.wikipedia.org/wiki/CPUID#EAX=80000000h:_Get_Highest_Extended_Function_Implemented.
ParseExtraAMDCpuId(X86Info * info,OsSupport os_support)1366 static void ParseExtraAMDCpuId(X86Info* info, OsSupport os_support) {
1367 const Leaf leaf_80000000 = CpuId(0x80000000);
1368 const uint32_t max_extended_cpuid_leaf = leaf_80000000.eax;
1369 const Leaf leaf_80000001 = SafeCpuId(max_extended_cpuid_leaf, 0x80000001);
1370
1371 X86Features* const features = &info->features;
1372
1373 if (os_support.have_sse_via_cpuid) {
1374 features->sse4a = IsBitSet(leaf_80000001.ecx, 6);
1375 }
1376
1377 if (os_support.have_avx) {
1378 features->fma4 = IsBitSet(leaf_80000001.ecx, 16);
1379 }
1380 }
1381
1382 static const X86Info kEmptyX86Info;
1383 static const CacheInfo kEmptyCacheInfo;
1384
GetX86Info(void)1385 X86Info GetX86Info(void) {
1386 X86Info info = kEmptyX86Info;
1387 const Leaf leaf_0 = CpuId(0);
1388 const bool is_intel = IsVendor(leaf_0, "GenuineIntel");
1389 const bool is_amd = IsVendor(leaf_0, "AuthenticAMD");
1390 SetVendor(leaf_0, info.vendor);
1391 if (is_intel || is_amd) {
1392 const uint32_t max_cpuid_leaf = leaf_0.eax;
1393 const OsSupport os_support = CheckOsSupport(max_cpuid_leaf);
1394 ParseCpuId(max_cpuid_leaf, os_support, &info);
1395 if (is_amd) {
1396 ParseExtraAMDCpuId(&info, os_support);
1397 }
1398 }
1399 return info;
1400 }
1401
GetX86CacheInfo(void)1402 CacheInfo GetX86CacheInfo(void) {
1403 CacheInfo info = kEmptyCacheInfo;
1404 const Leaf leaf_0 = CpuId(0);
1405 const uint32_t max_cpuid_leaf = leaf_0.eax;
1406 if (IsVendor(leaf_0, "GenuineIntel")) {
1407 ParseLeaf2(max_cpuid_leaf, &info);
1408 ParseLeaf4(max_cpuid_leaf, &info);
1409 }
1410 return info;
1411 }
1412
1413 #define CPUID(FAMILY, MODEL) ((((FAMILY)&0xFF) << 8) | ((MODEL)&0xFF))
1414
GetX86Microarchitecture(const X86Info * info)1415 X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
1416 if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
1417 switch (CPUID(info->family, info->model)) {
1418 case CPUID(0x06, 0x35):
1419 case CPUID(0x06, 0x36):
1420 // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
1421 return INTEL_ATOM_BNL;
1422 case CPUID(0x06, 0x37):
1423 case CPUID(0x06, 0x4C):
1424 // https://en.wikipedia.org/wiki/Silvermont
1425 return INTEL_ATOM_SMT;
1426 case CPUID(0x06, 0x5C):
1427 // https://en.wikipedia.org/wiki/Goldmont
1428 return INTEL_ATOM_GMT;
1429 case CPUID(0x06, 0x0F):
1430 case CPUID(0x06, 0x16):
1431 // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
1432 return INTEL_CORE;
1433 case CPUID(0x06, 0x17):
1434 case CPUID(0x06, 0x1D):
1435 // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
1436 return INTEL_PNR;
1437 case CPUID(0x06, 0x1A):
1438 case CPUID(0x06, 0x1E):
1439 case CPUID(0x06, 0x1F):
1440 case CPUID(0x06, 0x2E):
1441 // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
1442 return INTEL_NHM;
1443 case CPUID(0x06, 0x25):
1444 case CPUID(0x06, 0x2C):
1445 case CPUID(0x06, 0x2F):
1446 // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
1447 return INTEL_WSM;
1448 case CPUID(0x06, 0x2A):
1449 case CPUID(0x06, 0x2D):
1450 // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
1451 return INTEL_SNB;
1452 case CPUID(0x06, 0x3A):
1453 case CPUID(0x06, 0x3E):
1454 // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
1455 return INTEL_IVB;
1456 case CPUID(0x06, 0x3C):
1457 case CPUID(0x06, 0x3F):
1458 case CPUID(0x06, 0x45):
1459 case CPUID(0x06, 0x46):
1460 // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
1461 return INTEL_HSW;
1462 case CPUID(0x06, 0x3D):
1463 case CPUID(0x06, 0x47):
1464 case CPUID(0x06, 0x4F):
1465 case CPUID(0x06, 0x56):
1466 // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
1467 return INTEL_BDW;
1468 case CPUID(0x06, 0x4E):
1469 case CPUID(0x06, 0x55):
1470 case CPUID(0x06, 0x5E):
1471 // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
1472 return INTEL_SKL;
1473 case CPUID(0x06, 0x66):
1474 // https://en.wikipedia.org/wiki/Cannon_Lake_(microarchitecture)
1475 return INTEL_CNL;
1476 case CPUID(0x06, 0x7D): // client
1477 case CPUID(0x06, 0x7E): // client
1478 case CPUID(0x06, 0x9D): // NNP-I
1479 case CPUID(0x06, 0x6A): // server
1480 case CPUID(0x06, 0x6C): // server
1481 // https://en.wikipedia.org/wiki/Ice_Lake_(microprocessor)
1482 return INTEL_ICL;
1483 case CPUID(0x06, 0x8C):
1484 case CPUID(0x06, 0x8D):
1485 // https://en.wikipedia.org/wiki/Tiger_Lake_(microarchitecture)
1486 return INTEL_TGL;
1487 case CPUID(0x06, 0x8F):
1488 // https://en.wikipedia.org/wiki/Sapphire_Rapids
1489 return INTEL_SPR;
1490 case CPUID(0x06, 0x8E):
1491 switch (info->stepping) {
1492 case 9:
1493 return INTEL_KBL; // https://en.wikipedia.org/wiki/Kaby_Lake
1494 case 10:
1495 return INTEL_CFL; // https://en.wikipedia.org/wiki/Coffee_Lake
1496 case 11:
1497 return INTEL_WHL; // https://en.wikipedia.org/wiki/Whiskey_Lake_(microarchitecture)
1498 default:
1499 return X86_UNKNOWN;
1500 }
1501 case CPUID(0x06, 0x9E):
1502 if (info->stepping > 9) {
1503 // https://en.wikipedia.org/wiki/Coffee_Lake
1504 return INTEL_CFL;
1505 } else {
1506 // https://en.wikipedia.org/wiki/Kaby_Lake
1507 return INTEL_KBL;
1508 }
1509 default:
1510 return X86_UNKNOWN;
1511 }
1512 }
1513 if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
1514 switch (info->family) {
1515 // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
1516 case 0x0F:
1517 return AMD_HAMMER;
1518 case 0x10:
1519 return AMD_K10;
1520 case 0x14:
1521 return AMD_BOBCAT;
1522 case 0x15:
1523 return AMD_BULLDOZER;
1524 case 0x16:
1525 return AMD_JAGUAR;
1526 case 0x17:
1527 return AMD_ZEN;
1528 default:
1529 return X86_UNKNOWN;
1530 }
1531 }
1532 return X86_UNKNOWN;
1533 }
1534
SetString(const uint32_t max_cpuid_ext_leaf,const uint32_t leaf_id,char * buffer)1535 static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
1536 char* buffer) {
1537 const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
1538 // We allow calling memcpy from SetString which is only called when requesting
1539 // X86BrandString.
1540 memcpy(buffer, &leaf, sizeof(Leaf));
1541 }
1542
FillX86BrandString(char brand_string[49])1543 void FillX86BrandString(char brand_string[49]) {
1544 const Leaf leaf_ext_0 = CpuId(0x80000000);
1545 const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
1546 SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
1547 SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
1548 SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
1549 brand_string[48] = '\0';
1550 }
1551
1552 ////////////////////////////////////////////////////////////////////////////////
1553 // Introspection functions
1554
GetX86FeaturesEnumValue(const X86Features * features,X86FeaturesEnum value)1555 int GetX86FeaturesEnumValue(const X86Features* features,
1556 X86FeaturesEnum value) {
1557 if (value >= X86_LAST_) return false;
1558 return kGetters[value](features);
1559 }
1560
GetX86FeaturesEnumName(X86FeaturesEnum value)1561 const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
1562 if (value >= X86_LAST_) return "unknown_feature";
1563 return kCpuInfoFlags[value];
1564 }
1565
GetX86MicroarchitectureName(X86Microarchitecture uarch)1566 const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
1567 switch (uarch) {
1568 case X86_UNKNOWN:
1569 return "X86_UNKNOWN";
1570 case INTEL_CORE:
1571 return "INTEL_CORE";
1572 case INTEL_PNR:
1573 return "INTEL_PNR";
1574 case INTEL_NHM:
1575 return "INTEL_NHM";
1576 case INTEL_ATOM_BNL:
1577 return "INTEL_ATOM_BNL";
1578 case INTEL_WSM:
1579 return "INTEL_WSM";
1580 case INTEL_SNB:
1581 return "INTEL_SNB";
1582 case INTEL_IVB:
1583 return "INTEL_IVB";
1584 case INTEL_ATOM_SMT:
1585 return "INTEL_ATOM_SMT";
1586 case INTEL_HSW:
1587 return "INTEL_HSW";
1588 case INTEL_BDW:
1589 return "INTEL_BDW";
1590 case INTEL_SKL:
1591 return "INTEL_SKL";
1592 case INTEL_ATOM_GMT:
1593 return "INTEL_ATOM_GMT";
1594 case INTEL_KBL:
1595 return "INTEL_KBL";
1596 case INTEL_CFL:
1597 return "INTEL_CFL";
1598 case INTEL_WHL:
1599 return "INTEL_WHL";
1600 case INTEL_CNL:
1601 return "INTEL_CNL";
1602 case INTEL_ICL:
1603 return "INTEL_ICL";
1604 case INTEL_TGL:
1605 return "INTEL_TGL";
1606 case INTEL_SPR:
1607 return "INTEL_SPR";
1608 case AMD_HAMMER:
1609 return "AMD_HAMMER";
1610 case AMD_K10:
1611 return "AMD_K10";
1612 case AMD_BOBCAT:
1613 return "AMD_BOBCAT";
1614 case AMD_BULLDOZER:
1615 return "AMD_BULLDOZER";
1616 case AMD_JAGUAR:
1617 return "AMD_JAGUAR";
1618 case AMD_ZEN:
1619 return "AMD_ZEN";
1620 }
1621 return "unknown microarchitecture";
1622 }
1623