PageRenderTime 76ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/user/tango/core/Cpuid.d

https://github.com/fawzi/oldTango
D | 999 lines | 712 code | 43 blank | 244 comment | 115 complexity | d7a27d3d8d578ed2bbeca7cd34b43d67 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /**
  2. Identify the characteristics of the host CPU, providing information
  3. about cache sizes and assembly optimisation hints.
  4. Some of this information was extremely difficult to track down. Some of the
  5. documents below were found only in cached versions stored by search engines!
  6. This code relies on information found in:
  7. - "Intel(R) 64 and IA-32 Architectures Software Developers Manual,
  8. Volume 2A: Instruction Set Reference, A-M" (2007).
  9. - "AMD CPUID Specification", Advanced Micro Devices, Rev 2.28 (2008).
  10. - "AMD Processor Recognition Application Note For Processors Prior to AMD Family 0Fh Processors", Advanced Micro Devices, Rev 3.13 (2005).
  11. - "AMD Geode(TM) GX Processors Data Book", AMD, Publication ID 31505E, (2005).
  12. - "AMD K6 Processor Code Optimisation", Advanced Micro Devices, Rev D (2000).
  13. - "Application note 106: Software Customization for the 6x86 Family", Cyrix Corporation, Rev 1.5 (1998)
  14. - http://ftp.intron.ac/pub/document/cpu/cpuid.htm
  15. - "Geode(TM) GX1 Processor Series Low Power Integrated X86 Solution", National Semiconductor, (2002)
  16. - "The VIA Isaiah Architecture", G. Glenn Henry, Centaur Technology, Inc (2008).
  17. - http://www.sandpile.org/ia32/cpuid.htm
  18. - http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html
  19. - "What every programmer should know about memory", Ulrich Depper, Red Hat, Inc.
  20. (2007).
  21. AUTHORS: Don Clugston,
  22. Tomas Lindquist Olsen <tomas@famolsen.dk>
  23. Fawzi Mohamed
  24. COPYRIGHT: Public Domain
  25. BUGS: Currently only works on x86 CPUs.
  26. Many processors have bugs in their microcode for the CPUID instruction,
  27. so sometimes the cache information may be incorrect.
  28. */
  29. module tango.core.Cpuid;
  30. version(GNU){
  31. // GDC is a filthy liar. It can't actually do inline asm.
  32. } else version(D_InlineAsm_X86) {
  33. version = Really_D_InlineAsm_X86;
  34. }
  35. version(X86) {
  36. version=X86_CPU;
  37. } else version(X86_64) {
  38. version=X86_CPU;
  39. } else version ( PPC64 )
  40. {
  41. version=PPC_CPU;
  42. } else version ( PPC ) {
  43. version=PPC_CPU;
  44. } else version(ARM){
  45. } else version(SPARC){
  46. } else {
  47. static assert(0,"unknown cpu family");
  48. }
  49. /// Cache size and behaviour
  50. struct CacheInfo
  51. {
  52. /// Size of the cache, in kilobytes, per CPU.
  53. /// For L1 unified (data + code) caches, this size is half the physical size.
  54. /// (we don't halve it for larger sizes, since normally
  55. /// data size is much greater than code size for critical loops).
  56. size_t size;
  57. /// Number of ways of associativity, eg:
  58. /// 1 = direct mapped
  59. /// 2 = 2-way set associative
  60. /// 3 = 3-way set associative
  61. /// ubyte.max = fully associative
  62. ubyte associativity;
  63. /// Number of bytes read into the cache when a cache miss occurs.
  64. uint lineSize;
  65. /// how many threads share the cache, 0=unkown
  66. uint nThreadSharing;
  67. /// if you cannot really trust these numbers
  68. bool wildGuess;
  69. void clear(){
  70. size=cast(size_t)0;
  71. associativity=1;
  72. lineSize=0;
  73. nThreadSharing=0;
  74. wildGuess=true;
  75. }
  76. }
  77. /// the main type of cpu
  78. static CpuInfo mainCpu;
  79. static this(){
  80. version(X86_CPU){
  81. mainCpu=new CpuInfoX86();
  82. mainCpu.getCpuData();
  83. } else version(PPC_CPU){
  84. mainCpu=new CpuInfoPpc();
  85. } else version(ARM){
  86. mainCpu=new CpuInfoArm();
  87. } else version(SPARC){
  88. mainCpu=new CpuInfoSparc();
  89. }
  90. }
  91. /// the current type of cpu
  92. CpuInfo currentCpu() { return mainCpu; }
  93. /// if the system has only one kind of cpu (at the moment hardcoded to true)
  94. bool uniqueCpuType() { return true; }
  95. /// information on a cpu
  96. ///
  97. /// if you think x86,sparc,arm,ppc,... should be always defined, but throw or return null
  98. /// post a ticket explaining why
  99. class CpuInfo{
  100. protected:
  101. /// The data caches. If there are fewer than 5 physical caches levels,
  102. /// the remaining levels are set to size_t.max/1024 (== entire memory space)
  103. /// make this a function?
  104. CacheInfo[5] datacache;
  105. /// cache levels
  106. uint numCacheLevels;
  107. /// vendor name (only for display purposes)
  108. char [] vendorName;
  109. /// name of the processor (only for display purposes)
  110. char [] processorName;
  111. /// tries to get valid data from the current cpu, return false if it fails
  112. bool getCpuData(){
  113. clear();
  114. cacheFixup();
  115. return false;
  116. }
  117. public:
  118. this(){
  119. this.clear();
  120. }
  121. /// Returns vendor string, for display purposes only.
  122. /// Do NOT use this to determine features!
  123. /// Note that some CPUs have programmable vendorIDs.
  124. char[] vendor() { return vendorName; }
  125. /// Returns processor string, for display purposes only
  126. char[] processor() { return processorName; }
  127. /// Is hyperthreading supported?
  128. bool hyperThreading() {
  129. return threadsPerCPU()>coresPerCPU();
  130. }
  131. /// Returns number of threads per CPU
  132. uint threadsPerCPU(){
  133. return 1;
  134. }
  135. /// Returns number of cores in CPU
  136. uint coresPerCPU(){
  137. return 1;
  138. }
  139. /// clears info stored in this object
  140. void clear(){
  141. foreach (ref el;datacache){
  142. el.clear();
  143. }
  144. numCacheLevels=0;
  145. vendorName="unkown";
  146. processorName="unkown";
  147. }
  148. /// duplicates this object
  149. CpuInfo dup(){
  150. CpuInfo newInfo=cast(CpuInfo)this.classinfo.create();
  151. newInfo[]=this;
  152. return newInfo;
  153. }
  154. /// copies data from one object to the other
  155. CpuInfo opSliceAssign(CpuInfo other){
  156. assert(other.classinfo is this.classinfo);
  157. //datacache.length=other.datacache.length;
  158. datacache[]=other.datacache;
  159. numCacheLevels=other.numCacheLevels;
  160. vendorName=other.vendorName;
  161. processorName=other.processorName;
  162. return this;
  163. }
  164. /// sets unset values in cache info
  165. protected void cacheFixup(){
  166. if (datacache[0].size==0) {
  167. // Guess same as Pentium 1.
  168. datacache[0].size = 8;
  169. datacache[0].associativity = 2;
  170. datacache[0].lineSize = 32;
  171. datacache[0].wildGuess=true;
  172. }
  173. if (datacache[0].nThreadSharing==0){
  174. datacache[0].nThreadSharing=threadsPerCPU()/coresPerCPU();
  175. }
  176. numCacheLevels = 1;
  177. // And now fill up all the unused levels with full memory space.
  178. for (int i=1; i< datacache.length; ++i) {
  179. if (datacache[i].size==0) {
  180. // Set all remaining levels of cache equal to full address space.
  181. datacache[i].size = size_t.max/1024;
  182. datacache[i].associativity = 1;
  183. datacache[i].lineSize = datacache[i-1].lineSize;
  184. datacache[i].wildGuess=false;
  185. } else {
  186. numCacheLevels = i+1;
  187. }
  188. if (datacache[i].nThreadSharing==0){
  189. datacache[i].nThreadSharing=threadsPerCPU();
  190. }
  191. }
  192. }
  193. version(X86_CPU){
  194. /// utility method to get information about x86 processors
  195. final CpuInfoX86 x86(){
  196. if (auto res=cast(CpuInfoX86)this)
  197. return res;
  198. throw new Exception("non x86 cpu",__FILE__,__LINE__);
  199. }
  200. }
  201. version(PPC){
  202. /// utility method to get information about PPC processors
  203. final CpuInfoPpc ppc(){
  204. if (auto res=cast(CpuInfoPpc)this)
  205. return res;
  206. throw new Exception("non ppc cpu",__FILE__,__LINE__);
  207. }
  208. }
  209. version(ARM){
  210. /// utility method to get information about arm processors
  211. final CpuInfoArm arm(){
  212. if (auto res=cast(CpuInfoArm)this)
  213. return res;
  214. throw new Exception("non arm cpu",__FILE__,__LINE__);
  215. }
  216. }
  217. version(SPARC){
  218. /// utility method to get information about sparc processors
  219. final CpuInfoSparc sparc(){
  220. if (auto res=cast(CpuInfoSparc)this)
  221. return res;
  222. throw new Exception("non sparc cpu",__FILE__,__LINE__);
  223. }
  224. }
  225. }
  226. /// If optimizing for a particular processor, it is generally better
  227. /// to identify based on features rather than model. NOTE: Normally
  228. /// it's only worthwhile to optimise for the latest Intel and AMD CPU,
  229. /// with a backup for other CPUs.
  230. /// Pentium -- preferPentium1()
  231. /// PMMX -- + mmx()
  232. /// PPro -- default
  233. /// PII -- + mmx()
  234. /// PIII -- + mmx() + sse()
  235. /// PentiumM -- + mmx() + sse() + sse2()
  236. /// Pentium4 -- preferPentium4()
  237. /// PentiumD -- + isX86_64()
  238. /// Core2 -- default + isX86_64()
  239. /// AMD K5 -- preferPentium1()
  240. /// AMD K6 -- + mmx()
  241. /// AMD K6-II -- + mmx() + 3dnow()
  242. /// AMD K7 -- preferAthlon()
  243. /// AMD K8 -- + sse2()
  244. /// AMD K10 -- + isX86_64()
  245. /// Cyrix 6x86 -- preferPentium1()
  246. /// 6x86MX -- + mmx()
  247. final class CpuInfoX86: CpuInfo {
  248. private:
  249. bool probablyIntel; // true = _probably_ an Intel processor, might be faking
  250. bool probablyAMD; // true = _probably_ an AMD processor
  251. uint features; // mmx, sse, sse2, hyperthreading, etc
  252. uint miscfeatures; // sse3, etc.
  253. uint amdfeatures; // 3DNow!, mmxext, etc
  254. uint amdmiscfeatures; // sse4a, sse5, svm, etc
  255. uint maxCores;
  256. uint maxThreads;
  257. uint max_cpuid, max_extended_cpuid;
  258. public:
  259. // Note that this may indicate multi-core rather than hyperthreading.
  260. bool hyperThreadingBit() { return (features&HTT_BIT)!=0;}
  261. /// Processor type (vendor-dependent).
  262. /// This should be visible ONLY for display purposes.
  263. uint stepping, model, family;
  264. /// Does it have an x87 FPU on-chip?
  265. bool x87onChip() {return (features&FPU_BIT)!=0;}
  266. /// Is MMX supported?
  267. bool mmx() {return (features&MMX_BIT)!=0;}
  268. /// Is SSE supported?
  269. bool sse() {return (features&SSE_BIT)!=0;}
  270. /// Is SSE2 supported?
  271. bool sse2() {return (features&SSE2_BIT)!=0;}
  272. /// Is SSE3 supported?
  273. bool sse3() {return (miscfeatures&SSE3_BIT)!=0;}
  274. /// Is SSSE3 supported?
  275. bool ssse3() {return (miscfeatures&SSSE3_BIT)!=0;}
  276. /// Is SSE4.1 supported?
  277. bool sse41() {return (miscfeatures&SSE41_BIT)!=0;}
  278. /// Is SSE4.2 supported?
  279. bool sse42() {return (miscfeatures&SSE42_BIT)!=0;}
  280. /// Is SSE4a supported?
  281. bool sse4a() {return (amdmiscfeatures&SSE4A_BIT)!=0;}
  282. /// Is SSE5 supported?
  283. bool sse5() {return (amdmiscfeatures&SSE5_BIT)!=0;}
  284. /// Is AMD 3DNOW supported?
  285. bool amd3dnow() {return (amdfeatures&AMD_3DNOW_BIT)!=0;}
  286. /// Is AMD 3DNOW Ext supported?
  287. bool amd3dnowExt() {return (amdfeatures&AMD_3DNOW_EXT_BIT)!=0;}
  288. /// Are AMD extensions to MMX supported?
  289. bool amdMmx() {return (amdfeatures&AMD_MMX_BIT)!=0;}
  290. /// Is fxsave/fxrstor supported?
  291. bool hasFxsr() {return (features&FXSR_BIT)!=0;}
  292. /// Is cmov supported?
  293. bool hasCmov() {return (features&CMOV_BIT)!=0;}
  294. /// Is rdtsc supported?
  295. bool hasRdtsc() {return (features&TIMESTAMP_BIT)!=0;}
  296. /// Is cmpxchg8b supported?
  297. bool hasCmpxchg8b() {return (features&CMPXCHG8B_BIT)!=0;}
  298. /// Is cmpxchg8b supported?
  299. bool hasCmpxchg16b() {return (miscfeatures&CMPXCHG16B_BIT)!=0;}
  300. /// Is 3DNow prefetch supported?
  301. bool has3dnowPrefetch()
  302. {return (amdmiscfeatures&AMD_3DNOW_PREFETCH_BIT)!=0;}
  303. /// Are LAHF and SAHF supported in 64-bit mode?
  304. bool hasLahfSahf() {return (amdmiscfeatures&LAHFSAHF_BIT)!=0;}
  305. /// Is POPCNT supported?
  306. bool hasPopcnt() {return (miscfeatures&POPCNT_BIT)!=0;}
  307. /// Is LZCNT supported?
  308. bool hasLzcnt() {return (amdmiscfeatures&LZCNT_BIT)!=0;}
  309. /// Is this an Intel64 or AMD 64?
  310. bool isX86_64() {return (amdfeatures&AMD64_BIT)!=0;}
  311. /// Is this an IA64 (Itanium) processor?
  312. bool isItanium() { return (features&IA64_BIT)!=0; }
  313. /// Is hyperthreading supported?
  314. bool hyperThreading() { return maxThreads>maxCores; }
  315. /// Returns number of threads per CPU
  316. uint threadsPerCPU() { return maxThreads; }
  317. /// Returns number of cores in CPU
  318. uint coresPerCPU() { return maxCores; }
  319. /// Optimisation hints for assembly code.
  320. /// For forward compatibility, the CPU is compared against different
  321. /// microarchitectures. For 32-bit X86, comparisons are made against
  322. /// the Intel PPro/PII/PIII/PM family.
  323. ///
  324. /// The major 32-bit x86 microarchitecture 'dynasties' have been:
  325. /// (1) Intel P6 (PentiumPro, PII, PIII, PM, Core, Core2).
  326. /// (2) AMD Athlon (K7, K8, K10).
  327. /// (3) Intel NetBurst (Pentium 4, Pentium D).
  328. /// (4) In-order Pentium (Pentium1, PMMX)
  329. /// Other early CPUs (Nx586, AMD K5, K6, Centaur C3, Transmeta,
  330. /// Cyrix, Rise) were mostly in-order.
  331. /// Some new processors do not fit into the existing categories:
  332. /// Intel Atom 230/330 (family 6, model 0x1C) is an in-order core.
  333. /// Centaur Isiah = VIA Nano (family 6, model F) is an out-of-order core.
  334. ///
  335. /// Within each dynasty, the optimisation techniques are largely
  336. /// identical (eg, use instruction pairing for group 4). Major
  337. /// instruction set improvements occur within each group.
  338. /// Does this CPU perform better on AMD K7 code than PentiumPro..Core2 code?
  339. bool preferAthlon() { return probablyAMD && family >=6; }
  340. /// Does this CPU perform better on Pentium4 code than PentiumPro..Core2 code?
  341. bool preferPentium4() { return probablyIntel && family == 0xF; }
  342. /// Does this CPU perform better on Pentium I code than Pentium Pro code?
  343. bool preferPentium1() { return family < 6 || (family==6 && model < 0xF && !probablyIntel); }
  344. this(){
  345. super();
  346. }
  347. override void clear(){
  348. super.clear();
  349. stepping=0;
  350. model=0;
  351. family=0;
  352. probablyIntel=false;
  353. probablyAMD=false;
  354. vendorName="UnknownX86";
  355. processorName="UnknownX86";
  356. features=0;
  357. miscfeatures=0;
  358. amdmiscfeatures=0;
  359. amdfeatures=0;
  360. maxCores=1;
  361. maxThreads=1;
  362. }
  363. /// copies data from one object to the other
  364. CpuInfoX86 opSliceAssign(CpuInfo o){
  365. auto other=cast(CpuInfoX86)o;
  366. assert(other !is null);
  367. super.opSliceAssign(o);
  368. stepping=other.stepping;
  369. model=other.model;
  370. family=other.family;
  371. probablyIntel=other.probablyIntel;
  372. probablyAMD=other.probablyAMD;
  373. vendorName=other.vendorName;
  374. processorName=other.processorName;
  375. features=other.features;
  376. miscfeatures=other.miscfeatures;
  377. amdmiscfeatures=other.amdmiscfeatures;
  378. amdfeatures=other.amdfeatures;
  379. maxCores=other.maxCores;
  380. maxThreads=other.maxThreads;
  381. return this;
  382. }
  383. /// auto config for current cpu
  384. protected override bool getCpuData(){
  385. if (hasCPUID()) {
  386. cpuidX86();
  387. cacheFixup();
  388. return true;
  389. } else {
  390. // it's a 386 or 486, or a Cyrix 6x86.
  391. //Probably still has an external cache.
  392. clear();
  393. cacheFixup();
  394. return false;
  395. }
  396. }
  397. // feature flags CPUID1_EDX
  398. enum : uint
  399. {
  400. FPU_BIT = 1,
  401. TIMESTAMP_BIT = 1<<4, // rdtsc
  402. MDSR_BIT = 1<<5, // RDMSR/WRMSR
  403. CMPXCHG8B_BIT = 1<<8,
  404. CMOV_BIT = 1<<15,
  405. MMX_BIT = 1<<23,
  406. FXSR_BIT = 1<<24,
  407. SSE_BIT = 1<<25,
  408. SSE2_BIT = 1<<26,
  409. HTT_BIT = 1<<28,
  410. IA64_BIT = 1<<30
  411. }
  412. // feature flags misc CPUID1_ECX
  413. enum : uint
  414. {
  415. SSE3_BIT = 1,
  416. PCLMULQDQ_BIT = 1<<1, // from AVX
  417. MWAIT_BIT = 1<<3,
  418. SSSE3_BIT = 1<<9,
  419. FMA_BIT = 1<<12, // from AVX
  420. CMPXCHG16B_BIT = 1<<13,
  421. SSE41_BIT = 1<<19,
  422. SSE42_BIT = 1<<20,
  423. POPCNT_BIT = 1<<23,
  424. AES_BIT = 1<<25, // AES instructions from AVX
  425. OSXSAVE_BIT = 1<<27, // Used for AVX
  426. AVX_BIT = 1<<28
  427. }
  428. /+
  429. version(X86_64) {
  430. bool hasAVXinHardware() {
  431. // This only indicates hardware support, not OS support.
  432. return (miscfeatures&AVX_BIT) && (miscfeatures&OSXSAVE_BIT);
  433. }
  434. // Is AVX supported (in both hardware & OS)?
  435. bool Avx() {
  436. if (!hasAVXinHardware()) return false;
  437. // Check for OS support
  438. uint xfeatures;
  439. asm {mov ECX, 0; xgetbv; mov xfeatures, EAX; }
  440. return (xfeatures&0x6)==6;
  441. }
  442. bool hasAvxFma() {
  443. if (!AVX()) return false;
  444. return (features&FMA_BIT)!=0;
  445. }
  446. }
  447. +/
  448. // AMD feature flags CPUID80000001_EDX
  449. enum : uint
  450. {
  451. AMD_MMX_BIT = 1<<22,
  452. // FXR_OR_CYRIXMMX_BIT = 1<<24, // Cyrix/NS: 6x86MMX instructions.
  453. FFXSR_BIT = 1<<25,
  454. PAGE1GB_BIT = 1<<26, // support for 1GB pages
  455. RDTSCP_BIT = 1<<27,
  456. AMD64_BIT = 1<<29,
  457. AMD_3DNOW_EXT_BIT = 1<<30,
  458. AMD_3DNOW_BIT = 1<<31
  459. }
  460. // AMD misc feature flags CPUID80000001_ECX
  461. enum : uint
  462. {
  463. LAHFSAHF_BIT = 1,
  464. LZCNT_BIT = 1<<5,
  465. SSE4A_BIT = 1<<6,
  466. AMD_3DNOW_PREFETCH_BIT = 1<<8,
  467. SSE5_BIT = 1<<11
  468. }
  469. version(Really_D_InlineAsm_X86) {
  470. // Note that this code will also work for Itanium, after changing the
  471. // register names in the asm code.
  472. // CPUID2: "cache and tlb information"
  473. void getcacheinfoCPUID2()
  474. {
  475. // CPUID2 is a dog's breakfast. What was Intel thinking???
  476. // We are only interested in the data caches
  477. void decipherCpuid2(ubyte x) {
  478. if (x==0) return;
  479. // Values from http://www.sandpile.org/ia32/cpuid.htm.
  480. // Includes Itanium and non-Intel CPUs.
  481. //
  482. ubyte [] ids = [
  483. 0x0A, 0x0C, 0x2C, 0x60, 0x0E, 0x66, 0x67, 0x68,
  484. // level 2 cache
  485. 0x41, 0x42, 0x43, 0x44, 0x45, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7F,
  486. 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x49, 0x4E,
  487. 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x48, 0x80, 0x81,
  488. // level 3 cache
  489. 0x22, 0x23, 0x25, 0x29, 0x46, 0x47, 0x4A, 0x4B, 0x4C, 0x4D
  490. ];
  491. uint [] sizes = [
  492. 8, 16, 32, 16, 24, 8, 16, 32,
  493. 128, 256, 512, 1024, 2048, 1024, 128, 256, 512, 1024, 2048, 512,
  494. 256, 512, 1024, 2048, 512, 1024, 4096, 6*1024,
  495. 128, 192, 128, 256, 384, 512, 3072, 512, 128,
  496. 512, 1024, 2048, 4096, 4096, 8192, 6*1024, 8192, 12*1024, 16*1024
  497. ];
  498. // CPUBUG: Pentium M reports 0x2C but tests show it is only 4-way associative
  499. ubyte [] ways = [
  500. 2, 4, 8, 8, 6, 4, 4, 4,
  501. 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 2,
  502. 8, 8, 8, 8, 4, 8, 16, 24,
  503. 4, 6, 2, 4, 6, 4, 12, 8, 8,
  504. 4, 8, 8, 8, 4, 8, 12, 16, 12, 16
  505. ];
  506. enum { FIRSTDATA2 = 8, FIRSTDATA3 = 28+9 }
  507. for (int i=0; i< ids.length; ++i) {
  508. if (x==ids[i]) {
  509. int level = i< FIRSTDATA2 ? 0: i<FIRSTDATA3 ? 1 : 2;
  510. if (x==0x49 && family==0xF && model==0x6) level=2;
  511. datacache[level].size=sizes[i];
  512. datacache[level].associativity=ways[i];
  513. if (level == 3 || x==0x2C || (x>=0x48 && x<=0x80)
  514. || x==0x86 || x==0x87
  515. || (x>=0x66 && x<=0x68) || (x>=0x39 && x<=0x3E) ){
  516. datacache[level].lineSize = 64;
  517. } else datacache[level].lineSize = 32;
  518. }
  519. }
  520. }
  521. uint[4] a;
  522. bool firstTime = true;
  523. // On a multi-core system, this could theoretically fail, but it's only used
  524. // for old single-core CPUs.
  525. uint numinfos = 1;
  526. do {
  527. asm {
  528. mov EAX, 2;
  529. cpuid;
  530. mov a, EAX;
  531. mov a+4, EBX;
  532. mov a+8, ECX;
  533. mov a+12, EDX;
  534. }
  535. if (firstTime) {
  536. if (a[0]==0x0000_7001 && a[3]==0x80 && a[1]==0 && a[2]==0) {
  537. // Cyrix MediaGX MMXEnhanced returns: EAX= 00007001, EDX=00000080.
  538. // These are NOT standard Intel values
  539. // (TLB = 32 entry, 4 way associative, 4K pages)
  540. // (L1 cache = 16K, 4way, linesize16)
  541. datacache[0].size=8;
  542. datacache[0].associativity=4;
  543. datacache[0].lineSize=16;
  544. return;
  545. }
  546. // lsb of a is how many times to loop.
  547. numinfos = a[0] & 0xFF;
  548. // and otherwise it should be ignored
  549. a[0] &= 0xFFFF_FF00;
  550. firstTime = false;
  551. }
  552. for (int c=0; c<4;++c) {
  553. // high bit set == no info.
  554. if (a[c] & 0x8000_0000) continue;
  555. decipherCpuid2(cast(ubyte)(a[c] & 0xFF));
  556. decipherCpuid2(cast(ubyte)((a[c]>>8) & 0xFF));
  557. decipherCpuid2(cast(ubyte)((a[c]>>16) & 0xFF));
  558. decipherCpuid2(cast(ubyte)((a[c]>>24) & 0xFF));
  559. }
  560. } while (--numinfos);
  561. }
  562. // CPUID4: "Deterministic cache parameters" leaf
  563. void getcacheinfoCPUID4()
  564. {
  565. int cachenum = 0;
  566. for(;;) {
  567. uint a, b, number_of_sets;
  568. asm {
  569. mov EAX, 4;
  570. mov ECX, cachenum;
  571. cpuid;
  572. mov a, EAX;
  573. mov b, EBX;
  574. mov number_of_sets, ECX;
  575. }
  576. ++cachenum;
  577. if ((a&0x1F)==0) break; // no more caches
  578. uint numthreads = ((a>>14) & 0xFFF) + 1;
  579. uint numcores = ((a>>26) & 0x3F) + 1;
  580. if (numcores > maxCores) maxCores = numcores;
  581. if ((a&0x1F)!=1 && ((a&0x1F)!=3)) continue; // we only want data & unified caches
  582. ++number_of_sets;
  583. ubyte level = cast(ubyte)(((a>>5)&7)-1);
  584. if (level > datacache.length) continue; // ignore deep caches
  585. datacache[level].associativity = a & 0x200 ? ubyte.max :cast(ubyte)((b>>22)+1);
  586. datacache[level].lineSize = (b & 0xFFF)+ 1; // system coherency line size
  587. uint line_partitions = ((b >> 12)& 0x3FF) + 1;
  588. // Size = number of sets * associativity * cachelinesize * linepartitions
  589. // and must convert to Kb, also dividing by the number of cores.
  590. ulong sz = (datacache[level].associativity< ubyte.max)? number_of_sets *
  591. datacache[level].associativity : number_of_sets;
  592. datacache[level].size = cast(uint)(
  593. (sz * datacache[level].lineSize * line_partitions ) / (numcores *1024));
  594. if (level == 0 && (a&0xF)==3) {
  595. // Halve the size for unified L1 caches
  596. datacache[level].size/=2;
  597. }
  598. }
  599. }
  600. // CPUID8000_0005 & 6
  601. void getAMDcacheinfo()
  602. {
  603. uint c5, c6, d6;
  604. asm {
  605. mov EAX, 0x8000_0005; // L1 cache
  606. cpuid;
  607. // EAX has L1_TLB_4M.
  608. // EBX has L1_TLB_4K
  609. // EDX has L1 instruction cache
  610. mov c5, ECX;
  611. }
  612. datacache[0].size = ( (c5>>24) & 0xFF);
  613. datacache[0].associativity = cast(ubyte)( (c5 >> 16) & 0xFF);
  614. datacache[0].lineSize = c5 & 0xFF;
  615. if (max_extended_cpuid >= 0x8000_0006) {
  616. // AMD K6-III or K6-2+ or later.
  617. ubyte numcores = 1;
  618. if (max_extended_cpuid >=0x8000_0008) {
  619. asm {
  620. mov EAX, 0x8000_0008;
  621. cpuid;
  622. mov numcores, CL;
  623. }
  624. ++numcores;
  625. if (numcores>maxCores) maxCores = numcores;
  626. }
  627. asm {
  628. mov EAX, 0x8000_0006; // L2/L3 cache
  629. cpuid;
  630. mov c6, ECX; // L2 cache info
  631. mov d6, EDX; // L3 cache info
  632. }
  633. ubyte [] assocmap = [ 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, 0xFF ];
  634. datacache[1].size = (c6>>16) & 0xFFFF;
  635. datacache[1].associativity = assocmap[(c6>>12)&0xF];
  636. datacache[1].lineSize = c6 & 0xFF;
  637. // The L3 cache value is TOTAL, not per core.
  638. datacache[2].size = ((d6>>18)*512)/numcores; // could be up to 2 * this, -1.
  639. datacache[2].associativity = assocmap[(d6>>12)&0xF];
  640. datacache[2].lineSize = d6 & 0xFF;
  641. }
  642. }
  643. void cpuidX86()
  644. {
  645. char [12] vendorID;
  646. char [48] processorNameBuffer;
  647. uint m_1, m_2;
  648. char * venptr = vendorID.ptr;
  649. asm {
  650. mov EAX, 0;
  651. cpuid;
  652. mov m_1, EAX;
  653. mov EAX, venptr;
  654. mov [EAX], EBX;
  655. mov [EAX + 4], EDX;
  656. mov [EAX + 8], ECX;
  657. mov EAX, 0x8000_0000;
  658. cpuid;
  659. mov m_2, EAX;
  660. }
  661. max_cpuid=m_1;
  662. max_extended_cpuid=m_2;
  663. probablyIntel = vendorID == "GenuineIntel";
  664. probablyAMD = vendorID == "AuthenticAMD";
  665. vendorName=vendorID.dup;
  666. uint a, b, c, d;
  667. uint apic = 0; // brand index, apic id
  668. asm {
  669. mov EAX, 1; // model, stepping
  670. cpuid;
  671. mov a, EAX;
  672. mov apic, EBX;
  673. mov m_1, ECX;
  674. mov m_2, EDX;
  675. }
  676. miscfeatures=m_1;
  677. features=m_2;
  678. amdfeatures = 0;
  679. amdmiscfeatures = 0;
  680. if (max_extended_cpuid >= 0x8000_0001) {
  681. asm {
  682. mov EAX, 0x8000_0001;
  683. cpuid;
  684. mov m_1, ECX;
  685. mov m_2, EDX;
  686. }
  687. amdmiscfeatures=m_1;
  688. amdfeatures=m_2;
  689. }
  690. // Try to detect fraudulent vendorIDs
  691. if (amd3dnow) probablyIntel = false;
  692. stepping = a & 0xF;
  693. uint fbase = (a >> 8) & 0xF;
  694. uint mbase = (a >> 4) & 0xF;
  695. family = ((fbase == 0xF) || (fbase == 0)) ? fbase + (a >> 20) & 0xFF : fbase;
  696. model = ((fbase == 0xF) || (fbase == 6 && probablyIntel) ) ?
  697. mbase + ((a >> 12) & 0xF0) : mbase;
  698. if (!probablyIntel && max_extended_cpuid >= 0x8000_0008) {
  699. // determine max number of cores for AMD
  700. asm {
  701. mov EAX, 0x8000_0008;
  702. cpuid;
  703. mov c, ECX;
  704. }
  705. uint apicsize = (c>>12) & 0xF;
  706. if (apicsize == 0) {
  707. // use legacy method
  708. if (hyperThreadingBit) maxCores = c & 0xFF;
  709. else maxCores = 1;
  710. } else {
  711. // maxcores = 2^ apicsize
  712. maxCores = 1;
  713. while (apicsize) { maxCores<<=1; --apicsize; }
  714. }
  715. }
  716. if (max_extended_cpuid >= 0x8000_0004) {
  717. char *procptr = processorNameBuffer.ptr;
  718. asm {
  719. push ESI;
  720. mov ESI, procptr;
  721. mov EAX, 0x8000_0002;
  722. cpuid;
  723. mov [ESI], EAX;
  724. mov [ESI+4], EBX;
  725. mov [ESI+8], ECX;
  726. mov [ESI+12], EDX;
  727. mov EAX, 0x8000_0003;
  728. cpuid;
  729. mov [ESI+16], EAX;
  730. mov [ESI+20], EBX;
  731. mov [ESI+24], ECX;
  732. mov [ESI+28], EDX;
  733. mov EAX, 0x8000_0004;
  734. cpuid;
  735. mov [ESI+32], EAX;
  736. mov [ESI+36], EBX;
  737. mov [ESI+40], ECX;
  738. mov [ESI+44], EDX;
  739. pop ESI;
  740. }
  741. // Intel P4 and PM pad at front with spaces.
  742. // Other CPUs pad at end with nulls.
  743. int start = 0, end = 0;
  744. while (processorNameBuffer[start] == ' ') { ++start; }
  745. while (processorNameBuffer[$-end-1] == 0) { ++end; }
  746. processorName = processorNameBuffer[start..$-end].dup;
  747. } else {
  748. processorName = "Unknown CPU";
  749. }
  750. // Determine cache sizes
  751. // Intel docs specify that they return 0 for 0x8000_0005.
  752. // AMD docs do not specify the behaviour for 0004 and 0002.
  753. // Centaur/VIA and most other manufacturers use the AMD method,
  754. // except Cyrix MediaGX MMX Enhanced uses their OWN form of CPUID2!
  755. // NS Geode GX1 provides CyrixCPUID2 _and_ does the same wrong behaviour
  756. // for CPUID80000005. But Geode GX uses the AMD method
  757. // Deal with idiotic Geode GX1 - make it same as MediaGX MMX.
  758. if (max_extended_cpuid==0x8000_0005 && max_cpuid==2) {
  759. max_extended_cpuid = 0x8000_0004;
  760. }
  761. // Therefore, we try the AMD method unless it's an Intel chip.
  762. // If we still have no info, try the Intel methods.
  763. datacache[0].size = 0;
  764. if (max_cpuid<2 || !probablyIntel) {
  765. if (max_extended_cpuid >= 0x8000_0005) {
  766. getAMDcacheinfo();
  767. } else if (probablyAMD) {
  768. // According to AMDProcRecognitionAppNote, this means CPU
  769. // K5 model 0, or Am5x86 (model 4), or Am4x86DX4 (model 4)
  770. // Am5x86 has 16Kb 4-way unified data & code cache.
  771. datacache[0].size = 8;
  772. datacache[0].associativity = 4;
  773. datacache[0].lineSize = 32;
  774. } else {
  775. // Some obscure CPU.
  776. // Values for Cyrix 6x86MX (family 6, model 0)
  777. datacache[0].size = 64;
  778. datacache[0].associativity = 4;
  779. datacache[0].lineSize = 32;
  780. }
  781. }
  782. if ((datacache[0].size == 0) && max_cpuid>=4) {
  783. getcacheinfoCPUID4();
  784. }
  785. if ((datacache[0].size == 0) && max_cpuid>=2) {
  786. getcacheinfoCPUID2();
  787. }
  788. if (datacache[0].size == 0) {
  789. // Pentium, PMMX, late model 486, or an obscure CPU
  790. if (mmx) { // Pentium MMX. Also has 8kB code cache.
  791. datacache[0].size = 16;
  792. datacache[0].associativity = 4;
  793. datacache[0].lineSize = 32;
  794. } else { // Pentium 1 (which also has 8kB code cache)
  795. // or 486.
  796. // Cyrix 6x86: 16, 4way, 32 linesize
  797. datacache[0].size = 8;
  798. datacache[0].associativity = 2;
  799. datacache[0].lineSize = 32;
  800. }
  801. }
  802. if (hyperThreadingBit) maxThreads = (apic>>>16) & 0xFF;
  803. else maxThreads = maxCores;
  804. }
  805. // Return true if the cpuid instruction is supported.
  806. // BUG(WONTFIX): Doesn't work for Cyrix 6x86 and 6x86L.
  807. bool hasCPUID()
  808. {
  809. uint flags;
  810. asm {
  811. pushfd;
  812. pop EAX;
  813. mov flags, EAX;
  814. xor EAX, 0x0020_0000;
  815. push EAX;
  816. popfd;
  817. pushfd;
  818. pop EAX;
  819. xor flags, EAX;
  820. }
  821. return (flags & 0x0020_0000) !=0;
  822. }
  823. } else { // inline asm X86
  824. bool hasCPUID() { return false; }
  825. void cpuidX86()
  826. {
  827. datacache[0].size = 8;
  828. datacache[0].associativity = 2;
  829. datacache[0].lineSize = 32;
  830. }
  831. }
  832. }
  833. final class CpuInfoPpc: CpuInfo{
  834. bool hasfloatingpoint; // Floating Point Instructions
  835. bool hasaltivec; // AltiVec Instructions
  836. bool hasgraphicsops; // Graphics Operations
  837. bool has64bitops; // 64-bit Instructions
  838. bool hasfsqrt; // HW Floating Point Square Root Instruction
  839. bool hasstfiwx; // Store Floating Point as Integer Word Indexed Instructions
  840. bool hasdcba; // Data Cache Block Allocate Instruction
  841. bool hasdatastreams; // Data Streams Instructions
  842. bool hasdcbtstreams; // Data Cache Block Touch Steams Instruction Form
  843. this(){
  844. super();
  845. }
  846. override void clear(){
  847. super.clear();
  848. vendorName="Unknown";
  849. processorName="UnknownPPC";
  850. hasfloatingpoint= false;
  851. hasaltivec = false;
  852. hasgraphicsops = false;
  853. has64bitops = false;
  854. hasfsqrt = false;
  855. hasstfiwx = false;
  856. hasdcba = false;
  857. hasdatastreams = false;
  858. hasdcbtstreams = false;
  859. cacheFixup();
  860. }
  861. override CpuInfoPpc opSliceAssign(CpuInfo o){
  862. auto other=cast(CpuInfoPpc)o;
  863. assert(other !is null);
  864. super.opSliceAssign(o);
  865. hasfloatingpoint= other.hasfloatingpoint;
  866. hasaltivec = other.hasaltivec;
  867. hasgraphicsops = other.hasgraphicsops;
  868. has64bitops = other.has64bitops;
  869. hasfsqrt = other.hasfsqrt;
  870. hasstfiwx = other.hasstfiwx;
  871. hasdcba = other.hasdcba;
  872. hasdatastreams = other.hasdatastreams;
  873. hasdcbtstreams = other.hasdcbtstreams;
  874. return this;
  875. }
  876. enum PPC_Cputype:int { PPC601, PPC603, PPC603E, PPC604,
  877. PPC604E, PPC620, PPCG3, PPCG4, PPCG5 };
  878. // TODO: Implement this function with OS support
  879. void cpuidPPC(PPC_Cputype cputype)
  880. {
  881. // TODO:
  882. // asm { mfpvr; } returns the CPU version but unfortunately it can
  883. // only be used in kernel mode. So OS support is required.
  884. // 601 has a 8KB combined data & code L1 cache.
  885. uint sizes[] = [4, 8, 16, 16, 32, 32, 32, 32, 64];
  886. ubyte ways[] = [8, 2, 4, 4, 4, 8, 8, 8, 8];
  887. uint L2size[]= [0, 0, 0, 0, 0, 0, 0, 256, 512];
  888. uint L3size[]= [0, 0, 0, 0, 0, 0, 0, 2048, 0];
  889. datacache[0].size = sizes[cputype];
  890. datacache[0].associativity = ways[cputype];
  891. datacache[0].lineSize = (cputype==PPC_Cputype.PPCG5)? 128 :
  892. (cputype == PPC_Cputype.PPC620 || cputype == PPC_Cputype.PPCG3)? 64 : 32;
  893. datacache[1].size = L2size[cputype];
  894. datacache[2].size = L3size[cputype];
  895. datacache[1].lineSize = datacache[0].lineSize;
  896. datacache[2].lineSize = datacache[0].lineSize;
  897. cacheFixup();
  898. }
  899. }
  900. /// this should be expanded by someone using sparc
  901. final class CpuInfoSparc: CpuInfo{
  902. this(){ super(); }
  903. override void clear(){
  904. super.clear();
  905. vendorName="unknown";
  906. processorName="unknownSparc";
  907. }
  908. override CpuInfoSparc opSliceAssign(CpuInfo o){
  909. auto other=cast(CpuInfoSparc)o;
  910. assert(other !is null);
  911. super.opSliceAssign(o);
  912. return this;
  913. }
  914. enum Sparc_Cputype:int {
  915. UltraSparcIIi, UltraSparcIII, UltraSparcIIIi,UltraSparcIV,
  916. UltraSparcIVplus, Sparc64V
  917. }
  918. // TODO: Implement this function with OS support
  919. void cpuidSparc(Sparc_Cputype cputype)
  920. {
  921. size_t l1,l2;
  922. ubyte way1,way2;
  923. switch(cputype){
  924. case Sparc_Cputype.UltraSparcIIi:
  925. l1 = 16; way1=2; l2 = 512; way2=4;
  926. break;
  927. case Sparc_Cputype.UltraSparcIII:
  928. l1 = 64; way1=4; l2= 4096; way2=4; // or l2=8192;
  929. break;
  930. case Sparc_Cputype.UltraSparcIIIi:
  931. l1 = 64; way1=4; l2= 1024; way2=4;
  932. break;
  933. case Sparc_Cputype.UltraSparcIV:
  934. l1 = 64; way1=4; l2= 16*1024; way2=1;
  935. break;
  936. case Sparc_Cputype.UltraSparcIVplus:
  937. l1 = 64; way1=4; l2 = 2048; way2=1;
  938. datacache[3].size=32*1024;
  939. break;
  940. case Sparc_Cputype.Sparc64V:
  941. l1 = 128; way1=2; l2 = 4096; way2=4;
  942. break;
  943. default:
  944. throw new Exception("invalid cputype",__FILE__,__LINE__);
  945. }
  946. datacache[1].size=l1;
  947. datacache[1].associativity=way1;
  948. datacache[2].size=l2;
  949. datacache[2].associativity=way2;
  950. cacheFixup();
  951. }
  952. }