authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-28 19:59:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-08 19:37:29-07:00
log78002dbe47c9dec430754c3a3f1d62c1b11df08d
tree9e3537671e2e3a6b277817f71ec5e8cb1a37e840
parent06ee65af9ed6aa5ee4d1d7f4fab9d7acecf66e76

add detect-cpu subcommand for debugging CPU features

This brings back `detectNativeCpuWithLLVM` so that we can troubleshoot during LLVM upgrades. closes #19793

2 files changed, 148 insertions(+), 0 deletions(-)

src/codegen/llvm/bindings.zig+6
...@@ -400,3 +400,9 @@ extern fn ZigLLVMWriteImportLibrary(...@@ -400,3 +400,9 @@ extern fn ZigLLVMWriteImportLibrary(
400 output_lib_path: [*:0]const u8,400 output_lib_path: [*:0]const u8,
401 kill_at: bool,401 kill_at: bool,
402) bool;402) bool;
403
404pub const GetHostCPUName = LLVMGetHostCPUName;
405extern fn LLVMGetHostCPUName() ?[*:0]u8;
406
407pub const GetHostCPUFeatures = LLVMGetHostCPUFeatures;
408extern fn LLVMGetHostCPUFeatures() ?[*:0]u8;
src/main.zig+142
...@@ -117,6 +117,7 @@ const debug_usage = normal_usage ++...@@ -117,6 +117,7 @@ const debug_usage = normal_usage ++
117 \\117 \\
118 \\ changelist Compute mappings from old ZIR to new ZIR118 \\ changelist Compute mappings from old ZIR to new ZIR
119 \\ dump-zir Dump a file containing cached ZIR119 \\ dump-zir Dump a file containing cached ZIR
120 \\ detect-cpu Compare Zig's CPU feature detection vs LLVM
120 \\121 \\
121;122;
122123
...@@ -352,6 +353,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -352,6 +353,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
352 return io.getStdOut().writeAll(usage);353 return io.getStdOut().writeAll(usage);
353 } else if (mem.eql(u8, cmd, "ast-check")) {354 } else if (mem.eql(u8, cmd, "ast-check")) {
354 return cmdAstCheck(gpa, arena, cmd_args);355 return cmdAstCheck(gpa, arena, cmd_args);
356 } else if (mem.eql(u8, cmd, "detect-cpu")) {
357 return cmdDetectCpu(gpa, arena, cmd_args);
355 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {358 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
356 return cmdChangelist(gpa, arena, cmd_args);359 return cmdChangelist(gpa, arena, cmd_args);
357 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {360 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
...@@ -6150,6 +6153,145 @@ fn cmdAstCheck(...@@ -6150,6 +6153,145 @@ fn cmdAstCheck(
6150 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());6153 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
6151}6154}
61526155
6156fn cmdDetectCpu(
6157 gpa: Allocator,
6158 arena: Allocator,
6159 args: []const []const u8,
6160) !void {
6161 _ = gpa;
6162 _ = arena;
6163
6164 const detect_cpu_usage =
6165 \\Usage: zig detect-cpu [--llvm]
6166 \\
6167 \\ Print the host CPU name and feature set to stdout.
6168 \\
6169 \\Options:
6170 \\ -h, --help Print this help and exit
6171 \\ --llvm Detect using LLVM API
6172 \\
6173 ;
6174
6175 var use_llvm = false;
6176
6177 {
6178 var i: usize = 0;
6179 while (i < args.len) : (i += 1) {
6180 const arg = args[i];
6181 if (mem.startsWith(u8, arg, "-")) {
6182 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6183 const stdout = io.getStdOut().writer();
6184 try stdout.writeAll(detect_cpu_usage);
6185 return cleanExit();
6186 } else if (mem.eql(u8, arg, "--llvm")) {
6187 use_llvm = true;
6188 } else {
6189 fatal("unrecognized parameter: '{s}'", .{arg});
6190 }
6191 } else {
6192 fatal("unexpected extra parameter: '{s}'", .{arg});
6193 }
6194 }
6195 }
6196
6197 if (use_llvm) {
6198 if (!build_options.have_llvm)
6199 fatal("compiler does not use LLVM; cannot compare CPU features with LLVM", .{});
6200
6201 const llvm = @import("codegen/llvm/bindings.zig");
6202 const name = llvm.GetHostCPUName() orelse fatal("LLVM could not figure out the host cpu name", .{});
6203 const features = llvm.GetHostCPUFeatures() orelse fatal("LLVM could not figure out the host cpu feature set", .{});
6204 const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
6205 try printCpu(cpu);
6206 } else {
6207 const host_target = std.zig.resolveTargetQueryOrFatal(.{});
6208 try printCpu(host_target.cpu);
6209 }
6210}
6211
6212fn detectNativeCpuWithLLVM(
6213 arch: std.Target.Cpu.Arch,
6214 llvm_cpu_name_z: ?[*:0]const u8,
6215 llvm_cpu_features_opt: ?[*:0]const u8,
6216) !std.Target.Cpu {
6217 var result = std.Target.Cpu.baseline(arch);
6218
6219 if (llvm_cpu_name_z) |cpu_name_z| {
6220 const llvm_cpu_name = mem.span(cpu_name_z);
6221
6222 for (arch.allCpuModels()) |model| {
6223 const this_llvm_name = model.llvm_name orelse continue;
6224 if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
6225 // Here we use the non-dependencies-populated set,
6226 // so that subtracting features later in this function
6227 // affect the prepopulated set.
6228 result = std.Target.Cpu{
6229 .arch = arch,
6230 .model = model,
6231 .features = model.features,
6232 };
6233 break;
6234 }
6235 }
6236 }
6237
6238 const all_features = arch.allFeaturesList();
6239
6240 if (llvm_cpu_features_opt) |llvm_cpu_features| {
6241 var it = mem.tokenizeScalar(u8, mem.span(llvm_cpu_features), ',');
6242 while (it.next()) |decorated_llvm_feat| {
6243 var op: enum {
6244 add,
6245 sub,
6246 } = undefined;
6247 var llvm_feat: []const u8 = undefined;
6248 if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
6249 op = .add;
6250 llvm_feat = decorated_llvm_feat[1..];
6251 } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
6252 op = .sub;
6253 llvm_feat = decorated_llvm_feat[1..];
6254 } else {
6255 return error.InvalidLlvmCpuFeaturesFormat;
6256 }
6257 for (all_features, 0..) |feature, index_usize| {
6258 const this_llvm_name = feature.llvm_name orelse continue;
6259 if (mem.eql(u8, llvm_feat, this_llvm_name)) {
6260 const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6261 switch (op) {
6262 .add => result.features.addFeature(index),
6263 .sub => result.features.removeFeature(index),
6264 }
6265 break;
6266 }
6267 }
6268 }
6269 }
6270
6271 result.features.populateDependencies(all_features);
6272 return result;
6273}
6274
6275fn printCpu(cpu: std.Target.Cpu) !void {
6276 var bw = io.bufferedWriter(io.getStdOut().writer());
6277 const stdout = bw.writer();
6278
6279 if (cpu.model.llvm_name) |llvm_name| {
6280 try stdout.print("{s}\n", .{llvm_name});
6281 }
6282
6283 const all_features = cpu.arch.allFeaturesList();
6284 for (all_features, 0..) |feature, index_usize| {
6285 const llvm_name = feature.llvm_name orelse continue;
6286 const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6287 const is_enabled = cpu.features.isEnabled(index);
6288 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6289 try stdout.print("{c}{s}\n", .{ plus_or_minus, llvm_name });
6290 }
6291
6292 try bw.flush();
6293}
6294
6153/// This is only enabled for debug builds.6295/// This is only enabled for debug builds.
6154fn cmdDumpZir(6296fn cmdDumpZir(
6155 gpa: Allocator,6297 gpa: Allocator,