| ... | @@ -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 ZIR | 118 | \\ changelist Compute mappings from old ZIR to new ZIR |
| 119 | \\ dump-zir Dump a file containing cached ZIR | 119 | \\ dump-zir Dump a file containing cached ZIR |
| | 120 | \\ detect-cpu Compare Zig's CPU feature detection vs LLVM |
| 120 | \\ | 121 | \\ |
| 121 | ; | 122 | ; |
| 122 | | 123 | |
| ... | @@ -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 | } |
| 6152 | | 6155 | |
| | 6156 | fn 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 | |
| | 6212 | fn 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 | |
| | 6275 | fn 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. |
| 6154 | fn cmdDumpZir( | 6296 | fn cmdDumpZir( |
| 6155 | gpa: Allocator, | 6297 | gpa: Allocator, |