Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
aea6f9c
Update WASM_SURGICAL_LINKING_PLAN.md
lukewilliamboswell Mar 28, 2026
59ee1ba
Update WASM_SURGICAL_LINKING_PLAN.md
lukewilliamboswell Mar 28, 2026
d56f0af
Add padded LEB128 helpers for WASM surgical linking (Phase 1)
lukewilliamboswell Mar 28, 2026
0c7d04f
Add WASM linking data structures (Phase 2) and status table
lukewilliamboswell Mar 28, 2026
97b147a
Add WASM module parser (Phase 3) for surgical linking
lukewilliamboswell Mar 28, 2026
92a878e
Add surgical linking linkHostToAppCalls() (Phase 4) for WASM
lukewilliamboswell Mar 28, 2026
6691a50
Build wasm host as relocatable object and test parser against it
lukewilliamboswell Mar 29, 2026
a921be3
Add memory, table, and stack pointer ownership (Phase 5) for WASM sur…
lukewilliamboswell Mar 29, 2026
ea73e7d
Add WASM function pointer representation and RocOps layout (Phase 6) …
lukewilliamboswell Mar 29, 2026
1be0ab7
Add RocCall ABI entrypoint and eval wrapper (Phase 7a) for WASM surgi…
lukewilliamboswell Mar 29, 2026
928865b
Add CodeBuilder and refactor WasmCodeGen (Phase 7b) for WASM surgical…
lukewilliamboswell Mar 29, 2026
bc8a2d1
Add mergeModule, BuiltinSymbols, and relocation resolution (Phase 8a/…
lukewilliamboswell Mar 29, 2026
9279e40
Add missing builtin wrappers for list equality, list reverse, and int…
lukewilliamboswell Mar 29, 2026
83051bf
Remove all legacy builtin imports, add builtin_syms (Phase 8c+8d) for…
lukewilliamboswell Mar 29, 2026
89c4841
Update WASM surgical linking plan: Phase 8 complete
lukewilliamboswell Mar 29, 2026
4aabd76
Implement hosted call lowering for WASM backend (Phase 9) for WASM su…
lukewilliamboswell Mar 29, 2026
99f74af
Implement dead code elimination (Phase 10) for WASM surgical linking
lukewilliamboswell Mar 29, 2026
5d8e71f
Add serialization tests (Phase 11) for WASM surgical linking
lukewilliamboswell Mar 29, 2026
de7ff67
Implement CLI integration for wasm32 surgical linking (Phase 12)
lukewilliamboswell Mar 29, 2026
9339848
Add PIC WASM support, eval builtins merging, and ABI fixes (Phase 13)
lukewilliamboswell Mar 30, 2026
04c5af7
Fix list_append to use safe version and refcount-aware allocation
lukewilliamboswell Mar 30, 2026
9ca3f5c
Add regression tests for list of strings and empty list join_with
lukewilliamboswell Mar 30, 2026
f591669
Fix post-rebase issues: dead code, missing exports, build wiring
lukewilliamboswell Mar 31, 2026
640807a
Fix reloc.CODE offset test and remove unused variable suppressions
lukewilliamboswell Mar 31, 2026
0c968e1
WIP: Add debug diagnostics for wasm validation type mismatch
lukewilliamboswell Mar 31, 2026
fd41a8d
Fix mergeModule func_remap offset bug and add compiler-rt host functions
lukewilliamboswell Mar 31, 2026
2f2ac11
Update WASM surgical linking plan with rebase status and remaining work
lukewilliamboswell Mar 31, 2026
bc77510
Fix WASM eval pipeline: memory leaks, hangs, and null pointer trap
lukewilliamboswell Apr 1, 2026
16b6d03
Fix reloc.DATA resolution, host RocOps registration, and import verif…
lukewilliamboswell Apr 1, 2026
21545f1
Add missing doc comments to WasmCodeGen pub declarations
lukewilliamboswell Apr 1, 2026
963cbe3
Fix memory leak in mergeModule test and add bytebox to backend test step
lukewilliamboswell Apr 1, 2026
4ebb09e
Update str_with_capacity.md
lukewilliamboswell Apr 1, 2026
8dd15d3
Fix CI build: compile wasm32 builtins from source instead of expectin…
lukewilliamboswell Apr 1, 2026
0766af1
Add Stdout.line! hosted effect to WASM test platform and fix linker bugs
lukewilliamboswell Apr 1, 2026
dee8f6b
Mark app-compiled functions as live in WASM DCE
lukewilliamboswell Apr 1, 2026
0c1f7bc
Fix host data symbol addresses, app function DCE, and browser test page
lukewilliamboswell Apr 1, 2026
aaebe81
Fix wasm32 Dec formatting: trailing zeros not trimmed
lukewilliamboswell Apr 1, 2026
a0d735f
Fix WASM data symbol relocation: rebase absolute addresses during merge
lukewilliamboswell Apr 2, 2026
6b048d5
Remove flaky intermediate assertion in merge data reloc test
lukewilliamboswell Apr 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,679 changes: 2,679 additions & 0 deletions WASM_SURGICAL_LINKING_PLAN.md

Large diffs are not rendered by default.

106 changes: 87 additions & 19 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,38 @@ fn buildAndCopyTestPlatformHostLib(
return &copy_step.step;
}

/// Build the wasm test platform host as a relocatable .wasm object (not an archive).
/// Surgical linking operates on a single relocatable object with linking/reloc sections.
fn buildAndCopyWasmHostObject(
b: *std.Build,
target: ResolvedTarget,
optimize: OptimizeMode,
roc_modules: modules.RocModules,
strip: bool,
omit_frame_pointer: ?bool,
) *Step {
const obj = b.addObject(.{
.name = "host",
.root_module = b.createModule(.{
.root_source_file = b.path("test/wasm/platform/host.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
.omit_frame_pointer = omit_frame_pointer,
.pic = true,
}),
});
configureBackend(obj, target);
obj.root_module.addImport("builtins", roc_modules.builtins);
obj.root_module.addImport("build_options", roc_modules.build_options);

const dest_path = "test/wasm/platform/targets/wasm32/host.wasm";
const copy_step = b.addUpdateSourceFiles();
copy_step.addCopyFileToSource(obj.getEmittedBin(), dest_path);

return &copy_step.step;
}

// Workaround for Zig bug https://codeberg.org/ziglang/zig/issues/30572
const FixArchivePaddingStep = struct {
step: Step,
Expand Down Expand Up @@ -2044,7 +2076,7 @@ fn setupTestPlatforms(
strip: bool,
omit_frame_pointer: ?bool,
platform_filter: ?[]const u8,
) void {
) *Step {
// Clear the Roc cache when test platforms are rebuilt to ensure stale cached hosts aren't used
const clear_cache_step = createClearCacheStep(b);
const native_target_name = roc_target.RocTarget.fromStdTarget(target.result).toName();
Expand Down Expand Up @@ -2111,24 +2143,22 @@ fn setupTestPlatforms(
}
}

// Build the wasm test platform host for wasm32-freestanding
{
const wasm_target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none });
const copy_step = buildAndCopyTestPlatformHostLib(
b,
"wasm",
wasm_target,
"wasm32",
optimize,
roc_modules,
strip,
omit_frame_pointer,
);
clear_cache_step.dependOn(copy_step);
}
// Build the wasm test platform host as a relocatable .wasm object for surgical linking
const wasm_target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none });
const wasm_host_step = buildAndCopyWasmHostObject(
b,
wasm_target,
optimize,
roc_modules,
strip,
omit_frame_pointer,
);
clear_cache_step.dependOn(wasm_host_step);

b.getInstallStep().dependOn(clear_cache_step);
test_platforms_step.dependOn(clear_cache_step);

return wasm_host_step;
}

pub fn build(b: *std.Build) void {
Expand Down Expand Up @@ -2324,8 +2354,38 @@ pub fn build(b: *std.Build) void {
roc_modules.eval.addImport("bytebox", bytebox.module("bytebox"));
roc_modules.lsp.addImport("compiled_builtins", compiled_builtins_module);

// Build wasm32 builtins object at build time so the eval/REPL pipeline can
// merge real compiled builtins into WASM modules (instead of using host imports).
const wasm32_resolved_target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none });
const wasm32_builtins_obj = b.addObject(.{
.name = "roc_builtins_wasm32_eval",
.root_module = b.createModule(.{
.root_source_file = b.path("src/builtins/static_lib.zig"),
.target = wasm32_resolved_target,
.optimize = optimize,
.strip = strip,
.omit_frame_pointer = omit_frame_pointer,
.pic = true,
}),
});
wasm32_builtins_obj.root_module.addImport("tracy", b.addModule("tracy_stub_wasm32_eval", .{
.root_source_file = b.path("src/builtins/tracy_stub.zig"),
}));
wasm32_builtins_obj.bundle_compiler_rt = false;
configureBackend(wasm32_builtins_obj, wasm32_resolved_target);

const wasm32_builtins_files = b.addWriteFiles();
_ = wasm32_builtins_files.addCopyFile(wasm32_builtins_obj.getEmittedBin(), "roc_builtins.o");
const wasm32_builtins_module = b.createModule(.{
.root_source_file = wasm32_builtins_files.add("wasm32_builtins.zig",
\\pub const bytes = @embedFile("roc_builtins.o");
\\
),
});
roc_modules.eval.addImport("wasm32_builtins", wasm32_builtins_module);

// Setup test platform host libraries
setupTestPlatforms(b, target, optimize, roc_modules, test_platforms_step, strip, omit_frame_pointer, platform_filter);
const wasm_host_step = setupTestPlatforms(b, target, optimize, roc_modules, test_platforms_step, strip, omit_frame_pointer, platform_filter);

const roc_exe = addMainExe(b, roc_modules, target, optimize, strip, omit_frame_pointer, use_system_llvm, user_llvm_path, flag_enable_tracy, zstd, compiled_builtins_module, write_compiled_builtins, flag_enable_tracy) orelse return;
roc_modules.addAll(roc_exe);
Expand Down Expand Up @@ -2818,9 +2878,10 @@ pub fn build(b: *std.Build) void {
module_test.test_step.root_module.addImport("bytebox", bytebox.module("bytebox"));
}

// Add bytebox to eval tests for wasm backend testing
// Add bytebox and wasm32 builtins to eval tests for wasm backend testing
if (std.mem.eql(u8, module_test.test_step.name, "eval")) {
module_test.test_step.root_module.addImport("bytebox", bytebox.module("bytebox"));
module_test.test_step.root_module.addImport("wasm32_builtins", wasm32_builtins_module);
const compile_build_module = b.createModule(.{
.root_source_file = b.path("src/compile/compile_build.zig"),
});
Expand Down Expand Up @@ -2854,6 +2915,13 @@ pub fn build(b: *std.Build) void {
);
}

// Backend tests need the wasm host object and builtins for WASM linking tests
if (std.mem.eql(u8, module_test.test_step.name, "backend")) {
module_test.test_step.step.dependOn(wasm_host_step);
module_test.test_step.root_module.addImport("wasm32_builtins", wasm32_builtins_module);
module_test.test_step.root_module.addImport("bytebox", bytebox.module("bytebox"));
}

if (std.mem.eql(u8, module_test.test_step.name, "repl")) {
try addLlvmSupportToStep(
b,
Expand Down Expand Up @@ -3623,7 +3691,7 @@ fn addMainExe(
for (cross_compile_builtins_targets) |cross_target| {
const cross_resolved_target = b.resolveTargetQuery(cross_target.query);

// Build builtins object file for this target
// Build builtins object file for this target.
const cross_builtins_obj = b.addObject(.{
.name = b.fmt("roc_builtins_{s}", .{cross_target.name}),
.root_module = b.createModule(.{
Expand Down
Loading
Loading