authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-22 17:13:31-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-22 17:13:31-05:00
log48c7e6c48b81e6e0423b3e4aea238402189eecb7
tree1d585eaa73a43b473809ffdc85b4207e2e72ee9c
parentc6bfece1d54c54024397d7aff9f25087cc4dbfda
signature Commit is signed but in an unrecognized format.

std.Target.CpuFeatures is now a struct with both CPU and feature set

Previously it was a tagged union which was one of: * baseline * a specific CPU * a set of features Now, it's possible to have a CPU but also modify the CPU's feature set on top of that. This is closer to what LLVM does. This is more correct because Zig's notion of CPUs (and LLVM's) is not exact CPU models. For example "skylake" is not one very specific model; there are several different pieces of hardware that match "skylake" that have different feature sets enabled.

13 files changed, 557 insertions(+), 657 deletions(-)

lib/std/build.zig+35-17
...@@ -484,6 +484,7 @@ pub const Builder = struct {...@@ -484,6 +484,7 @@ pub const Builder = struct {
484 .arch = builtin.arch,484 .arch = builtin.arch,
485 .os = builtin.os,485 .os = builtin.os,
486 .abi = builtin.abi,486 .abi = builtin.abi,
487 .cpu_features = builtin.cpu_features,
487 },488 },
488 }).linuxTriple(self.allocator);489 }).linuxTriple(self.allocator);
489490
...@@ -1375,6 +1376,7 @@ pub const LibExeObjStep = struct {...@@ -1375,6 +1376,7 @@ pub const LibExeObjStep = struct {
1375 .arch = target_arch,1376 .arch = target_arch,
1376 .os = target_os,1377 .os = target_os,
1377 .abi = target_abi,1378 .abi = target_abi,
1379 .cpu_features = target_arch.getBaselineCpuFeatures(),
1378 },1380 },
1379 });1381 });
1380 }1382 }
...@@ -1972,25 +1974,41 @@ pub const LibExeObjStep = struct {...@@ -1972,25 +1974,41 @@ pub const LibExeObjStep = struct {
1972 try zig_args.append("-target");1974 try zig_args.append("-target");
1973 try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable);1975 try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable);
19741976
1975 switch (cross.cpu_features) {1977 const all_features = self.target.getArch().allFeaturesList();
1976 .baseline => {},1978 var populated_cpu_features = cross.cpu_features.cpu.features;
1977 .cpu => |cpu| {1979 populated_cpu_features.populateDependencies(all_features);
1980
1981 if (populated_cpu_features.eql(cross.cpu_features.features)) {
1982 // The CPU name alone is sufficient.
1983 // If it is the baseline CPU, no command line args are required.
1984 if (cross.cpu_features.cpu != self.target.getArch().getBaselineCpuFeatures().cpu) {
1978 try zig_args.append("-target-cpu");1985 try zig_args.append("-target-cpu");
1979 try zig_args.append(cpu.name);1986 try zig_args.append(cross.cpu_features.cpu.name);
1980 },1987 }
1981 .features => |features| {1988 } else {
1982 try zig_args.append("-target-cpu-features");1989 try zig_args.append("-target-cpu");
19831990 try zig_args.append(cross.cpu_features.cpu.name);
1984 var feature_str_buffer = try std.Buffer.initSize(builder.allocator, 0);1991
1985 for (self.target.getArch().allFeaturesList()) |feature, i| {1992 try zig_args.append("-target-feature");
1986 if (features.isEnabled(@intCast(Target.Cpu.Feature.Set.Index, i))) {1993 var feature_str_buffer = try std.Buffer.initSize(builder.allocator, 0);
1987 try feature_str_buffer.append(feature.name);1994 for (all_features) |feature, i_usize| {
1988 try feature_str_buffer.append(",");1995 const i = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
1989 }1996 const in_cpu_set = populated_cpu_features.isEnabled(i);
1997 const in_actual_set = cross.cpu_features.features.isEnabled(i);
1998 if (in_cpu_set and !in_actual_set) {
1999 try feature_str_buffer.appendByte('-');
2000 try feature_str_buffer.append(feature.name);
2001 try feature_str_buffer.appendByte(',');
2002 } else if (!in_cpu_set and in_actual_set) {
2003 try feature_str_buffer.appendByte('+');
2004 try feature_str_buffer.append(feature.name);
2005 try feature_str_buffer.appendByte(',');
1990 }2006 }
19912007 }
1992 try zig_args.append(feature_str_buffer.toSlice());2008 if (mem.endsWith(u8, feature_str_buffer.toSliceConst(), ",")) {
1993 },2009 feature_str_buffer.shrink(feature_str_buffer.len() - 1);
2010 }
2011 try zig_args.append(feature_str_buffer.toSliceConst());
1994 }2012 }
1995 },2013 },
1996 }2014 }
lib/std/target.zig+70-82
...@@ -172,6 +172,15 @@ pub const Target = union(enum) {...@@ -172,6 +172,15 @@ pub const Target = union(enum) {
172 r6,172 r6,
173 };173 };
174174
175 pub fn subArchName(arch: Arch) ?[]const u8 {
176 return switch (arch) {
177 .arm, .armeb, .thumb, .thumbeb => |arm32| @tagName(arm32),
178 .aarch64, .aarch64_be, .aarch64_32 => |arm64| @tagName(arm64),
179 .kalimba => |kalimba| @tagName(kalimba),
180 else => return null,
181 };
182 }
183
175 pub fn subArchFeature(arch: Arch) ?u8 {184 pub fn subArchFeature(arch: Arch) ?u8 {
176 return switch (arch) {185 return switch (arch) {
177 .arm, .armeb, .thumb, .thumbeb => |arm32| switch (arm32) {186 .arm, .armeb, .thumb, .thumbeb => |arm32| switch (arm32) {
...@@ -251,24 +260,12 @@ pub const Target = union(enum) {...@@ -251,24 +260,12 @@ pub const Target = union(enum) {
251 return error.UnknownCpu;260 return error.UnknownCpu;
252 }261 }
253262
254 /// This parsing function supports 2 syntaxes.263 /// Comma-separated list of features, with + or - in front of each feature. This
255 /// * Comma-separated list of features, with + or - in front of each feature. This264 /// form represents a deviation from baseline CPU, which is provided as a parameter.
256 /// form represents a deviation from baseline.
257 /// * Comma-separated list of features, with no + or - in front of each feature. This
258 /// form represents an exclusive list of enabled features; no other features besides
259 /// the ones listed, and their dependencies, will be enabled.
260 /// Extra commas are ignored.265 /// Extra commas are ignored.
261 pub fn parseCpuFeatureSet(arch: Arch, features_text: []const u8) !Cpu.Feature.Set {266 pub fn parseCpuFeatureSet(arch: Arch, cpu: *const Cpu, features_text: []const u8) !Cpu.Feature.Set {
262 // Here we compute both and choose the correct result at the end, based267 const all_features = arch.allFeaturesList();
263 // on whether or not we saw + and - signs.268 var set = cpu.features;
264 var whitelist_set = Cpu.Feature.Set.empty;
265 var baseline_set = arch.baselineFeatures();
266 var mode: enum {
267 unknown,
268 baseline,
269 whitelist,
270 } = .unknown;
271
272 var it = mem.tokenize(features_text, ",");269 var it = mem.tokenize(features_text, ",");
273 while (it.next()) |item_text| {270 while (it.next()) |item_text| {
274 var feature_name: []const u8 = undefined;271 var feature_name: []const u8 = undefined;
...@@ -277,40 +274,20 @@ pub const Target = union(enum) {...@@ -277,40 +274,20 @@ pub const Target = union(enum) {
277 sub,274 sub,
278 } = undefined;275 } = undefined;
279 if (mem.startsWith(u8, item_text, "+")) {276 if (mem.startsWith(u8, item_text, "+")) {
280 switch (mode) {
281 .unknown, .baseline => mode = .baseline,
282 .whitelist => return error.InvalidCpuFeatures,
283 }
284 op = .add;277 op = .add;
285 feature_name = item_text[1..];278 feature_name = item_text[1..];
286 } else if (mem.startsWith(u8, item_text, "-")) {279 } else if (mem.startsWith(u8, item_text, "-")) {
287 switch (mode) {
288 .unknown, .baseline => mode = .baseline,
289 .whitelist => return error.InvalidCpuFeatures,
290 }
291 op = .sub;280 op = .sub;
292 feature_name = item_text[1..];281 feature_name = item_text[1..];
293 } else {282 } else {
294 switch (mode) {283 return error.InvalidCpuFeatures;
295 .unknown, .whitelist => mode = .whitelist,
296 .baseline => return error.InvalidCpuFeatures,
297 }
298 op = .add;
299 feature_name = item_text;
300 }284 }
301 const all_features = arch.allFeaturesList();
302 for (all_features) |feature, index_usize| {285 for (all_features) |feature, index_usize| {
303 const index = @intCast(Cpu.Feature.Set.Index, index_usize);286 const index = @intCast(Cpu.Feature.Set.Index, index_usize);
304 if (mem.eql(u8, feature_name, feature.name)) {287 if (mem.eql(u8, feature_name, feature.name)) {
305 switch (op) {288 switch (op) {
306 .add => {289 .add => set.addFeature(index),
307 baseline_set.addFeature(index);290 .sub => set.removeFeature(index),
308 whitelist_set.addFeature(index);
309 },
310 .sub => {
311 baseline_set.removeFeature(index);
312 whitelist_set.removeFeature(index);
313 },
314 }291 }
315 break;292 break;
316 }293 }
...@@ -319,10 +296,8 @@ pub const Target = union(enum) {...@@ -319,10 +296,8 @@ pub const Target = union(enum) {
319 }296 }
320 }297 }
321298
322 return switch (mode) {299 set.populateDependencies(all_features);
323 .unknown, .whitelist => whitelist_set,300 return set;
324 .baseline => baseline_set,
325 };
326 }301 }
327302
328 pub fn toElfMachine(arch: Arch) std.elf.EM {303 pub fn toElfMachine(arch: Arch) std.elf.EM {
...@@ -485,29 +460,37 @@ pub const Target = union(enum) {...@@ -485,29 +460,37 @@ pub const Target = union(enum) {
485460
486 /// The "default" set of CPU features for cross-compiling. A conservative set461 /// The "default" set of CPU features for cross-compiling. A conservative set
487 /// of features that is expected to be supported on most available hardware.462 /// of features that is expected to be supported on most available hardware.
488 pub fn baselineFeatures(arch: Arch) Cpu.Feature.Set {463 pub fn getBaselineCpuFeatures(arch: Arch) CpuFeatures {
489 return switch (arch) {464 const S = struct {
490 .arm, .armeb, .thumb, .thumbeb => arm.cpu.generic.features,465 const generic_cpu = Cpu{
491 .aarch64, .aarch64_be, .aarch64_32 => aarch64.cpu.generic.features,466 .name = "generic",
492 .avr => avr.baseline_features,467 .llvm_name = null,
493 .bpfel, .bpfeb => bpf.cpu.generic.features,468 .features = Cpu.Feature.Set.empty,
494 .hexagon => hexagon.cpu.generic.features,469 };
495 .mips, .mipsel => mips.cpu.mips32.features,470 };
496 .mips64, .mips64el => mips.cpu.mips64.features,471 const cpu = switch (arch) {
497 .msp430 => msp430.cpu.generic.features,472 .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic,
498 .powerpc, .powerpc64, .powerpc64le => powerpc.cpu.generic.features,473 .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic,
499 .amdgcn => amdgpu.cpu.generic.features,474 .avr => &avr.cpu.avr1,
500 .riscv32 => riscv.baseline_32_features,475 .bpfel, .bpfeb => &bpf.cpu.generic,
501 .riscv64 => riscv.baseline_64_features,476 .hexagon => &hexagon.cpu.generic,
502 .sparc, .sparcv9, .sparcel => sparc.cpu.generic.features,477 .mips, .mipsel => &mips.cpu.mips32,
503 .s390x => systemz.cpu.generic.features,478 .mips64, .mips64el => &mips.cpu.mips64,
504 .i386 => x86.cpu.pentium4.features,479 .msp430 => &msp430.cpu.generic,
505 .x86_64 => x86.cpu.x86_64.features,480 .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic,
506 .nvptx, .nvptx64 => nvptx.cpu.sm_20.features,481 .amdgcn => &amdgpu.cpu.generic,
507 .wasm32, .wasm64 => wasm.cpu.generic.features,482 .riscv32 => &riscv.cpu.baseline_rv32,
508483 .riscv64 => &riscv.cpu.baseline_rv64,
509 else => Cpu.Feature.Set.empty,484 .sparc, .sparcv9, .sparcel => &sparc.cpu.generic,
485 .s390x => &systemz.cpu.generic,
486 .i386 => &x86.cpu.pentium4,
487 .x86_64 => &x86.cpu.x86_64,
488 .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
489 .wasm32, .wasm64 => &wasm.cpu.generic,
490
491 else => &S.generic_cpu,
510 };492 };
493 return CpuFeatures.initFromCpu(arch, cpu);
511 }494 }
512495
513 /// All CPUs Zig is aware of, sorted lexicographically by name.496 /// All CPUs Zig is aware of, sorted lexicographically by name.
...@@ -685,19 +668,28 @@ pub const Target = union(enum) {...@@ -685,19 +668,28 @@ pub const Target = union(enum) {
685 arch: Arch,668 arch: Arch,
686 os: Os,669 os: Os,
687 abi: Abi,670 abi: Abi,
688 cpu_features: CpuFeatures = .baseline,671 cpu_features: CpuFeatures,
689 };672 };
690673
691 pub const CpuFeatures = union(enum) {674 pub const CpuFeatures = struct {
692 /// The "default" set of CPU features for cross-compiling. A conservative set675 /// The CPU to target. It has a set of features
693 /// of features that is expected to be supported on most available hardware.676 /// which are overridden with the `features` field.
694 baseline,
695
696 /// Target one specific CPU.
697 cpu: *const Cpu,677 cpu: *const Cpu,
698678
699 /// Explicitly provide the entire CPU feature set.679 /// Explicitly provide the entire CPU feature set.
700 features: Cpu.Feature.Set,680 features: Cpu.Feature.Set,
681
682 pub fn initFromCpu(arch: Arch, cpu: *const Cpu) CpuFeatures {
683 var features = cpu.features;
684 if (arch.subArchFeature()) |sub_arch_index| {
685 features.addFeature(sub_arch_index);
686 }
687 features.populateDependencies(arch.allFeaturesList());
688 return CpuFeatures{
689 .cpu = cpu,
690 .features = features,
691 };
692 }
701 };693 };
702694
703 pub const current = Target{695 pub const current = Target{
...@@ -718,14 +710,6 @@ pub const Target = union(enum) {...@@ -718,14 +710,6 @@ pub const Target = union(enum) {
718 };710 };
719 }711 }
720712
721 pub fn cpuFeatureSet(self: Target) Cpu.Feature.Set {
722 return switch (self.getCpuFeatures()) {
723 .baseline => self.getArch().baselineFeatures(),
724 .cpu => |cpu| cpu.features,
725 .features => |features| features,
726 };
727 }
728
729 pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {713 pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
730 return std.fmt.allocPrint(allocator, "{}{}-{}-{}", .{714 return std.fmt.allocPrint(allocator, "{}{}-{}-{}", .{
731 @tagName(self.getArch()),715 @tagName(self.getArch()),
...@@ -791,14 +775,18 @@ pub const Target = union(enum) {...@@ -791,14 +775,18 @@ pub const Target = union(enum) {
791 });775 });
792 }776 }
793777
778 /// TODO: Support CPU features here?
779 /// https://github.com/ziglang/zig/issues/4261
794 pub fn parse(text: []const u8) !Target {780 pub fn parse(text: []const u8) !Target {
795 var it = mem.separate(text, "-");781 var it = mem.separate(text, "-");
796 const arch_name = it.next() orelse return error.MissingArchitecture;782 const arch_name = it.next() orelse return error.MissingArchitecture;
797 const os_name = it.next() orelse return error.MissingOperatingSystem;783 const os_name = it.next() orelse return error.MissingOperatingSystem;
798 const abi_name = it.next();784 const abi_name = it.next();
785 const arch = try parseArchSub(arch_name);
799786
800 var cross = Cross{787 var cross = Cross{
801 .arch = try parseArchSub(arch_name),788 .arch = arch,
789 .cpu_features = arch.getBaselineCpuFeatures(),
802 .os = try parseOs(os_name),790 .os = try parseOs(os_name),
803 .abi = undefined,791 .abi = undefined,
804 };792 };
lib/std/target/avr.zig-4
...@@ -2378,7 +2378,3 @@ pub const all_cpus = &[_]*const Cpu{...@@ -2378,7 +2378,3 @@ pub const all_cpus = &[_]*const Cpu{
2378 &cpu.avrxmega7,2378 &cpu.avrxmega7,
2379 &cpu.m3000,2379 &cpu.m3000,
2380};2380};
2381
2382pub const baseline_features = featureSet(&[_]Feature{
2383 .avr0,
2384});
lib/std/target/riscv.zig+30-19
...@@ -69,11 +69,39 @@ pub const all_features = blk: {...@@ -69,11 +69,39 @@ pub const all_features = blk: {
69};69};
7070
71pub const cpu = struct {71pub const cpu = struct {
72 pub const baseline_rv32 = Cpu{
73 .name = "baseline_rv32",
74 .llvm_name = "generic-rv32",
75 .features = featureSet(&[_]Feature{
76 .a,
77 .c,
78 .d,
79 .f,
80 .m,
81 .relax,
82 }),
83 };
84
85 pub const baseline_rv64 = Cpu{
86 .name = "baseline_rv64",
87 .llvm_name = "generic-rv64",
88 .features = featureSet(&[_]Feature{
89 .@"64bit",
90 .a,
91 .c,
92 .d,
93 .f,
94 .m,
95 .relax,
96 }),
97 };
98
72 pub const generic_rv32 = Cpu{99 pub const generic_rv32 = Cpu{
73 .name = "generic_rv32",100 .name = "generic_rv32",
74 .llvm_name = "generic-rv32",101 .llvm_name = "generic-rv32",
75 .features = featureSet(&[_]Feature{}),102 .features = featureSet(&[_]Feature{}),
76 };103 };
104
77 pub const generic_rv64 = Cpu{105 pub const generic_rv64 = Cpu{
78 .name = "generic_rv64",106 .name = "generic_rv64",
79 .llvm_name = "generic-rv64",107 .llvm_name = "generic-rv64",
...@@ -87,25 +115,8 @@ pub const cpu = struct {...@@ -87,25 +115,8 @@ pub const cpu = struct {
87/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1115/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
88/// compiler has inefficient memory and CPU usage, affecting build times.116/// compiler has inefficient memory and CPU usage, affecting build times.
89pub const all_cpus = &[_]*const Cpu{117pub const all_cpus = &[_]*const Cpu{
118 &cpu.baseline_rv32,
119 &cpu.baseline_rv64,
90 &cpu.generic_rv32,120 &cpu.generic_rv32,
91 &cpu.generic_rv64,121 &cpu.generic_rv64,
92};122};
93
94pub const baseline_32_features = featureSet(&[_]Feature{
95 .a,
96 .c,
97 .d,
98 .f,
99 .m,
100 .relax,
101});
102
103pub const baseline_64_features = featureSet(&[_]Feature{
104 .@"64bit",
105 .a,
106 .c,
107 .d,
108 .f,
109 .m,
110 .relax,
111});
src-self-hosted/print_targets.zig+5-7
...@@ -227,16 +227,14 @@ pub fn cmdTargets(...@@ -227,16 +227,14 @@ pub fn cmdTargets(
227 try jws.objectField("abi");227 try jws.objectField("abi");
228 try jws.emitString(@tagName(native_target.getAbi()));228 try jws.emitString(@tagName(native_target.getAbi()));
229 try jws.objectField("cpuName");229 try jws.objectField("cpuName");
230 switch (native_target.getCpuFeatures()) {230 const cpu_features = native_target.getCpuFeatures();
231 .baseline, .features => try jws.emitNull(),231 try jws.emitString(cpu_features.cpu.name);
232 .cpu => |cpu| try jws.emitString(cpu.name),
233 }
234 {232 {
235 try jws.objectField("cpuFeatures");233 try jws.objectField("cpuFeatures");
236 try jws.beginArray();234 try jws.beginArray();
237 const feature_set = native_target.cpuFeatureSet();235 for (native_target.getArch().allFeaturesList()) |feature, i_usize| {
238 for (native_target.getArch().allFeaturesList()) |feature, i| {236 const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
239 if (feature_set.isEnabled(@intCast(u8, i))) {237 if (cpu_features.features.isEnabled(index)) {
240 try jws.arrayElem();238 try jws.arrayElem();
241 try jws.emitString(feature.name);239 try jws.emitString(feature.name);
242 }240 }
src-self-hosted/stage1.zig+140-256
...@@ -540,74 +540,66 @@ export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usiz...@@ -540,74 +540,66 @@ export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usiz
540 node.context.maybeRefresh();540 node.context.maybeRefresh();
541}541}
542542
543/// I have observed the CPU name reported by LLVM being incorrect. On
544/// the SourceHut build services, LLVM 9.0 reports the CPU as "athlon-xp",
545/// which is a 32-bit CPU, even though the system is 64-bit and the reported
546/// CPU features include, among other things, +64bit.
547/// So the strategy taken here is that we observe both reported CPU, and the
548/// reported CPU features. The features are trusted more; but if the features
549/// match exactly the features of the reported CPU, then we trust the reported CPU.
550fn cpuFeaturesFromLLVM(543fn cpuFeaturesFromLLVM(
551 arch: Target.Arch,544 arch: Target.Arch,
552 llvm_cpu_name_z: ?[*:0]const u8,545 llvm_cpu_name_z: ?[*:0]const u8,
553 llvm_cpu_features_opt: ?[*:0]const u8,546 llvm_cpu_features_opt: ?[*:0]const u8,
554) !Target.CpuFeatures {547) !Target.CpuFeatures {
555 var set = arch.baselineFeatures();548 var result = arch.getBaselineCpuFeatures();
556 const llvm_cpu_features = llvm_cpu_features_opt orelse return Target.CpuFeatures{
557 .features = set,
558 };
559549
560 const all_features = arch.allFeaturesList();550 if (llvm_cpu_name_z) |cpu_name_z| {
551 const llvm_cpu_name = mem.toSliceConst(u8, cpu_name_z);
561552
562 var it = mem.tokenize(mem.toSliceConst(u8, llvm_cpu_features), ",");553 for (arch.allCpus()) |cpu| {
563 while (it.next()) |decorated_llvm_feat| {554 const this_llvm_name = cpu.llvm_name orelse continue;
564 var op: enum {555 if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
565 add,556 // Here we use the non-dependencies-populated set,
566 sub,557 // so that subtracting features later in this function
567 } = undefined;558 // affect the prepopulated set.
568 var llvm_feat: []const u8 = undefined;559 result = Target.CpuFeatures{
569 if (mem.startsWith(u8, decorated_llvm_feat, "+")) {560 .cpu = cpu,
570 op = .add;561 .features = cpu.features,
571 llvm_feat = decorated_llvm_feat[1..];562 };
572 } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
573 op = .sub;
574 llvm_feat = decorated_llvm_feat[1..];
575 } else {
576 return error.InvalidLlvmCpuFeaturesFormat;
577 }
578 for (all_features) |feature, index| {
579 const this_llvm_name = feature.llvm_name orelse continue;
580 if (mem.eql(u8, llvm_feat, this_llvm_name)) {
581 switch (op) {
582 .add => set.addFeature(@intCast(u8, index)),
583 .sub => set.removeFeature(@intCast(u8, index)),
584 }
585 break;563 break;
586 }564 }
587 }565 }
588 }566 }
589567
590 if (llvm_cpu_name_z) |cpu_name_z| {568 const all_features = arch.allFeaturesList();
591 const llvm_cpu_name = mem.toSliceConst(u8, cpu_name_z);
592569
593 for (arch.allCpus()) |cpu| {570 if (llvm_cpu_features_opt) |llvm_cpu_features| {
594 const this_llvm_name = cpu.llvm_name orelse continue;571 var it = mem.tokenize(mem.toSliceConst(u8, llvm_cpu_features), ",");
595 if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {572 while (it.next()) |decorated_llvm_feat| {
596 // Only trust the CPU if the reported features exactly match.573 var op: enum {
597 var populated_reported_features = set;574 add,
598 populated_reported_features.populateDependencies(all_features);575 sub,
599 var populated_cpu_features = cpu.features;576 } = undefined;
600 populated_cpu_features.populateDependencies(all_features);577 var llvm_feat: []const u8 = undefined;
601 if (populated_reported_features.eql(populated_cpu_features)) {578 if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
602 return Target.CpuFeatures{ .cpu = cpu };579 op = .add;
603 } else {580 llvm_feat = decorated_llvm_feat[1..];
604 return Target.CpuFeatures{ .features = set };581 } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
582 op = .sub;
583 llvm_feat = decorated_llvm_feat[1..];
584 } else {
585 return error.InvalidLlvmCpuFeaturesFormat;
586 }
587 for (all_features) |feature, index_usize| {
588 const this_llvm_name = feature.llvm_name orelse continue;
589 if (mem.eql(u8, llvm_feat, this_llvm_name)) {
590 const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
591 switch (op) {
592 .add => result.features.addFeature(index),
593 .sub => result.features.removeFeature(index),
594 }
595 break;
605 }596 }
606 }597 }
607 }598 }
608 }599 }
609600
610 return Target.CpuFeatures{ .features = set };601 result.features.populateDependencies(all_features);
602 return result;
611}603}
612604
613// ABI warning605// ABI warning
...@@ -639,7 +631,6 @@ const Stage2CpuFeatures = struct {...@@ -639,7 +631,6 @@ const Stage2CpuFeatures = struct {
639 allocator: *mem.Allocator,631 allocator: *mem.Allocator,
640 cpu_features: Target.CpuFeatures,632 cpu_features: Target.CpuFeatures,
641633
642 llvm_cpu_name: ?[*:0]const u8,
643 llvm_features_str: ?[*:0]const u8,634 llvm_features_str: ?[*:0]const u8,
644635
645 builtin_str: [:0]const u8,636 builtin_str: [:0]const u8,
...@@ -647,125 +638,64 @@ const Stage2CpuFeatures = struct {...@@ -647,125 +638,64 @@ const Stage2CpuFeatures = struct {
647638
648 const Self = @This();639 const Self = @This();
649640
650 fn createBaseline(allocator: *mem.Allocator, arch: Target.Arch) !*Self {641 fn createFromNative(allocator: *mem.Allocator) !*Self {
651 const self = try allocator.create(Self);642 const arch = Target.current.getArch();
652 errdefer allocator.destroy(self);643 const llvm = @import("llvm.zig");
653644 const llvm_cpu_name = llvm.GetHostCPUName();
654 const builtin_str = try std.fmt.allocPrint0(allocator, ".baseline;\n", .{});645 const llvm_cpu_features = llvm.GetNativeFeatures();
655 errdefer allocator.free(builtin_str);646 const cpu_features = try cpuFeaturesFromLLVM(arch, llvm_cpu_name, llvm_cpu_features);
656647 return createFromCpuFeatures(allocator, arch, cpu_features);
657 const cache_hash = try std.fmt.allocPrint0(allocator, "\n\n", .{});
658 errdefer allocator.free(cache_hash);
659
660 self.* = Self{
661 .allocator = allocator,
662 .cpu_features = .baseline,
663 .llvm_cpu_name = null,
664 .llvm_features_str = try initLLVMFeatures(allocator, arch, arch.baselineFeatures()),
665 .builtin_str = builtin_str,
666 .cache_hash = cache_hash,
667 };
668
669 return self;
670 }
671
672 fn createFromLLVM(
673 allocator: *mem.Allocator,
674 zig_triple: [*:0]const u8,
675 llvm_cpu_name_z: ?[*:0]const u8,
676 llvm_cpu_features: ?[*:0]const u8,
677 ) !*Self {
678 const target = try Target.parse(mem.toSliceConst(u8, zig_triple));
679 const arch = target.Cross.arch;
680 const cpu_features = try cpuFeaturesFromLLVM(arch, llvm_cpu_name_z, llvm_cpu_features);
681 switch (cpu_features) {
682 .baseline => return createBaseline(allocator, arch),
683 .cpu => |cpu| return createFromCpu(allocator, arch, cpu),
684 .features => |features| return createFromCpuFeatures(allocator, arch, features),
685 }
686 }
687
688 fn createFromCpu(allocator: *mem.Allocator, arch: Target.Arch, cpu: *const Target.Cpu) !*Self {
689 const self = try allocator.create(Self);
690 errdefer allocator.destroy(self);
691
692 const builtin_str = try std.fmt.allocPrint0(allocator, "CpuFeatures{{ .cpu = &Target.{}.cpu.{} }};\n", .{
693 arch.genericName(),
694 cpu.name,
695 });
696 errdefer allocator.free(builtin_str);
697
698 const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{ cpu.name, cpu.features.asBytes() });
699 errdefer allocator.free(cache_hash);
700
701 self.* = Self{
702 .allocator = allocator,
703 .cpu_features = .{ .cpu = cpu },
704 .llvm_cpu_name = if (cpu.llvm_name) |n| n.ptr else null,
705 .llvm_features_str = null,
706 .builtin_str = builtin_str,
707 .cache_hash = cache_hash,
708 };
709 return self;
710 }
711
712 fn initLLVMFeatures(
713 allocator: *mem.Allocator,
714 arch: Target.Arch,
715 feature_set: Target.Cpu.Feature.Set,
716 ) ![*:0]const u8 {
717 var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
718 defer llvm_features_buffer.deinit();
719
720 const all_features = arch.allFeaturesList();
721 var populated_feature_set = feature_set;
722 if (arch.subArchFeature()) |sub_arch_index| {
723 populated_feature_set.addFeature(sub_arch_index);
724 }
725 populated_feature_set.populateDependencies(all_features);
726 for (all_features) |feature, index| {
727 const llvm_name = feature.llvm_name orelse continue;
728 const plus_or_minus = "-+"[@boolToInt(populated_feature_set.isEnabled(@intCast(u8, index)))];
729 try llvm_features_buffer.appendByte(plus_or_minus);
730 try llvm_features_buffer.append(llvm_name);
731 try llvm_features_buffer.append(",");
732 }
733 assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
734 llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
735
736 return llvm_features_buffer.toOwnedSlice().ptr;
737 }648 }
738649
739 fn createFromCpuFeatures(650 fn createFromCpuFeatures(
740 allocator: *mem.Allocator,651 allocator: *mem.Allocator,
741 arch: Target.Arch,652 arch: Target.Arch,
742 feature_set: Target.Cpu.Feature.Set,653 cpu_features: Target.CpuFeatures,
743 ) !*Self {654 ) !*Self {
744 const self = try allocator.create(Self);655 const self = try allocator.create(Self);
745 errdefer allocator.destroy(self);656 errdefer allocator.destroy(self);
746657
747 const cache_hash = try std.fmt.allocPrint0(allocator, "\n{}", .{feature_set.asBytes()});658 const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{
659 cpu_features.cpu.name,
660 cpu_features.features.asBytes(),
661 });
748 errdefer allocator.free(cache_hash);662 errdefer allocator.free(cache_hash);
749663
750 const generic_arch_name = arch.genericName();664 const generic_arch_name = arch.genericName();
751 var builtin_str_buffer = try std.Buffer.allocPrint(665 var builtin_str_buffer = try std.Buffer.allocPrint(allocator,
752 allocator,
753 \\CpuFeatures{{666 \\CpuFeatures{{
667 \\ .cpu = &Target.{}.cpu.{},
754 \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{668 \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
755 \\669 \\
756 ,670 , .{
757 .{ generic_arch_name, generic_arch_name },671 generic_arch_name,
758 );672 cpu_features.cpu.name,
673 generic_arch_name,
674 generic_arch_name,
675 });
759 defer builtin_str_buffer.deinit();676 defer builtin_str_buffer.deinit();
760677
761 for (arch.allFeaturesList()) |feature, index| {678 var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
762 if (!feature_set.isEnabled(@intCast(u8, index))) continue;679 defer llvm_features_buffer.deinit();
680
681 for (arch.allFeaturesList()) |feature, index_usize| {
682 const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
683 const is_enabled = cpu_features.features.isEnabled(index);
684
685 if (feature.llvm_name) |llvm_name| {
686 const plus_or_minus = "-+"[@boolToInt(is_enabled)];
687 try llvm_features_buffer.appendByte(plus_or_minus);
688 try llvm_features_buffer.append(llvm_name);
689 try llvm_features_buffer.append(",");
690 }
763691
764 // TODO some kind of "zig identifier escape" function rather than692 if (is_enabled) {
765 // unconditionally using @"" syntax693 // TODO some kind of "zig identifier escape" function rather than
766 try builtin_str_buffer.append(" .@\"");694 // unconditionally using @"" syntax
767 try builtin_str_buffer.append(feature.name);695 try builtin_str_buffer.append(" .@\"");
768 try builtin_str_buffer.append("\",\n");696 try builtin_str_buffer.append(feature.name);
697 try builtin_str_buffer.append("\",\n");
698 }
769 }699 }
770700
771 try builtin_str_buffer.append(701 try builtin_str_buffer.append(
...@@ -774,11 +704,13 @@ const Stage2CpuFeatures = struct {...@@ -774,11 +704,13 @@ const Stage2CpuFeatures = struct {
774 \\704 \\
775 );705 );
776706
707 assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
708 llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
709
777 self.* = Self{710 self.* = Self{
778 .allocator = allocator,711 .allocator = allocator,
779 .cpu_features = .{ .features = feature_set },712 .cpu_features = cpu_features,
780 .llvm_cpu_name = null,713 .llvm_features_str = llvm_features_buffer.toOwnedSlice().ptr,
781 .llvm_features_str = try initLLVMFeatures(allocator, arch, feature_set),
782 .builtin_str = builtin_str_buffer.toOwnedSlice(),714 .builtin_str = builtin_str_buffer.toOwnedSlice(),
783 .cache_hash = cache_hash,715 .cache_hash = cache_hash,
784 };716 };
...@@ -794,12 +726,13 @@ const Stage2CpuFeatures = struct {...@@ -794,12 +726,13 @@ const Stage2CpuFeatures = struct {
794};726};
795727
796// ABI warning728// ABI warning
797export fn stage2_cpu_features_parse_cpu(729export fn stage2_cpu_features_parse(
798 result: **Stage2CpuFeatures,730 result: **Stage2CpuFeatures,
799 zig_triple: [*:0]const u8,731 zig_triple: ?[*:0]const u8,
800 cpu_name: [*:0]const u8,732 cpu_name: ?[*:0]const u8,
733 cpu_features: ?[*:0]const u8,
801) Error {734) Error {
802 result.* = parseCpu(zig_triple, cpu_name) catch |err| switch (err) {735 result.* = stage2ParseCpuFeatures(zig_triple, cpu_name, cpu_features) catch |err| switch (err) {
803 error.OutOfMemory => return .OutOfMemory,736 error.OutOfMemory => return .OutOfMemory,
804 error.UnknownArchitecture => return .UnknownArchitecture,737 error.UnknownArchitecture => return .UnknownArchitecture,
805 error.UnknownSubArchitecture => return .UnknownSubArchitecture,738 error.UnknownSubArchitecture => return .UnknownSubArchitecture,
...@@ -807,110 +740,61 @@ export fn stage2_cpu_features_parse_cpu(...@@ -807,110 +740,61 @@ export fn stage2_cpu_features_parse_cpu(
807 error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,740 error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,
808 error.MissingOperatingSystem => return .MissingOperatingSystem,741 error.MissingOperatingSystem => return .MissingOperatingSystem,
809 error.MissingArchitecture => return .MissingArchitecture,742 error.MissingArchitecture => return .MissingArchitecture,
810 };743 error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat,
811 return .None;
812}
813
814fn parseCpu(zig_triple: [*:0]const u8, cpu_name_z: [*:0]const u8) !*Stage2CpuFeatures {
815 const cpu_name = mem.toSliceConst(u8, cpu_name_z);
816 const target = try Target.parse(mem.toSliceConst(u8, zig_triple));
817 const arch = target.Cross.arch;
818 const cpu = arch.parseCpu(cpu_name) catch |err| switch (err) {
819 error.UnknownCpu => {
820 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
821 cpu_name,
822 @tagName(arch),
823 });
824 for (arch.allCpus()) |cpu| {
825 std.debug.warn(" {}\n", .{cpu.name});
826 }
827 process.exit(1);
828 },
829 else => |e| return e,
830 };
831 return Stage2CpuFeatures.createFromCpu(std.heap.c_allocator, arch, cpu);
832}
833
834// ABI warning
835export fn stage2_cpu_features_parse_features(
836 result: **Stage2CpuFeatures,
837 zig_triple: [*:0]const u8,
838 features_text: [*:0]const u8,
839) Error {
840 result.* = parseFeatures(zig_triple, features_text) catch |err| switch (err) {
841 error.OutOfMemory => return .OutOfMemory,
842 error.InvalidCpuFeatures => return .InvalidCpuFeatures,744 error.InvalidCpuFeatures => return .InvalidCpuFeatures,
843 error.UnknownArchitecture => return .UnknownArchitecture,
844 error.UnknownSubArchitecture => return .UnknownSubArchitecture,
845 error.UnknownOperatingSystem => return .UnknownOperatingSystem,
846 error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,
847 error.MissingOperatingSystem => return .MissingOperatingSystem,
848 error.MissingArchitecture => return .MissingArchitecture,
849 };745 };
850 return .None;746 return .None;
851}747}
852748
853fn parseFeatures(zig_triple: [*:0]const u8, features_text: [*:0]const u8) !*Stage2CpuFeatures {749fn stage2ParseCpuFeatures(
854 const target = try Target.parse(mem.toSliceConst(u8, zig_triple));750 zig_triple_oz: ?[*:0]const u8,
751 cpu_name_oz: ?[*:0]const u8,
752 cpu_features_oz: ?[*:0]const u8,
753) !*Stage2CpuFeatures {
754 const zig_triple_z = zig_triple_oz orelse return Stage2CpuFeatures.createFromNative(std.heap.c_allocator);
755 const target = try Target.parse(mem.toSliceConst(u8, zig_triple_z));
855 const arch = target.Cross.arch;756 const arch = target.Cross.arch;
856 const set = arch.parseCpuFeatureSet(mem.toSliceConst(u8, features_text)) catch |err| switch (err) {
857 error.UnknownCpuFeature => {
858 std.debug.warn("Unknown CPU features specified.\nAvailable CPU features for architecture '{}':\n", .{
859 @tagName(arch),
860 });
861 for (arch.allFeaturesList()) |feature| {
862 std.debug.warn(" {}\n", .{feature.name});
863 }
864 process.exit(1);
865 },
866 else => |e| return e,
867 };
868 return Stage2CpuFeatures.createFromCpuFeatures(std.heap.c_allocator, arch, set);
869}
870757
871// ABI warning758 const cpu = if (cpu_name_oz) |cpu_name_z| blk: {
872export fn stage2_cpu_features_baseline(result: **Stage2CpuFeatures, zig_triple: [*:0]const u8) Error {759 const cpu_name = mem.toSliceConst(u8, cpu_name_z);
873 result.* = cpuFeaturesBaseline(zig_triple) catch |err| switch (err) {760 break :blk arch.parseCpu(cpu_name) catch |err| switch (err) {
874 error.OutOfMemory => return .OutOfMemory,761 error.UnknownCpu => {
875 error.UnknownArchitecture => return .UnknownArchitecture,762 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
876 error.UnknownSubArchitecture => return .UnknownSubArchitecture,763 cpu_name,
877 error.UnknownOperatingSystem => return .UnknownOperatingSystem,764 @tagName(arch),
878 error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,765 });
879 error.MissingOperatingSystem => return .MissingOperatingSystem,766 for (arch.allCpus()) |cpu| {
880 error.MissingArchitecture => return .MissingArchitecture,767 std.debug.warn(" {}\n", .{cpu.name});
881 };768 }
882 return .None;769 process.exit(1);
883}770 },
884771 else => |e| return e,
885fn cpuFeaturesBaseline(zig_triple: [*:0]const u8) !*Stage2CpuFeatures {772 };
886 const target = try Target.parse(mem.toSliceConst(u8, zig_triple));773 } else target.Cross.cpu_features.cpu;
887 const arch = target.Cross.arch;774
888 return Stage2CpuFeatures.createBaseline(std.heap.c_allocator, arch);775 var set = if (cpu_features_oz) |cpu_features_z| blk: {
889}776 const cpu_features = mem.toSliceConst(u8, cpu_features_z);
777 break :blk arch.parseCpuFeatureSet(cpu, cpu_features) catch |err| switch (err) {
778 error.UnknownCpuFeature => {
779 std.debug.warn(
780 \\Unknown CPU features specified.
781 \\Available CPU features for architecture '{}':
782 \\
783 , .{@tagName(arch)});
784 for (arch.allFeaturesList()) |feature| {
785 std.debug.warn(" {}\n", .{feature.name});
786 }
787 process.exit(1);
788 },
789 else => |e| return e,
790 };
791 } else cpu.features;
890792
891// ABI warning793 set.populateDependencies(arch.allFeaturesList());
892export fn stage2_cpu_features_llvm(794 return Stage2CpuFeatures.createFromCpuFeatures(std.heap.c_allocator, arch, .{
893 result: **Stage2CpuFeatures,795 .cpu = cpu,
894 zig_triple: [*:0]const u8,796 .features = set,
895 llvm_cpu_name: ?[*:0]const u8,797 });
896 llvm_cpu_features: ?[*:0]const u8,
897) Error {
898 result.* = Stage2CpuFeatures.createFromLLVM(
899 std.heap.c_allocator,
900 zig_triple,
901 llvm_cpu_name,
902 llvm_cpu_features,
903 ) catch |err| switch (err) {
904 error.OutOfMemory => return .OutOfMemory,
905 error.UnknownArchitecture => return .UnknownArchitecture,
906 error.UnknownSubArchitecture => return .UnknownSubArchitecture,
907 error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat,
908 error.UnknownOperatingSystem => return .UnknownOperatingSystem,
909 error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,
910 error.MissingOperatingSystem => return .MissingOperatingSystem,
911 error.MissingArchitecture => return .MissingArchitecture,
912 };
913 return .None;
914}798}
915799
916// ABI warning800// ABI warning
...@@ -935,7 +819,7 @@ export fn stage2_cpu_features_get_builtin_str(...@@ -935,7 +819,7 @@ export fn stage2_cpu_features_get_builtin_str(
935819
936// ABI warning820// ABI warning
937export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 {821export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 {
938 return cpu_features.llvm_cpu_name;822 return if (cpu_features.cpu_features.cpu.llvm_name) |s| s.ptr else null;
939}823}
940824
941// ABI warning825// ABI warning
src/codegen.cpp+1-1
...@@ -8581,7 +8581,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -8581,7 +8581,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
8581 stage2_cpu_features_get_builtin_str(g->zig_target->cpu_features, &ptr, &len);8581 stage2_cpu_features_get_builtin_str(g->zig_target->cpu_features, &ptr, &len);
8582 buf_append_mem(contents, ptr, len);8582 buf_append_mem(contents, ptr, len);
8583 } else {8583 } else {
8584 buf_append_str(contents, ".baseline;\n");8584 buf_append_str(contents, "arch.getBaselineCpuFeatures();\n");
8585 }8585 }
8586 }8586 }
8587 if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) {8587 if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) {
src/main.cpp+4-29
...@@ -866,7 +866,7 @@ int main(int argc, char **argv) {...@@ -866,7 +866,7 @@ int main(int argc, char **argv) {
866 cpu = argv[i];866 cpu = argv[i];
867 } else if (strcmp(arg, "-target-feature") == 0) {867 } else if (strcmp(arg, "-target-feature") == 0) {
868 features = argv[i];868 features = argv[i];
869 }else {869 } else {
870 fprintf(stderr, "Invalid argument: %s\n", arg);870 fprintf(stderr, "Invalid argument: %s\n", arg);
871 return print_error_usage(arg0);871 return print_error_usage(arg0);
872 }872 }
...@@ -984,35 +984,10 @@ int main(int argc, char **argv) {...@@ -984,35 +984,10 @@ int main(int argc, char **argv) {
984 Buf zig_triple_buf = BUF_INIT;984 Buf zig_triple_buf = BUF_INIT;
985 target_triple_zig(&zig_triple_buf, &target);985 target_triple_zig(&zig_triple_buf, &target);
986986
987 if (cpu && features) {987 const char *stage2_triple_arg = target.is_native ? nullptr : buf_ptr(&zig_triple_buf);
988 fprintf(stderr, "-target-cpu and -target-feature options not allowed together\n");988 if ((err = stage2_cpu_features_parse(&target.cpu_features, stage2_triple_arg, cpu, features))) {
989 fprintf(stderr, "unable to initialize CPU features: %s\n", err_str(err));
989 return main_exit(root_progress_node, EXIT_FAILURE);990 return main_exit(root_progress_node, EXIT_FAILURE);
990 } else if (cpu) {
991 if ((err = stage2_cpu_features_parse_cpu(&target.cpu_features, buf_ptr(&zig_triple_buf), cpu))) {
992 fprintf(stderr, "-target-cpu error: %s\n", err_str(err));
993 return main_exit(root_progress_node, EXIT_FAILURE);
994 }
995 } else if (features) {
996 if ((err = stage2_cpu_features_parse_features(&target.cpu_features, buf_ptr(&zig_triple_buf),
997 features)))
998 {
999 fprintf(stderr, "-target-feature error: %s\n", err_str(err));
1000 return main_exit(root_progress_node, EXIT_FAILURE);
1001 }
1002 } else if (target.is_native) {
1003 const char *cpu_name = ZigLLVMGetHostCPUName();
1004 const char *cpu_features = ZigLLVMGetNativeFeatures();
1005 if ((err = stage2_cpu_features_llvm(&target.cpu_features, buf_ptr(&zig_triple_buf),
1006 cpu_name, cpu_features)))
1007 {
1008 fprintf(stderr, "unable to determine native CPU features: %s\n", err_str(err));
1009 return main_exit(root_progress_node, EXIT_FAILURE);
1010 }
1011 } else {
1012 if ((err = stage2_cpu_features_baseline(&target.cpu_features, buf_ptr(&zig_triple_buf)))) {
1013 fprintf(stderr, "unable to determine baseline CPU features: %s\n", err_str(err));
1014 return main_exit(root_progress_node, EXIT_FAILURE);
1015 }
1016 }991 }
1017992
1018 if (output_dir != nullptr && enable_cache == CacheOptOn) {993 if (output_dir != nullptr && enable_cache == CacheOptOn) {
src/userland.cpp+24-25
...@@ -2,7 +2,8 @@...@@ -2,7 +2,8 @@
2// src-self-hosted/stage1.zig2// src-self-hosted/stage1.zig
33
4#include "userland.h"4#include "userland.h"
5#include "ast_render.hpp"5#include "util.hpp"
6#include "zig_llvm.h"
6#include <stdio.h>7#include <stdio.h>
7#include <stdlib.h>8#include <stdlib.h>
8#include <string.h>9#include <string.h>
...@@ -96,32 +97,30 @@ struct Stage2CpuFeatures {...@@ -96,32 +97,30 @@ struct Stage2CpuFeatures {
96 const char *cache_hash;97 const char *cache_hash;
97};98};
9899
99Error stage2_cpu_features_parse_cpu(Stage2CpuFeatures **out, const char *zig_triple, const char *str) {100Error stage2_cpu_features_parse(struct Stage2CpuFeatures **out, const char *zig_triple,
100 const char *msg = "stage0 called stage2_cpu_features_parse_cpu";101 const char *cpu_name, const char *cpu_features)
101 stage2_panic(msg, strlen(msg));
102}
103Error stage2_cpu_features_parse_features(Stage2CpuFeatures **out, const char *zig_triple, const char *str) {
104 const char *msg = "stage0 called stage2_cpu_features_parse_features";
105 stage2_panic(msg, strlen(msg));
106}
107Error stage2_cpu_features_baseline(Stage2CpuFeatures **out, const char *zig_triple) {
108 Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");
109 result->builtin_str = ".baseline;\n";
110 result->cache_hash = "\n\n";
111 *out = result;
112 return ErrorNone;
113}
114Error stage2_cpu_features_llvm(Stage2CpuFeatures **out, const char *zig_triple,
115 const char *llvm_cpu_name, const char *llvm_features)
116{102{
117 Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");103 if (zig_triple == nullptr) {
118 result->llvm_cpu_name = llvm_cpu_name;104 Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");
119 result->llvm_cpu_features = llvm_features;105 result->llvm_cpu_name = ZigLLVMGetHostCPUName();
120 result->builtin_str = ".baseline;\n";106 result->llvm_cpu_features = ZigLLVMGetNativeFeatures();
121 result->cache_hash = "native\n\n";107 result->builtin_str = "arch.getBaselineCpuFeatures();\n";
122 *out = result;108 result->cache_hash = "native\n\n";
123 return ErrorNone;109 *out = result;
110 return ErrorNone;
111 }
112 if (cpu_name == nullptr && cpu_features == nullptr) {
113 Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");
114 result->builtin_str = "arch.getBaselineCpuFeatures();\n";
115 result->cache_hash = "\n\n";
116 *out = result;
117 return ErrorNone;
118 }
119
120 const char *msg = "stage0 called stage2_cpu_features_parse with non-null cpu name or features";
121 stage2_panic(msg, strlen(msg));
124}122}
123
125void stage2_cpu_features_get_cache_hash(const Stage2CpuFeatures *cpu_features,124void stage2_cpu_features_get_cache_hash(const Stage2CpuFeatures *cpu_features,
126 const char **ptr, size_t *len)125 const char **ptr, size_t *len)
127{126{
src/userland.h+2-14
...@@ -184,20 +184,8 @@ ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node,...@@ -184,20 +184,8 @@ ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node,
184struct Stage2CpuFeatures;184struct Stage2CpuFeatures;
185185
186// ABI warning186// ABI warning
187ZIG_EXTERN_C Error stage2_cpu_features_parse_cpu(struct Stage2CpuFeatures **result,187ZIG_EXTERN_C Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result,
188 const char *zig_triple, const char *cpu_name);188 const char *zig_triple, const char *cpu_name, const char *cpu_features);
189
190// ABI warning
191ZIG_EXTERN_C Error stage2_cpu_features_parse_features(struct Stage2CpuFeatures **result,
192 const char *zig_triple, const char *features);
193
194// ABI warning
195ZIG_EXTERN_C Error stage2_cpu_features_baseline(struct Stage2CpuFeatures **result,
196 const char *zig_triple);
197
198// ABI warning
199ZIG_EXTERN_C Error stage2_cpu_features_llvm(struct Stage2CpuFeatures **result,
200 const char *zig_triple, const char *llvm_cpu_name, const char *llvm_features);
201189
202// ABI warning190// ABI warning
203ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_cpu(const struct Stage2CpuFeatures *cpu_features);191ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_cpu(const struct Stage2CpuFeatures *cpu_features);
test/compile_errors.zig+7-4
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Target = @import("std").Target;
34
4pub fn addCases(cases: *tests.CompileErrorContext) void {5pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("non-exhaustive enums",6 cases.addTest("non-exhaustive enums",
...@@ -272,9 +273,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -272,9 +273,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
272 , &[_][]const u8{273 , &[_][]const u8{
273 "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack",274 "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack",
274 });275 });
275 tc.target = tests.Target{276 tc.target = Target{
276 .Cross = tests.CrossTarget{277 .Cross = .{
277 .arch = .wasm32,278 .arch = .wasm32,
279 .cpu_features = Target.Arch.wasm32.getBaselineCpuFeatures(),
278 .os = .wasi,280 .os = .wasi,
279 .abi = .none,281 .abi = .none,
280 },282 },
...@@ -673,9 +675,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -673,9 +675,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
673 , &[_][]const u8{675 , &[_][]const u8{
674 "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs",676 "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs",
675 });677 });
676 tc.target = tests.Target{678 tc.target = Target{
677 .Cross = tests.CrossTarget{679 .Cross = .{
678 .arch = .x86_64,680 .arch = .x86_64,
681 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
679 .os = .linux,682 .os = .linux,
680 .abi = .gnu,683 .abi = .gnu,
681 },684 },
test/tests.zig+220-196
...@@ -38,236 +38,260 @@ const TestTarget = struct {...@@ -38,236 +38,260 @@ const TestTarget = struct {
38 disable_native: bool = false,38 disable_native: bool = false,
39};39};
4040
41const test_targets = [_]TestTarget{41const test_targets = blk: {
42 TestTarget{},42 // getBaselineCpuFeatures calls populateDependencies which has a O(N ^ 2) algorithm
43 TestTarget{43 // (where N is roughly 160, which technically makes it O(1), but it adds up to a
44 .link_libc = true,44 // lot of branches)
45 },45 @setEvalBranchQuota(50000);
46 TestTarget{46 break :blk [_]TestTarget{
47 .single_threaded = true,47 TestTarget{},
48 },48 TestTarget{
4949 .link_libc = true,
50 TestTarget{50 },
51 .target = Target{51 TestTarget{
52 .Cross = CrossTarget{52 .single_threaded = true,
53 .os = .linux,53 },
54 .arch = .x86_64,54
55 .abi = .none,55 TestTarget{
56 .target = Target{
57 .Cross = CrossTarget{
58 .os = .linux,
59 .arch = .x86_64,
60 .abi = .none,
61 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
62 },
56 },63 },
57 },64 },
58 },65 TestTarget{
59 TestTarget{66 .target = Target{
60 .target = Target{67 .Cross = CrossTarget{
61 .Cross = CrossTarget{68 .os = .linux,
62 .os = .linux,69 .arch = .x86_64,
63 .arch = .x86_64,70 .abi = .gnu,
64 .abi = .gnu,71 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
72 },
65 },73 },
74 .link_libc = true,
66 },75 },
67 .link_libc = true,76 TestTarget{
68 },77 .target = Target{
69 TestTarget{78 .Cross = CrossTarget{
70 .target = Target{79 .os = .linux,
71 .Cross = CrossTarget{80 .arch = .x86_64,
72 .os = .linux,81 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
73 .arch = .x86_64,82 .abi = .musl,
74 .abi = .musl,83 },
75 },84 },
85 .link_libc = true,
76 },86 },
77 .link_libc = true,87
78 },88 TestTarget{
7989 .target = Target{
80 TestTarget{90 .Cross = CrossTarget{
81 .target = Target{91 .os = .linux,
82 .Cross = CrossTarget{92 .arch = .i386,
83 .os = .linux,93 .cpu_features = Target.Arch.i386.getBaselineCpuFeatures(),
84 .arch = .i386,94 .abi = .none,
85 .abi = .none,95 },
86 },96 },
87 },97 },
88 },98 TestTarget{
89 TestTarget{99 .target = Target{
90 .target = Target{100 .Cross = CrossTarget{
91 .Cross = CrossTarget{101 .os = .linux,
92 .os = .linux,102 .arch = .i386,
93 .arch = .i386,103 .cpu_features = Target.Arch.i386.getBaselineCpuFeatures(),
94 .abi = .musl,104 .abi = .musl,
105 },
95 },106 },
107 .link_libc = true,
96 },108 },
97 .link_libc = true,109
98 },110 TestTarget{
99111 .target = Target{
100 TestTarget{112 .Cross = CrossTarget{
101 .target = Target{113 .os = .linux,
102 .Cross = CrossTarget{114 .arch = Target.Arch{ .aarch64 = .v8_5a },
103 .os = .linux,115 .cpu_features = (Target.Arch{ .aarch64 = .v8_5a }).getBaselineCpuFeatures(),
104 .arch = builtin.Arch{ .aarch64 = builtin.Arch.Arm64.v8_5a },116 .abi = .none,
105 .abi = .none,117 },
106 },118 },
107 },119 },
108 },120 TestTarget{
109 TestTarget{121 .target = Target{
110 .target = Target{122 .Cross = CrossTarget{
111 .Cross = CrossTarget{123 .os = .linux,
112 .os = .linux,124 .arch = Target.Arch{ .aarch64 = .v8_5a },
113 .arch = builtin.Arch{ .aarch64 = builtin.Arch.Arm64.v8_5a },125 .cpu_features = (Target.Arch{ .aarch64 = .v8_5a }).getBaselineCpuFeatures(),
114 .abi = .musl,126 .abi = .musl,
127 },
115 },128 },
129 .link_libc = true,
116 },130 },
117 .link_libc = true,131 TestTarget{
118 },132 .target = Target{
119 TestTarget{133 .Cross = CrossTarget{
120 .target = Target{134 .os = .linux,
121 .Cross = CrossTarget{135 .arch = Target.Arch{ .aarch64 = .v8_5a },
122 .os = .linux,136 .cpu_features = (Target.Arch{ .aarch64 = .v8_5a }).getBaselineCpuFeatures(),
123 .arch = builtin.Arch{ .aarch64 = builtin.Arch.Arm64.v8_5a },137 .abi = .gnu,
124 .abi = .gnu,138 },
125 },139 },
140 .link_libc = true,
126 },141 },
127 .link_libc = true,142
128 },143 TestTarget{
129144 .target = Target{
130 TestTarget{145 .Cross = CrossTarget{
131 .target = Target{146 .os = .linux,
132 .Cross = CrossTarget{147 .arch = Target.Arch{ .arm = .v8_5a },
133 .os = .linux,148 .cpu_features = (Target.Arch{ .arm = .v8_5a }).getBaselineCpuFeatures(),
134 .arch = builtin.Arch{ .arm = builtin.Arch.Arm32.v8_5a },149 .abi = .none,
135 .abi = .none,150 },
136 },151 },
137 },152 },
138 },153 TestTarget{
139 TestTarget{154 .target = Target{
140 .target = Target{155 .Cross = CrossTarget{
141 .Cross = CrossTarget{156 .os = .linux,
142 .os = .linux,157 .arch = Target.Arch{ .arm = .v8_5a },
143 .arch = builtin.Arch{ .arm = builtin.Arch.Arm32.v8_5a },158 .cpu_features = (Target.Arch{ .arm = .v8_5a }).getBaselineCpuFeatures(),
144 .abi = .musleabihf,159 .abi = .musleabihf,
160 },
145 },161 },
162 .link_libc = true,
146 },163 },
147 .link_libc = true,164 // TODO https://github.com/ziglang/zig/issues/3287
148 },165 //TestTarget{
149 // TODO https://github.com/ziglang/zig/issues/3287166 // .target = Target{
150 //TestTarget{167 // .Cross = CrossTarget{
151 // .target = Target{168 // .os = .linux,
152 // .Cross = CrossTarget{169 // .arch = Target.Arch{ .arm = .v8_5a },
153 // .os = .linux,170 // .cpu_features = (Target.Arch{ .arm = .v8_5a }).getBaselineCpuFeatures(),
154 // .arch = builtin.Arch{ .arm = builtin.Arch.Arm32.v8_5a },171 // .abi = .gnueabihf,
155 // .abi = .gnueabihf,172 // },
156 // },173 // },
157 // },174 // .link_libc = true,
158 // .link_libc = true,175 //},
159 //},176
160177 TestTarget{
161 TestTarget{178 .target = Target{
162 .target = Target{179 .Cross = CrossTarget{
163 .Cross = CrossTarget{180 .os = .linux,
164 .os = .linux,181 .arch = .mipsel,
165 .arch = .mipsel,182 .cpu_features = Target.Arch.mipsel.getBaselineCpuFeatures(),
166 .abi = .none,183 .abi = .none,
184 },
167 },185 },
168 },186 },
169 },187 TestTarget{
170 TestTarget{188 .target = Target{
171 .target = Target{189 .Cross = CrossTarget{
172 .Cross = CrossTarget{190 .os = .linux,
173 .os = .linux,191 .arch = .mipsel,
174 .arch = .mipsel,192 .cpu_features = Target.Arch.mipsel.getBaselineCpuFeatures(),
175 .abi = .musl,193 .abi = .musl,
194 },
176 },195 },
196 .link_libc = true,
177 },197 },
178 .link_libc = true,198
179 },199 TestTarget{
180200 .target = Target{
181 TestTarget{201 .Cross = CrossTarget{
182 .target = Target{202 .os = .macosx,
183 .Cross = CrossTarget{203 .arch = .x86_64,
184 .os = .macosx,204 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
185 .arch = .x86_64,205 .abi = .gnu,
186 .abi = .gnu,206 },
187 },207 },
208 // TODO https://github.com/ziglang/zig/issues/3295
209 .disable_native = true,
188 },210 },
189 // TODO https://github.com/ziglang/zig/issues/3295211
190 .disable_native = true,212 TestTarget{
191 },213 .target = Target{
192214 .Cross = CrossTarget{
193 TestTarget{215 .os = .windows,
194 .target = Target{216 .arch = .i386,
195 .Cross = CrossTarget{217 .cpu_features = Target.Arch.i386.getBaselineCpuFeatures(),
196 .os = .windows,218 .abi = .msvc,
197 .arch = .i386,219 },
198 .abi = .msvc,
199 },220 },
200 },221 },
201 },222
202223 TestTarget{
203 TestTarget{224 .target = Target{
204 .target = Target{225 .Cross = CrossTarget{
205 .Cross = CrossTarget{226 .os = .windows,
206 .os = .windows,227 .arch = .x86_64,
207 .arch = .x86_64,228 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
208 .abi = .msvc,229 .abi = .msvc,
230 },
209 },231 },
210 },232 },
211 },233
212234 TestTarget{
213 TestTarget{235 .target = Target{
214 .target = Target{236 .Cross = CrossTarget{
215 .Cross = CrossTarget{237 .os = .windows,
216 .os = .windows,238 .arch = .i386,
217 .arch = .i386,239 .cpu_features = Target.Arch.i386.getBaselineCpuFeatures(),
218 .abi = .gnu,240 .abi = .gnu,
241 },
219 },242 },
243 .link_libc = true,
220 },244 },
221 .link_libc = true,245
222 },246 TestTarget{
223247 .target = Target{
224 TestTarget{248 .Cross = CrossTarget{
225 .target = Target{249 .os = .windows,
226 .Cross = CrossTarget{250 .arch = .x86_64,
227 .os = .windows,251 .cpu_features = Target.Arch.x86_64.getBaselineCpuFeatures(),
228 .arch = .x86_64,252 .abi = .gnu,
229 .abi = .gnu,253 },
230 },254 },
255 .link_libc = true,
256 },
257
258 // Do the release tests last because they take a long time
259 TestTarget{
260 .mode = .ReleaseFast,
261 },
262 TestTarget{
263 .link_libc = true,
264 .mode = .ReleaseFast,
231 },265 },
232 .link_libc = true,266 TestTarget{
233 },267 .mode = .ReleaseFast,
234268 .single_threaded = true,
235 // Do the release tests last because they take a long time269 },
236 TestTarget{270
237 .mode = .ReleaseFast,271 TestTarget{
238 },272 .mode = .ReleaseSafe,
239 TestTarget{273 },
240 .link_libc = true,274 TestTarget{
241 .mode = .ReleaseFast,275 .link_libc = true,
242 },276 .mode = .ReleaseSafe,
243 TestTarget{277 },
244 .mode = .ReleaseFast,278 TestTarget{
245 .single_threaded = true,279 .mode = .ReleaseSafe,
246 },280 .single_threaded = true,
247281 },
248 TestTarget{282
249 .mode = .ReleaseSafe,283 TestTarget{
250 },284 .mode = .ReleaseSmall,
251 TestTarget{285 },
252 .link_libc = true,286 TestTarget{
253 .mode = .ReleaseSafe,287 .link_libc = true,
254 },288 .mode = .ReleaseSmall,
255 TestTarget{289 },
256 .mode = .ReleaseSafe,290 TestTarget{
257 .single_threaded = true,291 .mode = .ReleaseSmall,
258 },292 .single_threaded = true,
259293 },
260 TestTarget{294 };
261 .mode = .ReleaseSmall,
262 },
263 TestTarget{
264 .link_libc = true,
265 .mode = .ReleaseSmall,
266 },
267 TestTarget{
268 .mode = .ReleaseSmall,
269 .single_threaded = true,
270 },
271};295};
272296
273const max_stdout_size = 1 * 1024 * 1024; // 1 MB297const max_stdout_size = 1 * 1024 * 1024; // 1 MB
test/translate_c.zig+19-3
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Target = @import("std").Target;
34
4pub fn addCases(cases: *tests.TranslateCContext) void {5pub fn addCases(cases: *tests.TranslateCContext) void {
5 cases.add("empty declaration",6 cases.add("empty declaration",
...@@ -1005,7 +1006,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1005,7 +1006,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1005 });1006 });
10061007
1007 cases.addWithTarget("Calling convention", tests.Target{1008 cases.addWithTarget("Calling convention", tests.Target{
1008 .Cross = .{ .os = .linux, .arch = .i386, .abi = .none },1009 .Cross = .{
1010 .os = .linux,
1011 .arch = .i386,
1012 .abi = .none,
1013 .cpu_features = Target.Arch.i386.getBaselineCpuFeatures(),
1014 },
1009 },1015 },
1010 \\void __attribute__((fastcall)) foo1(float *a);1016 \\void __attribute__((fastcall)) foo1(float *a);
1011 \\void __attribute__((stdcall)) foo2(float *a);1017 \\void __attribute__((stdcall)) foo2(float *a);
...@@ -1021,7 +1027,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1021,7 +1027,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1021 });1027 });
10221028
1023 cases.addWithTarget("Calling convention", tests.Target{1029 cases.addWithTarget("Calling convention", tests.Target{
1024 .Cross = .{ .os = .linux, .arch = .{ .arm = .v8_5a }, .abi = .none },1030 .Cross = .{
1031 .os = .linux,
1032 .arch = .{ .arm = .v8_5a },
1033 .abi = .none,
1034 .cpu_features = (Target.Arch{ .arm = .v8_5a }).getBaselineCpuFeatures(),
1035 },
1025 },1036 },
1026 \\void __attribute__((pcs("aapcs"))) foo1(float *a);1037 \\void __attribute__((pcs("aapcs"))) foo1(float *a);
1027 \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);1038 \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);
...@@ -1031,7 +1042,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1031,7 +1042,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1031 });1042 });
10321043
1033 cases.addWithTarget("Calling convention", tests.Target{1044 cases.addWithTarget("Calling convention", tests.Target{
1034 .Cross = .{ .os = .linux, .arch = .{ .aarch64 = .v8_5a }, .abi = .none },1045 .Cross = .{
1046 .os = .linux,
1047 .arch = .{ .aarch64 = .v8_5a },
1048 .abi = .none,
1049 .cpu_features = (Target.Arch{ .aarch64 = .v8_5a }).getBaselineCpuFeatures(),
1050 },
1035 },1051 },
1036 \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);1052 \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);
1037 , &[_][]const u8{1053 , &[_][]const u8{