authorgravatar for allisonalichay@gmail.comalichay <allisonalichay@gmail.com> 2020-03-05 22:12:15-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 18:52:09-05:00
logf19918256746ccc81b6ff11d17e98665b4853400
tree27cc0ce16501b11448ddfc7e724574d6c6077f2e
parente24f29bbadec8b22c11d876559cd80cd20ff623e
signaturelock-open Commit is signed but in an unrecognized format.

Cleaned up CPU detection and fixed incorrect detection bits.


3 files changed, 104 insertions(+), 178 deletions(-)

lib/std/zig/system.zig+17-9
......@@ -171,6 +171,8 @@ pub const NativeTargetInfo = struct {
171171
172172 dynamic_linker: DynamicLinker = DynamicLinker{},
173173
174 cpu_detected: bool = false,
175
174176 pub const DynamicLinker = Target.DynamicLinker;
175177
176178 pub const DetectError = error{
......@@ -191,6 +193,9 @@ pub const NativeTargetInfo = struct {
191193 /// deinitialization method.
192194 /// TODO Remove the Allocator requirement from this function.
193195 pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
196
197 var cpu_detected = true;
198
194199 const cpu = switch (cross_target.cpu_model) {
195200 .native => detectNativeCpuAndFeatures(cross_target),
196201 .baseline => baselineCpuAndFeatures(cross_target),
......@@ -203,6 +208,11 @@ pub const NativeTargetInfo = struct {
203208 cross_target.updateCpuFeatures(&adjusted_model.features);
204209 break :blk adjusted_model;
205210 },
211 } orelse backup_cpu_detection: {
212
213 // Temporarily use LLVM's cpu info as a backup
214 cpu_detected = false;
215 break :backup_cpu_detection baselineCpuAndFeatures(cross_target);
206216 };
207217
208218 var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
......@@ -318,7 +328,9 @@ pub const NativeTargetInfo = struct {
318328 os.version_range.linux.glibc = glibc;
319329 }
320330
321 return detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
331 var target = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
332 target.cpu_detected = cpu_detected;
333 return target;
322334 }
323335
324336 /// First we attempt to use the executable's own binary. If it is dynamically
......@@ -843,19 +855,15 @@ pub const NativeTargetInfo = struct {
843855 }
844856 }
845857
846 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
847
848 var baseline = baselineCpuAndFeatures(cross_target);
858 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) ?Target.Cpu {
849859
850860 switch(Target.current.cpu.arch) {
851861 .x86_64, .i386 => {
852 const x86_detection = @import("system/x86.zig");
853 x86_detection.detectNativeCpuAndFeatures(&baseline);
854 return baseline;
862 return @import("system/x86.zig").detectNativeCpuAndFeatures(cross_target);
855863 },
856864 else => {
857 // // TODO Detect native CPU model & features. Until that is implemented we use baseline.
858 return baseline;
865 // TODO flesh out CPU detection for more than just x86.
866 return null;
859867 }
860868 }
861869 }
lib/std/zig/system/x86.zig+86-167
......@@ -10,70 +10,66 @@ fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void
1010 else cpu.features.removeFeature(idx);
1111}
1212
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
1813inline fn bit(input: u32, offset: u5) bool {
1914 return (input >> offset) & 1 != 0;
2015}
2116
22pub fn detectNativeCpuAndFeatures(cpu: *Target.Cpu) void {
17pub fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
2318
24 defer {
25 // Whenever we find a model, add that model's featureset.
26 cpu.features.addFeatureSet(cpu.model.features);
27 }
19 var arch = cross_target.getCpuArch();
2820
29 // When we can't identify a specific model,
30 // we guess based on processor features.
31 // This seems to be the accepted standard.
21 var cpu = Target.Cpu {
22 .arch = arch,
23 .model = Target.Cpu.Model.baseline(arch),
24 .features = Target.Cpu.Feature.Set.empty,
25 };
3226
33 detectNativeFeatures(cpu);
27 detectNativeFeatures(&cpu, cross_target.getOsTag());
3428
3529 var leaf = cpuid(0, 0);
3630 const max_leaf = leaf.eax;
3731 const vendor = leaf.ebx;
38 if(max_leaf < 1) {
39 cpu.model = &Target.x86.cpu.generic;
40 return;
41 }
4232
43 leaf = cpuid(0x1, 0);
33 if(max_leaf > 0) {
34
35 leaf = cpuid(0x1, 0);
4436
45 const brand_id = leaf.ebx & 0xff;
46 var family: u32 = 0;
47 var model: u32 = 0;
37 const brand_id = leaf.ebx & 0xff;
38 var family: u32 = 0;
39 var model: u32 = 0;
4840
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;
41 { // Detect model and family
42 family = (leaf.eax >> 8) & 0xf;
43 model = (leaf.eax >> 4) & 0xf;
44 if (family == 6 or family == 0xf) {
45 if (family == 0xf) {
46 family += (leaf.eax >> 20) & 0xff;
47 }
48 model += ((leaf.eax >> 16) & 0xf) << 4;
5549 }
56 model += ((leaf.eax >> 16) & 0xf) << 4;
5750 }
58 }
5951
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 },
52 switch(vendor) {
53 0x756e6547 => {
54 detectIntelProcessor(&cpu, family, model, brand_id);
55 },
56 0x68747541 => {
57 detectAMDProcessor(&cpu, family, model);
58 },
59 else => {},
60 }
7061 }
7162
63 var model_features = cpu.model.features;
64 model_features.populateDependencies(cpu.arch.allFeaturesList());
65 cpu.features.addFeatureSet(model_features);
66
67 return cpu;
68
7269}
7370
7471fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32) void {
7572 if (brand_id != 0) {
76 cpu.model = &Target.x86.cpu.generic;
7773 return;
7874 }
7975 switch(family) {
......@@ -86,7 +82,7 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
8682 return;
8783 },
8884 5 => {
89 if(hasFeature(cpu, .mmx)) {
85 if(Target.x86.featureSetHas(cpu.features, .mmx)) {
9086 cpu.model = &Target.x86.cpu.pentium_mmx;
9187 return;
9288 }
......@@ -152,10 +148,10 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
152148 return;
153149 },
154150 0x55 => {
155 if(hasFeature(cpu, .avx512bf16)) {
151 if(Target.x86.featureSetHas(cpu.features, .avx512bf16)) {
156152 cpu.model = &Target.x86.cpu.cooperlake;
157153 return;
158 } else if(hasFeature(cpu, .avx512vnni)) {
154 } else if(Target.x86.featureSetHas(cpu.features, .avx512vnni)) {
159155 cpu.model = &Target.x86.cpu.cascadelake;
160156 return;
161157 } else {
......@@ -204,114 +200,18 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
204200 return;
205201 },
206202 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;
203 // Unknown CPU.
204 // Default to baseline x86_64 or i386 cpu.
305205 return;
306206 },
307207 }
308208 },
309209 15 => {
310 if(hasFeature(cpu, .@"64bit")) {
210 if(Target.x86.featureSetHas(cpu.features, .@"64bit")) {
311211 cpu.model = &Target.x86.cpu.nocona;
312212 return;
313213 }
314 if(hasFeature(cpu, .sse3)) {
214 if(Target.x86.featureSetHas(cpu.features, .sse3)) {
315215 cpu.model = &Target.x86.cpu.prescott;
316216 return;
317217 }
......@@ -319,7 +219,8 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
319219 return;
320220 },
321221 else => {
322 cpu.model = &Target.x86.cpu.generic;
222 // Unknown CPU.
223 // Default to baseline x86_64 or i386 cpu.
323224 return;
324225 }
325226 }
......@@ -358,7 +259,7 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
358259 return;
359260 },
360261 6 => {
361 if(hasFeature(cpu, .sse)) {
262 if(Target.x86.featureSetHas(cpu.features, .sse)) {
362263 cpu.model = &Target.x86.cpu.athlon_xp;
363264 return;
364265 }
......@@ -366,7 +267,7 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
366267 return;
367268 },
368269 15 => {
369 if(hasFeature(cpu, .sse3)) {
270 if(Target.x86.featureSetHas(cpu.features, .sse3)) {
370271 cpu.model = &Target.x86.cpu.k8_sse3;
371272 return;
372273 }
......@@ -410,13 +311,12 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
410311 return;
411312 },
412313 else => {
413 cpu.model = &Target.x86.cpu.generic;
414314 return;
415315 }
416316 }
417317}
418318
419fn detectNativeFeatures(cpu: *Target.Cpu) void {
319fn detectNativeFeatures(cpu: *Target.Cpu, os_type: Target.Os.Tag) void {
420320
421321 var leaf = cpuid(0, 0);
422322
......@@ -442,23 +342,42 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
442342 setFeature(cpu, .aes, bit(leaf.ecx, 25));
443343 setFeature(cpu, .rdrnd, bit(leaf.ecx, 30));
444344
445 const has_avx_save = bit(leaf.ecx, 27) and
446 bit(leaf.ecx, 28) and
447 ((leaf.eax & 0x6) == 0x6);
345 leaf.eax = getXCR0();
346
347 const has_avx = bit(leaf.ecx, 27) and
348 bit(leaf.ecx, 28) and
349 ((leaf.eax & 0x6) == 0x6);
350
351 // LLVM approaches avx512_save by hardcoding it to true on Darwin,
352 // because the kernel saves the context even if the bit is not set.
353 // https://github.com/llvm/llvm-project/blob/bca373f73fc82728a8335e7d6cd164e8747139ec/llvm/lib/Support/Host.cpp#L1378
354 //
355 // Google approaches this by using a different series of checks and flags,
356 // and this may report the feature more accurately on a technically correct
357 // but ultimately less useful level.
358 // https://github.com/google/cpu_features/blob/b5c271c53759b2b15ff91df19bd0b32f2966e275/src/cpuinfo_x86.c#L113
359 // (called from https://github.com/google/cpu_features/blob/b5c271c53759b2b15ff91df19bd0b32f2966e275/src/cpuinfo_x86.c#L1052)
360 //
361 // Right now, we use LLVM's approach, because even if the target doesn't support
362 // the feature, the kernel should provide the same functionality transparently,
363 // so the implementation details don't make a difference.
364 // That said, this flag impacts other CPU features' availability,
365 // so until we can verify that this doesn't come with side affects,
366 // we'll say TODO verify this.
448367
449368 // 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
369 // save the AVX512 context if we use AVX512 instructions, even if the bit is not
451370 // set right now.
452 const has_avx512_save = switch(Target.current.isDarwin()) {
371 const has_avx512_save = switch(os_type.isDarwin()) {
453372 true => true,
454 false => has_avx_save and ((leaf.eax & 0xE0) == 0xE0),
373 false => has_avx and ((leaf.eax & 0xE0) == 0xE0),
455374 };
456375
457 setFeature(cpu, .avx, has_avx_save);
458 setFeature(cpu, .fma, has_avx_save and bit(leaf.ecx, 12));
376 setFeature(cpu, .avx, has_avx);
377 setFeature(cpu, .fma, has_avx and bit(leaf.ecx, 12));
459378 // 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));
379 setFeature(cpu, .xsave, has_avx and bit(leaf.ecx, 26));
380 setFeature(cpu, .f16c, has_avx and bit(leaf.ecx, 29));
462381
463382 leaf = cpuid(0x80000000, 0);
464383 const max_ext_level = leaf.eax;
......@@ -469,9 +388,9 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
469388 setFeature(cpu, .lzcnt, bit(leaf.ecx, 5));
470389 setFeature(cpu, .sse4a, bit(leaf.ecx, 6));
471390 setFeature(cpu, .prfchw, bit(leaf.ecx, 8));
472 setFeature(cpu, .xop, bit(leaf.ecx, 11) and has_avx_save);
391 setFeature(cpu, .xop, bit(leaf.ecx, 11) and has_avx);
473392 setFeature(cpu, .lwp, bit(leaf.ecx, 15));
474 setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx_save);
393 setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx);
475394 setFeature(cpu, .tbm, bit(leaf.ecx, 21));
476395 setFeature(cpu, .mwaitx, bit(leaf.ecx, 29));
477396 setFeature(cpu, .@"64bit", bit(leaf.edx, 29));
......@@ -486,7 +405,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
486405
487406 // Misc. memory-related features.
488407 if(max_ext_level >= 0x80000008) {
489 leaf = cpuid(80000008, 0);
408 leaf = cpuid(0x80000008, 0);
490409 setFeature(cpu, .clzero, bit(leaf.ebx, 0));
491410 setFeature(cpu, .wbnoinvd, bit(leaf.ebx, 9));
492411 } else {
......@@ -495,14 +414,14 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
495414 }
496415 }
497416
498 if(max_level >= 7) {
417 if(max_level >= 0x7) {
499418 leaf = cpuid(0x7, 0);
500419
501420 setFeature(cpu, .fsgsbase, bit(leaf.ebx, 0));
502421 setFeature(cpu, .sgx, bit(leaf.ebx, 2));
503422 setFeature(cpu, .bmi, bit(leaf.ebx, 3));
504423 // 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);
424 setFeature(cpu, .avx2, bit(leaf.ebx, 5) and has_avx);
506425 setFeature(cpu, .bmi2, bit(leaf.ebx, 8));
507426 setFeature(cpu, .invpcid, bit(leaf.ebx, 10));
508427 setFeature(cpu, .rtm, bit(leaf.ebx, 11));
......@@ -528,8 +447,8 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
528447 setFeature(cpu, .avx512vbmi2, bit(leaf.ecx, 6) and has_avx512_save);
529448 setFeature(cpu, .shstk, bit(leaf.ecx, 7));
530449 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);
450 setFeature(cpu, .vaes, bit(leaf.ecx, 9) and has_avx);
451 setFeature(cpu, .vpclmulqdq, bit(leaf.ecx, 10) and has_avx);
533452 setFeature(cpu, .avx512vnni, bit(leaf.ecx, 11) and has_avx512_save);
534453 setFeature(cpu, .avx512bitalg, bit(leaf.ecx, 12) and has_avx512_save);
535454 setFeature(cpu, .avx512vpopcntdq, bit(leaf.ecx, 14) and has_avx512_save);
......@@ -580,7 +499,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu) void {
580499 }
581500 }
582501
583 if(max_level >= 0xD and has_avx_save) {
502 if(max_level >= 0xD and has_avx) {
584503 leaf = cpuid(0xD, 0x1);
585504 // Only enable XSAVE if OS has enabled support for saving YMM state.
586505 setFeature(cpu, .xsaveopt, bit(leaf.eax, 0));
src-self-hosted/stage2.zig+1-2
......@@ -1154,8 +1154,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum {
11541154
11551155fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {
11561156 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) and
1158 (Target.current.cpu.arch != .i386 and Target.current.cpu.arch != .x86_64)) {
1157 if ((cross_target.cpu_arch == null or cross_target.cpu_model == .native) and !info.cpu_detected) {
11591158 // TODO We want to just use detected_info.target but implementing
11601159 // CPU model & feature detection is todo so here we rely on LLVM.
11611160 const llvm = @import("llvm.zig");