From 74931fe25cdd94e1cd08b5ece9dcce19959bc079 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Tue, 28 Oct 2025 12:42:05 +0000 Subject: [PATCH 1/5] std.debug.lockStderrWriter: also return ttyconf `std.Io.tty.Config.detect` may be an expensive check (e.g. involving syscalls), and doing it every time we need to print isn't really necessary; under normal usage, we can compute the value once and cache it for the whole program's execution. Since anyone outputting to stderr may reasonably want this information (in fact they are very likely to), it makes sense to cache it and return it from `lockStderrWriter`. Call sites who do not need it will experience no significant overhead, and can just ignore the TTY config with a `const w, _` destructure. --- lib/compiler/build_runner.zig | 41 ++++++++------------ lib/compiler/resinator/cli.zig | 6 +-- lib/compiler/resinator/errors.zig | 11 ++---- lib/compiler/resinator/main.zig | 63 ++++++++++++++----------------- lib/compiler/std-docs.zig | 3 +- lib/std/Build.zig | 12 +++--- lib/std/Build/Fuzz.zig | 18 ++++----- lib/std/Build/Step/Compile.zig | 11 +++--- lib/std/Build/WebServer.zig | 9 ++--- lib/std/debug.zig | 38 +++++++++---------- lib/std/json/dynamic.zig | 2 +- lib/std/log.zig | 2 +- lib/std/testing.zig | 8 ++-- lib/std/zig.zig | 15 ++++---- lib/std/zig/ErrorBundle.zig | 17 ++++----- lib/std/zig/parser_test.zig | 2 +- src/Air/print.zig | 4 +- src/Compilation.zig | 6 +-- src/InternPool.zig | 4 +- src/Package/Fetch.zig | 4 +- src/Sema.zig | 2 +- src/Zcu/PerThread.zig | 2 +- src/codegen/aarch64/Select.zig | 2 +- src/crash_report.zig | 2 +- src/fmt.zig | 8 ++-- src/libs/mingw.zig | 8 ++-- src/libs/mingw/def.zig | 2 +- src/link.zig | 2 +- src/link/Coff.zig | 2 +- src/link/Elf2.zig | 2 +- src/main.zig | 18 ++++----- tools/gen_spirv_spec.zig | 7 ++-- tools/generate_linux_syscalls.zig | 4 +- tools/incr-check.zig | 15 +++----- tools/update_clang_options.zig | 4 +- tools/update_cpu_features.zig | 2 +- tools/update_crc_catalog.zig | 4 +- 37 files changed, 169 insertions(+), 193 deletions(-) diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 545cab6083768b35f768c05ba265909cc3c9c262..ae9eb53fb24d5f1ca373ecdfc7ce2cd79868730b 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -442,8 +442,7 @@ pub fn main() !void { if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); } - const stderr: std.fs.File = .stderr(); - const ttyconf = get_tty_conf(color, stderr); + const ttyconf = color.detectTtyConf(); switch (ttyconf) { .no_color => try graph.env_map.put("NO_COLOR", "1"), .escape_codes => try graph.env_map.put("CLICOLOR_FORCE", "1"), @@ -522,9 +521,9 @@ pub fn main() !void { .error_style = error_style, .multiline_errors = multiline_errors, .summary = summary orelse if (watch or webui_listen != null) .line else .failures, - .ttyconf = ttyconf, - .stderr = stderr, .thread_pool = undefined, + + .ttyconf = ttyconf, }; defer { run.memory_blocked_steps.deinit(gpa); @@ -563,9 +562,9 @@ pub fn main() !void { break :ws .init(.{ .gpa = gpa, .thread_pool = &run.thread_pool, + .ttyconf = ttyconf, .graph = &graph, .all_steps = run.step_stack.keys(), - .ttyconf = run.ttyconf, .root_prog_node = main_progress_node, .watch = watch, .listen_address = listen_address, @@ -578,7 +577,7 @@ pub fn main() !void { } rebuild: while (true) : (if (run.error_style.clearOnUpdate()) { - const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); try bw.writeAll("\x1B[2J\x1B[3J\x1B[H"); }) { @@ -682,13 +681,14 @@ const Run = struct { /// Allocated into `gpa`. step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), thread_pool: std.Thread.Pool, + /// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`, + /// but also respects the '--color' flag. + ttyconf: tty.Config, claimed_rss: usize, error_style: ErrorStyle, multiline_errors: MultilineErrors, summary: Summary, - ttyconf: tty.Config, - stderr: File, }; fn prepare( @@ -834,8 +834,6 @@ fn runStepNames( } } - const ttyconf = run.ttyconf; - if (fuzz) |mode| blk: { switch (builtin.os.tag) { // Current implementation depends on two things that need to be ported to Windows: @@ -863,9 +861,9 @@ fn runStepNames( gpa, io, thread_pool, + run.ttyconf, step_stack.keys(), parent_prog_node, - ttyconf, mode, ) catch |err| fatal("failed to start fuzzer: {s}", .{@errorName(err)}); defer f.deinit(); @@ -890,8 +888,9 @@ fn runStepNames( .none => break :summary, } - const w = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const w, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); + const ttyconf = run.ttyconf; const total_count = success_count + failure_count + pending_count + skipped_count; ttyconf.setColor(w, .cyan) catch {}; @@ -1399,9 +1398,10 @@ fn workerMakeOneStep( const show_error_msgs = s.result_error_msgs.items.len > 0; const show_stderr = s.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { - const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - printErrorMessages(run.gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.error_style, run.multiline_errors) catch {}; + const ttyconf = run.ttyconf; + printErrorMessages(run.gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {}; } handle_result: { @@ -1465,11 +1465,10 @@ pub fn printErrorMessages( failing_step: *Step, options: std.zig.ErrorBundle.RenderOptions, stderr: *Writer, + ttyconf: tty.Config, error_style: ErrorStyle, multiline_errors: MultilineErrors, ) !void { - const ttyconf = options.ttyconf; - if (error_style.verboseContext()) { // Provide context for where these error messages are coming from by // printing the corresponding Step subtree. @@ -1513,7 +1512,7 @@ pub fn printErrorMessages( } } - try failing_step.result_error_bundle.renderToWriter(options, stderr); + try failing_step.result_error_bundle.renderToWriter(options, stderr, ttyconf); for (failing_step.result_error_msgs.items) |msg| { try ttyconf.setColor(stderr, .red); @@ -1759,14 +1758,6 @@ const ErrorStyle = enum { const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; -fn get_tty_conf(color: Color, stderr: File) tty.Config { - return switch (color) { - .auto => tty.detectConfig(stderr), - .on => .escape_codes, - .off => .no_color, - }; -} - fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args); process.exit(1); diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index 2b31653cca59c181d8b63fb6cb884ab2bf3577a9..59568e9cef47464586185d2e16b1f9b905a9d48e 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -124,10 +124,10 @@ pub const Diagnostics = struct { try self.errors.append(self.allocator, error_details); } - pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.Io.tty.Config) void { - const stderr = std.debug.lockStderrWriter(&.{}); + pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8) void { + const stderr, const ttyconf = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - self.renderToWriter(args, stderr, config) catch return; + self.renderToWriter(args, stderr, ttyconf) catch return; } pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: *std.Io.Writer, config: std.Io.tty.Config) !void { diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index aad74a3ca3b329b0da52fb078133985728a06010..f9ccc43a7f245b6f5e38b88faf20d9bbda5cd8bb 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -67,20 +67,15 @@ pub const Diagnostics = struct { return @intCast(index); } - pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.Io.tty.Config, source_mappings: ?SourceMappings) void { + pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void { const io = self.io; - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, const ttyconf = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); for (self.errors.items) |err_details| { - renderErrorMessage(io, stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return; + renderErrorMessage(io, stderr, ttyconf, cwd, err_details, source, self.strings.items, source_mappings) catch return; } } - pub fn renderToStdErrDetectTTY(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void { - const tty_config = std.Io.tty.detectConfig(std.fs.File.stderr()); - return self.renderToStdErr(cwd, source, tty_config, source_mappings); - } - pub fn contains(self: *const Diagnostics, err: ErrorDetails.Error) bool { for (self.errors.items) |details| { if (details.err == err) return true; diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index b32237b06b7813369098c6ce5e1ea3296621a95e..6d6819f45ae3fdf02e4533349fb24953addf7431 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -28,13 +28,11 @@ pub fn main() !void { defer arena_state.deinit(); const arena = arena_state.allocator(); - const stderr = std.fs.File.stderr(); - const stderr_config = std.Io.tty.detectConfig(stderr); - const args = try std.process.argsAlloc(arena); if (args.len < 2) { - try renderErrorMessage(std.debug.lockStderrWriter(&.{}), stderr_config, .err, "expected zig lib dir as first argument", .{}); + const w, const ttyconf = std.debug.lockStderrWriter(&.{}); + try renderErrorMessage(w, ttyconf, .err, "expected zig lib dir as first argument", .{}); std.process.exit(1); } const zig_lib_dir = args[1]; @@ -56,9 +54,7 @@ pub fn main() !void { .in = undefined, // won't be receiving messages }, }, - false => .{ - .tty = stderr_config, - }, + false => .stderr, }; var options = options: { @@ -75,12 +71,14 @@ pub fn main() !void { if (!zig_integration) { // print any warnings/notes - cli_diagnostics.renderToStdErr(cli_args, stderr_config); + cli_diagnostics.renderToStdErr(cli_args); // If there was something printed, then add an extra newline separator // so that there is a clear separation between the cli diagnostics and whatever // gets printed after if (cli_diagnostics.errors.items.len > 0) { - try stderr.writeAll("\n"); + const stderr, _ = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + try stderr.writeByte('\n'); } } break :options options; @@ -130,17 +128,18 @@ pub fn main() !void { const aro_arena = aro_arena_state.allocator(); var stderr_buf: [512]u8 = undefined; - var stderr_writer = stderr.writer(&stderr_buf); - var diagnostics: aro.Diagnostics = switch (zig_integration) { - false => .{ .output = .{ .to_writer = .{ - .writer = &stderr_writer.interface, - .color = stderr_config, - } } }, - true => .{ .output = .{ .to_list = .{ - .arena = .init(gpa), - } } }, - }; - defer diagnostics.deinit(); + var diagnostics: aro.Diagnostics = .{ .output = output: { + if (zig_integration) break :output .{ .to_list = .{ .arena = .init(gpa) } }; + const w, const ttyconf = std.debug.lockStderrWriter(&stderr_buf); + break :output .{ .to_writer = .{ + .writer = w, + .color = ttyconf, + } }; + } }; + defer { + diagnostics.deinit(); + if (!zig_integration) std.debug.unlockStderrWriter(); + } var comp = aro.Compilation.init(aro_arena, aro_arena, io, &diagnostics, std.fs.cwd()); defer comp.deinit(); @@ -307,7 +306,7 @@ pub fn main() !void { // print any warnings/notes if (!zig_integration) { - diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings); + diagnostics.renderToStdErr(std.fs.cwd(), final_input, mapping_results.mappings); } // write the depfile @@ -660,7 +659,7 @@ const SourceMappings = @import("source_mapping.zig").SourceMappings; const ErrorHandler = union(enum) { server: std.zig.Server, - tty: std.Io.tty.Config, + stderr, pub fn emitCliDiagnostics( self: *ErrorHandler, @@ -675,9 +674,7 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, - .tty => { - diagnostics.renderToStdErr(args, self.tty); - }, + .stderr => diagnostics.renderToStdErr(args), } } @@ -698,11 +695,11 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, - .tty => { + .stderr => { // aro errors have already been emitted - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, const ttyconf = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - try renderErrorMessage(stderr, self.tty, .err, "{s}", .{fail_msg}); + try renderErrorMessage(stderr, ttyconf, .err, "{s}", .{fail_msg}); }, } } @@ -722,9 +719,7 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, - .tty => { - diagnostics.renderToStdErr(cwd, source, self.tty, mappings); - }, + .stderr => diagnostics.renderToStdErr(cwd, source, mappings), } } @@ -745,10 +740,10 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, - .tty => { - const stderr = std.debug.lockStderrWriter(&.{}); + .stderr => { + const stderr, const ttyconf = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - try renderErrorMessage(stderr, self.tty, msg_type, format, args); + try renderErrorMessage(stderr, ttyconf, msg_type, format, args); }, } } diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index e80c3d3dcd187f2040807986fcdccfafc070eef1..f560e05ccaa319fe7a06fd4a2ebfce77201d799a 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -394,8 +394,7 @@ fn buildWasmBinary( } if (result_error_bundle.errorMessageCount() > 0) { - const color = std.zig.Color.auto; - result_error_bundle.renderToStdErr(color.renderOptions()); + result_error_bundle.renderToStdErr(.{}, true); std.log.err("the following command failed with {d} compilation errors:\n{s}", .{ result_error_bundle.errorMessageCount(), try std.Build.Step.allocPrintCmd(arena, null, argv.items), diff --git a/lib/std/Build.zig b/lib/std/Build.zig index e9d2e81fba2bafafb91f9f52a5e493d533131ac9..3b78fc6f7100026a5ee5a5ddb6fc16bdbb73d344 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2257,8 +2257,8 @@ pub const GeneratedFile = struct { pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 { return gen.path orelse { - const w = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; + const w, const ttyconf = debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.step, w, ttyconf, src_builder, asking_step) catch {}; debug.unlockStderrWriter(); @panic("misconfigured build script"); }; @@ -2466,8 +2466,8 @@ pub const LazyPath = union(enum) { var file_path: Cache.Path = .{ .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { - const w = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; + const w, const ttyconf = debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.file.step, w, ttyconf, src_builder, asking_step) catch {}; debug.unlockStderrWriter(); @panic("misconfigured build script"); }, @@ -2558,13 +2558,11 @@ fn dumpBadDirnameHelp( comptime msg: []const u8, args: anytype, ) anyerror!void { - const w = debug.lockStderrWriter(&.{}); + const w, const tty_config = debug.lockStderrWriter(&.{}); defer debug.unlockStderrWriter(); try w.print(msg, args); - const tty_config = std.Io.tty.detectConfig(.stderr()); - if (fail_step) |s| { tty_config.setColor(w, .red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index d342628871c43fcdbc21c0cc12f643072ae8eab8..6dd4f70f9fed287dda6421174def6b9c5eac2e59 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -16,6 +16,7 @@ const build_runner = @import("root"); gpa: Allocator, io: Io, +ttyconf: tty.Config, mode: Mode, /// Allocated into `gpa`. @@ -25,7 +26,6 @@ wait_group: std.Thread.WaitGroup, root_prog_node: std.Progress.Node, prog_node: std.Progress.Node, thread_pool: *std.Thread.Pool, -ttyconf: tty.Config, /// Protects `coverage_files`. coverage_mutex: std.Thread.Mutex, @@ -79,9 +79,9 @@ pub fn init( gpa: Allocator, io: Io, thread_pool: *std.Thread.Pool, + ttyconf: tty.Config, all_steps: []const *Build.Step, root_prog_node: std.Progress.Node, - ttyconf: tty.Config, mode: Mode, ) Allocator.Error!Fuzz { const run_steps: []const *Step.Run = steps: { @@ -115,11 +115,11 @@ pub fn init( return .{ .gpa = gpa, .io = io, + .ttyconf = ttyconf, .mode = mode, .run_steps = run_steps, .wait_group = .{}, .thread_pool = thread_pool, - .ttyconf = ttyconf, .root_prog_node = root_prog_node, .prog_node = .none, .coverage_files = .empty, @@ -158,7 +158,7 @@ pub fn deinit(fuzz: *Fuzz) void { fuzz.gpa.free(fuzz.run_steps); } -fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: std.Io.tty.Config, parent_prog_node: std.Progress.Node) void { +fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) void { rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| { const compile = run.producer.?; log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ @@ -167,7 +167,7 @@ fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: std.Io.tty.Con }; } -fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.Io.tty.Config, parent_prog_node: std.Progress.Node) !void { +fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) !void { const compile = run.producer.?; const prog_node = parent_prog_node.start(compile.step.name, 0); defer prog_node.end(); @@ -180,9 +180,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.Io if (show_error_msgs or show_compile_errors or show_stderr) { var buf: [256]u8 = undefined; - const w = std.debug.lockStderrWriter(&buf); + const w, _ = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &compile.step, .{}, w, ttyconf, .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -206,9 +206,9 @@ fn fuzzWorkerRun( run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) { error.MakeFailed => { var buf: [256]u8 = undefined; - const w = std.debug.lockStderrWriter(&buf); + const w, _ = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = fuzz.ttyconf }, w, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &run.step, .{}, w, fuzz.ttyconf, .verbose, .indent) catch {}; return; }, else => { diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 2188d8bfc7c9cc5e446eca38e438c70802f6b346..a5f2d696be1b92e6f1c2e09321f6131a601c028a 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -1056,15 +1056,15 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking const maybe_path: ?*GeneratedFile = @field(compile, tag_name); const generated_file = maybe_path orelse { - const w = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; + const w, const ttyconf = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic("missing emit option for " ++ tag_name); }; const path = generated_file.path orelse { - const w = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; + const w, const ttyconf = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic(tag_name ++ " is null. Is there a missing step dependency?"); }; @@ -2027,10 +2027,9 @@ fn checkCompileErrors(compile: *Compile) !void { var aw: std.Io.Writer.Allocating = .init(arena); defer aw.deinit(); try actual_eb.renderToWriter(.{ - .ttyconf = .no_color, .include_reference_trace = false, .include_source_line = false, - }, &aw.writer); + }, &aw.writer, .no_color); break :ae try aw.toOwnedSlice(); }; diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 50e304c950a71def7d6994bd21762a7bad1c6a27..4a136ccbf40f7975ebea8d866a60282568640780 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -54,9 +54,9 @@ pub fn notifyUpdate(ws: *WebServer) void { pub const Options = struct { gpa: Allocator, thread_pool: *std.Thread.Pool, + ttyconf: Io.tty.Config, graph: *const std.Build.Graph, all_steps: []const *Build.Step, - ttyconf: Io.tty.Config, root_prog_node: std.Progress.Node, watch: bool, listen_address: net.IpAddress, @@ -101,10 +101,10 @@ pub fn init(opts: Options) WebServer { return .{ .gpa = opts.gpa, .thread_pool = opts.thread_pool, + .ttyconf = opts.ttyconf, .graph = opts.graph, .all_steps = all_steps, .listen_address = opts.listen_address, - .ttyconf = opts.ttyconf, .root_prog_node = opts.root_prog_node, .watch = opts.watch, @@ -236,9 +236,9 @@ pub fn finishBuild(ws: *WebServer, opts: struct { ws.gpa, ws.graph.io, ws.thread_pool, + ws.ttyconf, ws.all_steps, ws.root_prog_node, - ws.ttyconf, .{ .forever = .{ .ws = ws } }, ) catch |err| std.process.fatal("failed to start fuzzer: {s}", .{@errorName(err)}); ws.fuzz.?.start(); @@ -655,8 +655,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim } if (result_error_bundle.errorMessageCount() > 0) { - const color = std.zig.Color.auto; - result_error_bundle.renderToStdErr(color.renderOptions()); + result_error_bundle.renderToStdErr(.{}, .auto); log.err("the following command failed with {d} compilation errors:\n{s}", .{ result_error_bundle.errorMessageCount(), try Build.Step.allocPrintCmd(arena, null, argv.items), diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3bb5d6d7abc02c57d1a85dc33a7e17a07429e748..19b3a3c2fb394a15be7084a054933ec208a8af8c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -272,7 +272,7 @@ pub fn unlockStdErr() void { std.Progress.unlockStdErr(); } -/// Allows the caller to freely write to stderr until `unlockStdErr` is called. +/// Allows the caller to freely write to stderr until `unlockStderrWriter` is called. /// /// During the lock, any `std.Progress` information is cleared from the terminal. /// @@ -282,8 +282,16 @@ pub fn unlockStdErr() void { /// /// The returned `Writer` does not need to be manually flushed: flushing is performed automatically /// when the matching `unlockStderrWriter` call occurs. -pub fn lockStderrWriter(buffer: []u8) *Writer { - return std.Progress.lockStderrWriter(buffer); +pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { + const global = struct { + var conf: ?tty.Config = null; + }; + const w = std.Progress.lockStderrWriter(buffer); + // The stderr lock also locks access to `global.conf`. + if (global.conf == null) { + global.conf = .detect(.stderr()); + } + return .{ w, global.conf.? }; } pub fn unlockStderrWriter() void { @@ -297,7 +305,7 @@ pub fn unlockStderrWriter() void { /// function returns. pub fn print(comptime fmt: []const u8, args: anytype) void { var buffer: [64]u8 = undefined; - const bw = lockStderrWriter(&buffer); + const bw, _ = lockStderrWriter(&buffer); defer unlockStderrWriter(); nosuspend bw.print(fmt, args) catch return; } @@ -314,9 +322,8 @@ pub inline fn getSelfDebugInfo() !*SelfInfo { /// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. /// Obtains the stderr mutex while dumping. pub fn dumpHex(bytes: []const u8) void { - const bw = lockStderrWriter(&.{}); + const bw, const ttyconf = lockStderrWriter(&.{}); defer unlockStderrWriter(); - const ttyconf = tty.detectConfig(.stderr()); dumpHexFallible(bw, ttyconf, bytes) catch {}; } @@ -538,9 +545,7 @@ pub fn defaultPanic( _ = panicking.fetchAdd(1, .seq_cst); trace: { - const tty_config = tty.detectConfig(.stderr()); - - const stderr = lockStderrWriter(&.{}); + const stderr, const tty_config = lockStderrWriter(&.{}); defer unlockStderrWriter(); if (builtin.single_threaded) { @@ -743,8 +748,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri } /// A thin wrapper around `writeCurrentStackTrace` which writes to stderr and ignores write errors. pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { - const tty_config = tty.detectConfig(.stderr()); - const stderr = lockStderrWriter(&.{}); + const stderr, const tty_config = lockStderrWriter(&.{}); defer unlockStderrWriter(); writeCurrentStackTrace(.{ .first_address = a: { @@ -809,8 +813,7 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, tty_config: tty.C } /// A thin wrapper around `writeStackTrace` which writes to stderr and ignores write errors. pub fn dumpStackTrace(st: *const StackTrace) void { - const tty_config = tty.detectConfig(.stderr()); - const stderr = lockStderrWriter(&.{}); + const stderr, const tty_config = lockStderrWriter(&.{}); defer unlockStderrWriter(); writeStackTrace(st, stderr, tty_config) catch |err| switch (err) { error.WriteFailed => {}, @@ -1552,9 +1555,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex _ = panicking.fetchAdd(1, .seq_cst); trace: { - const tty_config = tty.detectConfig(.stderr()); - - const stderr = lockStderrWriter(&.{}); + const stderr, const tty_config = lockStderrWriter(&.{}); defer unlockStderrWriter(); if (addr) |a| { @@ -1612,7 +1613,7 @@ test "manage resources correctly" { &di, &discarding.writer, S.showMyTrace(), - tty.detectConfig(.stderr()), + .no_color, ); } @@ -1674,8 +1675,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize pub fn dump(t: @This()) void { if (!enabled) return; - const tty_config = tty.detectConfig(.stderr()); - const stderr = lockStderrWriter(&.{}); + const stderr, const tty_config = lockStderrWriter(&.{}); defer unlockStderrWriter(); const end = @min(t.index, size); for (t.addrs[0..end], 0..) |frames_array, i| { diff --git a/lib/std/json/dynamic.zig b/lib/std/json/dynamic.zig index 8aacf42865ca48f7cc2368c89ec01b8b1bf15467..c3cccd1a9157f318fc6c498ab42a1cf7f3914117 100644 --- a/lib/std/json/dynamic.zig +++ b/lib/std/json/dynamic.zig @@ -47,7 +47,7 @@ pub const Value = union(enum) { } pub fn dump(v: Value) void { - const w = std.debug.lockStderrWriter(&.{}); + const w, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); json.Stringify.value(v, .{}, w) catch return; diff --git a/lib/std/log.zig b/lib/std/log.zig index 113831e4622f300e80de40525ac6fb65b11421b8..1b1c98dd430c952132dc63d26287764b1165cbb4 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -151,7 +151,7 @@ pub fn defaultLog( const level_txt = comptime message_level.asText(); const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; var buffer: [64]u8 = undefined; - const stderr = std.debug.lockStderrWriter(&buffer); + const stderr, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; } diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 7cef6f9c58467804763a7e1f28db2f0ec3bc5493..b99542e7e57bdd921e618a5c4e280b1ff7b2fc9c 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -355,7 +355,7 @@ test expectApproxEqRel { /// This function is intended to be used only in tests. When the two slices are not /// equal, prints diagnostics to stderr to show exactly how they are not equal (with /// the differences highlighted in red), then returns a test failure error. -/// The colorized output is optional and controlled by the return of `std.Io.tty.detectConfig()`. +/// The colorized output is optional and controlled by the return of `std.Io.tty.Config.detect`. /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { const diff_index: usize = diff_index: { @@ -367,9 +367,9 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const break :diff_index if (expected.len == actual.len) return else shortest; }; if (!backend_can_print) return error.TestExpectedEqual; - const stderr_w = std.debug.lockStderrWriter(&.{}); + const stderr_w, const ttyconf = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - failEqualSlices(T, expected, actual, diff_index, stderr_w) catch {}; + failEqualSlices(T, expected, actual, diff_index, stderr_w, ttyconf) catch {}; return error.TestExpectedEqual; } @@ -379,6 +379,7 @@ fn failEqualSlices( actual: []const T, diff_index: usize, w: *std.Io.Writer, + ttyconf: std.Io.tty.Config, ) !void { try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); @@ -398,7 +399,6 @@ fn failEqualSlices( const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)]; const actual_truncated = window_start + actual_window.len < actual.len; - const ttyconf = std.Io.tty.detectConfig(.stderr()); var differ = if (T == u8) BytesDiffer{ .expected = expected_window, .actual = actual_window, diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 34851709aba306127b943da58678bfadf528d11c..dcdc727ae22638e01440e9df7a6e177bc797f401 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -53,17 +53,18 @@ pub const Color = enum { /// Assume stderr is a terminal. on, - pub fn get_tty_conf(color: Color) Io.tty.Config { + pub fn getTtyConf(color: Color, detected: Io.tty.Config) Io.tty.Config { return switch (color) { - .auto => Io.tty.detectConfig(std.fs.File.stderr()), + .auto => detected, .on => .escape_codes, .off => .no_color, }; } - - pub fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions { - return .{ - .ttyconf = get_tty_conf(color), + pub fn detectTtyConf(color: Color) Io.tty.Config { + return switch (color) { + .auto => .detect(.stderr()), + .on => .escape_codes, + .off => .no_color, }; } }; @@ -606,7 +607,7 @@ pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); } pub fn putAstErrorsIntoBundle( diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 2b2ad396defe2213a46152c980f2cb0c02332bc0..bcef7f407cdfbc5c561006bbd5f7b9abfeddc6f0 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -157,23 +157,22 @@ pub fn nullTerminatedString(eb: ErrorBundle, index: String) [:0]const u8 { } pub const RenderOptions = struct { - ttyconf: Io.tty.Config, include_reference_trace: bool = true, include_source_line: bool = true, include_log_text: bool = true, }; -pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { +pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions, color: std.zig.Color) void { var buffer: [256]u8 = undefined; - const w = std.debug.lockStderrWriter(&buffer); + const w, const ttyconf = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - renderToWriter(eb, options, w) catch return; + renderToWriter(eb, options, w, color.getTtyConf(ttyconf)) catch return; } -pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void { +pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer, ttyconf: Io.tty.Config) (Writer.Error || std.posix.UnexpectedError)!void { if (eb.extra.len == 0) return; for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, options, err_msg, w, "error", .red, 0); + try renderErrorMessageToWriter(eb, options, err_msg, w, ttyconf, "error", .red, 0); } if (options.include_log_text) { @@ -190,11 +189,11 @@ fn renderErrorMessageToWriter( options: RenderOptions, err_msg_index: MessageIndex, w: *Writer, + ttyconf: Io.tty.Config, kind: []const u8, color: Io.tty.Color, indent: usize, ) (Writer.Error || std.posix.UnexpectedError)!void { - const ttyconf = options.ttyconf; const err_msg = eb.getErrorMessage(err_msg_index); if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); @@ -251,7 +250,7 @@ fn renderErrorMessageToWriter( try ttyconf.setColor(w, .reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent); + try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent); } if (src.data.reference_trace_len > 0 and options.include_reference_trace) { try ttyconf.setColor(w, .reset); @@ -300,7 +299,7 @@ fn renderErrorMessageToWriter( } try ttyconf.setColor(w, .reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent + 4); + try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent + 4); } } } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index d08f5b60ba9dda1a7d72a5f8a4091535e50f436b..c3451f523876a8b242062f43286a549ee8b4ea47 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -6386,7 +6386,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { var buffer: [64]u8 = undefined; - const stderr = std.debug.lockStderrWriter(&buffer); + const stderr, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); var tree = try std.zig.Ast.parse(allocator, source, .zig); diff --git a/src/Air/print.zig b/src/Air/print.zig index 7f3758d47b7ed7d7de440d4c18426f62dfd849f1..73cf2ed9b31e93bd094553d97586a2ca0f258d17 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -73,13 +73,13 @@ pub fn writeInst( } pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - const stderr_bw = std.debug.lockStderrWriter(&.{}); + const stderr_bw, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); air.write(stderr_bw, pt, liveness); } pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - const stderr_bw = std.debug.lockStderrWriter(&.{}); + const stderr_bw, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); air.writeInst(stderr_bw, inst, pt, liveness); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 3670bc51b5f0bcc7b7e61bd8b83f0a6b172fa6af..c72ef32fb56b90ac99ffa080da0dbc59eb424f7a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2093,7 +2093,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, if (options.verbose_llvm_cpu_features) { if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: { - const stderr_w = std.debug.lockStderrWriter(&.{}); + const stderr_w, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); stderr_w.print("compilation: {s}\n", .{options.root_name}) catch break :print; stderr_w.print(" target: {s}\n", .{try target.zigTriple(arena)}) catch break :print; @@ -4270,7 +4270,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { // However, we haven't reported any such error. // This is a compiler bug. print_ctx: { - var stderr_w = std.debug.lockStderrWriter(&.{}); + var stderr_w, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); stderr_w.writeAll("referenced transitive analysis errors, but none actually emitted\n") catch break :print_ctx; stderr_w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}) catch break :print_ctx; @@ -7752,7 +7752,7 @@ pub fn lockAndSetMiscFailure( pub fn dump_argv(argv: []const []const u8) void { var buffer: [64]u8 = undefined; - const stderr = std.debug.lockStderrWriter(&buffer); + const stderr, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); nosuspend { for (argv, 0..) |arg, i| { diff --git a/src/InternPool.zig b/src/InternPool.zig index a595fa502c0ba5f4ba9b88de2f2207e6558c08be..2a5436787c427fbe7ff89ac55a4b4ccff5418cd3 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -11330,7 +11330,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { fn dumpAllFallible(ip: *const InternPool) anyerror!void { var buffer: [4096]u8 = undefined; - const stderr_bw = std.debug.lockStderrWriter(&buffer); + const stderr_bw, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); for (ip.locals, 0..) |*local, tid| { const items = local.shared.items.view(); @@ -11462,7 +11462,7 @@ pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator) } var buffer: [4096]u8 = undefined; - const stderr_bw = std.debug.lockStderrWriter(&buffer); + const stderr_bw, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); const SortContext = struct { diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 46be6fa0699047eedcdec302fa2266e93a37df7d..83d8aa7e5ee4d08550ed4ec721420902d0b79f7a 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -2043,7 +2043,7 @@ const UnpackResult = struct { defer errors.deinit(gpa); var aw: Io.Writer.Allocating = .init(gpa); defer aw.deinit(); - try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); + try errors.renderToWriter(.{}, &aw.writer, .no_color); try std.testing.expectEqualStrings( \\error: unable to unpack \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError @@ -2360,7 +2360,7 @@ const TestFetchBuilder = struct { } var aw: Io.Writer.Allocating = .init(std.testing.allocator); defer aw.deinit(); - try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); + try errors.renderToWriter(.{}, &aw.writer, .no_color); try std.testing.expectEqualStrings(msg, aw.written()); } }; diff --git a/src/Sema.zig b/src/Sema.zig index 341cb1c85523fe1c12c31ce90e0e781cfc48abb6..4016041d82b5169ab827108f9e9508d4062891d5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2631,7 +2631,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); std.debug.print("compile error during Sema:\n", .{}); var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); - error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); + error_bundle.renderToStdErr(.{}, .auto); std.debug.panicExtra(@returnAddress(), "unexpected compile error occurred", .{}); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 474ccc710df5373087c42df19b693962ca6f985f..20aaa3d3c258c7e8c123494087ab96fddefc8b45 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4473,7 +4473,7 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e defer if (liveness) |*l| l.deinit(gpa); if (build_options.enable_debug_extensions and comp.verbose_air) { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); stderr.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}) catch {}; air.write(stderr, pt, liveness); diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 43edde274caf76340dbfdc99de110f2c58b2ed07..8103a1a53d17777fb6aeed03889c1d588d873ef5 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -11188,7 +11188,7 @@ fn initValueAdvanced( } pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { errdefer |err| @panic(@errorName(err)); - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); const zcu = isel.pt.zcu; diff --git a/src/crash_report.zig b/src/crash_report.zig index b051752c7a02e92082a3c282d0427c96a9205548..d525d4b3b5d91e32b715723fd871439ac8f28cd2 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -95,7 +95,7 @@ fn dumpCrashContext() Io.Writer.Error!void { // TODO: this does mean that a different thread could grab the stderr mutex between the context // and the actual panic printing, which would be quite confusing. - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); try stderr.writeAll("Compiler crash context:\n"); diff --git a/src/fmt.zig b/src/fmt.zig index 344a89d6ed5083deeb877fab11a151132e3a1d9e..80925200d62551b14c2a399f7872e1c8c131549f 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -124,7 +124,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! try wip_errors.addZirErrorMessages(zir, tree, source_code, ""); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); process.exit(2); } } else { @@ -138,7 +138,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! try wip_errors.addZoirErrorMessages(zoir, tree, source_code, ""); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); process.exit(2); } } @@ -317,7 +317,7 @@ fn fmtPathFile( try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(fmt.color.renderOptions()); + error_bundle.renderToStdErr(.{}, fmt.color); fmt.any_error = true; } }, @@ -332,7 +332,7 @@ fn fmtPathFile( try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(fmt.color.renderOptions()); + error_bundle.renderToStdErr(.{}, fmt.color); fmt.any_error = true; } }, diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 1773c321e141708d6df270f575e39f4d0e90b118..e45e6c2856727d2686381ed7000df53aa674fad5 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -312,7 +312,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { const include_dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "mingw", "def-include" }); if (comp.verbose_cc) print: { - var stderr = std.debug.lockStderrWriter(&.{}); + var stderr, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); nosuspend stderr.print("def file: {s}\n", .{def_file_path}) catch break :print; nosuspend stderr.print("include dir: {s}\n", .{include_dir}) catch break :print; @@ -332,11 +332,11 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { if (aro_comp.diagnostics.output.to_list.messages.items.len != 0) { var buffer: [64]u8 = undefined; - const w = std.debug.lockStderrWriter(&buffer); + const w, const ttyconf = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); for (aro_comp.diagnostics.output.to_list.messages.items) |msg| { if (msg.kind == .@"fatal error" or msg.kind == .@"error") { - msg.write(w, .detect(std.fs.File.stderr()), true) catch {}; + msg.write(w, ttyconf, true) catch {}; return error.AroPreprocessorFailed; } } @@ -356,7 +356,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { error.OutOfMemory => |e| return e, error.ParseError => { var buffer: [64]u8 = undefined; - const w = std.debug.lockStderrWriter(&buffer); + const w, _ = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); try w.writeAll("error: "); try def_diagnostics.writeMsg(w, input); diff --git a/src/libs/mingw/def.zig b/src/libs/mingw/def.zig index 98ea1a53967850bf1f72d5829563d1f9f620aabb..24dc95c13cc3d53474329459e8011301c5319d46 100644 --- a/src/libs/mingw/def.zig +++ b/src/libs/mingw/def.zig @@ -1028,7 +1028,7 @@ fn testParse(machine_type: std.coff.IMAGE.FILE.MACHINE, source: [:0]const u8, ex const module = parse(std.testing.allocator, source, machine_type, .mingw, &diagnostics) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.ParseError => { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); try diagnostics.writeMsg(stderr, source); try stderr.writeByte('\n'); diff --git a/src/link.zig b/src/link.zig index 7cf8e5c1a6009fb76164b43ec805bcd2b3e8385a..5c4354411ba4fd6dec297296d2cea03984a0e750 100644 --- a/src/link.zig +++ b/src/link.zig @@ -2215,7 +2215,7 @@ fn resolvePathInputLib( var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); std.process.exit(1); } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 32889684dc0a0f439a0046c104d5e5c2753b8690..9dbbccf70e66a0878fba5839daeba5511d0ebbbe 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -2335,7 +2335,7 @@ pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTe } pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) void { - const w = std.debug.lockStderrWriter(&.{}); + const w, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); coff.printNode(tid, w, .root, 0) catch {}; } diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 93fa4909075186123a527672cf28fed53ecd8b4f..6f1f9d6f0ef34d9b09b0356a671e9f2c9b063a11 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -1965,7 +1965,7 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm } pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) void { - const w = std.debug.lockStderrWriter(&.{}); + const w, _ = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); elf.printNode(tid, w, .root, 0) catch {}; } diff --git a/src/main.zig b/src/main.zig index 03d42ba8983110837e4e7b669ca24565b4b8fc87..d134dd9bd239f193097d35429154150769745112 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4520,7 +4520,7 @@ fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) defer errors.deinit(comp.gpa); if (errors.errorMessageCount() > 0) { - errors.renderToStdErr(color.renderOptions()); + errors.renderToStdErr(.{}, color); return error.CompileErrorsReported; } } @@ -4573,7 +4573,7 @@ fn cmdTranslateC( return; } else { const color: Color = .auto; - result.errors.renderToStdErr(color.renderOptions()); + result.errors.renderToStdErr(.{}, color); process.exit(1); } } @@ -5199,7 +5199,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) if (fetch.error_bundle.root_list.items.len > 0) { var errors = try fetch.error_bundle.toOwnedBundle(""); - errors.renderToStdErr(color.renderOptions()); + errors.renderToStdErr(.{}, color); process.exit(1); } @@ -6135,7 +6135,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { try wip_errors.init(arena); try wip_errors.addZirErrorMessages(zir, tree, source, display_path); var error_bundle = try wip_errors.toOwnedBundle(""); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); if (zir.loweringFailed()) { process.exit(1); } @@ -6206,7 +6206,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { try wip_errors.init(arena); try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path); var error_bundle = try wip_errors.toOwnedBundle(""); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); process.exit(1); } @@ -6479,7 +6479,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { try wip_errors.init(arena); try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path); var error_bundle = try wip_errors.toOwnedBundle(""); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); process.exit(1); } @@ -6491,7 +6491,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { try wip_errors.init(arena); try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path); var error_bundle = try wip_errors.toOwnedBundle(""); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, color); process.exit(1); } @@ -6948,7 +6948,7 @@ fn cmdFetch( if (fetch.error_bundle.root_list.items.len > 0) { var errors = try fetch.error_bundle.toOwnedBundle(""); - errors.renderToStdErr(color.renderOptions()); + errors.renderToStdErr(.{}, color); process.exit(1); } @@ -7304,7 +7304,7 @@ fn loadManifest( var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(options.color.renderOptions()); + error_bundle.renderToStdErr(.{}, options.color); process.exit(2); } diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 702866824c499d07e9ec94d42cc30cef302c2073..c211d1022ea8b69592d69967fe0a58025c88c37d 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -89,10 +89,9 @@ pub fn main() !void { const output = allocating.written()[0 .. allocating.written().len - 1 :0]; var tree = try std.zig.Ast.parse(allocator, output, .zig); - var color: std.zig.Color = .on; if (tree.errors.len != 0) { - try std.zig.printAstErrorsToStderr(allocator, tree, "", color); + try std.zig.printAstErrorsToStderr(allocator, tree, "", .auto); return; } @@ -104,7 +103,7 @@ pub fn main() !void { try wip_errors.addZirErrorMessages(zir, tree, output, ""); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(allocator); - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, .auto); } const formatted_output = try tree.renderAlloc(allocator); @@ -931,7 +930,7 @@ fn parseHexInt(text: []const u8) !u31 { } fn usageAndExit(arg0: []const u8, code: u8) noreturn { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); stderr.print( \\Usage: {s} \\ diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 2705618a7dbb2c9507db92fcd26b5b75204b8030..e009715e2f6579dd383e800761a3b37311c3f94a 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -177,7 +177,9 @@ pub fn main() !void { const args = try std.process.argsAlloc(gpa); if (args.len < 2 or mem.eql(u8, args[1], "--help")) { - usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); + const w, _ = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + usage(w, args[0]) catch std.process.exit(2); std.process.exit(1); } const linux_path = args[1]; diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 22031e147a7679e517fdbbb6ed86051c52d2751e..dffd3ccb34ac13dba6b59c8a2cb97209105db9ee 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -340,8 +340,7 @@ const Eval = struct { .unknown => return, .compile_errors => |ce| ce, .stdout, .exit_code => { - const color: std.zig.Color = .auto; - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, .auto); eval.fatal("update '{s}': unexpected compile errors", .{update.name}); }, }; @@ -350,8 +349,7 @@ const Eval = struct { for (error_bundle.getMessages()) |err_idx| { if (expected_idx == expected.errors.len) { - const color: std.zig.Color = .auto; - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, .auto); eval.fatal("update '{s}': more errors than expected", .{update.name}); } try eval.checkOneError(update, error_bundle, expected.errors[expected_idx], false, err_idx); @@ -359,8 +357,7 @@ const Eval = struct { for (error_bundle.getNotes(err_idx)) |note_idx| { if (expected_idx == expected.errors.len) { - const color: std.zig.Color = .auto; - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, .auto); eval.fatal("update '{s}': more error notes than expected", .{update.name}); } try eval.checkOneError(update, error_bundle, expected.errors[expected_idx], true, note_idx); @@ -369,8 +366,7 @@ const Eval = struct { } if (!std.mem.eql(u8, error_bundle.getCompileLogOutput(), expected.compile_log_output)) { - const color: std.zig.Color = .auto; - error_bundle.renderToStdErr(color.renderOptions()); + error_bundle.renderToStdErr(.{}, .auto); eval.fatal("update '{s}': unexpected compile log output", .{update.name}); } } @@ -404,8 +400,7 @@ const Eval = struct { expected.column != src.column + 1 or !std.mem.eql(u8, expected.msg, msg)) { - const color: std.zig.Color = .auto; - eb.renderToStdErr(color.renderOptions()); + eb.renderToStdErr(.{}, .auto); eval.fatal("update '{s}': compile error did not match expected error", .{update.name}); } } diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 9b054478a246940b73c256647e4adc2ebf5e0c5e..c8b941cbfac3f1d3fe5b2fb97dd3d1c2244af87c 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -961,7 +961,9 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool { } fn printUsageAndExit(arg0: []const u8) noreturn { - printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); + const w, _ = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + printUsage(w, arg0) catch std.process.exit(2); std.process.exit(1); } diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 786c78544df09edfda309f99763ab7fcb9ad6b96..eeaa2bb39321a8cde4f029be3fbaaa90dec5852c 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -2167,7 +2167,7 @@ fn processOneTarget(job: Job) void { } fn usageAndExit(arg0: []const u8, code: u8) noreturn { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr, _ = std.debug.lockStderrWriter(&.{}); stderr.print( \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter] \\ diff --git a/tools/update_crc_catalog.zig b/tools/update_crc_catalog.zig index a973a1b75a527af4cbc831196468d9ced7bc8695..4c8614e02fda9fe027ddb69e42239663030393d7 100644 --- a/tools/update_crc_catalog.zig +++ b/tools/update_crc_catalog.zig @@ -190,7 +190,9 @@ pub fn main() anyerror!void { } fn printUsageAndExit(arg0: []const u8) noreturn { - printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); + const w, _ = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + printUsage(w, arg0) catch std.process.exit(2); std.process.exit(1); } -- 2.54.0 From 92151216882e2bfacbbb1b6cae3c5281bf9dd03c Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Tue, 28 Oct 2025 12:43:13 +0000 Subject: [PATCH 2/5] std.log: colorize output in default implementation Also remove the example implementation from the file doc comment; it's better to just link to `defaultLog` as an example, since this avoids writing the example implementation twice and prevents the example from bitrotting. --- lib/std/log.zig | 111 +++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 77 deletions(-) diff --git a/lib/std/log.zig b/lib/std/log.zig index 1b1c98dd430c952132dc63d26287764b1165cbb4..51b01fe47f7bd7bac63c04f8364a4327b9db7fb7 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -13,63 +13,15 @@ //! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its //! log messages. //! -//! An example `logFn` might look something like this: -//! +//! For an example implementation of the `logFn` function, see `defaultLog`, +//! which is the default implementation. It outputs to stderr, using color if +//! the detected `std.Io.tty.Config` supports it. Its output looks like this: //! ``` -//! const std = @import("std"); -//! -//! pub const std_options: std.Options = .{ -//! // Set the log level to info -//! .log_level = .info, -//! -//! // Define logFn to override the std implementation -//! .logFn = myLogFn, -//! }; -//! -//! pub fn myLogFn( -//! comptime level: std.log.Level, -//! comptime scope: @Type(.enum_literal), -//! comptime format: []const u8, -//! args: anytype, -//! ) void { -//! // Ignore all non-error logging from sources other than -//! // .my_project, .nice_library and the default -//! const scope_prefix = "(" ++ switch (scope) { -//! .my_project, .nice_library, std.log.default_log_scope => @tagName(scope), -//! else => if (@intFromEnum(level) <= @intFromEnum(std.log.Level.err)) -//! @tagName(scope) -//! else -//! return, -//! } ++ "): "; -//! -//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix; -//! -//! // Print the message to stderr, silently ignoring any errors -//! std.debug.lockStdErr(); -//! defer std.debug.unlockStdErr(); -//! var stderr = std.fs.File.stderr().writer(&.{}); -//! nosuspend stderr.interface.print(prefix ++ format ++ "\n", args) catch return; -//! } -//! -//! pub fn main() void { -//! // Using the default scope: -//! std.log.debug("A borderline useless debug log message", .{}); // Won't be printed as log_level is .info -//! std.log.info("Flux capacitor is starting to overheat", .{}); -//! -//! // Using scoped logging: -//! const my_project_log = std.log.scoped(.my_project); -//! const nice_library_log = std.log.scoped(.nice_library); -//! const verbose_lib_log = std.log.scoped(.verbose_lib); -//! -//! my_project_log.debug("Starting up", .{}); // Won't be printed as log_level is .info -//! nice_library_log.warn("Something went very wrong, sorry", .{}); -//! verbose_lib_log.warn("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function -//! } -//! ``` -//! Which produces the following output: -//! ``` -//! [info] (default): Flux capacitor is starting to overheat -//! [warning] (nice_library): Something went very wrong, sorry +//! error: this is an error +//! error(scope): this is an error with a non-default scope +//! warning: this is a warning +//! info: this is an informative message +//! debug: this is a debugging message //! ``` const std = @import("std.zig"); @@ -104,37 +56,28 @@ pub const default_level: Level = switch (builtin.mode) { .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, }; -const level = std.options.log_level; - pub const ScopeLevel = struct { scope: @Type(.enum_literal), level: Level, }; -const scope_levels = std.options.log_scope_levels; - fn log( - comptime message_level: Level, + comptime level: Level, comptime scope: @Type(.enum_literal), comptime format: []const u8, args: anytype, ) void { - if (comptime !logEnabled(message_level, scope)) return; + if (comptime !logEnabled(level, scope)) return; - std.options.logFn(message_level, scope, format, args); + std.options.logFn(level, scope, format, args); } /// Determine if a specific log message level and scope combination are enabled for logging. -pub fn logEnabled(comptime message_level: Level, comptime scope: @Type(.enum_literal)) bool { - inline for (scope_levels) |scope_level| { - if (scope_level.scope == scope) return @intFromEnum(message_level) <= @intFromEnum(scope_level.level); +pub fn logEnabled(comptime level: Level, comptime scope: @Type(.enum_literal)) bool { + inline for (std.options.log_scope_levels) |scope_level| { + if (scope_level.scope == scope) return @intFromEnum(level) <= @intFromEnum(scope_level.level); } - return @intFromEnum(message_level) <= @intFromEnum(level); -} - -/// Determine if a specific log message level using the default log scope is enabled for logging. -pub fn defaultLogEnabled(comptime message_level: Level) bool { - return comptime logEnabled(message_level, default_log_scope); + return @intFromEnum(level) <= @intFromEnum(std.options.log_level); } /// The default implementation for the log function. Custom log functions may @@ -143,17 +86,31 @@ pub fn defaultLogEnabled(comptime message_level: Level) bool { /// Uses a 64-byte buffer for formatted printing which is flushed before this /// function returns. pub fn defaultLog( - comptime message_level: Level, + comptime level: Level, comptime scope: @Type(.enum_literal), comptime format: []const u8, args: anytype, ) void { - const level_txt = comptime message_level.asText(); - const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; var buffer: [64]u8 = undefined; - const stderr, _ = std.debug.lockStderrWriter(&buffer); + const stderr, const ttyconf = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; + ttyconf.setColor(stderr, switch (level) { + .err => .red, + .warn => .yellow, + .info => .green, + .debug => .magenta, + }) catch {}; + ttyconf.setColor(stderr, .bold) catch {}; + stderr.writeAll(level.asText()) catch return; + ttyconf.setColor(stderr, .reset) catch {}; + ttyconf.setColor(stderr, .dim) catch {}; + ttyconf.setColor(stderr, .bold) catch {}; + if (scope != .default) { + stderr.print("({s})", .{@tagName(scope)}) catch return; + } + stderr.writeAll(": ") catch return; + ttyconf.setColor(stderr, .reset) catch {}; + stderr.print(format ++ "\n", args) catch return; } /// Returns a scoped logging namespace that logs all messages using the scope -- 2.54.0 From 0dde70ef763d7d21ea2fe703deb253a9ae59cf72 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Tue, 28 Oct 2025 13:34:53 +0000 Subject: [PATCH 3/5] std.Build: fix '--webui' crash Using '--webui' without '--time-report' when there are Run steps in the graph was regressed by https://github.com/ziglang/zig/pull/25029. --- lib/std/Build/Step/Run.zig | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 314862e201434465f3f775bae036a581d9f863a8..eb1de3dd3b7be0796d08ac246016a66dd478b52a 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1587,11 +1587,15 @@ fn spawnChildAndCollect( run.step.test_results = res.test_results; if (res.test_metadata) |tm| { run.cached_test_metadata = tm.toCachedTestMetadata(); - if (options.web_server) |ws| ws.updateTimeReportRunTest( - run, - &run.cached_test_metadata.?, - tm.ns_per_test, - ); + if (options.web_server) |ws| { + if (b.graph.time_report) { + ws.updateTimeReportRunTest( + run, + &run.cached_test_metadata.?, + tm.ns_per_test, + ); + } + } } return null; } else { -- 2.54.0 From 402c14f86a97f47ca429565fddb7ffca5c08d873 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 29 Oct 2025 15:17:03 -0400 Subject: [PATCH 4/5] aarch64: implement optional comparisons --- src/codegen/aarch64/Disassemble.zig | 40 +- src/codegen/aarch64/Mir.zig | 4 +- src/codegen/aarch64/Select.zig | 618 ++++++++++++++++------------ test/behavior/enum.zig | 2 - test/behavior/optional.zig | 2 - test/behavior/switch.zig | 2 - test/behavior/tuple.zig | 1 - test/behavior/union.zig | 6 - 8 files changed, 384 insertions(+), 291 deletions(-) diff --git a/src/codegen/aarch64/Disassemble.zig b/src/codegen/aarch64/Disassemble.zig index 8aecb8128e2fb143378b9020bcbc00122c8316dd..e2d00e3bb3ec070320b9744d1f06855fee169760 100644 --- a/src/codegen/aarch64/Disassemble.zig +++ b/src/codegen/aarch64/Disassemble.zig @@ -74,10 +74,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr dis.operands_separator, imm12, }); - return if (!elide_shift) writer.print("{s}{f} #{s}", .{ + return if (!elide_shift) writer.print("{s}{f} #{t}", .{ dis.operands_separator, fmtCase(.lsl, dis.case), - @tagName(sh), + sh, }); }, .add_subtract_immediate_with_tags => |add_subtract_immediate_with_tags| { @@ -176,10 +176,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr dis.operands_separator, imm16, }); - return if (!elide_shift) writer.print("{s}{f} #{s}", .{ + return if (!elide_shift) writer.print("{s}{f} #{t}", .{ dis.operands_separator, fmtCase(.lsl, dis.case), - @tagName(hw), + hw, }); }, .bitfield => |bitfield| { @@ -833,8 +833,36 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr }, .rotate_right_into_flags => {}, .evaluate_into_flags => {}, - .conditional_compare_register => {}, - .conditional_compare_immediate => {}, + .conditional_compare_register => |conditional_compare_register| { + const group = conditional_compare_register.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}{f}{s}#0x{x}{s}{f}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + group.Rn.decode(.{}).general(sf).fmtCase(dis.case), + dis.operands_separator, + group.Rm.decode(.{}).general(sf).fmtCase(dis.case), + dis.operands_separator, + @as(u4, @bitCast(group.nzcv)), + dis.operands_separator, + fmtCase(group.cond, dis.case), + }); + }, + .conditional_compare_immediate => |conditional_compare_immediate| { + const group = conditional_compare_immediate.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}#0x{x}{s}#0x{x}{s}{f}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + group.Rn.decode(.{}).general(sf).fmtCase(dis.case), + dis.operands_separator, + group.imm5, + dis.operands_separator, + @as(u4, @bitCast(group.nzcv)), + dis.operands_separator, + fmtCase(group.cond, dis.case), + }); + }, .conditional_select => |conditional_select| { const decoded = conditional_select.decode(); if (decoded == .unallocated) break :unallocated; diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig index 3e89e2882558260e280cce7e411359d074bd73d4..cdb9a847d897ff8055b031c0e17c9d0531466984 100644 --- a/src/codegen/aarch64/Mir.zig +++ b/src/codegen/aarch64/Mir.zig @@ -136,7 +136,7 @@ pub fn emit( mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err| return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)}) else - return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}), + return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}), mir.body[lazy_reloc.reloc.label], body_end - Instruction.size * (1 + lazy_reloc.reloc.label), lazy_reloc.reloc.addend, @@ -150,7 +150,7 @@ pub fn emit( else if (lf.cast(.macho)) |mf| try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null) else - return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}), + return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}), mir.body[global_reloc.reloc.label], body_end - Instruction.size * (1 + global_reloc.reloc.label), global_reloc.reloc.addend, diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 8103a1a53d17777fb6aeed03889c1d588d873ef5..8d6ffcce2e519f6c65f42bc6e975a9ab87eae946 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -961,7 +961,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, .inst_index = undefined, }; air_tag: switch (air.next().?) { - else => |air_tag| return isel.fail("unimplemented {s}", .{@tagName(air_tag)}), + else => |air_tag| return isel.fail("unimplemented {t}", .{air_tag}), .arg => { const arg_vi = isel.live_values.fetchRemove(air.inst_index).?.value; defer arg_vi.deref(isel); @@ -1117,12 +1117,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, 32, 64 => |bits| switch (int_info.signedness) { - .signed => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + .signed => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }), .unsigned => { const res_ra = try res_vi.value.defReg(isel) orelse break :unused; const lhs_vi = try isel.use(bin_op.lhs); @@ -1160,7 +1160,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try lhs_mat.finish(isel); }, }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -1172,7 +1172,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); if (!ty.isRuntimeFloat()) { - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -1318,7 +1318,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try rhs_lo64_mat.finish(isel); try lhs_lo64_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } else switch (ty.floatBits(isel.target)) { else => unreachable, @@ -1421,7 +1421,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.signedness) { .signed => switch (int_info.bits) { @@ -1443,7 +1443,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try rhs_mat.finish(isel); try lhs_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), }, .unsigned => switch (int_info.bits) { 0 => unreachable, @@ -1545,8 +1545,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try rhs_mat.finish(isel); try lhs_mat.finish(isel); }, - 65...128 => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + 65...128 => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), }, } } @@ -1558,7 +1558,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -1784,7 +1784,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try rhs_mat.finish(isel); try lhs_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -1897,7 +1897,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); if (!ty.isRuntimeFloat()) { - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -1970,7 +1970,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, else => unreachable, .div_trunc, .div_exact => {}, .div_floor => switch (int_info.signedness) { - .signed => return isel.fail("unimplemented {s}", .{@tagName(air_tag)}), + .signed => return isel.fail("unimplemented {t}", .{air_tag}), .unsigned => {}, }, } @@ -2012,7 +2012,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); try call.finishParams(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } else switch (ty.floatBits(isel.target)) { else => unreachable, @@ -2169,9 +2169,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); if (!ty.isRuntimeFloat()) { - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const res_ra = try res_vi.value.defReg(isel) orelse break :unused; const lhs_vi = try isel.use(bin_op.lhs); @@ -2494,9 +2494,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); if (!ty.isRuntimeFloat()) { - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const res_ra = try res_vi.value.defReg(isel) orelse break :unused; const lhs_vi = try isel.use(bin_op.lhs); @@ -2920,8 +2920,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, else if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else - return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); - if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); + if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const lhs_vi = try isel.use(bin_op.lhs); const rhs_vi = try isel.use(bin_op.rhs); @@ -2968,7 +2968,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -3161,7 +3161,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try lhs_hi64_mat.finish(isel); break :unused; }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -3174,10 +3174,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty = ty_op.ty.toType(); const int_info: std.builtin.Type.Int = int_info: { if (ty_op.ty == .bool_type) break :int_info .{ .signedness = .unsigned, .bits = 1 }; - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); break :int_info ty.intInfo(zcu); }; - if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const src_vi = try isel.use(ty_op.operand); var offset = res_vi.value.size(isel); @@ -3302,7 +3302,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } }, 128 => try dst_vi.value.move(isel, ty_op.operand), - else => return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + else => return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }), } } else if ((dst_ty.isPtrAtRuntime(zcu) or dst_ty.isAbiInt(zcu)) and (src_ty.isPtrAtRuntime(zcu) or src_ty.isAbiInt(zcu))) { try dst_vi.value.move(isel, ty_op.operand); @@ -3313,7 +3313,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu)); if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) { try dst_vi.value.move(isel, ty_op.operand); - } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } else if (dst_tag == .float and src_tag == .float) { assert(dst_ty.floatBits(isel.target) == src_ty.floatBits(isel.target)); try dst_vi.value.move(isel, ty_op.operand); @@ -3483,7 +3483,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try call.paramAddress(isel, src_vi, .r1); try call.paramAddress(isel, dst_vi.value, .r0); try call.finishParams(isel); - } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } else if (dst_tag == .array and dst_ty.childType(zcu).isAbiInt(zcu) and src_ty.isAbiInt(zcu)) { const dst_child_int_info = dst_ty.childType(zcu).intInfo(zcu); const src_int_info = src_ty.intInfo(zcu); @@ -3510,8 +3510,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try call.paramAddress(isel, src_vi, .r1); try call.paramAddress(isel, dst_vi.value, .r0); try call.finishParams(isel); - } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); - } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -3737,7 +3737,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = isel.air.typeOf(ty_op.operand, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -3769,7 +3769,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_hi64_mat.finish(isel); try src_lo64_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -3780,7 +3780,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = isel.air.typeOf(ty_op.operand, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); switch (int_info.bits) { 0 => unreachable, @@ -3812,7 +3812,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_hi64_mat.finish(isel); try src_lo64_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -3823,9 +3823,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = isel.air.typeOf(ty_op.operand, ip); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const res_ra = try res_vi.value.defReg(isel) orelse break :unused; const src_vi = try isel.use(ty_op.operand); @@ -3877,9 +3877,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = ty_op.ty.toType(); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); if (int_info.bits == 8) break :unused try res_vi.value.move(isel, ty_op.operand); const res_ra = try res_vi.value.defReg(isel) orelse break :unused; @@ -3941,9 +3941,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = ty_op.ty.toType(); - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); const res_ra = try res_vi.value.defReg(isel) orelse break :unused; const src_vi = try isel.use(ty_op.operand); @@ -4244,7 +4244,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const ty = ty_op.ty.toType(); if (!ty.isRuntimeFloat()) { - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); switch (ty.intInfo(zcu).bits) { 0 => unreachable, 1...32 => { @@ -4306,7 +4306,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_lo64_mat.finish(isel); try src_hi64_mat.finish(isel); }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }), } } else switch (ty.floatBits(isel.target)) { else => unreachable, @@ -4465,216 +4465,61 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { defer res_vi.value.deref(isel); - var bin_op = air.data(air.inst_index).bin_op; + const bin_op = air.data(air.inst_index).bin_op; const ty = isel.air.typeOf(bin_op.lhs, ip); - if (!ty.isRuntimeFloat()) { - const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type) - .{ .signedness = .unsigned, .bits = 1 } - else if (ty.isAbiInt(zcu)) - ty.intInfo(zcu) - else if (ty.isPtrAtRuntime(zcu)) - .{ .signedness = .unsigned, .bits = 64 } - else - return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); - if (int_info.bits > 256) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); - - const res_ra = try res_vi.value.defReg(isel) orelse break :unused; - try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) { + switch (ip.indexToKey(ty.toIntern())) { + else => {}, + .opt_type => |payload_ty| switch (air_tag) { else => unreachable, - .cmp_lt => switch (int_info.signedness) { - .signed => .lt, - .unsigned => .lo, + .cmp_eq, .cmp_neq => if (!ty.optionalReprIsPayload(zcu)) { + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + const payload_size = ZigType.abiSize(.fromInterned(payload_ty), zcu); + var lhs_payload_part_it = lhs_vi.field(ty, 0, payload_size); + const lhs_payload_part_vi = try lhs_payload_part_it.only(isel); + var rhs_payload_part_it = rhs_vi.field(ty, 0, payload_size); + const rhs_payload_part_vi = try rhs_payload_part_it.only(isel); + const cmp_info = try isel.cmp( + try res_vi.value.defReg(isel) orelse break :unused, + .fromInterned(payload_ty), + lhs_payload_part_vi.?, + air_tag.toCmpOp().?, + rhs_payload_part_vi.?, + ); + try isel.emit(.@"b."( + .vc, + @intCast((isel.instructions.items.len + 1 - cmp_info.cset_label) << 2), + )); + var lhs_has_value_part_it = lhs_vi.field(ty, payload_size, 1); + const lhs_has_value_part_vi = try lhs_has_value_part_it.only(isel); + const lhs_has_value_part_mat = try lhs_has_value_part_vi.?.matReg(isel); + var rhs_has_value_part_it = rhs_vi.field(ty, payload_size, 1); + const rhs_has_value_part_vi = try rhs_has_value_part_it.only(isel); + const rhs_has_value_part_mat = try rhs_has_value_part_vi.?.matReg(isel); + try isel.emit(.ccmp( + lhs_has_value_part_mat.ra.w(), + .{ .register = rhs_has_value_part_mat.ra.w() }, + .{ .n = false, .z = false, .c = false, .v = true }, + .eq, + )); + try isel.emit(.ands( + .wzr, + lhs_has_value_part_mat.ra.w(), + .{ .register = rhs_has_value_part_mat.ra.w() }, + )); + try rhs_has_value_part_mat.finish(isel); + try lhs_has_value_part_mat.finish(isel); + break :unused; }, - .cmp_lte => switch (int_info.bits) { - else => unreachable, - 1...64 => switch (int_info.signedness) { - .signed => .le, - .unsigned => .ls, - }, - 65...128 => { - std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); - continue :cond .cmp_gte; - }, - }, - .cmp_eq => .eq, - .cmp_gte => switch (int_info.signedness) { - .signed => .ge, - .unsigned => .hs, - }, - .cmp_gt => switch (int_info.bits) { - else => unreachable, - 1...64 => switch (int_info.signedness) { - .signed => .gt, - .unsigned => .hi, - }, - 65...128 => { - std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); - continue :cond .cmp_lt; - }, - }, - .cmp_neq => .ne, - }))); - - const lhs_vi = try isel.use(bin_op.lhs); - const rhs_vi = try isel.use(bin_op.rhs); - var part_offset = lhs_vi.size(isel); - while (part_offset > 0) { - const part_size = @min(part_offset, 8); - part_offset -= part_size; - var lhs_part_it = lhs_vi.field(ty, part_offset, part_size); - const lhs_part_vi = try lhs_part_it.only(isel); - const lhs_part_mat = try lhs_part_vi.?.matReg(isel); - var rhs_part_it = rhs_vi.field(ty, part_offset, part_size); - const rhs_part_vi = try rhs_part_it.only(isel); - const rhs_part_mat = try rhs_part_vi.?.matReg(isel); - try isel.emit(switch (part_size) { - else => unreachable, - 1...4 => switch (part_offset) { - 0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), - else => switch (air_tag) { - else => unreachable, - .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs( - .wzr, - lhs_part_mat.ra.w(), - rhs_part_mat.ra.w(), - ), - .cmp_eq, .cmp_neq => .ccmp( - lhs_part_mat.ra.w(), - .{ .register = rhs_part_mat.ra.w() }, - .{ .n = false, .z = false, .c = false, .v = false }, - .eq, - ), - }, - }, - 5...8 => switch (part_offset) { - 0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), - else => switch (air_tag) { - else => unreachable, - .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs( - .xzr, - lhs_part_mat.ra.x(), - rhs_part_mat.ra.x(), - ), - .cmp_eq, .cmp_neq => .ccmp( - lhs_part_mat.ra.x(), - .{ .register = rhs_part_mat.ra.x() }, - .{ .n = false, .z = false, .c = false, .v = false }, - .eq, - ), - }, - }, - }); - try rhs_part_mat.finish(isel); - try lhs_part_mat.finish(isel); - } - } else switch (ty.floatBits(isel.target)) { - else => unreachable, - 16, 32, 64 => |bits| { - const res_ra = try res_vi.value.defReg(isel) orelse break :unused; - const need_fcvt = switch (bits) { - else => unreachable, - 16 => !isel.target.cpu.has(.aarch64, .fullfp16), - 32, 64 => false, - }; - try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (air_tag) { - else => unreachable, - .cmp_lt => .lo, - .cmp_lte => .ls, - .cmp_eq => .eq, - .cmp_gte => .ge, - .cmp_gt => .gt, - .cmp_neq => .ne, - }))); - - const lhs_vi = try isel.use(bin_op.lhs); - const rhs_vi = try isel.use(bin_op.rhs); - const lhs_mat = try lhs_vi.matReg(isel); - const rhs_mat = try rhs_vi.matReg(isel); - const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; - defer if (need_fcvt) isel.freeReg(lhs_ra); - const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; - defer if (need_fcvt) isel.freeReg(rhs_ra); - try isel.emit(bits: switch (bits) { - else => unreachable, - 16 => if (need_fcvt) - continue :bits 32 - else - .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }), - 32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }), - 64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }), - }); - if (need_fcvt) { - try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); - try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); - } - try rhs_mat.finish(isel); - try lhs_mat.finish(isel); - }, - 80, 128 => |bits| { - const res_ra = try res_vi.value.defReg(isel) orelse break :unused; - - try call.prepareReturn(isel); - try call.returnFill(isel, .r0); - try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) { - else => unreachable, - .cmp_lt => .lt, - .cmp_lte => .le, - .cmp_eq => .eq, - .cmp_gte => { - std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); - continue :cond .cmp_lte; - }, - .cmp_gt => { - std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); - continue :cond .cmp_lt; - }, - .cmp_neq => .ne, - }))); - try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 })); - try call.finishReturn(isel); - - try call.prepareCallee(isel); - try isel.global_relocs.append(gpa, .{ - .name = switch (bits) { - else => unreachable, - 16 => "__cmphf2", - 32 => "__cmpsf2", - 64 => "__cmpdf2", - 80 => "__cmpxf2", - 128 => "__cmptf2", - }, - .reloc = .{ .label = @intCast(isel.instructions.items.len) }, - }); - try isel.emit(.bl(0)); - try call.finishCallee(isel); - - try call.prepareParams(isel); - const lhs_vi = try isel.use(bin_op.lhs); - const rhs_vi = try isel.use(bin_op.rhs); - switch (bits) { - else => unreachable, - 16, 32, 64, 128 => { - try call.paramLiveOut(isel, rhs_vi, .v1); - try call.paramLiveOut(isel, lhs_vi, .v0); - }, - 80 => { - var rhs_hi16_it = rhs_vi.field(ty, 8, 8); - const rhs_hi16_vi = try rhs_hi16_it.only(isel); - try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3); - var rhs_lo64_it = rhs_vi.field(ty, 0, 8); - const rhs_lo64_vi = try rhs_lo64_it.only(isel); - try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2); - var lhs_hi16_it = lhs_vi.field(ty, 8, 8); - const lhs_hi16_vi = try lhs_hi16_it.only(isel); - try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); - var lhs_lo64_it = lhs_vi.field(ty, 0, 8); - const lhs_lo64_vi = try lhs_lo64_it.only(isel); - try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); - }, - } - try call.finishParams(isel); }, } + _ = try isel.cmp( + try res_vi.value.defReg(isel) orelse break :unused, + ty, + try isel.use(bin_op.lhs), + air_tag.toCmpOp().?, + try isel.use(bin_op.rhs), + ); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -5497,7 +5342,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_mat.finish(isel); }, }; - } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -5517,7 +5362,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, .int => .integer_out_of_bounds, .@"enum" => { if (!dst_ty.isNonexhaustiveEnum(zcu)) { - return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } break :panic_id .invalid_enum_value; }, @@ -5599,7 +5444,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_mat.finish(isel); } } - } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -5610,7 +5455,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const dst_ty = ty_op.ty.toType(); const src_ty = isel.air.typeOf(ty_op.operand, ip); - if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); const dst_int_info = dst_ty.intInfo(zcu); switch (dst_int_info.bits) { 0 => unreachable, @@ -5683,9 +5528,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try src_lo64_vi.?.liveOut(isel, dst_lo64_ra); } }, - else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }), }, - else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -6487,7 +6332,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_op = air.data(air.inst_index).ty_op; const dst_ty = ty_op.ty.toType(); const src_ty = isel.air.typeOf(ty_op.operand, ip); - if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); const dst_int_info = dst_ty.intInfo(zcu); const src_bits = src_ty.floatBits(isel.target); switch (@max(dst_int_info.bits, src_bits)) { @@ -6617,7 +6462,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } try call.finishParams(isel); }, - else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -6630,7 +6475,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const dst_ty = ty_op.ty.toType(); const src_ty = isel.air.typeOf(ty_op.operand, ip); const dst_bits = dst_ty.floatBits(isel.target); - if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); const src_int_info = src_ty.intInfo(zcu); switch (@max(dst_bits, src_int_info.bits)) { 0 => unreachable, @@ -6757,7 +6602,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } try call.finishParams(isel); }, - else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }), } } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; @@ -6836,7 +6681,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(dst_ty) }), } }; @@ -7157,7 +7002,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, else => unreachable, }, }), - else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(union_ty) }), + else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(union_ty) }), } } var payload_it = union_vi.value.field(union_ty, union_layout.payloadOffset(), union_layout.payload_size); @@ -8499,6 +8344,217 @@ fn ctzLimb( } } +fn cmp( + isel: *Select, + res_ra: Register.Alias, + ty: ZigType, + orig_lhs_vi: Value.Index, + op: std.math.CompareOperator, + orig_rhs_vi: Value.Index, +) !struct { cset_label: usize } { + var lhs_vi = orig_lhs_vi; + var rhs_vi = orig_rhs_vi; + if (!ty.isRuntimeFloat()) { + const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type) + .{ .signedness = .unsigned, .bits = 1 } + else if (ty.isAbiInt(isel.pt.zcu)) + ty.intInfo(isel.pt.zcu) + else if (ty.isPtrAtRuntime(isel.pt.zcu)) + .{ .signedness = .unsigned, .bits = 64 } + else + return isel.fail("bad cmp_{t} {f}", .{ op, isel.fmtType(ty) }); + if (int_info.bits > 256) return isel.fail("too big cmp_{t} {f}", .{ op, isel.fmtType(ty) }); + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) { + .lt => switch (int_info.signedness) { + .signed => .lt, + .unsigned => .lo, + }, + .lte => switch (int_info.bits) { + else => unreachable, + 1...64 => switch (int_info.signedness) { + .signed => .le, + .unsigned => .ls, + }, + 65...128 => { + std.mem.swap(Value.Index, &lhs_vi, &rhs_vi); + continue :cond .gte; + }, + }, + .eq => .eq, + .gte => switch (int_info.signedness) { + .signed => .ge, + .unsigned => .hs, + }, + .gt => switch (int_info.bits) { + else => unreachable, + 1...64 => switch (int_info.signedness) { + .signed => .gt, + .unsigned => .hi, + }, + 65...128 => { + std.mem.swap(Value.Index, &lhs_vi, &rhs_vi); + continue :cond .lt; + }, + }, + .neq => .ne, + }))); + const cset_label = isel.instructions.items.len; + + var part_offset = lhs_vi.size(isel); + while (part_offset > 0) { + const part_size = @min(part_offset, 8); + part_offset -= part_size; + var lhs_part_it = lhs_vi.field(ty, part_offset, part_size); + const lhs_part_vi = try lhs_part_it.only(isel); + const lhs_part_mat = try lhs_part_vi.?.matReg(isel); + var rhs_part_it = rhs_vi.field(ty, part_offset, part_size); + const rhs_part_vi = try rhs_part_it.only(isel); + const rhs_part_mat = try rhs_part_vi.?.matReg(isel); + try isel.emit(switch (part_size) { + else => unreachable, + 1...4 => switch (part_offset) { + 0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + else => switch (op) { + .lt, .lte, .gte, .gt => .sbcs( + .wzr, + lhs_part_mat.ra.w(), + rhs_part_mat.ra.w(), + ), + .eq, .neq => .ccmp( + lhs_part_mat.ra.w(), + .{ .register = rhs_part_mat.ra.w() }, + .{ .n = false, .z = false, .c = false, .v = false }, + .eq, + ), + }, + }, + 5...8 => switch (part_offset) { + 0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + else => switch (op) { + .lt, .lte, .gte, .gt => .sbcs( + .xzr, + lhs_part_mat.ra.x(), + rhs_part_mat.ra.x(), + ), + .eq, .neq => .ccmp( + lhs_part_mat.ra.x(), + .{ .register = rhs_part_mat.ra.x() }, + .{ .n = false, .z = false, .c = false, .v = false }, + .eq, + ), + }, + }, + }); + try rhs_part_mat.finish(isel); + try lhs_part_mat.finish(isel); + } + return .{ .cset_label = cset_label }; + } + switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const need_fcvt = switch (bits) { + else => unreachable, + 16 => !isel.target.cpu.has(.aarch64, .fullfp16), + 32, 64 => false, + }; + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (op) { + .lt => .lo, + .lte => .ls, + .eq => .eq, + .gte => .ge, + .gt => .gt, + .neq => .ne, + }))); + const cset_label = isel.instructions.items.len; + + const lhs_mat = try lhs_vi.matReg(isel); + const rhs_mat = try rhs_vi.matReg(isel); + const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) + continue :bits 32 + else + .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }), + 32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }), + 64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }), + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + return .{ .cset_label = cset_label }; + }, + 80, 128 => |bits| { + try call.prepareReturn(isel); + try call.returnFill(isel, .r0); + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) { + .lt => .lt, + .lte => .le, + .eq => .eq, + .gte => { + std.mem.swap(Value.Index, &lhs_vi, &rhs_vi); + continue :cond .lte; + }, + .gt => { + std.mem.swap(Value.Index, &lhs_vi, &rhs_vi); + continue :cond .lt; + }, + .neq => .ne, + }))); + const cset_label = isel.instructions.items.len; + try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 })); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(isel.pt.zcu.gpa, .{ + .name = switch (bits) { + else => unreachable, + 16 => "__cmphf2", + 32 => "__cmpsf2", + 64 => "__cmpdf2", + 80 => "__cmpxf2", + 128 => "__cmptf2", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + var rhs_hi16_it = rhs_vi.field(ty, 8, 8); + const rhs_hi16_vi = try rhs_hi16_it.only(isel); + try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3); + var rhs_lo64_it = rhs_vi.field(ty, 0, 8); + const rhs_lo64_vi = try rhs_lo64_it.only(isel); + try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + return .{ .cset_label = cset_label }; + }, + } +} + fn loadReg( isel: *Select, ra: Register.Alias, @@ -9272,9 +9328,9 @@ pub const Value = struct { opts: AddOrSubtractOptions, ) !void { const zcu = isel.pt.zcu; - if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(op), isel.fmtType(ty) }); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ op, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(op), isel.fmtType(ty) }); + if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ op, isel.fmtType(ty) }); var part_offset = res_vi.size(isel); var need_wrap = switch (opts.overflow) { .@"unreachable" => false, @@ -10783,7 +10839,7 @@ pub const Value = struct { .err_name => continue :constant_key .{ .undef = error_union_type.payload_type }, .payload => |payload| { constant = payload; - constant_key = ip.indexToKey(payload); + constant_key = ip.indexToKey(constant); continue :constant_key constant_key; }, } @@ -11017,7 +11073,7 @@ pub const Value = struct { } } else .{ .undef = child_ty }, else => |child| { constant = child; - constant_key = ip.indexToKey(child); + constant_key = ip.indexToKey(constant); continue :constant_key constant_key; }, }; @@ -11040,7 +11096,7 @@ pub const Value = struct { }, .repeated_elem => |repeated_elem| { constant = repeated_elem; - constant_key = ip.indexToKey(repeated_elem); + constant_key = ip.indexToKey(constant); continue :constant_key constant_key; }, }; @@ -11099,6 +11155,28 @@ pub const Value = struct { } }, }, + .un => |un| { + const loaded_union = ip.loadUnionType(un.ty); + const union_layout = ZigType.getUnionLayout(loaded_union, zcu); + if (loaded_union.hasTag(ip)) { + const tag_offset = union_layout.tagOffset(); + if (offset >= tag_offset and offset + size <= tag_offset + union_layout.tag_size) { + offset -= tag_offset; + continue :constant_key switch (ip.indexToKey(un.tag)) { + else => unreachable, + .int => |int| .{ .int = int }, + .enum_tag => |enum_tag| .{ .enum_tag = enum_tag }, + }; + } + } + const payload_offset = union_layout.payloadOffset(); + if (offset >= payload_offset and offset + size <= payload_offset + union_layout.payload_size) { + offset -= payload_offset; + constant = un.val; + constant_key = ip.indexToKey(constant); + continue :constant_key constant_key; + } + }, else => {}, } var buffer: [16]u8 = @splat(0); @@ -11259,7 +11337,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { first = false; }; if (reverse_live_registers.get(vi)) |ra| { - try stderr.print("{s}{s}", .{ if (first) " <- " else ", ", @tagName(ra) }); + try stderr.print("{s}{t}", .{ if (first) " <- " else ", ", ra }); first = false; } } @@ -11267,8 +11345,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { switch (value.flags.parent_tag) { .unallocated => if (value.offset_from_parent != 0) try stderr.print(" +0x{x}", .{value.offset_from_parent}), .stack_slot => { - try stderr.print(" [{s}, #{s}0x{x}", .{ - @tagName(value.parent_payload.stack_slot.base), + try stderr.print(" [{t}, #{s}0x{x}", .{ + value.parent_payload.stack_slot.base, if (value.parent_payload.stack_slot.offset < 0) "-" else "", @abs(value.parent_payload.stack_slot.offset), }); @@ -11282,7 +11360,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { isel.fmtConstant(value.parent_payload.constant), }), } - try stderr.print(" align({s})", .{@tagName(value.flags.alignment)}); + try stderr.print(" align({t})", .{value.flags.alignment}); switch (value.flags.location_tag) { .large => try stderr.print(" size=0x{x} large", .{value.location_payload.large.size}), .small => { @@ -11292,8 +11370,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { .unsigned => {}, .signed => try stderr.writeAll(" signed"), } - if (loc.hint != .zr) try stderr.print(" hint={s}", .{@tagName(loc.hint)}); - if (loc.register != .zr) try stderr.print(" loc={s}", .{@tagName(loc.register)}); + if (loc.hint != .zr) try stderr.print(" hint={t}", .{loc.hint}); + if (loc.register != .zr) try stderr.print(" loc={t}", .{loc.register}); }, } try stderr.print(" refs={d}\n", .{value.refs}); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 8fb5d288e390f55ac978dd31b273363b8e0f4a49..175449b566434f9013c63fd5ec9c3fdff7b79878 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -899,7 +899,6 @@ test "enum value allocation" { } test "enum literal casting to tagged union" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -935,7 +934,6 @@ test "enum literal casting to error union with payload enum" { } test "constant enum initialization with differing sizes" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index d8d26d6a0fc6657ec43dc66b5f61a7488d28a233..7c70101c57fe8948fab9efecade23ac05f1b9c60 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -149,7 +149,6 @@ test "nested optional field in struct" { } test "equality compare optionals and non-optionals" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -209,7 +208,6 @@ test "equality compare optionals and non-optionals" { } test "compare optionals with modified payloads" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var lhs: ?bool = false; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b5540664c94729dde4327724e3e84dc829c11a95..170e8d97785e47ea78098f53e1bdf99844d640e9 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -576,7 +576,6 @@ test "switch with null and T peer types and inferred result location type" { } test "switch prongs with cases with identical payload types" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -824,7 +823,6 @@ test "comptime inline switch" { } test "switch capture peer type resolution" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const U = union(enum) { diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index dfd72fea7ac36ee9ed566b19f253555bff9b8f46..54d73eee8a5ea0bcc1144c9b6e5f8b5dbb9bf215 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -496,7 +496,6 @@ test "anon tuple field referencing comptime var isn't comptime" { } test "tuple with runtime value coerced into a slice with a sentinel" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index b4477990aa94a2f99b3669fd85c707a3dffa9ee2..27663feeb670285bb9701055579acdd12d01f8d7 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -208,7 +208,6 @@ const Payload = union(Letter) { }; test "union with specified enum tag" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @@ -219,7 +218,6 @@ test "union with specified enum tag" { } test "packed union generates correctly aligned type" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; @@ -605,7 +603,6 @@ fn returnAnInt(x: i32) TaggedFoo { } test "tagged union with all void fields but a meaningful tag" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1032,7 +1029,6 @@ test "containers with single-field enums" { } test "@unionInit on union with tag but no fields" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1446,8 +1442,6 @@ test "access the tag of a global tagged union" { } test "coerce enum literal to union in result loc" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - const U = union(enum) { a, b: u8, -- 2.54.0 From 32779a7c7392d823c945ae0a13a65cf94a4044b8 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 29 Oct 2025 16:41:54 -0400 Subject: [PATCH 5/5] aarch64: fix macho external references --- src/codegen/aarch64/Mir.zig | 76 ++++++++++++++++++++++++++-------- src/codegen/aarch64/Select.zig | 10 ++++- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig index cdb9a847d897ff8055b031c0e17c9d0531466984..d71c410b1dc10694a32865b9dc7a50e9eefc8522 100644 --- a/src/codegen/aarch64/Mir.zig +++ b/src/codegen/aarch64/Mir.zig @@ -107,6 +107,7 @@ pub fn emit( mir.body[nav_reloc.reloc.label], body_end - Instruction.size * (1 + nav_reloc.reloc.label), nav_reloc.reloc.addend, + if (ip.getNav(nav_reloc.nav).getExtern(ip)) |_| .got_load else .direct, ); for (mir.uav_relocs) |uav_reloc| try emitReloc( lf, @@ -124,6 +125,7 @@ pub fn emit( mir.body[uav_reloc.reloc.label], body_end - Instruction.size * (1 + uav_reloc.reloc.label), uav_reloc.reloc.addend, + .direct, ); for (mir.lazy_relocs) |lazy_reloc| try emitReloc( lf, @@ -140,6 +142,7 @@ pub fn emit( mir.body[lazy_reloc.reloc.label], body_end - Instruction.size * (1 + lazy_reloc.reloc.label), lazy_reloc.reloc.addend, + .direct, ); for (mir.global_relocs) |global_reloc| try emitReloc( lf, @@ -154,6 +157,7 @@ pub fn emit( mir.body[global_reloc.reloc.label], body_end - Instruction.size * (1 + global_reloc.reloc.label), global_reloc.reloc.addend, + .direct, ); const literal_reloc_offset: i19 = @intCast(mir.epilogue.len + literals_align_gap); for (mir.literal_relocs) |literal_reloc| { @@ -188,6 +192,7 @@ fn emitReloc( instruction: Instruction, offset: u32, addend: u64, + kind: enum { direct, got_load }, ) !void { const gpa = zcu.gpa; switch (instruction.decode()) { @@ -198,11 +203,20 @@ fn emitReloc( const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) { else => unreachable, .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) { - .adr => .ADR_PREL_LO21, - .adrp => .ADR_PREL_PG_HI21, + .adr => switch (kind) { + .direct => .ADR_PREL_LO21, + .got_load => unreachable, + }, + .adrp => switch (kind) { + .direct => .ADR_PREL_PG_HI21, + .got_load => .ADR_GOT_PAGE, + }, }, .add_subtract_immediate => |add_subtract_immediate| switch (add_subtract_immediate.group.op) { - .add => .ADD_ABS_LO12_NC, + .add => switch (kind) { + .direct => .ADD_ABS_LO12_NC, + .got_load => unreachable, + }, .sub => unreachable, }, }; @@ -223,7 +237,10 @@ fn emitReloc( .offset = offset, .target = sym_index, .addend = @bitCast(addend), - .type = .page, + .type = switch (kind) { + .direct => .page, + .got_load => .got_load_page, + }, .meta = .{ .pcrel = true, .has_subtractor = false, @@ -238,7 +255,10 @@ fn emitReloc( .offset = offset, .target = sym_index, .addend = @bitCast(addend), - .type = .pageoff, + .type = switch (kind) { + .direct => .pageoff, + .got_load => .got_load_pageoff, + }, .meta = .{ .pcrel = false, .has_subtractor = false, @@ -285,20 +305,39 @@ fn emitReloc( const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) { .integer => |integer| switch (integer.decode()) { .unallocated, .prfm => unreachable, - .strb, .ldrb, .ldrsb => .LDST8_ABS_LO12_NC, - .strh, .ldrh, .ldrsh => .LDST16_ABS_LO12_NC, - .ldrsw => .LDST32_ABS_LO12_NC, - inline .str, .ldr => |encoded| switch (encoded.sf) { + .strb, .ldrb, .ldrsb => switch (kind) { + .direct => .LDST8_ABS_LO12_NC, + .got_load => unreachable, + }, + .strh, .ldrh, .ldrsh => switch (kind) { + .direct => .LDST16_ABS_LO12_NC, + .got_load => unreachable, + }, + .ldrsw => switch (kind) { + .direct => .LDST32_ABS_LO12_NC, + .got_load => unreachable, + }, + inline .str, .ldr => |encoded, mnemonic| switch (encoded.sf) { .word => .LDST32_ABS_LO12_NC, - .doubleword => .LDST64_ABS_LO12_NC, + .doubleword => switch (kind) { + .direct => .LDST64_ABS_LO12_NC, + .got_load => switch (mnemonic) { + else => comptime unreachable, + .str => unreachable, + .ldr => .LD64_GOT_LO12_NC, + }, + }, }, }, - .vector => |vector| switch (vector.group.opc1.decode(vector.group.size)) { - .byte => .LDST8_ABS_LO12_NC, - .half => .LDST16_ABS_LO12_NC, - .single => .LDST32_ABS_LO12_NC, - .double => .LDST64_ABS_LO12_NC, - .quad => .LDST128_ABS_LO12_NC, + .vector => |vector| switch (kind) { + .direct => switch (vector.group.opc1.decode(vector.group.size)) { + .byte => .LDST8_ABS_LO12_NC, + .half => .LDST16_ABS_LO12_NC, + .single => .LDST32_ABS_LO12_NC, + .double => .LDST64_ABS_LO12_NC, + .quad => .LDST128_ABS_LO12_NC, + }, + .got_load => unreachable, }, }; try atom.addReloc(gpa, .{ @@ -314,7 +353,10 @@ fn emitReloc( .offset = offset, .target = sym_index, .addend = @bitCast(addend), - .type = .pageoff, + .type = switch (kind) { + .direct => .pageoff, + .got_load => .got_load_pageoff, + }, .meta = .{ .pcrel = false, .has_subtractor = false, diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 8d6ffcce2e519f6c65f42bc6e975a9ab87eae946..4fe798271fd9a13820cfb1864360d2739d166795 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -7257,7 +7257,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, .nav = ty_nav.nav, .reloc = .{ .label = @intCast(isel.instructions.items.len) }, }); - try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 })); + if (ip.getNav(ty_nav.nav).getExtern(ip)) |_| + try isel.emit(.ldr(ptr_ra.x(), .{ .unsigned_offset = .{ .base = ptr_ra.x(), .offset = 0 } })) + else + try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 })); try isel.nav_relocs.append(gpa, .{ .nav = ty_nav.nav, .reloc = .{ .label = @intCast(isel.instructions.items.len) }, @@ -10971,7 +10974,10 @@ pub const Value = struct { .addend = ptr.byte_offset, }, }); - try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 })); + if (ip.getNav(nav).getExtern(ip)) |_| + try isel.emit(.ldr(mat.ra.x(), .{ .unsigned_offset = .{ .base = mat.ra.x(), .offset = 0 } })) + else + try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 })); try isel.nav_relocs.append(zcu.gpa, .{ .nav = nav, .reloc = .{ -- 2.54.0