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