From 23541774d6b01b1b614cc5aa3514295ce88917a5 Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Sun, 16 Aug 2026 19:01:56 +0200 Subject: [PATCH 1/2] wasm: enable incremental tests for selfhosted Closes #31810 --- build.zig | 2 +- src/link/Wasm.zig | 3 --- test/tests.zig | 16 +++++++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/build.zig b/build.zig index e6e6a4dcbb5ecf31d5f778e84980a21339db4900..da346f86474aebeb8663de55da7f278090d0a4dd 100644 --- a/build.zig +++ b/build.zig @@ -772,7 +772,7 @@ pub fn build(b: *std.Build) !void { } const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases"); - try tests.addIncrementalTests(b, test_incremental_step, test_filters); + try tests.addIncrementalTests(b, test_incremental_step, test_filters, test_target_filters); if (!skip_test_incremental) test_step.dependOn(test_incremental_step); if (tests.addLibcTestNszTests(b, .{ diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 5a59c890e84080fbdff073b37ceb7bc77bfd1484..1857259fa64bf354bf7ba65846ee65c3c413ccfe 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -3754,9 +3754,6 @@ pub fn updateExports( const ip = &zcu.intern_pool; const is_obj = wasm.base.comp.config.output_mode == .Obj; - wasm.nav_exports.clearRetainingCapacity(); - wasm.uav_exports.clearRetainingCapacity(); - for (export_indices) |export_idx| { const exp = export_idx.ptr(zcu); const name_slice = exp.opts.name.toSlice(ip); diff --git a/test/tests.zig b/test/tests.zig index 302570d313b69de36c2893104f9ccf3cf925d88e..b03229804938b806d47cb7d45328752966a5b371 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2237,8 +2237,7 @@ const incremental_targets: []const []const u8 = &.{ "x86_64-linux-selfhosted", // https://codeberg.org/ziglang/zig/issues/31773 //"x86_64-windows-selfhosted", - // https://codeberg.org/ziglang/zig/issues/31810 - //"wasm32-wasi-selfhosted", + "wasm32-wasi-selfhosted", }; fn compatible32bitArch(host: *const std.Target) ?std.Target.Cpu.Arch { @@ -3248,7 +3247,12 @@ pub fn addDebuggerTests(b: *std.Build, options: DebuggerContext.Options) ?*Step return step; } -pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []const []const u8) !void { +pub fn addIncrementalTests( + b: *std.Build, + test_step: *Step, + test_filters: []const []const u8, + test_target_filters: []const []const u8, +) !void { const io = b.graph.io; const incr_check = b.addExecutable(.{ @@ -3283,6 +3287,12 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons b.dependOnFileContents(b.path(b.pathJoin(&.{ "test", "incremental", entry.path }))); for (incremental_targets) |target_str| { + if (test_target_filters.len > 0) { + for (test_target_filters) |filter| { + if (std.mem.find(u8, target_str, filter) != null) break; + } else continue; + } + const run = b.addRunArtifact(incr_check); run.setName(b.fmt("incr-check {s} '{s}'", .{ target_str, entry.basename })); -- 2.54.0 From 46af37a1ad8bcff989ae18624b02760ab7a5bfa5 Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Sun, 16 Aug 2026 23:23:57 +0200 Subject: [PATCH 2/2] wasm: fix and test when dead function emitted after incremental update --- src/link/Wasm/Flush.zig | 9 ++++ .../remove_function_with_changed_callee | 50 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/incremental/remove_function_with_changed_callee diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 0c259d404407d396e80ceaf329bf69a0abf055cf..7f18b297730f166451eb8826c1d222079ed4ccc6 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -249,6 +249,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { }; const is_obj = comp.config.output_mode == .Obj; const allow_undefined = is_obj or wasm.import_symbols; + const zcu_references = if (comp.zcu) |zcu| try zcu.resolveReferences() else null; const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none; @@ -1223,6 +1224,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try emitTagIndexFunction(wasm, binary_bytes, ip_index); }, else => { + if (!zcu_references.?.contains(.wrap(.{ .func = ip_index }))) { + try binary_bytes.appendSlice(gpa, &.{ + 0, // no locals + @backingInt(std.wasm.Opcode.@"unreachable"), + @backingInt(std.wasm.Opcode.end), + }); + continue; + } const func = i.value(wasm).function; const mir: Mir = .{ .instructions = wasm.mir_instructions.slice().subslice(func.instructions_off, func.instructions_len), diff --git a/test/incremental/remove_function_with_changed_callee b/test/incremental/remove_function_with_changed_callee new file mode 100644 index 0000000000000000000000000000000000000000..31e962124dfb10374fd9b0dd70bc743edde143bd --- /dev/null +++ b/test/incremental/remove_function_with_changed_callee @@ -0,0 +1,50 @@ +#update=initial version +#file=main.zig +const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.io(); + +const A = enum(u32) { + zero, + one, +}; + +fn foo() !void { + try bar(.zero); +} + +fn bar(a: A) !void { + const msg = switch (a) { + .zero => "0", + .one => "1", + }; + try std.Io.File.stdout().writeStreamingAll(io, msg); +} + +pub fn main() !void { + try foo(); +} +#expect_stdout="0" +#update=remove foo and change bar wasm function type +#file=main.zig +//! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. +//! If `foo` code is preserved in the binary without any changes, wasm type checking will fail. +const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.io(); + +const A = enum(u64) { + zero, + one, +}; + +fn bar(a: A) !void { + const msg = switch (a) { + .zero => "0", + .one => "1", + }; + try std.Io.File.stdout().writeStreamingAll(io, msg); +} + +pub fn main() !void { + try bar(.one); +} +#expect_stdout="1" -- 2.54.0