Commit 78002dbe47
Changed files (2)
src
codegen
llvm
src/codegen/llvm/bindings.zig
@@ -400,3 +400,9 @@ extern fn ZigLLVMWriteImportLibrary(
output_lib_path: [*:0]const u8,
kill_at: bool,
) bool;
+
+pub const GetHostCPUName = LLVMGetHostCPUName;
+extern fn LLVMGetHostCPUName() ?[*:0]u8;
+
+pub const GetHostCPUFeatures = LLVMGetHostCPUFeatures;
+extern fn LLVMGetHostCPUFeatures() ?[*:0]u8;
src/main.zig
@@ -117,6 +117,7 @@ const debug_usage = normal_usage ++
\\
\\ changelist Compute mappings from old ZIR to new ZIR
\\ dump-zir Dump a file containing cached ZIR
+ \\ detect-cpu Compare Zig's CPU feature detection vs LLVM
\\
;
@@ -352,6 +353,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
return io.getStdOut().writeAll(usage);
} else if (mem.eql(u8, cmd, "ast-check")) {
return cmdAstCheck(gpa, arena, cmd_args);
+ } else if (mem.eql(u8, cmd, "detect-cpu")) {
+ return cmdDetectCpu(gpa, arena, cmd_args);
} else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
return cmdChangelist(gpa, arena, cmd_args);
} else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
@@ -6150,6 +6153,145 @@ fn cmdAstCheck(
return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
}
+fn cmdDetectCpu(
+ gpa: Allocator,
+ arena: Allocator,
+ args: []const []const u8,
+) !void {
+ _ = gpa;
+ _ = arena;
+
+ const detect_cpu_usage =
+ \\Usage: zig detect-cpu [--llvm]
+ \\
+ \\ Print the host CPU name and feature set to stdout.
+ \\
+ \\Options:
+ \\ -h, --help Print this help and exit
+ \\ --llvm Detect using LLVM API
+ \\
+ ;
+
+ var use_llvm = false;
+
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
+ const stdout = io.getStdOut().writer();
+ try stdout.writeAll(detect_cpu_usage);
+ return cleanExit();
+ } else if (mem.eql(u8, arg, "--llvm")) {
+ use_llvm = true;
+ } else {
+ fatal("unrecognized parameter: '{s}'", .{arg});
+ }
+ } else {
+ fatal("unexpected extra parameter: '{s}'", .{arg});
+ }
+ }
+ }
+
+ if (use_llvm) {
+ if (!build_options.have_llvm)
+ fatal("compiler does not use LLVM; cannot compare CPU features with LLVM", .{});
+
+ const llvm = @import("codegen/llvm/bindings.zig");
+ const name = llvm.GetHostCPUName() orelse fatal("LLVM could not figure out the host cpu name", .{});
+ const features = llvm.GetHostCPUFeatures() orelse fatal("LLVM could not figure out the host cpu feature set", .{});
+ const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
+ try printCpu(cpu);
+ } else {
+ const host_target = std.zig.resolveTargetQueryOrFatal(.{});
+ try printCpu(host_target.cpu);
+ }
+}
+
+fn detectNativeCpuWithLLVM(
+ arch: std.Target.Cpu.Arch,
+ llvm_cpu_name_z: ?[*:0]const u8,
+ llvm_cpu_features_opt: ?[*:0]const u8,
+) !std.Target.Cpu {
+ var result = std.Target.Cpu.baseline(arch);
+
+ if (llvm_cpu_name_z) |cpu_name_z| {
+ const llvm_cpu_name = mem.span(cpu_name_z);
+
+ for (arch.allCpuModels()) |model| {
+ const this_llvm_name = model.llvm_name orelse continue;
+ if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
+ // Here we use the non-dependencies-populated set,
+ // so that subtracting features later in this function
+ // affect the prepopulated set.
+ result = std.Target.Cpu{
+ .arch = arch,
+ .model = model,
+ .features = model.features,
+ };
+ break;
+ }
+ }
+ }
+
+ const all_features = arch.allFeaturesList();
+
+ if (llvm_cpu_features_opt) |llvm_cpu_features| {
+ var it = mem.tokenizeScalar(u8, mem.span(llvm_cpu_features), ',');
+ while (it.next()) |decorated_llvm_feat| {
+ var op: enum {
+ add,
+ sub,
+ } = undefined;
+ var llvm_feat: []const u8 = undefined;
+ if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
+ op = .add;
+ llvm_feat = decorated_llvm_feat[1..];
+ } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
+ op = .sub;
+ llvm_feat = decorated_llvm_feat[1..];
+ } else {
+ return error.InvalidLlvmCpuFeaturesFormat;
+ }
+ for (all_features, 0..) |feature, index_usize| {
+ const this_llvm_name = feature.llvm_name orelse continue;
+ if (mem.eql(u8, llvm_feat, this_llvm_name)) {
+ const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
+ switch (op) {
+ .add => result.features.addFeature(index),
+ .sub => result.features.removeFeature(index),
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ result.features.populateDependencies(all_features);
+ return result;
+}
+
+fn printCpu(cpu: std.Target.Cpu) !void {
+ var bw = io.bufferedWriter(io.getStdOut().writer());
+ const stdout = bw.writer();
+
+ if (cpu.model.llvm_name) |llvm_name| {
+ try stdout.print("{s}\n", .{llvm_name});
+ }
+
+ const all_features = cpu.arch.allFeaturesList();
+ for (all_features, 0..) |feature, index_usize| {
+ const llvm_name = feature.llvm_name orelse continue;
+ const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
+ const is_enabled = cpu.features.isEnabled(index);
+ const plus_or_minus = "-+"[@intFromBool(is_enabled)];
+ try stdout.print("{c}{s}\n", .{ plus_or_minus, llvm_name });
+ }
+
+ try bw.flush();
+}
+
/// This is only enabled for debug builds.
fn cmdDumpZir(
gpa: Allocator,