authorgravatar for alichay@alichay-fedora.homealichay <alichay@alichay-fedora.home> 2020-03-04 07:40:30-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 18:52:09-05:00
loge24f29bbadec8b22c11d876559cd80cd20ff623e
tree6e0bd27a935aeb7d0a51a08f64dbceabb54a3a20
parent7f975bf09f4e6e81d68c9a573ac2e31b997b5816
signaturelock-open Commit is signed but in an unrecognized format.

Added self-hosted x86 CPU detection.


3 files changed, 681 insertions(+), 4 deletions(-)

lib/std/zig/system.zig+15-3
...@@ -844,8 +844,20 @@ pub const NativeTargetInfo = struct {...@@ -844,8 +844,20 @@ pub const NativeTargetInfo = struct {
844 }844 }
845845
846 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {846 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
847 // TODO Detect native CPU model & features. Until that is implemented we use baseline.847
848 return baselineCpuAndFeatures(cross_target);848 var baseline = baselineCpuAndFeatures(cross_target);
849
850 switch(Target.current.cpu.arch) {
851 .x86_64, .i386 => {
852 const x86_detection = @import("system/x86.zig");
853 x86_detection.detectNativeCpuAndFeatures(&baseline);
854 return baseline;
855 },
856 else => {
857 // // TODO Detect native CPU model & features. Until that is implemented we use baseline.
858 return baseline;
859 }
860 }
849 }861 }
850862
851 fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {863 fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
...@@ -853,4 +865,4 @@ pub const NativeTargetInfo = struct {...@@ -853,4 +865,4 @@ pub const NativeTargetInfo = struct {
853 cross_target.updateCpuFeatures(&adjusted_baseline.features);865 cross_target.updateCpuFeatures(&adjusted_baseline.features);
854 return adjusted_baseline;866 return adjusted_baseline;
855 }867 }
856};868};
\ No newline at end of file
lib/std/zig/system/x86.zig created+664
...@@ -0,0 +1,664 @@
1const std = @import("std");
2const Target = std.Target;
3const CrossTarget = std.zig.CrossTarget;
4
5fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void {
6
7 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
8
9 if(enabled) cpu.features.addFeature(idx)
10 else cpu.features.removeFeature(idx);
11}
12
13fn hasFeature(cpu: *Target.Cpu, feature: Target.x86.Feature) bool {
14 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
15 return cpu.features.isEnabled(idx);
16}
17
18inline fn bit(input: u32, offset: u5) bool {
19 return (input >> offset) & 1 != 0;
20}
21
22pub fn detectNativeCpuAndFeatures(cpu: *Target.Cpu) void {
23
24 defer {
25 // Whenever we find a model, add that model's featureset.
26 cpu.features.addFeatureSet(cpu.model.features);
27 }
28
29 // When we can't identify a specific model,
30 // we guess based on processor features.
31 // This seems to be the accepted standard.
32
33 detectNativeFeatures(cpu);
34
35 var leaf = cpuid(0, 0);
36 const max_leaf = leaf.eax;
37 const vendor = leaf.ebx;
38 if(max_leaf < 1) {
39 cpu.model = &Target.x86.cpu.generic;
40 return;
41 }
42
43 leaf = cpuid(0x1, 0);
44
45 const brand_id = leaf.ebx & 0xff;
46 var family: u32 = 0;
47 var model: u32 = 0;
48
49 { // Detect model and family
50 family = (leaf.eax >> 8) & 0xf;
51 model = (leaf.eax >> 4) & 0xf;
52 if (family == 6 or family == 0xf) {
53 if (family == 0xf) {
54 family += (leaf.eax >> 20) & 0xff;
55 }
56 model += ((leaf.eax >> 16) & 0xf) << 4;
57 }
58 }
59
60 switch(vendor) {
61 0x756e6547 => {
62 detectIntelProcessor(cpu, family, model, brand_id);
63 },
64 0x68747541 => {
65 detectAMDProcessor(cpu, family, model);
66 },
67 else => {
68 cpu.model = &Target.x86.cpu.generic;
69 },
70 }
71
72}
73
74fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32) void {
75 if (brand_id != 0) {
76 cpu.model = &Target.x86.cpu.generic;
77 return;
78 }
79 switch(family) {
80 3 => {
81 cpu.model = &Target.x86.cpu._i386;
82 return;
83 },
84 4 => {
85 cpu.model = &Target.x86.cpu._i486;
86 return;
87 },
88 5 => {
89 if(hasFeature(cpu, .mmx)) {
90 cpu.model = &Target.x86.cpu.pentium_mmx;
91 return;
92 }
93 cpu.model = &Target.x86.cpu.pentium;
94 return;
95 },
96 6 => {
97 switch(model) {
98 0x01 => {
99 cpu.model = &Target.x86.cpu.pentiumpro;
100 return;
101 },
102 0x03, 0x05, 0x06 => {
103 cpu.model = &Target.x86.cpu.pentium2;
104 return;
105 },
106 0x07, 0x08, 0x0a, 0x0b => {
107 cpu.model = &Target.x86.cpu.pentium3;
108 return;
109 },
110 0x09, 0x0d, 0x15 => {
111 cpu.model = &Target.x86.cpu.pentium_m;
112 return;
113 },
114 0x0e => {
115 cpu.model = &Target.x86.cpu.yonah;
116 return;
117 },
118 0x0f, 0x16 => {
119 cpu.model = &Target.x86.cpu.core2;
120 return;
121 },
122 0x17, 0x1d => {
123 cpu.model = &Target.x86.cpu.penryn;
124 return;
125 },
126 0x1a, 0x1e, 0x1f, 0x2e => {
127 cpu.model = &Target.x86.cpu.nehalem;
128 return;
129 },
130 0x25, 0x2c, 0x2f => {
131 cpu.model = &Target.x86.cpu.westmere;
132 return;
133 },
134 0x2a, 0x2d => {
135 cpu.model = &Target.x86.cpu.sandybridge;
136 return;
137 },
138 0x3a, 0x3e => {
139 cpu.model = &Target.x86.cpu.ivybridge;
140 return;
141 },
142 0x3c, 0x3f, 0x45, 0x46 => {
143 cpu.model = &Target.x86.cpu.haswell;
144 return;
145 },
146 0x3d, 0x47, 0x4f, 0x56 => {
147 cpu.model = &Target.x86.cpu.broadwell;
148 return;
149 },
150 0x4e, 0x5e, 0x8e, 0x9e => {
151 cpu.model = &Target.x86.cpu.skylake;
152 return;
153 },
154 0x55 => {
155 if(hasFeature(cpu, .avx512bf16)) {
156 cpu.model = &Target.x86.cpu.cooperlake;
157 return;
158 } else if(hasFeature(cpu, .avx512vnni)) {
159 cpu.model = &Target.x86.cpu.cascadelake;
160 return;
161 } else {
162 cpu.model = &Target.x86.cpu.skylake_avx512;
163 return;
164 }
165 },
166 0x66 => {
167 cpu.model = &Target.x86.cpu.cannonlake;
168 return;
169 },
170 0x7d, 0x7e => {
171 cpu.model = &Target.x86.cpu.icelake_client;
172 return;
173 },
174 0x6a, 0x6c => {
175 cpu.model = &Target.x86.cpu.icelake_server;
176 return;
177 },
178 0x1c, 0x26, 0x27, 0x35, 0x36 => {
179 cpu.model = &Target.x86.cpu.bonnell;
180 return;
181 },
182 0x37, 0x4a, 0x4d, 0x5a, 0x5d, 0x4c => {
183 cpu.model = &Target.x86.cpu.silvermont;
184 return;
185 },
186 0x5c, 0x5f => {
187 cpu.model = &Target.x86.cpu.goldmont;
188 return;
189 },
190 0x7a => {
191 cpu.model = &Target.x86.cpu.goldmont_plus;
192 return;
193 },
194 0x86 => {
195 cpu.model = &Target.x86.cpu.tremont;
196 return;
197 },
198 0x57 => {
199 cpu.model = &Target.x86.cpu.knl;
200 return;
201 },
202 0x85 => {
203 cpu.model = &Target.x86.cpu.knm;
204 return;
205 },
206 else => {
207 // Unknown, try to guess.
208 // TODO detect tigerlake host
209 if(hasFeature(cpu, .avx512vp2intersect)) {
210 // TODO no tigerlake entry in Target.x86.cpu
211 //cpu.model = &Target.x86.cpu.tigerlake;
212 cpu.model = &Target.x86.cpu.nehalem;
213 return;
214 }
215 if(hasFeature(cpu, .avx512vbmi2)) {
216 cpu.model = &Target.x86.cpu.icelake_client;
217 return;
218 }
219 if(hasFeature(cpu, .avx512vbmi)) {
220 cpu.model = &Target.x86.cpu.cannonlake;
221 return;
222 }
223 if(hasFeature(cpu, .avx512bf16)) {
224 cpu.model = &Target.x86.cpu.cooperlake;
225 return;
226 }
227 if(hasFeature(cpu, .avx512vnni)) {
228 cpu.model = &Target.x86.cpu.cascadelake;
229 return;
230 }
231 if(hasFeature(cpu, .avx512vl)) {
232 cpu.model = &Target.x86.cpu.skylake_avx512;
233 return;
234 }
235 if(hasFeature(cpu, .avx512er)) {
236 cpu.model = &Target.x86.cpu.knl;
237 return;
238 }
239 if(hasFeature(cpu, .clflushopt)) {
240 if(hasFeature(cpu, .sha)) {
241 cpu.model = &Target.x86.cpu.goldmont;
242 return;
243 } else {
244 cpu.model = &Target.x86.cpu.skylake;
245 return;
246 }
247 }
248 if(hasFeature(cpu, .adx)) {
249 cpu.model = &Target.x86.cpu.broadwell;
250 return;
251 }
252 if(hasFeature(cpu, .avx2)) {
253 cpu.model = &Target.x86.cpu.haswell;
254 return;
255 }
256 if(hasFeature(cpu, .avx)) {
257 cpu.model = &Target.x86.cpu.sandybridge;
258 return;
259 }
260 if(hasFeature(cpu, .sse4_2)) {
261 if(hasFeature(cpu, .movbe)) {
262 cpu.model = &Target.x86.cpu.silvermont;
263 return;
264 } else {
265 cpu.model = &Target.x86.cpu.nehalem;
266 return;
267 }
268 }
269 if(hasFeature(cpu, .sse4_1)) {
270 cpu.model = &Target.x86.cpu.penryn;
271 return;
272 }
273 if(hasFeature(cpu, .sse3)) {
274 if(hasFeature(cpu, .movbe)) {
275 cpu.model = &Target.x86.cpu.bonnell;
276 return;
277 } else {
278 cpu.model = &Target.x86.cpu.core2;
279 return;
280 }
281 }
282
283 if(hasFeature(cpu, .@"64bit")) {
284 cpu.model = &Target.x86.cpu.core2;
285 return;
286 }
287
288 if(hasFeature(cpu, .sse3)) {
289 cpu.model = &Target.x86.cpu.yonah;
290 return;
291 }
292 if(hasFeature(cpu, .sse2)) {
293 cpu.model = &Target.x86.cpu.pentium_m;
294 return;
295 }
296 if(hasFeature(cpu, .sse)) {
297 cpu.model = &Target.x86.cpu.pentium3;
298 return;
299 }
300 if(hasFeature(cpu, .mmx)) {
301 cpu.model = &Target.x86.cpu.pentium2;
302 return;
303 }
304 cpu.model = &Target.x86.cpu.pentiumpro;
305 return;
306 },
307 }
308 },
309 15 => {
310 if(hasFeature(cpu, .@"64bit")) {
311 cpu.model = &Target.x86.cpu.nocona;
312 return;
313 }
314 if(hasFeature(cpu, .sse3)) {
315 cpu.model = &Target.x86.cpu.prescott;
316 return;
317 }
318 cpu.model = &Target.x86.cpu.pentium4;
319 return;
320 },
321 else => {
322 cpu.model = &Target.x86.cpu.generic;
323 return;
324 }
325 }
326}
327
328fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
329 // AMD's cpuid information is less than optimal for determining a CPU model.
330 // This is very unscientific, and not necessarily correct.
331
332 switch(family) {
333 4 => {
334 cpu.model = &Target.x86.cpu._i486;
335 return;
336 },
337 5 => {
338 cpu.model = &Target.x86.cpu.pentium;
339 switch(model) {
340 6, 7 => {
341 cpu.model = &Target.x86.cpu.k6;
342 return;
343 },
344 8 => {
345 cpu.model = &Target.x86.cpu.k6_2;
346 return;
347 },
348 9, 13 => {
349 cpu.model = &Target.x86.cpu.k6_3;
350 return;
351 },
352 10 => {
353 cpu.model = &Target.x86.cpu.geode;
354 return;
355 },
356 else => {},
357 }
358 return;
359 },
360 6 => {
361 if(hasFeature(cpu, .sse)) {
362 cpu.model = &Target.x86.cpu.athlon_xp;
363 return;
364 }
365 cpu.model = &Target.x86.cpu.athlon;
366 return;
367 },
368 15 => {
369 if(hasFeature(cpu, .sse3)) {
370 cpu.model = &Target.x86.cpu.k8_sse3;
371 return;
372 }
373 cpu.model = &Target.x86.cpu.k8;
374 return;
375 },
376 16 => {
377 cpu.model = &Target.x86.cpu.amdfam10;
378 return;
379 },
380 20 => {
381 cpu.model = &Target.x86.cpu.btver1;
382 return;
383 },
384 21 => {
385 cpu.model = &Target.x86.cpu.bdver1;
386 if(model >= 0x60 and model <= 0x7f) {
387 cpu.model = &Target.x86.cpu.bdver4;
388 return;
389 }
390 if(model >= 0x30 and model <= 0x3f) {
391 cpu.model = &Target.x86.cpu.bdver3;
392 return;
393 }
394 if((model >= 0x10 and model <= 0x1f) or model == 0x02) {
395 cpu.model = &Target.x86.cpu.bdver2;
396 return;
397 }
398 return;
399 },
400 22 => {
401 cpu.model = &Target.x86.cpu.btver2;
402 return;
403 },
404 23 => {
405 cpu.model = &Target.x86.cpu.znver1;
406 if((model >= 0x30 and model <= 0x3f) or model == 0x71) {
407 cpu.model = &Target.x86.cpu.znver2;
408 return;
409 }
410 return;
411 },
412 else => {
413 cpu.model = &Target.x86.cpu.generic;
414 return;
415 }
416 }
417}
418
419fn detectNativeFeatures(cpu: *Target.Cpu) void {
420
421 var leaf = cpuid(0, 0);
422
423 const max_level = leaf.eax;
424
425 leaf = cpuid(1, 0);
426
427 setFeature(cpu, .cx8, bit(leaf.edx, 8));
428 setFeature(cpu, .cx8, bit(leaf.edx, 8));
429 setFeature(cpu, .cmov, bit(leaf.edx, 15));
430 setFeature(cpu, .mmx, bit(leaf.edx, 23));
431 setFeature(cpu, .fxsr, bit(leaf.edx, 24));
432 setFeature(cpu, .sse, bit(leaf.edx, 25));
433 setFeature(cpu, .sse2, bit(leaf.edx, 26));
434 setFeature(cpu, .sse3, bit(leaf.ecx, 0));
435 setFeature(cpu, .pclmul, bit(leaf.ecx, 1));
436 setFeature(cpu, .ssse3, bit(leaf.ecx, 9));
437 setFeature(cpu, .cx16, bit(leaf.ecx, 13));
438 setFeature(cpu, .sse4_1, bit(leaf.ecx, 19));
439 setFeature(cpu, .sse4_2, bit(leaf.ecx, 20));
440 setFeature(cpu, .movbe, bit(leaf.ecx, 22));
441 setFeature(cpu, .popcnt, bit(leaf.ecx, 23));
442 setFeature(cpu, .aes, bit(leaf.ecx, 25));
443 setFeature(cpu, .rdrnd, bit(leaf.ecx, 30));
444
445 const has_avx_save = bit(leaf.ecx, 27) and
446 bit(leaf.ecx, 28) and
447 ((leaf.eax & 0x6) == 0x6);
448
449 // Darwin lazily saves the AVX512 context on first use: trust that the OS will
450 // save the AVX512 context if we use AVX512 instructions, even the bit is not
451 // set right now.
452 const has_avx512_save = switch(Target.current.isDarwin()) {
453 true => true,
454 false => has_avx_save and ((leaf.eax & 0xE0) == 0xE0),
455 };
456
457 setFeature(cpu, .avx, has_avx_save);
458 setFeature(cpu, .fma, has_avx_save and bit(leaf.ecx, 12));
459 // Only enable XSAVE if OS has enabled support for saving YMM state.
460 setFeature(cpu, .xsave, has_avx_save and bit(leaf.ecx, 26));
461 setFeature(cpu, .f16c, has_avx_save and bit(leaf.ecx, 29));
462
463 leaf = cpuid(0x80000000, 0);
464 const max_ext_level = leaf.eax;
465
466 if(max_ext_level >= 0x80000001) {
467 leaf = cpuid(0x80000001, 0);
468 setFeature(cpu, .sahf, bit(leaf.ecx, 0));
469 setFeature(cpu, .lzcnt, bit(leaf.ecx, 5));
470 setFeature(cpu, .sse4a, bit(leaf.ecx, 6));
471 setFeature(cpu, .prfchw, bit(leaf.ecx, 8));
472 setFeature(cpu, .xop, bit(leaf.ecx, 11) and has_avx_save);
473 setFeature(cpu, .lwp, bit(leaf.ecx, 15));
474 setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx_save);
475 setFeature(cpu, .tbm, bit(leaf.ecx, 21));
476 setFeature(cpu, .mwaitx, bit(leaf.ecx, 29));
477 setFeature(cpu, .@"64bit", bit(leaf.edx, 29));
478 } else {
479 for([_]Target.x86.Feature{
480 .sahf, .lzcnt, .sse4a, .prfchw, .xop,
481 .lwp, .fma4, .tbm, .mwaitx, .@"64bit"
482 }) |feat| {
483 setFeature(cpu, feat, false);
484 }
485 }
486
487 // Misc. memory-related features.
488 if(max_ext_level >= 0x80000008) {
489 leaf = cpuid(80000008, 0);
490 setFeature(cpu, .clzero, bit(leaf.ebx, 0));
491 setFeature(cpu, .wbnoinvd, bit(leaf.ebx, 9));
492 } else {
493 for([_]Target.x86.Feature{ .clzero, .wbnoinvd }) |feat| {
494 setFeature(cpu, feat, false);
495 }
496 }
497
498 if(max_level >= 7) {
499 leaf = cpuid(0x7, 0);
500
501 setFeature(cpu, .fsgsbase, bit(leaf.ebx, 0));
502 setFeature(cpu, .sgx, bit(leaf.ebx, 2));
503 setFeature(cpu, .bmi, bit(leaf.ebx, 3));
504 // AVX2 is only supported if we have the OS save support from AVX.
505 setFeature(cpu, .avx2, bit(leaf.ebx, 5) and has_avx_save);
506 setFeature(cpu, .bmi2, bit(leaf.ebx, 8));
507 setFeature(cpu, .invpcid, bit(leaf.ebx, 10));
508 setFeature(cpu, .rtm, bit(leaf.ebx, 11));
509 // AVX512 is only supported if the OS supports the context save for it.
510 setFeature(cpu, .avx512f, bit(leaf.ebx, 16) and has_avx512_save);
511 setFeature(cpu, .avx512dq, bit(leaf.ebx, 17) and has_avx512_save);
512 setFeature(cpu, .rdseed, bit(leaf.ebx, 18));
513 setFeature(cpu, .adx, bit(leaf.ebx, 19));
514 setFeature(cpu, .avx512ifma, bit(leaf.ebx, 21) and has_avx512_save);
515 setFeature(cpu, .clflushopt, bit(leaf.ebx, 23));
516 setFeature(cpu, .clwb, bit(leaf.ebx, 24));
517 setFeature(cpu, .avx512pf, bit(leaf.ebx, 26) and has_avx512_save);
518 setFeature(cpu, .avx512er, bit(leaf.ebx, 27) and has_avx512_save);
519 setFeature(cpu, .avx512cd, bit(leaf.ebx, 28) and has_avx512_save);
520 setFeature(cpu, .sha, bit(leaf.ebx, 29));
521 setFeature(cpu, .avx512bw, bit(leaf.ebx, 30) and has_avx512_save);
522 setFeature(cpu, .avx512vl, bit(leaf.ebx, 31) and has_avx512_save);
523
524 setFeature(cpu, .prefetchwt1, bit(leaf.ecx, 0));
525 setFeature(cpu, .avx512vbmi, bit(leaf.ecx, 1) and has_avx512_save);
526 setFeature(cpu, .pku, bit(leaf.ecx, 4));
527 setFeature(cpu, .waitpkg, bit(leaf.ecx, 5));
528 setFeature(cpu, .avx512vbmi2, bit(leaf.ecx, 6) and has_avx512_save);
529 setFeature(cpu, .shstk, bit(leaf.ecx, 7));
530 setFeature(cpu, .gfni, bit(leaf.ecx, 8));
531 setFeature(cpu, .vaes, bit(leaf.ecx, 9) and has_avx_save);
532 setFeature(cpu, .vpclmulqdq, bit(leaf.ecx, 10) and has_avx_save);
533 setFeature(cpu, .avx512vnni, bit(leaf.ecx, 11) and has_avx512_save);
534 setFeature(cpu, .avx512bitalg, bit(leaf.ecx, 12) and has_avx512_save);
535 setFeature(cpu, .avx512vpopcntdq, bit(leaf.ecx, 14) and has_avx512_save);
536 setFeature(cpu, .avx512vp2intersect, bit(leaf.edx, 8) and has_avx512_save);
537 setFeature(cpu, .rdpid, bit(leaf.ecx, 22));
538 setFeature(cpu, .cldemote, bit(leaf.ecx, 25));
539 setFeature(cpu, .movdiri, bit(leaf.ecx, 27));
540 setFeature(cpu, .movdir64b, bit(leaf.ecx, 28));
541 setFeature(cpu, .enqcmd, bit(leaf.ecx, 29));
542
543 // There are two CPUID leafs which information associated with the pconfig
544 // instruction:
545 // EAX=0x7, ECX=0x0 indicates the availability of the instruction (via the 18th
546 // bit of EDX), while the EAX=0x1b leaf returns information on the
547 // availability of specific pconfig leafs.
548 // The target feature here only refers to the the first of these two.
549 // Users might need to check for the availability of specific pconfig
550 // leaves using cpuid, since that information is ignored while
551 // detecting features using the "-march=native" flag.
552 // For more info, see X86 ISA docs.
553 setFeature(cpu, .pconfig, bit(leaf.edx, 18));
554
555 // TODO I feel unsure about this check.
556 // It doesn't really seem to check for 7.1, just for 7.
557 // Is this a sound assumption to make?
558 // Note that this is what other implementations do, so I kind of trust it.
559 const has_leaf_7_1 = max_level >= 7;
560 if(has_leaf_7_1) {
561 leaf = cpuid(0x7, 0x1);
562 setFeature(cpu, .avx512bf16, bit(leaf.eax, 5) and has_avx512_save);
563 } else {
564 setFeature(cpu, .avx512bf16, false);
565 }
566 } else {
567 for([_]Target.x86.Feature{
568 .fsgsbase, .sgx, .bmi, .avx2,
569 .bmi2, .invpcid, .rtm, .avx512f,
570 .avx512dq, .rdseed, .adx, .avx512ifma,
571 .clflushopt, .clwb, .avx512pf, .avx512er,
572 .avx512cd, .sha, .avx512bw, .avx512vl,
573 .prefetchwt1, .avx512vbmi, .pku, .waitpkg,
574 .avx512vbmi2, .shstk, .gfni, .vaes,
575 .vpclmulqdq, .avx512vnni, .avx512bitalg, .avx512vpopcntdq,
576 .avx512vp2intersect, .rdpid, .cldemote, .movdiri,
577 .movdir64b, .enqcmd, .pconfig, .avx512bf16,
578 }) |feat| {
579 setFeature(cpu, feat, false);
580 }
581 }
582
583 if(max_level >= 0xD and has_avx_save) {
584 leaf = cpuid(0xD, 0x1);
585 // Only enable XSAVE if OS has enabled support for saving YMM state.
586 setFeature(cpu, .xsaveopt, bit(leaf.eax, 0));
587 setFeature(cpu, .xsavec, bit(leaf.eax, 1));
588 setFeature(cpu, .xsaves, bit(leaf.eax, 3));
589
590 } else {
591 for([_]Target.x86.Feature{ .xsaveopt, .xsavec, .xsaves }) |feat| {
592 setFeature(cpu, feat, false);
593 }
594 }
595
596 if(max_level >= 0x14) {
597 leaf = cpuid(0x14, 0);
598 setFeature(cpu, .ptwrite, bit(leaf.ebx, 4));
599 } else {
600 setFeature(cpu, .ptwrite, false);
601 }
602
603}
604
605const CpuidLeaf = packed struct {
606 eax: u32,
607 ebx: u32,
608 ecx: u32,
609 edx: u32,
610};
611
612fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
613 // Workaround for https://github.com/ziglang/zig/issues/215
614 // Inline assembly in zig only supports one output,
615 // so we pass a pointer to the struct.
616 var cpuid_leaf = CpuidLeaf {.eax = 0, .ebx = 0, .ecx = 0, .edx = 0};
617 var leaf_ptr = &cpuid_leaf;
618 switch(Target.current.cpu.arch) {
619 .i386 => {
620 _ = asm volatile (
621 \\ cpuid
622 \\ movl %%eax, (%%edi)
623 \\ movl %%ebx, 4(%%edi)
624 \\ movl %%ecx, 8(%%edi)
625 \\ movl %%edx, 12(%%edi)
626 : :
627 [leaf_id] "{eax}" (leaf_id),
628 [subid] "{ecx}" (subid),
629 [leaf_ptr] "{edi}" (leaf_ptr),
630 : "eax", "ebx", "ecx", "edx"
631 );
632 },
633 .x86_64 => {
634 _ = asm volatile (
635 \\ cpuid
636 \\ movl %%eax, (%%rdi)
637 \\ movl %%ebx, 4(%%rdi)
638 \\ movl %%ecx, 8(%%rdi)
639 \\ movl %%edx, 12(%%rdi)
640 : :
641 [leaf_id] "{eax}" (leaf_id),
642 [subid] "{ecx}" (subid),
643 [leaf_ptr] "{rdi}" (leaf_ptr),
644 : "eax", "ebx", "ecx", "edx"
645 );
646 },
647 else => unreachable,
648 }
649 return cpuid_leaf;
650}
651
652// Read control register 0 (XCR0). Used to detect features such as AVX.
653fn getXCR0() u32 {
654
655 return asm (
656 \\ .byte 0x0F, 0x01, 0xD0
657 : [ret] "={eax}" (-> u32)
658 : [number] "{eax}" (@as(u32, 0)),
659 [number] "{edx}" (@as(u32, 0)),
660 [number] "{ecx}" (@as(u32, 0)),
661 :
662 );
663}
664
src-self-hosted/stage2.zig+2-1
...@@ -1154,7 +1154,8 @@ fn enumInt(comptime Enum: type, int: c_int) Enum {...@@ -1154,7 +1154,8 @@ fn enumInt(comptime Enum: type, int: c_int) Enum {
11541154
1155fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {1155fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {
1156 var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);1156 var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
1157 if (cross_target.cpu_arch == null or cross_target.cpu_model == .native) {1157 if ((cross_target.cpu_arch == null or cross_target.cpu_model == .native) and
1158 (Target.current.cpu.arch != .i386 and Target.current.cpu.arch != .x86_64)) {
1158 // TODO We want to just use detected_info.target but implementing1159 // TODO We want to just use detected_info.target but implementing
1159 // CPU model & feature detection is todo so here we rely on LLVM.1160 // CPU model & feature detection is todo so here we rely on LLVM.
1160 const llvm = @import("llvm.zig");1161 const llvm = @import("llvm.zig");