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;