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