authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 20:47:34-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-06 20:47:34-05:00
log54799ccaf819f6c6934327416f2f28587e40ffa7
tree259c43a1d3d9838036db9188f1ee1ccc4f6957e0
parent7f975bf09f4e6e81d68c9a573ac2e31b997b5816
parente0d5f94a70faafbb07978f39394077820ff007ec
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'alichay-master'

Closes #4627

4 files changed, 616 insertions(+), 28 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+49-22
......@@ -171,6 +171,11 @@ pub const NativeTargetInfo = struct {
171171
172172 dynamic_linker: DynamicLinker = DynamicLinker{},
173173
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,
178
174179 pub const DynamicLinker = Target.DynamicLinker;
175180
176181 pub const DetectError = error{
......@@ -191,20 +196,6 @@ pub const NativeTargetInfo = struct {
191196 /// deinitialization method.
192197 /// TODO Remove the Allocator requirement from this function.
193198 pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
194 const cpu = switch (cross_target.cpu_model) {
195 .native => detectNativeCpuAndFeatures(cross_target),
196 .baseline => baselineCpuAndFeatures(cross_target),
197 .determined_by_cpu_arch => if (cross_target.cpu_arch == null)
198 detectNativeCpuAndFeatures(cross_target)
199 else
200 baselineCpuAndFeatures(cross_target),
201 .explicit => |model| blk: {
202 var adjusted_model = model.toCpu(cross_target.getCpuArch());
203 cross_target.updateCpuFeatures(&adjusted_model.features);
204 break :blk adjusted_model;
205 },
206 };
207
208199 var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
209200 if (cross_target.os_tag == null) {
210201 switch (Target.current.os.tag) {
......@@ -289,9 +280,12 @@ pub const NativeTargetInfo = struct {
289280 }
290281 },
291282 .freebsd => {
292 // 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.
293288 },
294 else => {},
295289 }
296290 }
297291
......@@ -318,7 +312,32 @@ pub const NativeTargetInfo = struct {
318312 os.version_range.linux.glibc = glibc;
319313 }
320314
321 return detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
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
338 var target = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
339 target.cpu_detection_unimplemented = cpu_detection_unimplemented;
340 return target;
322341 }
323342
324343 /// First we attempt to use the executable's own binary. If it is dynamically
......@@ -843,13 +862,21 @@ pub const NativeTargetInfo = struct {
843862 }
844863 }
845864
846 fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
847 // TODO Detect native CPU model & features. Until that is implemented we use baseline.
848 return baselineCpuAndFeatures(cross_target);
865 fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) ?Target.Cpu {
866 switch (cpu_arch) {
867 .x86_64, .i386 => {
868 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
869 },
870 else => {
871 // This architecture does not have CPU model & feature detection yet.
872 // See https://github.com/ziglang/zig/issues/4591
873 return null;
874 },
875 }
849876 }
850877
851 fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
852 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);
853880 cross_target.updateCpuFeatures(&adjusted_baseline.features);
854881 return adjusted_baseline;
855882 }
lib/std/zig/system/x86.zig created+549
......@@ -0,0 +1,549 @@
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 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
7
8 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
9}
10
11inline fn bit(input: u32, offset: u5) bool {
12 return (input >> offset) & 1 != 0;
13}
14
15pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) Target.Cpu {
16 var cpu = Target.Cpu{
17 .arch = arch,
18 .model = Target.Cpu.Model.generic(arch),
19 .features = Target.Cpu.Feature.Set.empty,
20 };
21
22 // First we detect features, to use as hints when detecting CPU Model.
23 detectNativeFeatures(&cpu, os.tag);
24
25 var leaf = cpuid(0, 0);
26 const max_leaf = leaf.eax;
27 const vendor = leaf.ebx;
28
29 if (max_leaf > 0) {
30 leaf = cpuid(0x1, 0);
31
32 const brand_id = leaf.ebx & 0xff;
33 var family: u32 = 0;
34 var model: u32 = 0;
35
36 { // Detect model and family
37 family = (leaf.eax >> 8) & 0xf;
38 model = (leaf.eax >> 4) & 0xf;
39 if (family == 6 or family == 0xf) {
40 if (family == 0xf) {
41 family += (leaf.eax >> 20) & 0xff;
42 }
43 model += ((leaf.eax >> 16) & 0xf) << 4;
44 }
45 }
46
47 // Now we detect the model.
48 switch (vendor) {
49 0x756e6547 => {
50 detectIntelProcessor(&cpu, family, model, brand_id);
51 },
52 0x68747541 => {
53 detectAMDProcessor(&cpu, family, model);
54 },
55 else => {},
56 }
57 }
58
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);
63
64 cpu.features.populateDependencies(cpu.arch.allFeaturesList());
65
66 return cpu;
67}
68
69fn detectIntelProcessor(cpu: *Target.Cpu, family: u32, model: u32, brand_id: u32) void {
70 if (brand_id != 0) {
71 return;
72 }
73 switch (family) {
74 3 => {
75 cpu.model = &Target.x86.cpu._i386;
76 return;
77 },
78 4 => {
79 cpu.model = &Target.x86.cpu._i486;
80 return;
81 },
82 5 => {
83 if (Target.x86.featureSetHas(cpu.features, .mmx)) {
84 cpu.model = &Target.x86.cpu.pentium_mmx;
85 return;
86 }
87 cpu.model = &Target.x86.cpu.pentium;
88 return;
89 },
90 6 => {
91 switch (model) {
92 0x01 => {
93 cpu.model = &Target.x86.cpu.pentiumpro;
94 return;
95 },
96 0x03, 0x05, 0x06 => {
97 cpu.model = &Target.x86.cpu.pentium2;
98 return;
99 },
100 0x07, 0x08, 0x0a, 0x0b => {
101 cpu.model = &Target.x86.cpu.pentium3;
102 return;
103 },
104 0x09, 0x0d, 0x15 => {
105 cpu.model = &Target.x86.cpu.pentium_m;
106 return;
107 },
108 0x0e => {
109 cpu.model = &Target.x86.cpu.yonah;
110 return;
111 },
112 0x0f, 0x16 => {
113 cpu.model = &Target.x86.cpu.core2;
114 return;
115 },
116 0x17, 0x1d => {
117 cpu.model = &Target.x86.cpu.penryn;
118 return;
119 },
120 0x1a, 0x1e, 0x1f, 0x2e => {
121 cpu.model = &Target.x86.cpu.nehalem;
122 return;
123 },
124 0x25, 0x2c, 0x2f => {
125 cpu.model = &Target.x86.cpu.westmere;
126 return;
127 },
128 0x2a, 0x2d => {
129 cpu.model = &Target.x86.cpu.sandybridge;
130 return;
131 },
132 0x3a, 0x3e => {
133 cpu.model = &Target.x86.cpu.ivybridge;
134 return;
135 },
136 0x3c, 0x3f, 0x45, 0x46 => {
137 cpu.model = &Target.x86.cpu.haswell;
138 return;
139 },
140 0x3d, 0x47, 0x4f, 0x56 => {
141 cpu.model = &Target.x86.cpu.broadwell;
142 return;
143 },
144 0x4e, 0x5e, 0x8e, 0x9e => {
145 cpu.model = &Target.x86.cpu.skylake;
146 return;
147 },
148 0x55 => {
149 if (Target.x86.featureSetHas(cpu.features, .avx512bf16)) {
150 cpu.model = &Target.x86.cpu.cooperlake;
151 return;
152 } else if (Target.x86.featureSetHas(cpu.features, .avx512vnni)) {
153 cpu.model = &Target.x86.cpu.cascadelake;
154 return;
155 } else {
156 cpu.model = &Target.x86.cpu.skylake_avx512;
157 return;
158 }
159 },
160 0x66 => {
161 cpu.model = &Target.x86.cpu.cannonlake;
162 return;
163 },
164 0x7d, 0x7e => {
165 cpu.model = &Target.x86.cpu.icelake_client;
166 return;
167 },
168 0x6a, 0x6c => {
169 cpu.model = &Target.x86.cpu.icelake_server;
170 return;
171 },
172 0x1c, 0x26, 0x27, 0x35, 0x36 => {
173 cpu.model = &Target.x86.cpu.bonnell;
174 return;
175 },
176 0x37, 0x4a, 0x4d, 0x5a, 0x5d, 0x4c => {
177 cpu.model = &Target.x86.cpu.silvermont;
178 return;
179 },
180 0x5c, 0x5f => {
181 cpu.model = &Target.x86.cpu.goldmont;
182 return;
183 },
184 0x7a => {
185 cpu.model = &Target.x86.cpu.goldmont_plus;
186 return;
187 },
188 0x86 => {
189 cpu.model = &Target.x86.cpu.tremont;
190 return;
191 },
192 0x57 => {
193 cpu.model = &Target.x86.cpu.knl;
194 return;
195 },
196 0x85 => {
197 cpu.model = &Target.x86.cpu.knm;
198 return;
199 },
200 else => return, // Unknown CPU Model
201 }
202 },
203 15 => {
204 if (Target.x86.featureSetHas(cpu.features, .@"64bit")) {
205 cpu.model = &Target.x86.cpu.nocona;
206 return;
207 }
208 if (Target.x86.featureSetHas(cpu.features, .sse3)) {
209 cpu.model = &Target.x86.cpu.prescott;
210 return;
211 }
212 cpu.model = &Target.x86.cpu.pentium4;
213 return;
214 },
215 else => return, // Unknown CPU Model
216 }
217}
218
219fn detectAMDProcessor(cpu: *Target.Cpu, family: u32, model: u32) void {
220 // AMD's cpuid information is less than optimal for determining a CPU model.
221 // This is very unscientific, and not necessarily correct.
222 switch (family) {
223 4 => {
224 cpu.model = &Target.x86.cpu._i486;
225 return;
226 },
227 5 => {
228 cpu.model = &Target.x86.cpu.pentium;
229 switch (model) {
230 6, 7 => {
231 cpu.model = &Target.x86.cpu.k6;
232 return;
233 },
234 8 => {
235 cpu.model = &Target.x86.cpu.k6_2;
236 return;
237 },
238 9, 13 => {
239 cpu.model = &Target.x86.cpu.k6_3;
240 return;
241 },
242 10 => {
243 cpu.model = &Target.x86.cpu.geode;
244 return;
245 },
246 else => {},
247 }
248 return;
249 },
250 6 => {
251 if (Target.x86.featureSetHas(cpu.features, .sse)) {
252 cpu.model = &Target.x86.cpu.athlon_xp;
253 return;
254 }
255 cpu.model = &Target.x86.cpu.athlon;
256 return;
257 },
258 15 => {
259 if (Target.x86.featureSetHas(cpu.features, .sse3)) {
260 cpu.model = &Target.x86.cpu.k8_sse3;
261 return;
262 }
263 cpu.model = &Target.x86.cpu.k8;
264 return;
265 },
266 16 => {
267 cpu.model = &Target.x86.cpu.amdfam10;
268 return;
269 },
270 20 => {
271 cpu.model = &Target.x86.cpu.btver1;
272 return;
273 },
274 21 => {
275 cpu.model = &Target.x86.cpu.bdver1;
276 if (model >= 0x60 and model <= 0x7f) {
277 cpu.model = &Target.x86.cpu.bdver4;
278 return;
279 }
280 if (model >= 0x30 and model <= 0x3f) {
281 cpu.model = &Target.x86.cpu.bdver3;
282 return;
283 }
284 if ((model >= 0x10 and model <= 0x1f) or model == 0x02) {
285 cpu.model = &Target.x86.cpu.bdver2;
286 return;
287 }
288 return;
289 },
290 22 => {
291 cpu.model = &Target.x86.cpu.btver2;
292 return;
293 },
294 23 => {
295 cpu.model = &Target.x86.cpu.znver1;
296 if ((model >= 0x30 and model <= 0x3f) or model == 0x71) {
297 cpu.model = &Target.x86.cpu.znver2;
298 return;
299 }
300 return;
301 },
302 else => {
303 return;
304 },
305 }
306}
307
308fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {
309 var leaf = cpuid(0, 0);
310
311 const max_level = leaf.eax;
312
313 leaf = cpuid(1, 0);
314
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));
326 setFeature(cpu, .sse4_1, bit(leaf.ecx, 19));
327 setFeature(cpu, .sse4_2, bit(leaf.ecx, 20));
328 setFeature(cpu, .movbe, bit(leaf.ecx, 22));
329 setFeature(cpu, .popcnt, bit(leaf.ecx, 23));
330 setFeature(cpu, .aes, bit(leaf.ecx, 25));
331 setFeature(cpu, .rdrnd, bit(leaf.ecx, 30));
332
333 leaf.eax = getXCR0();
334
335 const has_avx = bit(leaf.ecx, 27) and
336 bit(leaf.ecx, 28) and
337 ((leaf.eax & 0x6) == 0x6);
338
339 // LLVM approaches avx512_save by hardcoding it to true on Darwin,
340 // because the kernel saves the context even if the bit is not set.
341 // https://github.com/llvm/llvm-project/blob/bca373f73fc82728a8335e7d6cd164e8747139ec/llvm/lib/Support/Host.cpp#L1378
342 //
343 // Google approaches this by using a different series of checks and flags,
344 // and this may report the feature more accurately on a technically correct
345 // but ultimately less useful level.
346 // https://github.com/google/cpu_features/blob/b5c271c53759b2b15ff91df19bd0b32f2966e275/src/cpuinfo_x86.c#L113
347 // (called from https://github.com/google/cpu_features/blob/b5c271c53759b2b15ff91df19bd0b32f2966e275/src/cpuinfo_x86.c#L1052)
348 //
349 // Right now, we use LLVM's approach, because even if the target doesn't support
350 // the feature, the kernel should provide the same functionality transparently,
351 // so the implementation details don't make a difference.
352 // That said, this flag impacts other CPU features' availability,
353 // so until we can verify that this doesn't come with side affects,
354 // we'll say TODO verify this.
355
356 // Darwin lazily saves the AVX512 context on first use: trust that the OS will
357 // save the AVX512 context if we use AVX512 instructions, even if the bit is not
358 // set right now.
359 const has_avx512_save = switch (os_tag.isDarwin()) {
360 true => true,
361 false => has_avx and ((leaf.eax & 0xE0) == 0xE0),
362 };
363
364 setFeature(cpu, .avx, has_avx);
365 setFeature(cpu, .fma, has_avx and bit(leaf.ecx, 12));
366 // Only enable XSAVE if OS has enabled support for saving YMM state.
367 setFeature(cpu, .xsave, has_avx and bit(leaf.ecx, 26));
368 setFeature(cpu, .f16c, has_avx and bit(leaf.ecx, 29));
369
370 leaf = cpuid(0x80000000, 0);
371 const max_ext_level = leaf.eax;
372
373 if (max_ext_level >= 0x80000001) {
374 leaf = cpuid(0x80000001, 0);
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));
385 } else {
386 for ([_]Target.x86.Feature{
387 .sahf, .lzcnt, .sse4a, .prfchw, .xop,
388 .lwp, .fma4, .tbm, .mwaitx, .@"64bit",
389 }) |feat| {
390 setFeature(cpu, feat, false);
391 }
392 }
393
394 // Misc. memory-related features.
395 if (max_ext_level >= 0x80000008) {
396 leaf = cpuid(0x80000008, 0);
397 setFeature(cpu, .clzero, bit(leaf.ebx, 0));
398 setFeature(cpu, .wbnoinvd, bit(leaf.ebx, 9));
399 } else {
400 for ([_]Target.x86.Feature{ .clzero, .wbnoinvd }) |feat| {
401 setFeature(cpu, feat, false);
402 }
403 }
404
405 if (max_level >= 0x7) {
406 leaf = cpuid(0x7, 0);
407
408 setFeature(cpu, .fsgsbase, bit(leaf.ebx, 0));
409 setFeature(cpu, .sgx, bit(leaf.ebx, 2));
410 setFeature(cpu, .bmi, bit(leaf.ebx, 3));
411 // AVX2 is only supported if we have the OS save support from AVX.
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));
416 // AVX512 is only supported if the OS supports the context save for it.
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));
449
450 // There are two CPUID leafs which information associated with the pconfig
451 // instruction:
452 // EAX=0x7, ECX=0x0 indicates the availability of the instruction (via the 18th
453 // bit of EDX), while the EAX=0x1b leaf returns information on the
454 // availability of specific pconfig leafs.
455 // The target feature here only refers to the the first of these two.
456 // Users might need to check for the availability of specific pconfig
457 // leaves using cpuid, since that information is ignored while
458 // detecting features using the "-march=native" flag.
459 // For more info, see X86 ISA docs.
460 setFeature(cpu, .pconfig, bit(leaf.edx, 18));
461
462 // TODO I feel unsure about this check.
463 // It doesn't really seem to check for 7.1, just for 7.
464 // Is this a sound assumption to make?
465 // Note that this is what other implementations do, so I kind of trust it.
466 const has_leaf_7_1 = max_level >= 7;
467 if (has_leaf_7_1) {
468 leaf = cpuid(0x7, 0x1);
469 setFeature(cpu, .avx512bf16, bit(leaf.eax, 5) and has_avx512_save);
470 } else {
471 setFeature(cpu, .avx512bf16, false);
472 }
473 } else {
474 for ([_]Target.x86.Feature{
475 .fsgsbase, .sgx, .bmi, .avx2,
476 .bmi2, .invpcid, .rtm, .avx512f,
477 .avx512dq, .rdseed, .adx, .avx512ifma,
478 .clflushopt, .clwb, .avx512pf, .avx512er,
479 .avx512cd, .sha, .avx512bw, .avx512vl,
480 .prefetchwt1, .avx512vbmi, .pku, .waitpkg,
481 .avx512vbmi2, .shstk, .gfni, .vaes,
482 .vpclmulqdq, .avx512vnni, .avx512bitalg, .avx512vpopcntdq,
483 .avx512vp2intersect, .rdpid, .cldemote, .movdiri,
484 .movdir64b, .enqcmd, .pconfig, .avx512bf16,
485 }) |feat| {
486 setFeature(cpu, feat, false);
487 }
488 }
489
490 if (max_level >= 0xD and has_avx) {
491 leaf = cpuid(0xD, 0x1);
492 // Only enable XSAVE if OS has enabled support for saving YMM state.
493 setFeature(cpu, .xsaveopt, bit(leaf.eax, 0));
494 setFeature(cpu, .xsavec, bit(leaf.eax, 1));
495 setFeature(cpu, .xsaves, bit(leaf.eax, 3));
496 } else {
497 for ([_]Target.x86.Feature{ .xsaveopt, .xsavec, .xsaves }) |feat| {
498 setFeature(cpu, feat, false);
499 }
500 }
501
502 if (max_level >= 0x14) {
503 leaf = cpuid(0x14, 0);
504 setFeature(cpu, .ptwrite, bit(leaf.ebx, 4));
505 } else {
506 setFeature(cpu, .ptwrite, false);
507 }
508}
509
510const CpuidLeaf = packed struct {
511 eax: u32,
512 ebx: u32,
513 ecx: u32,
514 edx: u32,
515};
516
517fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
518 // Workaround for https://github.com/ziglang/zig/issues/215
519 // Inline assembly in zig only supports one output,
520 // so we pass a pointer to the struct.
521 var cpuid_leaf = CpuidLeaf{ .eax = 0, .ebx = 0, .ecx = 0, .edx = 0 };
522 const leaf_ptr = &cpuid_leaf;
523
524 // valid for both x86 and x86_64
525 asm volatile (
526 \\ cpuid
527 \\ movl %%eax, (%[leaf_ptr])
528 \\ movl %%ebx, 4(%[leaf_ptr])
529 \\ movl %%ecx, 8(%[leaf_ptr])
530 \\ movl %%edx, 12(%[leaf_ptr])
531 :
532 : [leaf_id] "{eax}" (leaf_id),
533 [subid] "{ecx}" (subid),
534 [leaf_ptr] "r" (leaf_ptr)
535 : "eax", "ebx", "ecx", "edx"
536 );
537 return cpuid_leaf;
538}
539
540// Read control register 0 (XCR0). Used to detect features such as AVX.
541fn getXCR0() u32 {
542 return asm (
543 \\ .byte 0x0F, 0x01, 0xD0
544 : [ret] "={eax}" (-> u32)
545 : [number] "{eax}" (@as(u32, 0)),
546 [number] "{edx}" (@as(u32, 0)),
547 [number] "{ecx}" (@as(u32, 0))
548 );
549}
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) {
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");