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 545cab6083..ae9eb53fb2 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 2b31653cca..59568e9cef 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 aad74a3ca3..f9ccc43a7f 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 b32237b06b..6d6819f45a 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 e80c3d3dcd..f560e05cca 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 e9d2e81fba..3b78fc6f71 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 d342628871..6dd4f70f9f 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 2188d8bfc7..a5f2d696be 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 50e304c950..4a136ccbf4 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 3bb5d6d7ab..19b3a3c2fb 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 8aacf42865..c3cccd1a91 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 113831e462..1b1c98dd43 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 7cef6f9c58..b99542e7e5 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 34851709ab..dcdc727ae2 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 2b2ad396de..bcef7f407c 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 d08f5b60ba..c3451f5238 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 7f3758d47b..73cf2ed9b3 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 3670bc51b5..c72ef32fb5 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 a595fa502c..2a5436787c 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 46be6fa069..83d8aa7e5e 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 341cb1c855..4016041d82 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 474ccc710d..20aaa3d3c2 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 43edde274c..8103a1a53d 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 b051752c7a..d525d4b3b5 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 344a89d6ed..80925200d6 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 1773c321e1..e45e6c2856 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 98ea1a5396..24dc95c13c 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 7cf8e5c1a6..5c4354411b 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 32889684dc..9dbbccf70e 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 93fa490907..6f1f9d6f0e 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 03d42ba898..d134dd9bd2 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 702866824c..c211d1022e 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 2705618a7d..e009715e2f 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 22031e147a..dffd3ccb34 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 9b054478a2..c8b941cbfa 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 786c78544d..eeaa2bb393 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 a973a1b75a..4c8614e02f 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); } 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 1b1c98dd43..51b01fe47f 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 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 314862e201..eb1de3dd3b 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 { 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 8aecb8128e..e2d00e3bb3 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 3e89e28825..cdb9a847d8 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 8103a1a53d..8d6ffcce2e 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 8fb5d288e3..175449b566 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 d8d26d6a0f..7c70101c57 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 b5540664c9..170e8d9778 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 dfd72fea7a..54d73eee8a 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 b4477990aa..27663feeb6 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, 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 cdb9a847d8..d71c410b1d 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 8d6ffcce2e..4fe798271f 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 = .{