1 //===-- cpu_model.c - Support for __cpu_model builtin  ------------*- C -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file is based on LLVM's lib/Support/Host.cpp.
11 //  It implements the operating system Host concept and builtin
12 //  __cpu_model for the compiler_rt library, for x86 only.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #if (defined(__i386__) || defined(_M_IX86) || \
17      defined(__x86_64__) || defined(_M_X64)) && \
18     (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))
19 
20 #include <assert.h>
21 
22 #define bool int
23 #define true 1
24 #define false 0
25 
26 #ifdef _MSC_VER
27 #include <intrin.h>
28 #endif
29 
30 #ifndef __has_attribute
31 #define __has_attribute(attr) 0
32 #endif
33 
34 enum VendorSignatures {
35   SIG_INTEL = 0x756e6547 /* Genu */,
36   SIG_AMD = 0x68747541 /* Auth */
37 };
38 
39 enum ProcessorVendors {
40   VENDOR_INTEL = 1,
41   VENDOR_AMD,
42   VENDOR_OTHER,
43   VENDOR_MAX
44 };
45 
46 enum ProcessorTypes {
47   INTEL_BONNELL = 1,
48   INTEL_CORE2,
49   INTEL_COREI7,
50   AMDFAM10H,
51   AMDFAM15H,
52   INTEL_SILVERMONT,
53   INTEL_KNL,
54   AMD_BTVER1,
55   AMD_BTVER2,
56   AMDFAM17H,
57   INTEL_KNM,
58   CPU_TYPE_MAX
59 };
60 
61 enum ProcessorSubtypes {
62   INTEL_COREI7_NEHALEM = 1,
63   INTEL_COREI7_WESTMERE,
64   INTEL_COREI7_SANDYBRIDGE,
65   AMDFAM10H_BARCELONA,
66   AMDFAM10H_SHANGHAI,
67   AMDFAM10H_ISTANBUL,
68   AMDFAM15H_BDVER1,
69   AMDFAM15H_BDVER2,
70   AMDFAM15H_BDVER3,
71   AMDFAM15H_BDVER4,
72   AMDFAM17H_ZNVER1,
73   INTEL_COREI7_IVYBRIDGE,
74   INTEL_COREI7_HASWELL,
75   INTEL_COREI7_BROADWELL,
76   INTEL_COREI7_SKYLAKE,
77   INTEL_COREI7_SKYLAKE_AVX512,
78   CPU_SUBTYPE_MAX
79 };
80 
81 enum ProcessorFeatures {
82   FEATURE_CMOV = 0,
83   FEATURE_MMX,
84   FEATURE_POPCNT,
85   FEATURE_SSE,
86   FEATURE_SSE2,
87   FEATURE_SSE3,
88   FEATURE_SSSE3,
89   FEATURE_SSE4_1,
90   FEATURE_SSE4_2,
91   FEATURE_AVX,
92   FEATURE_AVX2,
93   FEATURE_SSE4_A,
94   FEATURE_FMA4,
95   FEATURE_XOP,
96   FEATURE_FMA,
97   FEATURE_AVX512F,
98   FEATURE_BMI,
99   FEATURE_BMI2,
100   FEATURE_AES,
101   FEATURE_PCLMUL,
102   FEATURE_AVX512VL,
103   FEATURE_AVX512BW,
104   FEATURE_AVX512DQ,
105   FEATURE_AVX512CD,
106   FEATURE_AVX512ER,
107   FEATURE_AVX512PF,
108   FEATURE_AVX512VBMI,
109   FEATURE_AVX512IFMA,
110   FEATURE_AVX5124VNNIW,
111   FEATURE_AVX5124FMAPS,
112   FEATURE_AVX512VPOPCNTDQ
113 };
114 
115 // The check below for i386 was copied from clang's cpuid.h (__get_cpuid_max).
116 // Check motivated by bug reports for OpenSSL crashing on CPUs without CPUID
117 // support. Consequently, for i386, the presence of CPUID is checked first
118 // via the corresponding eflags bit.
119 static bool isCpuIdSupported() {
120 #if defined(__GNUC__) || defined(__clang__)
121 #if defined(__i386__)
122   int __cpuid_supported;
123   __asm__("  pushfl\n"
124           "  popl   %%eax\n"
125           "  movl   %%eax,%%ecx\n"
126           "  xorl   $0x00200000,%%eax\n"
127           "  pushl  %%eax\n"
128           "  popfl\n"
129           "  pushfl\n"
130           "  popl   %%eax\n"
131           "  movl   $0,%0\n"
132           "  cmpl   %%eax,%%ecx\n"
133           "  je     1f\n"
134           "  movl   $1,%0\n"
135           "1:"
136           : "=r"(__cpuid_supported)
137           :
138           : "eax", "ecx");
139   if (!__cpuid_supported)
140     return false;
141 #endif
142   return true;
143 #endif
144   return true;
145 }
146 
147 // This code is copied from lib/Support/Host.cpp.
148 // Changes to either file should be mirrored in the other.
149 
150 /// getX86CpuIDAndInfo - Execute the specified cpuid and return the 4 values in
151 /// the specified arguments.  If we can't run cpuid on the host, return true.
152 static bool getX86CpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
153                                unsigned *rECX, unsigned *rEDX) {
154 #if defined(__GNUC__) || defined(__clang__)
155 #if defined(__x86_64__)
156   // gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
157   // FIXME: should we save this for Clang?
158   __asm__("movq\t%%rbx, %%rsi\n\t"
159           "cpuid\n\t"
160           "xchgq\t%%rbx, %%rsi\n\t"
161           : "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
162           : "a"(value));
163   return false;
164 #elif defined(__i386__)
165   __asm__("movl\t%%ebx, %%esi\n\t"
166           "cpuid\n\t"
167           "xchgl\t%%ebx, %%esi\n\t"
168           : "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
169           : "a"(value));
170   return false;
171 #else
172   return true;
173 #endif
174 #elif defined(_MSC_VER)
175   // The MSVC intrinsic is portable across x86 and x64.
176   int registers[4];
177   __cpuid(registers, value);
178   *rEAX = registers[0];
179   *rEBX = registers[1];
180   *rECX = registers[2];
181   *rEDX = registers[3];
182   return false;
183 #else
184   return true;
185 #endif
186 }
187 
188 /// getX86CpuIDAndInfoEx - Execute the specified cpuid with subleaf and return
189 /// the 4 values in the specified arguments.  If we can't run cpuid on the host,
190 /// return true.
191 static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
192                                  unsigned *rEAX, unsigned *rEBX, unsigned *rECX,
193                                  unsigned *rEDX) {
194 #if defined(__GNUC__) || defined(__clang__)
195 #if defined(__x86_64__)
196   // gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
197   // FIXME: should we save this for Clang?
198   __asm__("movq\t%%rbx, %%rsi\n\t"
199           "cpuid\n\t"
200           "xchgq\t%%rbx, %%rsi\n\t"
201           : "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
202           : "a"(value), "c"(subleaf));
203   return false;
204 #elif defined(__i386__)
205   __asm__("movl\t%%ebx, %%esi\n\t"
206           "cpuid\n\t"
207           "xchgl\t%%ebx, %%esi\n\t"
208           : "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
209           : "a"(value), "c"(subleaf));
210   return false;
211 #else
212   return true;
213 #endif
214 #elif defined(_MSC_VER)
215   int registers[4];
216   __cpuidex(registers, value, subleaf);
217   *rEAX = registers[0];
218   *rEBX = registers[1];
219   *rECX = registers[2];
220   *rEDX = registers[3];
221   return false;
222 #else
223   return true;
224 #endif
225 }
226 
227 // Read control register 0 (XCR0). Used to detect features such as AVX.
228 static bool getX86XCR0(unsigned *rEAX, unsigned *rEDX) {
229 #if defined(__GNUC__) || defined(__clang__)
230   // Check xgetbv; this uses a .byte sequence instead of the instruction
231   // directly because older assemblers do not include support for xgetbv and
232   // there is no easy way to conditionally compile based on the assembler used.
233   __asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(*rEAX), "=d"(*rEDX) : "c"(0));
234   return false;
235 #elif defined(_MSC_FULL_VER) && defined(_XCR_XFEATURE_ENABLED_MASK)
236   unsigned long long Result = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
237   *rEAX = Result;
238   *rEDX = Result >> 32;
239   return false;
240 #else
241   return true;
242 #endif
243 }
244 
245 static void detectX86FamilyModel(unsigned EAX, unsigned *Family,
246                                  unsigned *Model) {
247   *Family = (EAX >> 8) & 0xf; // Bits 8 - 11
248   *Model = (EAX >> 4) & 0xf;  // Bits 4 - 7
249   if (*Family == 6 || *Family == 0xf) {
250     if (*Family == 0xf)
251       // Examine extended family ID if family ID is F.
252       *Family += (EAX >> 20) & 0xff; // Bits 20 - 27
253     // Examine extended model ID if family ID is 6 or F.
254     *Model += ((EAX >> 16) & 0xf) << 4; // Bits 16 - 19
255   }
256 }
257 
258 static void
259 getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model,
260                                 unsigned Brand_id, unsigned Features,
261                                 unsigned *Type, unsigned *Subtype) {
262   if (Brand_id != 0)
263     return;
264   switch (Family) {
265   case 6:
266     switch (Model) {
267     case 0x0f: // Intel Core 2 Duo processor, Intel Core 2 Duo mobile
268                // processor, Intel Core 2 Quad processor, Intel Core 2 Quad
269                // mobile processor, Intel Core 2 Extreme processor, Intel
270                // Pentium Dual-Core processor, Intel Xeon processor, model
271                // 0Fh. All processors are manufactured using the 65 nm process.
272     case 0x16: // Intel Celeron processor model 16h. All processors are
273                // manufactured using the 65 nm process
274     case 0x17: // Intel Core 2 Extreme processor, Intel Xeon processor, model
275                // 17h. All processors are manufactured using the 45 nm process.
276                //
277                // 45nm: Penryn , Wolfdale, Yorkfield (XE)
278     case 0x1d: // Intel Xeon processor MP. All processors are manufactured using
279                // the 45 nm process.
280       *Type = INTEL_CORE2; // "penryn"
281       break;
282     case 0x1a: // Intel Core i7 processor and Intel Xeon processor. All
283                // processors are manufactured using the 45 nm process.
284     case 0x1e: // Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz.
285                // As found in a Summer 2010 model iMac.
286     case 0x1f:
287     case 0x2e:             // Nehalem EX
288       *Type = INTEL_COREI7; // "nehalem"
289       *Subtype = INTEL_COREI7_NEHALEM;
290       break;
291     case 0x25: // Intel Core i7, laptop version.
292     case 0x2c: // Intel Core i7 processor and Intel Xeon processor. All
293                // processors are manufactured using the 32 nm process.
294     case 0x2f: // Westmere EX
295       *Type = INTEL_COREI7; // "westmere"
296       *Subtype = INTEL_COREI7_WESTMERE;
297       break;
298     case 0x2a: // Intel Core i7 processor. All processors are manufactured
299                // using the 32 nm process.
300     case 0x2d:
301       *Type = INTEL_COREI7; //"sandybridge"
302       *Subtype = INTEL_COREI7_SANDYBRIDGE;
303       break;
304     case 0x3a:
305     case 0x3e:             // Ivy Bridge EP
306       *Type = INTEL_COREI7; // "ivybridge"
307       *Subtype = INTEL_COREI7_IVYBRIDGE;
308       break;
309 
310     // Haswell:
311     case 0x3c:
312     case 0x3f:
313     case 0x45:
314     case 0x46:
315       *Type = INTEL_COREI7; // "haswell"
316       *Subtype = INTEL_COREI7_HASWELL;
317       break;
318 
319     // Broadwell:
320     case 0x3d:
321     case 0x47:
322     case 0x4f:
323     case 0x56:
324       *Type = INTEL_COREI7; // "broadwell"
325       *Subtype = INTEL_COREI7_BROADWELL;
326       break;
327 
328     // Skylake:
329     case 0x4e: // Skylake mobile
330     case 0x5e: // Skylake desktop
331     case 0x8e: // Kaby Lake mobile
332     case 0x9e: // Kaby Lake desktop
333       *Type = INTEL_COREI7; // "skylake"
334       *Subtype = INTEL_COREI7_SKYLAKE;
335       break;
336 
337     // Skylake Xeon:
338     case 0x55:
339       *Type = INTEL_COREI7;
340       *Subtype = INTEL_COREI7_SKYLAKE_AVX512; // "skylake-avx512"
341       break;
342 
343     case 0x1c: // Most 45 nm Intel Atom processors
344     case 0x26: // 45 nm Atom Lincroft
345     case 0x27: // 32 nm Atom Medfield
346     case 0x35: // 32 nm Atom Midview
347     case 0x36: // 32 nm Atom Midview
348       *Type = INTEL_BONNELL;
349       break; // "bonnell"
350 
351     // Atom Silvermont codes from the Intel software optimization guide.
352     case 0x37:
353     case 0x4a:
354     case 0x4d:
355     case 0x5a:
356     case 0x5d:
357     case 0x4c: // really airmont
358       *Type = INTEL_SILVERMONT;
359       break; // "silvermont"
360 
361     case 0x57:
362       *Type = INTEL_KNL; // knl
363       break;
364 
365     case 0x85:
366       *Type = INTEL_KNM; // knm
367       break;
368 
369     default: // Unknown family 6 CPU.
370       break;
371     break;
372     }
373   default:
374     break; // Unknown.
375   }
376 }
377 
378 static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model,
379                                           unsigned Features, unsigned *Type,
380                                           unsigned *Subtype) {
381   // FIXME: this poorly matches the generated SubtargetFeatureKV table.  There
382   // appears to be no way to generate the wide variety of AMD-specific targets
383   // from the information returned from CPUID.
384   switch (Family) {
385   case 16:
386     *Type = AMDFAM10H; // "amdfam10"
387     switch (Model) {
388     case 2:
389       *Subtype = AMDFAM10H_BARCELONA;
390       break;
391     case 4:
392       *Subtype = AMDFAM10H_SHANGHAI;
393       break;
394     case 8:
395       *Subtype = AMDFAM10H_ISTANBUL;
396       break;
397     }
398     break;
399   case 20:
400     *Type = AMD_BTVER1;
401     break; // "btver1";
402   case 21:
403     *Type = AMDFAM15H;
404     if (Model >= 0x60 && Model <= 0x7f) {
405       *Subtype = AMDFAM15H_BDVER4;
406       break; // "bdver4"; 60h-7Fh: Excavator
407     }
408     if (Model >= 0x30 && Model <= 0x3f) {
409       *Subtype = AMDFAM15H_BDVER3;
410       break; // "bdver3"; 30h-3Fh: Steamroller
411     }
412     if (Model >= 0x10 && Model <= 0x1f) {
413       *Subtype = AMDFAM15H_BDVER2;
414       break; // "bdver2"; 10h-1Fh: Piledriver
415     }
416     if (Model <= 0x0f) {
417       *Subtype = AMDFAM15H_BDVER1;
418       break; // "bdver1"; 00h-0Fh: Bulldozer
419     }
420     break;
421   case 22:
422     *Type = AMD_BTVER2;
423     break; // "btver2"
424   case 23:
425     *Type = AMDFAM17H;
426     *Subtype = AMDFAM17H_ZNVER1;
427     break;
428   default:
429     break; // "generic"
430   }
431 }
432 
433 static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
434                                  unsigned *FeaturesOut) {
435   unsigned Features = 0;
436   unsigned EAX, EBX;
437 
438   if ((EDX >> 15) & 1)
439     Features |= 1 << FEATURE_CMOV;
440   if ((EDX >> 23) & 1)
441     Features |= 1 << FEATURE_MMX;
442   if ((EDX >> 25) & 1)
443     Features |= 1 << FEATURE_SSE;
444   if ((EDX >> 26) & 1)
445     Features |= 1 << FEATURE_SSE2;
446 
447   if ((ECX >> 0) & 1)
448     Features |= 1 << FEATURE_SSE3;
449   if ((ECX >> 1) & 1)
450     Features |= 1 << FEATURE_PCLMUL;
451   if ((ECX >> 9) & 1)
452     Features |= 1 << FEATURE_SSSE3;
453   if ((ECX >> 12) & 1)
454     Features |= 1 << FEATURE_FMA;
455   if ((ECX >> 19) & 1)
456     Features |= 1 << FEATURE_SSE4_1;
457   if ((ECX >> 20) & 1)
458     Features |= 1 << FEATURE_SSE4_2;
459   if ((ECX >> 23) & 1)
460     Features |= 1 << FEATURE_POPCNT;
461   if ((ECX >> 25) & 1)
462     Features |= 1 << FEATURE_AES;
463 
464   // If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV
465   // indicates that the AVX registers will be saved and restored on context
466   // switch, then we have full AVX support.
467   const unsigned AVXBits = (1 << 27) | (1 << 28);
468   bool HasAVX = ((ECX & AVXBits) == AVXBits) && !getX86XCR0(&EAX, &EDX) &&
469                 ((EAX & 0x6) == 0x6);
470   bool HasAVX512Save = HasAVX && ((EAX & 0xe0) == 0xe0);
471 
472   if (HasAVX)
473     Features |= 1 << FEATURE_AVX;
474 
475   bool HasLeaf7 =
476       MaxLeaf >= 0x7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX);
477 
478   if (HasLeaf7 && ((EBX >> 3) & 1))
479     Features |= 1 << FEATURE_BMI;
480   if (HasLeaf7 && ((EBX >> 5) & 1) && HasAVX)
481     Features |= 1 << FEATURE_AVX2;
482   if (HasLeaf7 && ((EBX >> 9) & 1))
483     Features |= 1 << FEATURE_BMI2;
484   if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save)
485     Features |= 1 << FEATURE_AVX512F;
486   if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save)
487     Features |= 1 << FEATURE_AVX512DQ;
488   if (HasLeaf7 && ((EBX >> 21) & 1) && HasAVX512Save)
489     Features |= 1 << FEATURE_AVX512IFMA;
490   if (HasLeaf7 && ((EBX >> 26) & 1) && HasAVX512Save)
491     Features |= 1 << FEATURE_AVX512PF;
492   if (HasLeaf7 && ((EBX >> 27) & 1) && HasAVX512Save)
493     Features |= 1 << FEATURE_AVX512ER;
494   if (HasLeaf7 && ((EBX >> 28) & 1) && HasAVX512Save)
495     Features |= 1 << FEATURE_AVX512CD;
496   if (HasLeaf7 && ((EBX >> 30) & 1) && HasAVX512Save)
497     Features |= 1 << FEATURE_AVX512BW;
498   if (HasLeaf7 && ((EBX >> 31) & 1) && HasAVX512Save)
499     Features |= 1 << FEATURE_AVX512VL;
500 
501   if (HasLeaf7 && ((ECX >> 1) & 1) && HasAVX512Save)
502     Features |= 1 << FEATURE_AVX512VBMI;
503   if (HasLeaf7 && ((ECX >> 14) & 1) && HasAVX512Save)
504     Features |= 1 << FEATURE_AVX512VPOPCNTDQ;
505 
506   if (HasLeaf7 && ((EDX >> 2) & 1) && HasAVX512Save)
507     Features |= 1 << FEATURE_AVX5124VNNIW;
508   if (HasLeaf7 && ((EDX >> 3) & 1) && HasAVX512Save)
509     Features |= 1 << FEATURE_AVX5124FMAPS;
510 
511   unsigned MaxExtLevel;
512   getX86CpuIDAndInfo(0x80000000, &MaxExtLevel, &EBX, &ECX, &EDX);
513 
514   bool HasExtLeaf1 = MaxExtLevel >= 0x80000001 &&
515                      !getX86CpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
516   if (HasExtLeaf1 && ((ECX >> 6) & 1))
517     Features |= 1 << FEATURE_SSE4_A;
518   if (HasExtLeaf1 && ((ECX >> 11) & 1))
519     Features |= 1 << FEATURE_XOP;
520   if (HasExtLeaf1 && ((ECX >> 16) & 1))
521     Features |= 1 << FEATURE_FMA4;
522 
523   *FeaturesOut = Features;
524 }
525 
526 #if defined(HAVE_INIT_PRIORITY)
527 #define CONSTRUCTOR_ATTRIBUTE __attribute__((__constructor__ 101))
528 #elif __has_attribute(__constructor__)
529 #define CONSTRUCTOR_ATTRIBUTE __attribute__((__constructor__))
530 #else
531 // FIXME: For MSVC, we should make a function pointer global in .CRT$X?? so that
532 // this runs during initialization.
533 #define CONSTRUCTOR_ATTRIBUTE
534 #endif
535 
536 int __cpu_indicator_init(void) CONSTRUCTOR_ATTRIBUTE;
537 
538 struct __processor_model {
539   unsigned int __cpu_vendor;
540   unsigned int __cpu_type;
541   unsigned int __cpu_subtype;
542   unsigned int __cpu_features[1];
543 } __cpu_model = {0, 0, 0, {0}};
544 
545 /* A constructor function that is sets __cpu_model and __cpu_features with
546    the right values.  This needs to run only once.  This constructor is
547    given the highest priority and it should run before constructors without
548    the priority set.  However, it still runs after ifunc initializers and
549    needs to be called explicitly there.  */
550 
551 int CONSTRUCTOR_ATTRIBUTE
552 __cpu_indicator_init(void) {
553   unsigned EAX, EBX, ECX, EDX;
554   unsigned MaxLeaf = 5;
555   unsigned Vendor;
556   unsigned Model, Family, Brand_id;
557   unsigned Features = 0;
558 
559   /* This function needs to run just once.  */
560   if (__cpu_model.__cpu_vendor)
561     return 0;
562 
563   if (!isCpuIdSupported())
564     return -1;
565 
566   /* Assume cpuid insn present. Run in level 0 to get vendor id. */
567   if (getX86CpuIDAndInfo(0, &MaxLeaf, &Vendor, &ECX, &EDX) || MaxLeaf < 1) {
568     __cpu_model.__cpu_vendor = VENDOR_OTHER;
569     return -1;
570   }
571   getX86CpuIDAndInfo(1, &EAX, &EBX, &ECX, &EDX);
572   detectX86FamilyModel(EAX, &Family, &Model);
573   Brand_id = EBX & 0xff;
574 
575   /* Find available features. */
576   getAvailableFeatures(ECX, EDX, MaxLeaf, &Features);
577   __cpu_model.__cpu_features[0] = Features;
578 
579   if (Vendor == SIG_INTEL) {
580     /* Get CPU type.  */
581     getIntelProcessorTypeAndSubtype(Family, Model, Brand_id, Features,
582                                     &(__cpu_model.__cpu_type),
583                                     &(__cpu_model.__cpu_subtype));
584     __cpu_model.__cpu_vendor = VENDOR_INTEL;
585   } else if (Vendor == SIG_AMD) {
586     /* Get CPU type.  */
587     getAMDProcessorTypeAndSubtype(Family, Model, Features,
588                                   &(__cpu_model.__cpu_type),
589                                   &(__cpu_model.__cpu_subtype));
590     __cpu_model.__cpu_vendor = VENDOR_AMD;
591   } else
592     __cpu_model.__cpu_vendor = VENDOR_OTHER;
593 
594   assert(__cpu_model.__cpu_vendor < VENDOR_MAX);
595   assert(__cpu_model.__cpu_type < CPU_TYPE_MAX);
596   assert(__cpu_model.__cpu_subtype < CPU_SUBTYPE_MAX);
597 
598   return 0;
599 }
600 
601 #endif
602