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: Ilya Yaroshenko 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 import cpuid.x86_any; 105 106 mir_cpuid_x86_any_init(); 107 108 static import cpuid.intel; 109 static import cpuid.amd; 110 111 /// for old CPUs 112 if(htt) 113 { 114 _threads._mut = _cores._mut = maxLogicalProcessors; 115 _cores._mut /= 2; 116 } 117 118 if (vendorIndex == VendorIndex.amd || 119 vendorIndex == VendorIndex.amd_old || 120 vendorIndex == VendorIndex.centaur || 121 vendorIndex == VendorIndex.hygon) 122 { 123 // Caches and TLB 124 if(maxExtendedLeaf >= 0x8000_0005) 125 { 126 // Level 1 127 auto leafExt5 = cpuid.amd.LeafExt5Information(_cpuid(0x8000_0005)); 128 129 alias CacheAssoc = typeof(Cache.associative); 130 alias TlbAssoc = typeof(Tlb.associative); 131 132 if(leafExt5.L1DTlb4KSize) 133 { 134 _dTlb._mut[0].page = 4; 135 _dTlb._mut[0].entries = leafExt5.L1DTlb4KSize; 136 _dTlb._mut[0].associative = leafExt5.L1DTlb4KAssoc.assocCopy!TlbAssoc; 137 _dTlb_length._mut = 1; 138 } 139 if(leafExt5.L1ITlb4KSize) 140 { 141 _iTlb._mut[0].page = 4; 142 _iTlb._mut[0].entries = leafExt5.L1ITlb4KSize; 143 _iTlb._mut[0].associative = leafExt5.L1ITlb4KAssoc.assocCopy!TlbAssoc; 144 _iTlb_length._mut = 1; 145 } 146 if(leafExt5.L1DcSize) 147 { 148 _dCache_length._mut = 1; 149 _dCache._mut[0].size = leafExt5.L1DcSize; 150 _dCache._mut[0].line = leafExt5.L1DcLineSize; 151 _dCache._mut[0].associative = leafExt5.L1DcAssoc.assocCopy!CacheAssoc; 152 } 153 if(leafExt5.L1IcSize) 154 { 155 _iCache_length._mut = 1; 156 _iCache._mut[0].size = leafExt5.L1IcSize; 157 _iCache._mut[0].line = leafExt5.L1IcLineSize; 158 _iCache._mut[0].associative = leafExt5.L1IcAssoc.assocCopy!CacheAssoc; 159 } 160 161 // Levels 2 and 3 162 if(maxExtendedLeaf >= 0x8000_0006) 163 { 164 import cpuid.amd: decodeL2or3Assoc; 165 auto leafExt6 = cpuid.amd.LeafExt6Information(_cpuid(0x8000_0006)); 166 167 if(leafExt6.L2DTlb4KSize) 168 { 169 _dTlb._mut[_dTlb_length].page = 4; 170 _dTlb._mut[_dTlb_length].entries = leafExt6.L2DTlb4KSize; 171 _dTlb._mut[_dTlb_length].associative = leafExt6.L2DTlb4KAssoc.decodeL2or3Assoc!TlbAssoc; 172 _dTlb_length._mut++; 173 } 174 if(leafExt6.L2ITlb4KSize) 175 { 176 _iTlb._mut[_iTlb_length].page = 4; 177 _iTlb._mut[_iTlb_length].entries = leafExt6.L2ITlb4KSize; 178 _iTlb._mut[_iTlb_length].associative = leafExt6.L2ITlb4KAssoc.decodeL2or3Assoc!TlbAssoc; 179 _iTlb_length._mut++; 180 } 181 if(leafExt6.L2Size) 182 { 183 _uCache._mut[_uCache_length].size = leafExt6.L2Size; 184 _uCache._mut[_uCache_length].line = cast(typeof(Cache.line)) leafExt6.L2LineSize; 185 _uCache._mut[_uCache_length].associative = leafExt6.L2Assoc.decodeL2or3Assoc!CacheAssoc; 186 _uCache_length._mut++; 187 } 188 if(leafExt6.L3Size) 189 { 190 _uCache._mut[_uCache_length].size = leafExt6.L3Size * 512; 191 _uCache._mut[_uCache_length].line = cast(typeof(Cache.line)) leafExt6.L3LineSize; 192 _uCache._mut[_uCache_length].associative = leafExt6.L3Assoc.decodeL2or3Assoc!CacheAssoc; 193 _uCache_length._mut++; 194 } 195 196 if(maxExtendedLeaf >= 0x8000_0008) 197 { 198 auto leafExt8 = cpuid.amd.LeafExt8Information(_cpuid(0x8000_0008)); 199 _threads._mut = leafExt8.NC + 1; 200 201 if (maxExtendedLeaf >= 0x8000_001E) 202 { 203 auto leafExt1E = cpuid.amd.LeafExt1EInformation(_cpuid(0x8000_001E)); 204 _cores._mut = _threads / (leafExt1E.ThreadsPerCore + 1); 205 } 206 } 207 } 208 } 209 } 210 else 211 { 212 /// Other vendors 213 if(maxBasicLeaf >= 0x2) 214 { 215 /// Get TLB and Cache info 216 auto leaf2 = cpuid.intel.Leaf2Information(_cpuid(2)); 217 218 /// Fill cache info 219 if(leaf2.dtlb.size) 220 { 221 _dTlb._mut[0] = leaf2.dtlb; 222 _dTlb_length._mut = 1; 223 } 224 if(leaf2.dtlb1.size) 225 { 226 _dTlb._mut[_dTlb_length] = leaf2.dtlb1; 227 _dTlb_length._mut++; 228 } 229 if(leaf2.itlb.size) 230 { 231 _iTlb._mut[0] = leaf2.itlb; 232 _iTlb_length._mut = 1; 233 } 234 if(leaf2.utlb.size) 235 { 236 _uTlb._mut[0] = leaf2.utlb; 237 _uTlb_length._mut = 1; 238 } 239 240 if(maxBasicLeaf >= 0x4) 241 { 242 /// Fill cache info from leaf 4 243 cpuid.intel.Leaf4Information leaf4 = void; 244 Cache cache; 245 Leaf4Loop: foreach(uint ecx; 0 .. 12) 246 { 247 leaf4.info = _cpuid(4, ecx); 248 leaf4.fill(cache); 249 250 with(cpuid.intel.Leaf4Information.Type) 251 switch(leaf4.type) 252 { 253 case data: 254 if(_dCache_length < _dCache.length) 255 _dCache._mut[_dCache_length._mut++] = cache; 256 break; 257 case instruction: 258 if(_iCache_length < _iCache.length) 259 _iCache._mut[_iCache_length._mut++] = cache; 260 break; 261 case unified: 262 if(_uCache_length < _uCache.length) 263 _uCache._mut[_uCache_length._mut++] = cache; 264 break; 265 default: break Leaf4Loop; 266 } 267 /// Fill core number for old CPUs 268 _cores._mut = leaf4.maxCorePerCPU; 269 } 270 if(maxBasicLeaf >= 0xB) 271 { 272 auto th = cast(ushort) _cpuid(0xB, 1).b; 273 if(th > 0) 274 _threads._mut = th; 275 auto threadsPerCore = cast(ushort) _cpuid(0xB, 0).b; 276 if(threadsPerCore) 277 { 278 _cores._mut = _threads / threadsPerCore; 279 } 280 } 281 } 282 else 283 { 284 /// Fill cache info from leaf 2 285 if(leaf2.l1.size) 286 { 287 _dCache._mut[0] = leaf2.l1; 288 _dCache_length._mut = 1; 289 } 290 if(leaf2.il1.size) 291 { 292 _iCache._mut[0] = leaf2.il1; 293 _iCache_length._mut = 1; 294 } 295 if(leaf2.l2.size) 296 { 297 _uCache._mut[0] = leaf2.l2; 298 _uCache_length._mut = 1; 299 } 300 if(leaf2.l3.size) 301 { 302 _uCache._mut[_uCache_length] = leaf2.l3; 303 _uCache_length._mut++; 304 } 305 } 306 } 307 } 308 309 if(!_cpus) _cpus._mut = 1; 310 if(!_cores) _cores._mut = 1; 311 if(!_threads) _threads._mut = 1; 312 if(_threads < _cores) _threads._mut = _cores; 313 314 if(_iCache_length) _iCache._mut[0].cores = 1; 315 if(_dCache_length) _dCache._mut[0].cores = 1; 316 switch(_uCache_length) 317 { 318 case 0: 319 break; 320 case 1: 321 _uCache._mut[0].cores = cast(typeof(Cache.cores)) _cores; 322 break; 323 default: 324 _uCache._mut[0].cores = 1; 325 foreach(i; 1.._uCache_length) 326 _uCache._mut[i].cores = cast(typeof(Cache.cores)) _cores; 327 } 328 } 329 else 330 pragma(crt_constructor) 331 void mir_cpuid_init() 332 { 333 _cpus._mut = 1; 334 _cores._mut = 1; 335 _threads._mut = 1; 336 } 337 /// ditto 338 339 alias cpuid_init = mir_cpuid_init; 340 341 pure @trusted: 342 343 /++ 344 Total number of CPU packages. 345 Note: not implemented 346 +/ 347 uint mir_cpuid_cpus() { return _cpus; } 348 /// ditto 349 alias cpus = mir_cpuid_cpus; 350 351 /++ 352 Total number of cores per CPU. 353 +/ 354 uint mir_cpuid_cores() { return _cores; } 355 /// ditto 356 alias cores = mir_cpuid_cores; 357 358 /++ 359 Total number of threads per CPU. 360 +/ 361 uint mir_cpuid_threads() { return _threads; } 362 /// ditto 363 alias threads = mir_cpuid_threads; 364 365 /++ 366 Data Caches 367 368 Returns: 369 Array composed of detected data caches. Array is sorted in ascending order. 370 +/ 371 immutable(Cache)[] mir_cpuid_dCache() { return _dCache[0 .. _dCache_length]; } 372 /// ditto 373 alias dCache = mir_cpuid_dCache; 374 375 /++ 376 Instruction Caches 377 378 Returns: 379 Array composed of detected instruction caches. Array is sorted in ascending order. 380 +/ 381 immutable(Cache)[] mir_cpuid_iCache() { return _iCache[0 .. _iCache_length]; } 382 /// ditto 383 alias iCache = mir_cpuid_iCache; 384 385 /++ 386 Unified Caches 387 388 Returns: 389 Array composed of detected unified caches. Array is sorted in ascending order. 390 +/ 391 immutable(Cache)[] mir_cpuid_uCache() { return _uCache[0 .. _uCache_length]; } 392 /// ditto 393 alias uCache = mir_cpuid_uCache; 394 395 /++ 396 Data Translation Lookaside Buffers 397 398 Returns: 399 Array composed of detected data translation lookaside buffers. Array is sorted in ascending order. 400 +/ 401 immutable(Tlb)[] mir_cpuid_dTlb() { return _dTlb[0 .. _dTlb_length]; } 402 /// ditto 403 alias dTlb = mir_cpuid_dTlb; 404 405 /++ 406 Instruction Translation Lookaside Buffers 407 408 Returns: 409 Array composed of detected instruction translation lookaside buffers. Array is sorted in ascending order. 410 +/ 411 immutable(Tlb)[] mir_cpuid_iTlb() { return _iTlb[0 .. _iTlb_length]; } 412 /// ditto 413 alias iTlb = mir_cpuid_iTlb; 414 415 /++ 416 Unified Translation Lookaside Buffers 417 418 Returns: 419 Array composed of detected unified translation lookaside buffers. Array is sorted in ascending order. 420 +/ 421 immutable(Tlb)[] mir_cpuid_uTlb() { return _uTlb[0 .. _uTlb_length]; } 422 /// ditto 423 alias uTlb = mir_cpuid_uTlb;