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;