From d61920d80e0e6d2c2a058c96d1b916c7300ddab5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 14 Aug 2026 20:23:50 -0700 Subject: [PATCH 1/2] lib-vt: disable logging in wasm release builds --- src/lib_vt.zig | 19 +++++++++---------- src/os/wasm/log.zig | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/lib_vt.zig b/src/lib_vt.zig index 66da515a8..3b66930f2 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -391,16 +391,15 @@ pub const std_options: std.Options = opts: { var options: std.Options = .{}; if (builtin.target.cpu.arch.isWasm()) { - // Wasm builds we specifically want to optimize for space with small - // releases so we bump up to warn. Everything else acts pretty normal. - options.log_level = switch (builtin.mode) { - .Debug => .debug, - .ReleaseSmall => .warn, - else => .info, - }; - - // Wasm doesn't have access to stdio so we have a custom log function. - options.logFn = @import("os/wasm/log.zig").log; + // In non-debug modes, we want to ship effectively no logging + // warn and lower add ~200KB at the time of this comment. + if (builtin.mode == .Debug) { + options.log_level = .debug; + options.logFn = @import("os/wasm/log.zig").log; + } else { + options.log_level = .err; + options.logFn = @import("os/wasm/log.zig").noop; + } } else if (terminal.options.c_abi) { // For C ABI builds, use a custom log function that dispatches to an // embedder-provided callback (or silently discards when none is set). diff --git a/src/os/wasm/log.zig b/src/os/wasm/log.zig index faa885c6e..5c5a8f66c 100644 --- a/src/os/wasm/log.zig +++ b/src/os/wasm/log.zig @@ -4,6 +4,22 @@ const wasm = @import("../wasm.zig"); // Use the correct implementation pub const log = Freestanding.log; +/// A log function that discards everything. Since the format string and +/// scope are comptime parameters, using this compiles out all logging +/// machinery: call sites, format strings, and the std.fmt code they +/// reference (~220KB in a ReleaseFast wasm build). +pub fn noop( + comptime level: std.log.Level, + comptime scope: @TypeOf(.EnumLiteral), + comptime format: []const u8, + args: anytype, +) void { + _ = level; + _ = scope; + _ = format; + _ = args; +} + /// Freestanding implementation calls an extern "log" function. pub const Freestanding = struct { // The function std.log will call. From 51a4311ef18b0971c112967cf24a538a2c71ea36 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 14 Aug 2026 20:44:18 -0700 Subject: [PATCH 2/2] terminal: clean up overzealous inlining --- src/terminal/Terminal.zig | 9 ++++++- src/terminal/formatter.zig | 50 +++++++++++++++----------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index ce22ae084..e1729b461 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -498,7 +498,14 @@ pub fn printRepeat(self: *Terminal, count_req: usize) !void { // identical to calling print per codepoint: ineligible characters // or terminal states (insert mode, grapheme clustering, hyperlinks, // etc.) fall back to the per-codepoint print() path internally. - var buf: [4096]u32 = @splat(c); + // + // The buffer is filled with a runtime-bounded loop rather than + // `= @splat(c)`: a comptime-known 4096-element splat gets fully + // unrolled into ~33KB of consecutive stores (LLVM won't re-roll + // or vectorize it, see quirks_memset.zig), and it would fill the + // whole buffer even for the typical small repeat counts. + var buf: [4096]u32 = undefined; + for (buf[0..@min(remaining, buf.len)]) |*cp| cp.* = c; while (remaining > 0) { const n = @min(remaining, buf.len); try self.printSlice(buf[0..n]); diff --git a/src/terminal/formatter.zig b/src/terminal/formatter.zig index ab9edbffe..676a502dc 100644 --- a/src/terminal/formatter.zig +++ b/src/terminal/formatter.zig @@ -1268,32 +1268,17 @@ pub const PageFormatter = struct { if (style_id == invalid_style_id) break :fast; } - // Specialized on point tracking so that the common - // non-tracking case has zero per-cell overhead. - const consumed = if (self.point_map == null) - try self.writeCellRun( - emit, - false, - writer, - cells_subset[cell_i..], - x, - y, - style_id, - current_hyperlink_id, - &blank_cells, - ) - else - try self.writeCellRun( - emit, - true, - writer, - cells_subset[cell_i..], - x, - y, - style_id, - current_hyperlink_id, - &blank_cells, - ); + const consumed = try self.writeCellRun( + emit, + self.point_map != null, + writer, + cells_subset[cell_i..], + x, + y, + style_id, + current_hyperlink_id, + &blank_cells, + ); // Zero cells consumed means the first cell isn't // eligible for the fast path; handle it below. @@ -1579,10 +1564,15 @@ pub const PageFormatter = struct { /// Blank cell accounting matches the slow path: accumulated blanks /// are only materialized once a non-blank cell is found, and any /// remainder is written back to `blank_cells`. - fn writeCellRun( + // Deliberately not inlined: this is instantiated per emit format and + // inlining every copy into formatWithStateEmit's row loop bloats the + // binary. track_points is a runtime bool for the same reason: a + // comptime bool doubles the instantiation count for one predictable + // branch per emitted cell. + noinline fn writeCellRun( self: *const PageFormatter, comptime emit: Format, - comptime track_points: bool, + track_points: bool, writer: *std.Io.Writer, cells: []const Cell, run_x: size.CellCountInt, @@ -1666,7 +1656,7 @@ pub const PageFormatter = struct { // This cell produces output: materialize accumulated blanks. if (pending > 0) { - if (comptime track_points) try self.appendBlankPoints( + if (track_points) try self.appendBlankPoints( &self.point_map.?, pending, x, @@ -1718,7 +1708,7 @@ pub const PageFormatter = struct { } // All of the cell's bytes map to the cell's coordinate. - if (comptime track_points) { + if (track_points) { const map = &self.point_map.?; map.map.appendNTimes( map.alloc,