authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 19:41:44-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 19:47:03-05:00
log49817c6adde3e9e98282dcecd017ab076faa1d85
treee4a9863a9cf0c8d3fbdb1e1745573e7e5b499587
parentf19918256746ccc81b6ff11d17e98665b4853400
signature Commit is signed but in an unrecognized format.

cleanup CPU model & feature detection

Add std.Target.Cpu.Model.generic which is even more empty than baseline. CPU model and feature detection uses this rather than baseline. Rename cpu_detected to cpu_detection_unimplemented and flip the logic. It can be relied on by stage2.zig to decide whether the LLVM workaround is needed without also checking the CrossTarget. Move the CPU detection to after the OS detection, and use the detected OS for the CPU detection. This is relevant because operating systems sometimes emulate certain CPU features, so knowing the OS and version is relevant for determining CPU features. Prepare for #4592 by passing the CPU arch to the detection code, instead of having it rely on Target.current. The CPU model & feature detection logic is modified. Before: * Detect actual features * Use as hint when detecting CPU model * Populate dependencies of CPU model features * Merge that into the actual features set After: * Detect actual features * Use as hint when detecting CPU model * Add known CPU model features to actual features * Detect actual features again, overriding known CPU model features * Populate dependencies

4 files changed, 192 insertions(+), 190 deletions(-)

lib/std/target.zig+17-5
......@@ -907,7 +907,7 @@ pub const Target = struct {
907907 };
908908 }
909909
910 pub fn baseline(arch: Arch) *const Model {
910 pub fn generic(arch: Arch) *const Model {
911911 const S = struct {
912912 const generic_model = Model{
913913 .name = "generic",
......@@ -916,7 +916,7 @@ pub const Target = struct {
916916 };
917917 };
918918 return switch (arch) {
919 .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline,
919 .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic,
920920 .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic,
921921 .avr => &avr.cpu.avr1,
922922 .bpfel, .bpfeb => &bpf.cpu.generic,
......@@ -926,11 +926,11 @@ pub const Target = struct {
926926 .msp430 => &msp430.cpu.generic,
927927 .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic,
928928 .amdgcn => &amdgpu.cpu.generic,
929 .riscv32 => &riscv.cpu.baseline_rv32,
930 .riscv64 => &riscv.cpu.baseline_rv64,
929 .riscv32 => &riscv.cpu.generic_rv32,
930 .riscv64 => &riscv.cpu.generic_rv64,
931931 .sparc, .sparcv9, .sparcel => &sparc.cpu.generic,
932932 .s390x => &systemz.cpu.generic,
933 .i386 => &x86.cpu.pentium4,
933 .i386 => &x86.cpu._i386,
934934 .x86_64 => &x86.cpu.x86_64,
935935 .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
936936 .wasm32, .wasm64 => &wasm.cpu.generic,
......@@ -938,6 +938,18 @@ pub const Target = struct {
938938 else => &S.generic_model,
939939 };
940940 }
941
942 pub fn baseline(arch: Arch) *const Model {
943 return switch (arch) {
944 .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline,
945 .riscv32 => &riscv.cpu.baseline_rv32,
946 .riscv64 => &riscv.cpu.baseline_rv64,
947 .i386 => &x86.cpu.pentium4,
948 .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
949
950 else => generic(arch),
951 };
952 }
941953 };
942954
943955 /// The "default" set of CPU features for cross-compiling. A conservative set
lib/std/zig/system.zig+42-35
......@@ -171,7 +171,10 @@ pub const NativeTargetInfo = struct {
171171
172172 dynamic_linker: DynamicLinker = DynamicLinker{},
173173
174 cpu_detected: bool = false,
174 /// Only some architectures have CPU detection implemented. This field reveals whether
175 /// CPU detection actually occurred. When this is `true` it means that the reported
176 /// CPU is baseline only because of a missing implementation for that architecture.
177 cpu_detection_unimplemented: bool = false,
175178
176179 pub const DynamicLinker = Target.DynamicLinker;
177180
......@@ -193,28 +196,6 @@ pub const NativeTargetInfo = struct {
193196 /// deinitialization method.
194197 /// TODO Remove the Allocator requirement from this function.
195198 pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
196
197 var cpu_detected = true;
198
199 const cpu = switch (cross_target.cpu_model) {
200 .native => detectNativeCpuAndFeatures(cross_target),
201 .baseline => baselineCpuAndFeatures(cross_target),
202 .determined_by_cpu_arch => if (cross_target.cpu_arch == null)
203 detectNativeCpuAndFeatures(cross_target)
204 else
205 baselineCpuAndFeatures(cross_target),
206 .explicit => |model| blk: {
207 var adjusted_model = model.toCpu(cross_target.getCpuArch());
208 cross_target.updateCpuFeatures(&adjusted_model.features);
209 break :blk adjusted_model;
210 },
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);
216 };
217
218199 var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
219200 if (cross_target.os_tag == null) {
220201 switch (Target.current.os.tag) {
......@@ -299,9 +280,12 @@ pub const NativeTargetInfo = struct {
299280 }
300281 },
301282 .freebsd => {
302 // TODO Detect native operating system version.
283 // Unimplemented, fall back to default.
284 // https://github.com/ziglang/zig/issues/4582
285 },
286 else => {
287 // Unimplemented, fall back to default version range.
303288 },
304 else => {},
305289 }
306290 }
307291
......@@ -328,8 +312,31 @@ pub const NativeTargetInfo = struct {
328312 os.version_range.linux.glibc = glibc;
329313 }
330314
315 var cpu_detection_unimplemented = false;
316
317 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
318 // native CPU architecture as being different than the current target), we use this:
319 const cpu_arch = cross_target.getCpuArch();
320
321 const cpu = switch (cross_target.cpu_model) {
322 .native => detectNativeCpuAndFeatures(cpu_arch, os, cross_target),
323 .baseline => baselineCpuAndFeatures(cpu_arch, cross_target),
324 .determined_by_cpu_arch => if (cross_target.cpu_arch == null)
325 detectNativeCpuAndFeatures(cpu_arch, os, cross_target)
326 else
327 baselineCpuAndFeatures(cpu_arch, cross_target),
328 .explicit => |model| blk: {
329 var adjusted_model = model.toCpu(cpu_arch);
330 cross_target.updateCpuFeatures(&adjusted_model.features);
331 break :blk adjusted_model;
332 },
333 } orelse backup_cpu_detection: {
334 cpu_detection_unimplemented = true;
335 break :backup_cpu_detection baselineCpuAndFeatures(cpu_arch, cross_target);
336 };
337
331338 var target = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
332 target.cpu_detected = cpu_detected;
339 target.cpu_detection_unimplemented = cpu_detection_unimplemented;
333340 return target;
334341 }
335342
......@@ -855,22 +862,22 @@ pub const NativeTargetInfo = struct {
855862 }
856863 }
857864
858 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) ?Target.Cpu {
859
860 switch(Target.current.cpu.arch) {
865 fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) ?Target.Cpu {
866 switch (cpu_arch) {
861867 .x86_64, .i386 => {
862 return @import("system/x86.zig").detectNativeCpuAndFeatures(cross_target);
868 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
863869 },
864870 else => {
865 // TODO flesh out CPU detection for more than just x86.
871 // This architecture does not have CPU model & feature detection yet.
872 // See https://github.com/ziglang/zig/issues/4591
866873 return null;
867 }
874 },
868875 }
869876 }
870877
871 fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
872 var adjusted_baseline = Target.Cpu.baseline(cross_target.getCpuArch());
878 fn baselineCpuAndFeatures(cpu_arch: Target.Cpu.Arch, cross_target: CrossTarget) Target.Cpu {
879 var adjusted_baseline = Target.Cpu.baseline(cpu_arch);
873880 cross_target.updateCpuFeatures(&adjusted_baseline.features);
874881 return adjusted_baseline;
875882 }
876};
\ No newline at end of file
883};
lib/std/zig/system/x86.zig+132-149
......@@ -3,35 +3,30 @@ const Target = std.Target;
33const CrossTarget = std.zig.CrossTarget;
44
55fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void {
6
76 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
87
9 if(enabled) cpu.features.addFeature(idx)
10 else cpu.features.removeFeature(idx);
8 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
119}
1210
1311inline fn bit(input: u32, offset: u5) bool {
1412 return (input >> offset) & 1 != 0;
1513}
1614
17pub fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
18
19 var arch = cross_target.getCpuArch();
20
21 var cpu = Target.Cpu {
15pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) Target.Cpu {
16 var cpu = Target.Cpu{
2217 .arch = arch,
23 .model = Target.Cpu.Model.baseline(arch),
18 .model = Target.Cpu.Model.generic(arch),
2419 .features = Target.Cpu.Feature.Set.empty,
2520 };
2621
27 detectNativeFeatures(&cpu, cross_target.getOsTag());
22 // First we detect features, to use as hints when detecting CPU Model.
23 detectNativeFeatures(&cpu, os.tag);
2824
2925 var leaf = cpuid(0, 0);
3026 const max_leaf = leaf.eax;
3127 const vendor = leaf.ebx;
3228
33 if(max_leaf > 0) {
34
29 if (max_leaf > 0) {
3530 leaf = cpuid(0x1, 0);
3631
3732 const brand_id = leaf.ebx & 0xff;
......@@ -49,7 +44,8 @@ pub fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
4944 }
5045 }
5146
52 switch(vendor) {
47 // Now we detect the model.
48 switch (vendor) {
5349 0x756e6547 => {
5450 detectIntelProcessor(&cpu, family, model, brand_id);
5551 },
......@@ -60,19 +56,21 @@ pub fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
6056 }
6157 }
6258
63 var model_features = cpu.model.features;
64 model_features.populateDependencies(cpu.arch.allFeaturesList());
65 cpu.features.addFeatureSet(model_features);
59 // Add the CPU model's feature set into the working set, but then
60 // override with actual detected features again.
61 cpu.features.addFeatureSet(cpu.model.features);
62 detectNativeFeatures(&cpu, os.tag);
6663
67 return cpu;
64 cpu.features.populateDependencies(cpu.arch.allFeaturesList());
6865
66 return cpu;
6967}
7068
7169fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32) void {
7270 if (brand_id != 0) {
7371 return;
7472 }
75 switch(family) {
73 switch (family) {
7674 3 => {
7775 cpu.model = &Target.x86.cpu._i386;
7876 return;
......@@ -82,7 +80,7 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
8280 return;
8381 },
8482 5 => {
85 if(Target.x86.featureSetHas(cpu.features, .mmx)) {
83 if (Target.x86.featureSetHas(cpu.features, .mmx)) {
8684 cpu.model = &Target.x86.cpu.pentium_mmx;
8785 return;
8886 }
......@@ -90,7 +88,7 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
9088 return;
9189 },
9290 6 => {
93 switch(model) {
91 switch (model) {
9492 0x01 => {
9593 cpu.model = &Target.x86.cpu.pentiumpro;
9694 return;
......@@ -148,10 +146,10 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
148146 return;
149147 },
150148 0x55 => {
151 if(Target.x86.featureSetHas(cpu.features, .avx512bf16)) {
149 if (Target.x86.featureSetHas(cpu.features, .avx512bf16)) {
152150 cpu.model = &Target.x86.cpu.cooperlake;
153151 return;
154 } else if(Target.x86.featureSetHas(cpu.features, .avx512vnni)) {
152 } else if (Target.x86.featureSetHas(cpu.features, .avx512vnni)) {
155153 cpu.model = &Target.x86.cpu.cascadelake;
156154 return;
157155 } else {
......@@ -199,45 +197,36 @@ fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32
199197 cpu.model = &Target.x86.cpu.knm;
200198 return;
201199 },
202 else => {
203 // Unknown CPU.
204 // Default to baseline x86_64 or i386 cpu.
205 return;
206 },
200 else => return, // Unknown CPU Model
207201 }
208202 },
209203 15 => {
210 if(Target.x86.featureSetHas(cpu.features, .@"64bit")) {
204 if (Target.x86.featureSetHas(cpu.features, .@"64bit")) {
211205 cpu.model = &Target.x86.cpu.nocona;
212206 return;
213207 }
214 if(Target.x86.featureSetHas(cpu.features, .sse3)) {
208 if (Target.x86.featureSetHas(cpu.features, .sse3)) {
215209 cpu.model = &Target.x86.cpu.prescott;
216210 return;
217211 }
218212 cpu.model = &Target.x86.cpu.pentium4;
219213 return;
220214 },
221 else => {
222 // Unknown CPU.
223 // Default to baseline x86_64 or i386 cpu.
224 return;
225 }
215 else => return, // Unknown CPU Model
226216 }
227217}
228218
229219fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
230220 // AMD's cpuid information is less than optimal for determining a CPU model.
231221 // This is very unscientific, and not necessarily correct.
232
233 switch(family) {
222 switch (family) {
234223 4 => {
235224 cpu.model = &Target.x86.cpu._i486;
236225 return;
237226 },
238227 5 => {
239228 cpu.model = &Target.x86.cpu.pentium;
240 switch(model) {
229 switch (model) {
241230 6, 7 => {
242231 cpu.model = &Target.x86.cpu.k6;
243232 return;
......@@ -259,7 +248,7 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
259248 return;
260249 },
261250 6 => {
262 if(Target.x86.featureSetHas(cpu.features, .sse)) {
251 if (Target.x86.featureSetHas(cpu.features, .sse)) {
263252 cpu.model = &Target.x86.cpu.athlon_xp;
264253 return;
265254 }
......@@ -267,7 +256,7 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
267256 return;
268257 },
269258 15 => {
270 if(Target.x86.featureSetHas(cpu.features, .sse3)) {
259 if (Target.x86.featureSetHas(cpu.features, .sse3)) {
271260 cpu.model = &Target.x86.cpu.k8_sse3;
272261 return;
273262 }
......@@ -284,15 +273,15 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
284273 },
285274 21 => {
286275 cpu.model = &Target.x86.cpu.bdver1;
287 if(model >= 0x60 and model <= 0x7f) {
276 if (model >= 0x60 and model <= 0x7f) {
288277 cpu.model = &Target.x86.cpu.bdver4;
289278 return;
290279 }
291 if(model >= 0x30 and model <= 0x3f) {
280 if (model >= 0x30 and model <= 0x3f) {
292281 cpu.model = &Target.x86.cpu.bdver3;
293282 return;
294283 }
295 if((model >= 0x10 and model <= 0x1f) or model == 0x02) {
284 if ((model >= 0x10 and model <= 0x1f) or model == 0x02) {
296285 cpu.model = &Target.x86.cpu.bdver2;
297286 return;
298287 }
......@@ -304,7 +293,7 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
304293 },
305294 23 => {
306295 cpu.model = &Target.x86.cpu.znver1;
307 if((model >= 0x30 and model <= 0x3f) or model == 0x71) {
296 if ((model >= 0x30 and model <= 0x3f) or model == 0x71) {
308297 cpu.model = &Target.x86.cpu.znver2;
309298 return;
310299 }
......@@ -312,41 +301,40 @@ fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
312301 },
313302 else => {
314303 return;
315 }
304 },
316305 }
317306}
318307
319fn detectNativeFeatures(cpu: *Target.Cpu, os_type: Target.Os.Tag) void {
320
308fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {
321309 var leaf = cpuid(0, 0);
322310
323311 const max_level = leaf.eax;
324
312
325313 leaf = cpuid(1, 0);
326314
327 setFeature(cpu, .cx8, bit(leaf.edx, 8));
328 setFeature(cpu, .cx8, bit(leaf.edx, 8));
329 setFeature(cpu, .cmov, bit(leaf.edx, 15));
330 setFeature(cpu, .mmx, bit(leaf.edx, 23));
331 setFeature(cpu, .fxsr, bit(leaf.edx, 24));
332 setFeature(cpu, .sse, bit(leaf.edx, 25));
333 setFeature(cpu, .sse2, bit(leaf.edx, 26));
334 setFeature(cpu, .sse3, bit(leaf.ecx, 0));
335 setFeature(cpu, .pclmul, bit(leaf.ecx, 1));
336 setFeature(cpu, .ssse3, bit(leaf.ecx, 9));
337 setFeature(cpu, .cx16, bit(leaf.ecx, 13));
315 setFeature(cpu, .cx8, bit(leaf.edx, 8));
316 setFeature(cpu, .cx8, bit(leaf.edx, 8));
317 setFeature(cpu, .cmov, bit(leaf.edx, 15));
318 setFeature(cpu, .mmx, bit(leaf.edx, 23));
319 setFeature(cpu, .fxsr, bit(leaf.edx, 24));
320 setFeature(cpu, .sse, bit(leaf.edx, 25));
321 setFeature(cpu, .sse2, bit(leaf.edx, 26));
322 setFeature(cpu, .sse3, bit(leaf.ecx, 0));
323 setFeature(cpu, .pclmul, bit(leaf.ecx, 1));
324 setFeature(cpu, .ssse3, bit(leaf.ecx, 9));
325 setFeature(cpu, .cx16, bit(leaf.ecx, 13));
338326 setFeature(cpu, .sse4_1, bit(leaf.ecx, 19));
339327 setFeature(cpu, .sse4_2, bit(leaf.ecx, 20));
340 setFeature(cpu, .movbe, bit(leaf.ecx, 22));
328 setFeature(cpu, .movbe, bit(leaf.ecx, 22));
341329 setFeature(cpu, .popcnt, bit(leaf.ecx, 23));
342 setFeature(cpu, .aes, bit(leaf.ecx, 25));
343 setFeature(cpu, .rdrnd, bit(leaf.ecx, 30));
330 setFeature(cpu, .aes, bit(leaf.ecx, 25));
331 setFeature(cpu, .rdrnd, bit(leaf.ecx, 30));
344332
345333 leaf.eax = getXCR0();
346334
347335 const has_avx = bit(leaf.ecx, 27) and
348 bit(leaf.ecx, 28) and
349 ((leaf.eax & 0x6) == 0x6);
336 bit(leaf.ecx, 28) and
337 ((leaf.eax & 0x6) == 0x6);
350338
351339 // LLVM approaches avx512_save by hardcoding it to true on Darwin,
352340 // because the kernel saves the context even if the bit is not set.
......@@ -368,96 +356,96 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_type: Target.Os.Tag) void {
368356 // Darwin lazily saves the AVX512 context on first use: trust that the OS will
369357 // save the AVX512 context if we use AVX512 instructions, even if the bit is not
370358 // set right now.
371 const has_avx512_save = switch(os_type.isDarwin()) {
372 true => true,
359 const has_avx512_save = switch (os_tag.isDarwin()) {
360 true => true,
373361 false => has_avx and ((leaf.eax & 0xE0) == 0xE0),
374362 };
375363
376 setFeature(cpu, .avx, has_avx);
377 setFeature(cpu, .fma, has_avx and bit(leaf.ecx, 12));
364 setFeature(cpu, .avx, has_avx);
365 setFeature(cpu, .fma, has_avx and bit(leaf.ecx, 12));
378366 // Only enable XSAVE if OS has enabled support for saving YMM state.
379 setFeature(cpu, .xsave, has_avx and bit(leaf.ecx, 26));
380 setFeature(cpu, .f16c, has_avx and bit(leaf.ecx, 29));
367 setFeature(cpu, .xsave, has_avx and bit(leaf.ecx, 26));
368 setFeature(cpu, .f16c, has_avx and bit(leaf.ecx, 29));
381369
382370 leaf = cpuid(0x80000000, 0);
383371 const max_ext_level = leaf.eax;
384372
385 if(max_ext_level >= 0x80000001) {
373 if (max_ext_level >= 0x80000001) {
386374 leaf = cpuid(0x80000001, 0);
387 setFeature(cpu, .sahf, bit(leaf.ecx, 0));
388 setFeature(cpu, .lzcnt, bit(leaf.ecx, 5));
389 setFeature(cpu, .sse4a, bit(leaf.ecx, 6));
390 setFeature(cpu, .prfchw, bit(leaf.ecx, 8));
391 setFeature(cpu, .xop, bit(leaf.ecx, 11) and has_avx);
392 setFeature(cpu, .lwp, bit(leaf.ecx, 15));
393 setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx);
394 setFeature(cpu, .tbm, bit(leaf.ecx, 21));
395 setFeature(cpu, .mwaitx, bit(leaf.ecx, 29));
396 setFeature(cpu, .@"64bit", bit(leaf.edx, 29));
375 setFeature(cpu, .sahf, bit(leaf.ecx, 0));
376 setFeature(cpu, .lzcnt, bit(leaf.ecx, 5));
377 setFeature(cpu, .sse4a, bit(leaf.ecx, 6));
378 setFeature(cpu, .prfchw, bit(leaf.ecx, 8));
379 setFeature(cpu, .xop, bit(leaf.ecx, 11) and has_avx);
380 setFeature(cpu, .lwp, bit(leaf.ecx, 15));
381 setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx);
382 setFeature(cpu, .tbm, bit(leaf.ecx, 21));
383 setFeature(cpu, .mwaitx, bit(leaf.ecx, 29));
384 setFeature(cpu, .@"64bit", bit(leaf.edx, 29));
397385 } else {
398 for([_]Target.x86.Feature{
386 for ([_]Target.x86.Feature{
399387 .sahf, .lzcnt, .sse4a, .prfchw, .xop,
400 .lwp, .fma4, .tbm, .mwaitx, .@"64bit"
388 .lwp, .fma4, .tbm, .mwaitx, .@"64bit",
401389 }) |feat| {
402390 setFeature(cpu, feat, false);
403391 }
404392 }
405393
406394 // Misc. memory-related features.
407 if(max_ext_level >= 0x80000008) {
395 if (max_ext_level >= 0x80000008) {
408396 leaf = cpuid(0x80000008, 0);
409 setFeature(cpu, .clzero, bit(leaf.ebx, 0));
397 setFeature(cpu, .clzero, bit(leaf.ebx, 0));
410398 setFeature(cpu, .wbnoinvd, bit(leaf.ebx, 9));
411399 } else {
412 for([_]Target.x86.Feature{ .clzero, .wbnoinvd }) |feat| {
400 for ([_]Target.x86.Feature{ .clzero, .wbnoinvd }) |feat| {
413401 setFeature(cpu, feat, false);
414402 }
415403 }
416404
417 if(max_level >= 0x7) {
405 if (max_level >= 0x7) {
418406 leaf = cpuid(0x7, 0);
419407
420 setFeature(cpu, .fsgsbase, bit(leaf.ebx, 0));
421 setFeature(cpu, .sgx, bit(leaf.ebx, 2));
422 setFeature(cpu, .bmi, bit(leaf.ebx, 3));
408 setFeature(cpu, .fsgsbase, bit(leaf.ebx, 0));
409 setFeature(cpu, .sgx, bit(leaf.ebx, 2));
410 setFeature(cpu, .bmi, bit(leaf.ebx, 3));
423411 // AVX2 is only supported if we have the OS save support from AVX.
424 setFeature(cpu, .avx2, bit(leaf.ebx, 5) and has_avx);
425 setFeature(cpu, .bmi2, bit(leaf.ebx, 8));
426 setFeature(cpu, .invpcid, bit(leaf.ebx, 10));
427 setFeature(cpu, .rtm, bit(leaf.ebx, 11));
412 setFeature(cpu, .avx2, bit(leaf.ebx, 5) and has_avx);
413 setFeature(cpu, .bmi2, bit(leaf.ebx, 8));
414 setFeature(cpu, .invpcid, bit(leaf.ebx, 10));
415 setFeature(cpu, .rtm, bit(leaf.ebx, 11));
428416 // AVX512 is only supported if the OS supports the context save for it.
429 setFeature(cpu, .avx512f, bit(leaf.ebx, 16) and has_avx512_save);
430 setFeature(cpu, .avx512dq, bit(leaf.ebx, 17) and has_avx512_save);
431 setFeature(cpu, .rdseed, bit(leaf.ebx, 18));
432 setFeature(cpu, .adx, bit(leaf.ebx, 19));
433 setFeature(cpu, .avx512ifma, bit(leaf.ebx, 21) and has_avx512_save);
434 setFeature(cpu, .clflushopt, bit(leaf.ebx, 23));
435 setFeature(cpu, .clwb, bit(leaf.ebx, 24));
436 setFeature(cpu, .avx512pf, bit(leaf.ebx, 26) and has_avx512_save);
437 setFeature(cpu, .avx512er, bit(leaf.ebx, 27) and has_avx512_save);
438 setFeature(cpu, .avx512cd, bit(leaf.ebx, 28) and has_avx512_save);
439 setFeature(cpu, .sha, bit(leaf.ebx, 29));
440 setFeature(cpu, .avx512bw, bit(leaf.ebx, 30) and has_avx512_save);
441 setFeature(cpu, .avx512vl, bit(leaf.ebx, 31) and has_avx512_save);
442
443 setFeature(cpu, .prefetchwt1, bit(leaf.ecx, 0));
444 setFeature(cpu, .avx512vbmi, bit(leaf.ecx, 1) and has_avx512_save);
445 setFeature(cpu, .pku, bit(leaf.ecx, 4));
446 setFeature(cpu, .waitpkg, bit(leaf.ecx, 5));
447 setFeature(cpu, .avx512vbmi2, bit(leaf.ecx, 6) and has_avx512_save);
448 setFeature(cpu, .shstk, bit(leaf.ecx, 7));
449 setFeature(cpu, .gfni, bit(leaf.ecx, 8));
450 setFeature(cpu, .vaes, bit(leaf.ecx, 9) and has_avx);
451 setFeature(cpu, .vpclmulqdq, bit(leaf.ecx, 10) and has_avx);
452 setFeature(cpu, .avx512vnni, bit(leaf.ecx, 11) and has_avx512_save);
453 setFeature(cpu, .avx512bitalg, bit(leaf.ecx, 12) and has_avx512_save);
454 setFeature(cpu, .avx512vpopcntdq, bit(leaf.ecx, 14) and has_avx512_save);
455 setFeature(cpu, .avx512vp2intersect, bit(leaf.edx, 8) and has_avx512_save);
456 setFeature(cpu, .rdpid, bit(leaf.ecx, 22));
457 setFeature(cpu, .cldemote, bit(leaf.ecx, 25));
458 setFeature(cpu, .movdiri, bit(leaf.ecx, 27));
459 setFeature(cpu, .movdir64b, bit(leaf.ecx, 28));
460 setFeature(cpu, .enqcmd, bit(leaf.ecx, 29));
417 setFeature(cpu, .avx512f, bit(leaf.ebx, 16) and has_avx512_save);
418 setFeature(cpu, .avx512dq, bit(leaf.ebx, 17) and has_avx512_save);
419 setFeature(cpu, .rdseed, bit(leaf.ebx, 18));
420 setFeature(cpu, .adx, bit(leaf.ebx, 19));
421 setFeature(cpu, .avx512ifma, bit(leaf.ebx, 21) and has_avx512_save);
422 setFeature(cpu, .clflushopt, bit(leaf.ebx, 23));
423 setFeature(cpu, .clwb, bit(leaf.ebx, 24));
424 setFeature(cpu, .avx512pf, bit(leaf.ebx, 26) and has_avx512_save);
425 setFeature(cpu, .avx512er, bit(leaf.ebx, 27) and has_avx512_save);
426 setFeature(cpu, .avx512cd, bit(leaf.ebx, 28) and has_avx512_save);
427 setFeature(cpu, .sha, bit(leaf.ebx, 29));
428 setFeature(cpu, .avx512bw, bit(leaf.ebx, 30) and has_avx512_save);
429 setFeature(cpu, .avx512vl, bit(leaf.ebx, 31) and has_avx512_save);
430
431 setFeature(cpu, .prefetchwt1, bit(leaf.ecx, 0));
432 setFeature(cpu, .avx512vbmi, bit(leaf.ecx, 1) and has_avx512_save);
433 setFeature(cpu, .pku, bit(leaf.ecx, 4));
434 setFeature(cpu, .waitpkg, bit(leaf.ecx, 5));
435 setFeature(cpu, .avx512vbmi2, bit(leaf.ecx, 6) and has_avx512_save);
436 setFeature(cpu, .shstk, bit(leaf.ecx, 7));
437 setFeature(cpu, .gfni, bit(leaf.ecx, 8));
438 setFeature(cpu, .vaes, bit(leaf.ecx, 9) and has_avx);
439 setFeature(cpu, .vpclmulqdq, bit(leaf.ecx, 10) and has_avx);
440 setFeature(cpu, .avx512vnni, bit(leaf.ecx, 11) and has_avx512_save);
441 setFeature(cpu, .avx512bitalg, bit(leaf.ecx, 12) and has_avx512_save);
442 setFeature(cpu, .avx512vpopcntdq, bit(leaf.ecx, 14) and has_avx512_save);
443 setFeature(cpu, .avx512vp2intersect, bit(leaf.edx, 8) and has_avx512_save);
444 setFeature(cpu, .rdpid, bit(leaf.ecx, 22));
445 setFeature(cpu, .cldemote, bit(leaf.ecx, 25));
446 setFeature(cpu, .movdiri, bit(leaf.ecx, 27));
447 setFeature(cpu, .movdir64b, bit(leaf.ecx, 28));
448 setFeature(cpu, .enqcmd, bit(leaf.ecx, 29));
461449
462450 // There are two CPUID leafs which information associated with the pconfig
463451 // instruction:
......@@ -469,21 +457,21 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_type: Target.Os.Tag) void {
469457 // leaves using cpuid, since that information is ignored while
470458 // detecting features using the "-march=native" flag.
471459 // For more info, see X86 ISA docs.
472 setFeature(cpu, .pconfig, bit(leaf.edx, 18));
460 setFeature(cpu, .pconfig, bit(leaf.edx, 18));
473461
474462 // TODO I feel unsure about this check.
475463 // It doesn't really seem to check for 7.1, just for 7.
476464 // Is this a sound assumption to make?
477465 // Note that this is what other implementations do, so I kind of trust it.
478466 const has_leaf_7_1 = max_level >= 7;
479 if(has_leaf_7_1) {
467 if (has_leaf_7_1) {
480468 leaf = cpuid(0x7, 0x1);
481469 setFeature(cpu, .avx512bf16, bit(leaf.eax, 5) and has_avx512_save);
482470 } else {
483471 setFeature(cpu, .avx512bf16, false);
484472 }
485473 } else {
486 for([_]Target.x86.Feature{
474 for ([_]Target.x86.Feature{
487475 .fsgsbase, .sgx, .bmi, .avx2,
488476 .bmi2, .invpcid, .rtm, .avx512f,
489477 .avx512dq, .rdseed, .adx, .avx512ifma,
......@@ -499,26 +487,24 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_type: Target.Os.Tag) void {
499487 }
500488 }
501489
502 if(max_level >= 0xD and has_avx) {
490 if (max_level >= 0xD and has_avx) {
503491 leaf = cpuid(0xD, 0x1);
504492 // Only enable XSAVE if OS has enabled support for saving YMM state.
505493 setFeature(cpu, .xsaveopt, bit(leaf.eax, 0));
506 setFeature(cpu, .xsavec, bit(leaf.eax, 1));
507 setFeature(cpu, .xsaves, bit(leaf.eax, 3));
508
494 setFeature(cpu, .xsavec, bit(leaf.eax, 1));
495 setFeature(cpu, .xsaves, bit(leaf.eax, 3));
509496 } else {
510 for([_]Target.x86.Feature{ .xsaveopt, .xsavec, .xsaves }) |feat| {
497 for ([_]Target.x86.Feature{ .xsaveopt, .xsavec, .xsaves }) |feat| {
511498 setFeature(cpu, feat, false);
512499 }
513500 }
514501
515 if(max_level >= 0x14) {
502 if (max_level >= 0x14) {
516503 leaf = cpuid(0x14, 0);
517504 setFeature(cpu, .ptwrite, bit(leaf.ebx, 4));
518505 } else {
519506 setFeature(cpu, .ptwrite, false);
520507 }
521
522508}
523509
524510const CpuidLeaf = packed struct {
......@@ -532,9 +518,9 @@ fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
532518 // Workaround for https://github.com/ziglang/zig/issues/215
533519 // Inline assembly in zig only supports one output,
534520 // so we pass a pointer to the struct.
535 var cpuid_leaf = CpuidLeaf {.eax = 0, .ebx = 0, .ecx = 0, .edx = 0};
521 var cpuid_leaf = CpuidLeaf{ .eax = 0, .ebx = 0, .ecx = 0, .edx = 0 };
536522 var leaf_ptr = &cpuid_leaf;
537 switch(Target.current.cpu.arch) {
523 switch (Target.current.cpu.arch) {
538524 .i386 => {
539525 _ = asm volatile (
540526 \\ cpuid
......@@ -542,10 +528,10 @@ fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
542528 \\ movl %%ebx, 4(%%edi)
543529 \\ movl %%ecx, 8(%%edi)
544530 \\ movl %%edx, 12(%%edi)
545 : :
546 [leaf_id] "{eax}" (leaf_id),
547 [subid] "{ecx}" (subid),
548 [leaf_ptr] "{edi}" (leaf_ptr),
531 :
532 : [leaf_id] "{eax}" (leaf_id),
533 [subid] "{ecx}" (subid),
534 [leaf_ptr] "{edi}" (leaf_ptr)
549535 : "eax", "ebx", "ecx", "edx"
550536 );
551537 },
......@@ -556,10 +542,10 @@ fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
556542 \\ movl %%ebx, 4(%%rdi)
557543 \\ movl %%ecx, 8(%%rdi)
558544 \\ movl %%edx, 12(%%rdi)
559 : :
560 [leaf_id] "{eax}" (leaf_id),
561 [subid] "{ecx}" (subid),
562 [leaf_ptr] "{rdi}" (leaf_ptr),
545 :
546 : [leaf_id] "{eax}" (leaf_id),
547 [subid] "{ecx}" (subid),
548 [leaf_ptr] "{rdi}" (leaf_ptr)
563549 : "eax", "ebx", "ecx", "edx"
564550 );
565551 },
......@@ -570,14 +556,11 @@ fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
570556
571557// Read control register 0 (XCR0). Used to detect features such as AVX.
572558fn getXCR0() u32 {
573
574559 return asm (
575560 \\ .byte 0x0F, 0x01, 0xD0
576561 : [ret] "={eax}" (-> u32)
577562 : [number] "{eax}" (@as(u32, 0)),
578563 [number] "{edx}" (@as(u32, 0)),
579 [number] "{ecx}" (@as(u32, 0)),
580 :
564 [number] "{ecx}" (@as(u32, 0))
581565 );
582566}
583
src-self-hosted/stage2.zig+1-1
......@@ -1154,7 +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 !info.cpu_detected) {
1157 if (info.cpu_detection_unimplemented) {
11581158 // TODO We want to just use detected_info.target but implementing
11591159 // CPU model & feature detection is todo so here we rely on LLVM.
11601160 const llvm = @import("llvm.zig");