1 /++ 2 $(H2 High level abstraction on top of all architectures.) 3 4 $(GREEN This module is compatible with betterC compilation mode.) 5 6 7 License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 8 9 Authors: Ilia Ki 10 +/ 11 module cpuid.unified; 12 13 /// 14 unittest 15 { 16 void smallReport() 17 { 18 import cpuid.unified; 19 20 import std.stdio: writefln; 21 enum fmt = "%14s: %s"; 22 23 fmt.writefln("cores", cores); 24 fmt.writefln("threads", threads); 25 26 fmt.writefln("data caches", dCache.length); 27 fmt.writefln("code caches", iCache.length); 28 fmt.writefln("unified caches", uCache.length); 29 30 fmt.writefln("data TLBs", dTlb.length); 31 fmt.writefln("code TLBs", iTlb.length); 32 fmt.writefln("unified TLBs", uTlb.length); 33 } 34 } 35 36 public import cpuid.common; 37 38 version(X86) 39 version = X86_Any; 40 version(X86_64) 41 version = X86_Any; 42 43 version(all) 44 { 45 enum uint _dCache_max_length = 1; 46 enum uint _iCache_max_length = 1; 47 enum uint _uCache_max_length = 3; 48 49 enum uint _dTlb_max_length = 2; 50 enum uint _iTlb_max_length = 2; 51 enum uint _uTlb_max_length = 1; 52 } 53 else 54 static assert(0); 55 56 private __gshared 57 { 58 immutable uint _cpus; 59 immutable uint _cores; 60 immutable uint _threads; 61 immutable uint _iCache_length; immutable Cache[_iCache_max_length] _iCache; 62 immutable uint _dCache_length; immutable Cache[_dCache_max_length] _dCache; 63 immutable uint _uCache_length; immutable Cache[_uCache_max_length] _uCache; 64 immutable uint _iTlb_length; immutable Tlb[_iTlb_max_length] _iTlb; 65 immutable uint _dTlb_length; immutable Tlb[_dTlb_max_length] _dTlb; 66 immutable uint _uTlb_length; immutable Tlb[_uTlb_max_length] _uTlb; 67 } 68 69 private T2 assocCopy(T2, T1)(T1 from) 70 { 71 import std.traits: Unqual; 72 Unqual!T2 to = cast(T2) from; 73 static if(!is(Unqual!T1 == Unqual!T2)) 74 { 75 if(from == T1.max) 76 { 77 to = T2.max; 78 } 79 } 80 return to; 81 } 82 83 package ref T _mut(T)(return ref immutable T value) 84 { 85 return *cast(T*)&value; 86 } 87 88 export 89 nothrow @nogc 90 extern(C): 91 92 /++ 93 Initialize basic CPU information including basic architecture. 94 It is safe to call this function multiple times. 95 It calls appropriate basic initialization for each module (`cpuid_x86_any_init` for X86 machines). 96 +/ 97 version(X86_Any) 98 pragma(crt_constructor) 99 void mir_cpuid_init() 100 { 101 static if (__VERSION__ >= 2068) 102 pragma(inline, false); 103 104 if (_cpus) 105 return; // already initialized 106 107 import cpuid.x86_any; 108 109 mir_cpuid_x86_any_init(); 110 111 static import cpuid.intel; 112 static import cpuid.amd; 113 114 /// for old CPUs 115 if(htt) 116 { 117 _threads._mut = _cores._mut = maxLogicalProcessors; 118 _cores._mut /= 2; 119 } 120 121 if (vendorIndex == VendorIndex.amd || 122 vendorIndex == VendorIndex.amd_old || 123 vendorIndex == VendorIndex.centaur || 124 vendorIndex == VendorIndex.hygon) 125 { 126 // Caches and TLB 127 if(maxExtendedLeaf >= 0x8000_0005) 128 { 129 // Level 1 130 auto leafExt5 = cpuid.amd.LeafExt5Information(_cpuid(0x8000_0005)); 131 132 alias CacheAssoc = typeof(Cache.associative); 133 alias TlbAssoc = typeof(Tlb.associative); 134 135 if(leafExt5.L1DTlb4KSize) 136 { 137 _dTlb._mut[0].page = 4; 138 _dTlb._mut[0].entries = leafExt5.L1DTlb4KSize; 139 _dTlb._mut[0].associative = leafExt5.L1DTlb4KAssoc.assocCopy!TlbAssoc; 140 _dTlb_length._mut = 1; 141 } 142 if(leafExt5.L1ITlb4KSize) 143 { 144 _iTlb._mut[0].page = 4; 145 _iTlb._mut[0].entries = leafExt5.L1ITlb4KSize; 146 _iTlb._mut[0].associative = leafExt5.L1ITlb4KAssoc.assocCopy!TlbAssoc; 147 _iTlb_length._mut = 1; 148 } 149 if(leafExt5.L1DcSize) 150 { 151 _dCache_length._mut = 1; 152 _dCache._mut[0].size = leafExt5.L1DcSize; 153 _dCache._mut[0].line = leafExt5.L1DcLineSize; 154 _dCache._mut[0].associative = leafExt5.L1DcAssoc.assocCopy!CacheAssoc; 155 } 156 if(leafExt5.L1IcSize) 157 { 158 _iCache_length._mut = 1; 159 _iCache._mut[0].size = leafExt5.L1IcSize; 160 _iCache._mut[0].line = leafExt5.L1IcLineSize; 161 _iCache._mut[0].associative = leafExt5.L1IcAssoc.assocCopy!CacheAssoc; 162 } 163 164 // Levels 2 and 3 165 if(maxExtendedLeaf >= 0x8000_0006) 166 { 167 import cpuid.amd: decodeL2or3Assoc; 168 auto leafExt6 = cpuid.amd.LeafExt6Information(_cpuid(0x8000_0006)); 169 170 if(leafExt6.L2DTlb4KSize) 171 { 172 _dTlb._mut[_dTlb_length].page = 4; 173 _dTlb._mut[_dTlb_length].entries = leafExt6.L2DTlb4KSize; 174 _dTlb._mut[_dTlb_length].associative = leafExt6.L2DTlb4KAssoc.decodeL2or3Assoc!TlbAssoc; 175 _dTlb_length._mut++; 176 } 177 if(leafExt6.L2ITlb4KSize) 178 { 179 _iTlb._mut[_iTlb_length].page = 4; 180 _iTlb._mut[_iTlb_length].entries = leafExt6.L2ITlb4KSize; 181 _iTlb._mut[_iTlb_length].associative = leafExt6.L2ITlb4KAssoc.decodeL2or3Assoc!TlbAssoc; 182 _iTlb_length._mut++; 183 } 184 if(leafExt6.L2Size) 185 { 186 _uCache._mut[_uCache_length].size = leafExt6.L2Size; 187 _uCache._mut[_uCache_length].line = cast(typeof(Cache.line)) leafExt6.L2LineSize; 188 _uCache._mut[_uCache_length].associative = leafExt6.L2Assoc.decodeL2or3Assoc!CacheAssoc; 189 _uCache_length._mut++; 190 } 191 if(leafExt6.L3Size) 192 { 193 _uCache._mut[_uCache_length].size = leafExt6.L3Size * 512; 194 _uCache._mut[_uCache_length].line = cast(typeof(Cache.line)) leafExt6.L3LineSize; 195 _uCache._mut[_uCache_length].associative = leafExt6.L3Assoc.decodeL2or3Assoc!CacheAssoc; 196 _uCache_length._mut++; 197 } 198 199 if(maxExtendedLeaf >= 0x8000_0008) 200 { 201 auto leafExt8 = cpuid.amd.LeafExt8Information(_cpuid(0x8000_0008)); 202 _threads._mut = leafExt8.NC + 1; 203 204 if (maxExtendedLeaf >= 0x8000_001E) 205 { 206 auto leafExt1E = cpuid.amd.LeafExt1EInformation(_cpuid(0x8000_001E)); 207 _cores._mut = _threads / (leafExt1E.ThreadsPerCore + 1); 208 } 209 } 210 } 211 } 212 } 213 else 214 { 215 /// Other vendors 216 if(maxBasicLeaf >= 0x2) 217 { 218 /// Get TLB and Cache info 219 auto leaf2 = cpuid.intel.Leaf2Information(_cpuid(2)); 220 221 /// Fill cache info 222 if(leaf2.dtlb.size) 223 { 224 _dTlb._mut[0] = leaf2.dtlb; 225 _dTlb_length._mut = 1; 226 } 227 if(leaf2.dtlb1.size) 228 { 229 _dTlb._mut[_dTlb_length] = leaf2.dtlb1; 230 _dTlb_length._mut++; 231 } 232 if(leaf2.itlb.size) 233 { 234 _iTlb._mut[0] = leaf2.itlb; 235 _iTlb_length._mut = 1; 236 } 237 if(leaf2.utlb.size) 238 { 239 _uTlb._mut[0] = leaf2.utlb; 240 _uTlb_length._mut = 1; 241 } 242 243 if(maxBasicLeaf >= 0x4) 244 { 245 /// Fill cache info from leaf 4 246 cpuid.intel.Leaf4Information leaf4 = void; 247 Cache cache; 248 Leaf4Loop: foreach(uint ecx; 0 .. 12) 249 { 250 leaf4.info = _cpuid(4, ecx); 251 leaf4.fill(cache); 252 253 with(cpuid.intel.Leaf4Information.Type) 254 switch(leaf4.type) 255 { 256 case data: 257 if(_dCache_length < _dCache.length) 258 _dCache._mut[_dCache_length._mut++] = cache; 259 break; 260 case instruction: 261 if(_iCache_length < _iCache.length) 262 _iCache._mut[_iCache_length._mut++] = cache; 263 break; 264 case unified: 265 if(_uCache_length < _uCache.length) 266 _uCache._mut[_uCache_length._mut++] = cache; 267 break; 268 default: break Leaf4Loop; 269 } 270 /// Fill core number for old CPUs 271 _cores._mut = leaf4.maxCorePerCPU; 272 } 273 if(maxBasicLeaf >= 0xB) 274 { 275 auto th = cast(ushort) _cpuid(0xB, 1).b; 276 if(th > 0) 277 _threads._mut = th; 278 auto threadsPerCore = cast(ushort) _cpuid(0xB, 0).b; 279 if(threadsPerCore) 280 { 281 _cores._mut = _threads / threadsPerCore; 282 } 283 } 284 } 285 else 286 { 287 /// Fill cache info from leaf 2 288 if(leaf2.l1.size) 289 { 290 _dCache._mut[0] = leaf2.l1; 291 _dCache_length._mut = 1; 292 } 293 if(leaf2.il1.size) 294 { 295 _iCache._mut[0] = leaf2.il1; 296 _iCache_length._mut = 1; 297 } 298 if(leaf2.l2.size) 299 { 300 _uCache._mut[0] = leaf2.l2; 301 _uCache_length._mut = 1; 302 } 303 if(leaf2.l3.size) 304 { 305 _uCache._mut[_uCache_length] = leaf2.l3; 306 _uCache_length._mut++; 307 } 308 } 309 } 310 } 311 312 if(!_cpus) _cpus._mut = 1; 313 if(!_cores) _cores._mut = 1; 314 if(!_threads) _threads._mut = 1; 315 if(_threads < _cores) _threads._mut = _cores; 316 317 if(_iCache_length) _iCache._mut[0].cores = 1; 318 if(_dCache_length) _dCache._mut[0].cores = 1; 319 switch(_uCache_length) 320 { 321 case 0: 322 break; 323 case 1: 324 _uCache._mut[0].cores = cast(typeof(Cache.cores)) _cores; 325 break; 326 default: 327 _uCache._mut[0].cores = 1; 328 foreach(i; 1.._uCache_length) 329 _uCache._mut[i].cores = cast(typeof(Cache.cores)) _cores; 330 } 331 } 332 else 333 pragma(crt_constructor) 334 void mir_cpuid_init() 335 { 336 _cpus._mut = 1; 337 _cores._mut = 1; 338 _threads._mut = 1; 339 } 340 /// ditto 341 342 alias cpuid_init = mir_cpuid_init; 343 344 unittest // make sure a 2nd invocation after the implicit CRT constructor doesn't throw 345 { 346 mir_cpuid_init(); 347 } 348 349 pure @trusted: 350 351 /++ 352 Total number of CPU packages. 353 Note: not implemented 354 +/ 355 uint mir_cpuid_cpus() { return _cpus; } 356 /// ditto 357 alias cpus = mir_cpuid_cpus; 358 359 /++ 360 Total number of cores per CPU. 361 +/ 362 uint mir_cpuid_cores() { return _cores; } 363 /// ditto 364 alias cores = mir_cpuid_cores; 365 366 /++ 367 Total number of threads per CPU. 368 +/ 369 uint mir_cpuid_threads() { return _threads; } 370 /// ditto 371 alias threads = mir_cpuid_threads; 372 373 /++ 374 Data Caches 375 376 Returns: 377 Array composed of detected data caches. Array is sorted in ascending order. 378 +/ 379 immutable(Cache)[] mir_cpuid_dCache() { return _dCache[0 .. _dCache_length]; } 380 /// ditto 381 alias dCache = mir_cpuid_dCache; 382 383 /++ 384 Instruction Caches 385 386 Returns: 387 Array composed of detected instruction caches. Array is sorted in ascending order. 388 +/ 389 immutable(Cache)[] mir_cpuid_iCache() { return _iCache[0 .. _iCache_length]; } 390 /// ditto 391 alias iCache = mir_cpuid_iCache; 392 393 /++ 394 Unified Caches 395 396 Returns: 397 Array composed of detected unified caches. Array is sorted in ascending order. 398 +/ 399 immutable(Cache)[] mir_cpuid_uCache() { return _uCache[0 .. _uCache_length]; } 400 /// ditto 401 alias uCache = mir_cpuid_uCache; 402 403 /++ 404 Data Translation Lookaside Buffers 405 406 Returns: 407 Array composed of detected data translation lookaside buffers. Array is sorted in ascending order. 408 +/ 409 immutable(Tlb)[] mir_cpuid_dTlb() { return _dTlb[0 .. _dTlb_length]; } 410 /// ditto 411 alias dTlb = mir_cpuid_dTlb; 412 413 /++ 414 Instruction Translation Lookaside Buffers 415 416 Returns: 417 Array composed of detected instruction translation lookaside buffers. Array is sorted in ascending order. 418 +/ 419 immutable(Tlb)[] mir_cpuid_iTlb() { return _iTlb[0 .. _iTlb_length]; } 420 /// ditto 421 alias iTlb = mir_cpuid_iTlb; 422 423 /++ 424 Unified Translation Lookaside Buffers 425 426 Returns: 427 Array composed of detected unified translation lookaside buffers. Array is sorted in ascending order. 428 +/ 429 immutable(Tlb)[] mir_cpuid_uTlb() { return _uTlb[0 .. _uTlb_length]; } 430 /// ditto 431 alias uTlb = mir_cpuid_uTlb;