diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-04-28 19:59:56 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-05-08 19:37:29 -0700 |
| commit | 78002dbe47c9dec430754c3a3f1d62c1b11df08d (patch) | |
| tree | 9e3537671e2e3a6b277817f71ec5e8cb1a37e840 /src/main.zig | |
| parent | 06ee65af9ed6aa5ee4d1d7f4fab9d7acecf66e76 (diff) | |
| download | zig-78002dbe47c9dec430754c3a3f1d62c1b11df08d.tar.gz zig-78002dbe47c9dec430754c3a3f1d62c1b11df08d.zip | |
add detect-cpu subcommand for debugging CPU features
This brings back `detectNativeCpuWithLLVM` so that we can troubleshoot
during LLVM upgrades.
closes #19793
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig index 0541d78cd9..0f3338025b 100644 --- a/src/main.zig +++ b/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, |
