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