authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-28 06:31:26+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-29 15:24:08+01:00
log196ba706a05046b2209529744d2df47215819691
tree38939524afabc97b9c56bf8f393bc7bc1990e437
parent5ba5a2c133be5e06083f088aa875ff18658fbf8c
signaturelock-open Commit is signed but in an unrecognized format.

wasm: gc fixes and re-enable linker tests

Certain symbols were left unmarked, meaning they would not be emit into the final binary incorrectly. We now mark the synthetic symbols to ensure they are emit as they are already created under the circumstance they're needed for. This also re-enables disabled tests that were left disabled in a previous merge conflict. Lastly, this adds the shared-memory test to the test harnass as it was previously forgotten and therefore regressed.

8 files changed, 117 insertions(+), 95 deletions(-)

src/link/Wasm.zig+38-17
......@@ -11,6 +11,7 @@ const leb = std.leb;
1111const link = @import("../link.zig");
1212const lldMain = @import("../main.zig").lldMain;
1313const log = std.log.scoped(.link);
14const gc_log = std.log.scoped(.gc);
1415const mem = std.mem;
1516const trace = @import("../tracy.zig").trace;
1617const types = @import("Wasm/types.zig");
......@@ -525,6 +526,7 @@ pub fn createEmpty(
525526 const symbol = loc.getSymbol(wasm);
526527 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
527528 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
529 symbol.mark();
528530 try wasm.wasm_globals.append(gpa, .{
529531 .global_type = .{ .valtype = .i32, .mutable = true },
530532 .init = .{ .i32_const = undefined },
......@@ -535,6 +537,7 @@ pub fn createEmpty(
535537 const symbol = loc.getSymbol(wasm);
536538 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
537539 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
540 symbol.mark();
538541 try wasm.wasm_globals.append(gpa, .{
539542 .global_type = .{ .valtype = .i32, .mutable = false },
540543 .init = .{ .i32_const = undefined },
......@@ -545,6 +548,7 @@ pub fn createEmpty(
545548 const symbol = loc.getSymbol(wasm);
546549 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
547550 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
551 symbol.mark();
548552 try wasm.wasm_globals.append(gpa, .{
549553 .global_type = .{ .valtype = .i32, .mutable = false },
550554 .init = .{ .i32_const = undefined },
......@@ -968,6 +972,8 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
968972 if (!wasm.hasPassiveInitializationSegments()) {
969973 return;
970974 }
975 const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function);
976 sym_loc.getSymbol(wasm).mark();
971977
972978 const flag_address: u32 = if (shared_memory) address: {
973979 // when we have passive initialization segments and shared memory
......@@ -1130,7 +1136,8 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
11301136 return;
11311137 }
11321138
1133 // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs");
1139 const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs", .function);
1140 loc.getSymbol(wasm).mark();
11341141 var function_body = std.ArrayList(u8).init(gpa);
11351142 defer function_body.deinit();
11361143 const writer = function_body.writer();
......@@ -1833,8 +1840,7 @@ fn createSyntheticFunction(
18331840 function_body: *std.ArrayList(u8),
18341841) !void {
18351842 const gpa = wasm.base.comp.gpa;
1836 const loc = wasm.findGlobalSymbol(symbol_name) orelse
1837 try wasm.createSyntheticSymbol(symbol_name, .function);
1843 const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol?
18381844 const symbol = loc.getSymbol(wasm);
18391845 if (symbol.isDead()) {
18401846 return;
......@@ -1884,6 +1890,9 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
18841890
18851891 if (!shared_memory) return;
18861892
1893 // ensure function is marked as we must emit it
1894 wasm.findGlobalSymbol("__wasm_init_tls").?.getSymbol(wasm).mark();
1895
18871896 var function_body = std.ArrayList(u8).init(gpa);
18881897 defer function_body.deinit();
18891898 const writer = function_body.writer();
......@@ -1932,6 +1941,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
19321941 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
19331942 try writer.writeByte(std.wasm.opcode(.call));
19341943 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
1944 loc.getSymbol(wasm).mark();
19351945 }
19361946
19371947 try writer.writeByte(std.wasm.opcode(.end));
......@@ -2039,14 +2049,19 @@ fn mergeSections(wasm: *Wasm) !void {
20392049 // We found an alias to the same function, discard this symbol in favor of
20402050 // the original symbol and point the discard function to it. This ensures
20412051 // we only emit a single function, instead of duplicates.
2042 symbol.unmark();
2043 try wasm.discarded.putNoClobber(
2044 gpa,
2045 sym_loc,
2046 .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index },
2047 );
2048 try removed_duplicates.append(sym_loc);
2049 continue;
2052 // we favor keeping the global over a local.
2053 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
2054 const original_sym = original_loc.getSymbol(wasm);
2055 if (original_sym.isLocal() and symbol.isGlobal()) {
2056 original_sym.unmark();
2057 try wasm.discarded.put(gpa, original_loc, sym_loc);
2058 try removed_duplicates.append(original_loc);
2059 } else {
2060 symbol.unmark();
2061 try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc);
2062 try removed_duplicates.append(sym_loc);
2063 continue;
2064 }
20502065 }
20512066 gop.value_ptr.* = .{ .func = obj_file.function(sym_loc.index), .sym_index = sym_loc.index };
20522067 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
......@@ -2073,6 +2088,7 @@ fn mergeSections(wasm: *Wasm) !void {
20732088 // For any removed duplicates, remove them from the resolved symbols list
20742089 for (removed_duplicates.items) |sym_loc| {
20752090 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2091 gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)});
20762092 }
20772093
20782094 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
......@@ -2119,12 +2135,7 @@ fn mergeTypes(wasm: *Wasm) !void {
21192135 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
21202136}
21212137
2122fn setupExports(wasm: *Wasm) !void {
2123 const comp = wasm.base.comp;
2124 const gpa = comp.gpa;
2125 if (comp.config.output_mode == .Obj) return;
2126 log.debug("Building exports from symbols", .{});
2127
2138fn checkExportNames(wasm: *Wasm) !void {
21282139 const force_exp_names = wasm.export_symbol_names;
21292140 if (force_exp_names.len > 0) {
21302141 var failed_exports = false;
......@@ -2144,6 +2155,13 @@ fn setupExports(wasm: *Wasm) !void {
21442155 return error.FlushFailure;
21452156 }
21462157 }
2158}
2159
2160fn setupExports(wasm: *Wasm) !void {
2161 const comp = wasm.base.comp;
2162 const gpa = comp.gpa;
2163 if (comp.config.output_mode == .Obj) return;
2164 log.debug("Building exports from symbols", .{});
21472165
21482166 for (wasm.resolved_symbols.keys()) |sym_loc| {
21492167 const symbol = sym_loc.getSymbol(wasm);
......@@ -2272,6 +2290,7 @@ fn setupMemory(wasm: *Wasm) !void {
22722290 memory_ptr = mem.alignForward(u64, memory_ptr, 4);
22732291 const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
22742292 const sym = loc.getSymbol(wasm);
2293 sym.mark();
22752294 sym.virtual_address = @as(u32, @intCast(memory_ptr));
22762295 memory_ptr += 4;
22772296 }
......@@ -2561,6 +2580,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
25612580 if (comp.link_errors.items.len > 0) return error.FlushFailure;
25622581 try wasm.resolveLazySymbols();
25632582 try wasm.checkUndefinedSymbols();
2583 try wasm.checkExportNames();
25642584
25652585 try wasm.setupInitFunctions();
25662586 if (comp.link_errors.items.len > 0) return error.FlushFailure;
......@@ -4044,6 +4064,7 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
40444064 return;
40454065 }
40464066 symbol.mark();
4067 gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)});
40474068 if (symbol.isUndefined()) {
40484069 // undefined symbols do not have an associated `Atom` and therefore also
40494070 // do not contain relocations.
src/link/Wasm/Object.zig+1-1
......@@ -15,7 +15,7 @@ const Allocator = std.mem.Allocator;
1515const leb = std.leb;
1616const meta = std.meta;
1717
18const log = std.log.scoped(.link);
18const log = std.log.scoped(.object);
1919
2020/// Index into the list of relocatable object files within the linker driver.
2121index: File.Index = .null,
src/link/Wasm/ZigObject.zig+6-4
......@@ -315,7 +315,7 @@ fn finishUpdateDecl(
315315 const atom_index = decl_info.atom;
316316 const atom = wasm_file.getAtomPtr(atom_index);
317317 const sym = zig_object.symbol(atom.sym_index);
318 const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
318 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
319319 sym.name = try zig_object.string_table.insert(gpa, full_name);
320320 try atom.code.appendSlice(gpa, code);
321321 atom.size = @intCast(code.len);
......@@ -401,7 +401,7 @@ pub fn getOrCreateAtomForDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_ind
401401 gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) };
402402 const mod = wasm_file.base.comp.module.?;
403403 const decl = mod.declPtr(decl_index);
404 const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
404 const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
405405 const sym = zig_object.symbol(sym_index);
406406 sym.name = try zig_object.string_table.insert(gpa, full_name);
407407 }
......@@ -455,7 +455,7 @@ pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, tv: TypedValu
455455 const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
456456 const parent_atom = wasm_file.getAtom(parent_atom_index);
457457 const local_index = parent_atom.locals.items.len;
458 const fqn = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
458 const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
459459 const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{
460460 fqn, local_index,
461461 });
......@@ -838,6 +838,7 @@ pub fn updateExports(
838838 const atom = wasm_file.getAtom(atom_index);
839839 const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*;
840840 const gpa = mod.gpa;
841 log.debug("Updating exports for decl '{s}'", .{mod.intern_pool.stringToSlice(decl.name)});
841842
842843 for (exports) |exp| {
843844 if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| {
......@@ -888,6 +889,7 @@ pub fn updateExports(
888889 if (exp.opts.visibility == .hidden) {
889890 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
890891 }
892 log.debug(" with name '{s}' - {}", .{ export_string, sym });
891893 try zig_object.global_syms.put(gpa, export_name, sym_index);
892894 try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index);
893895 }
......@@ -1061,7 +1063,7 @@ pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, inde
10611063pub fn updateDeclLineNumber(zig_object: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void {
10621064 if (zig_object.dwarf) |*dw| {
10631065 const decl = mod.declPtr(decl_index);
1064 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
1066 const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod));
10651067
10661068 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
10671069 try dw.updateDeclLineNumber(mod, decl_index);
test/link.zig+12-10
......@@ -35,11 +35,10 @@ pub const cases = [_]Case{
3535 },
3636
3737 // WASM Cases
38 // https://github.com/ziglang/zig/issues/16938
39 //.{
40 // .build_root = "test/link/wasm/archive",
41 // .import = @import("link/wasm/archive/build.zig"),
42 //},
38 .{
39 .build_root = "test/link/wasm/archive",
40 .import = @import("link/wasm/archive/build.zig"),
41 },
4342 .{
4443 .build_root = "test/link/wasm/basic-features",
4544 .import = @import("link/wasm/basic-features/build.zig"),
......@@ -52,11 +51,10 @@ pub const cases = [_]Case{
5251 .build_root = "test/link/wasm/export",
5352 .import = @import("link/wasm/export/build.zig"),
5453 },
55 // https://github.com/ziglang/zig/issues/16937
56 //.{
57 // .build_root = "test/link/wasm/export-data",
58 // .import = @import("link/wasm/export-data/build.zig"),
59 //},
54 .{
55 .build_root = "test/link/wasm/export-data",
56 .import = @import("link/wasm/export-data/build.zig"),
57 },
6058 .{
6159 .build_root = "test/link/wasm/extern",
6260 .import = @import("link/wasm/extern/build.zig"),
......@@ -81,6 +79,10 @@ pub const cases = [_]Case{
8179 .build_root = "test/link/wasm/segments",
8280 .import = @import("link/wasm/segments/build.zig"),
8381 },
82 .{
83 .build_root = "test/link/wasm/shared-memory",
84 .import = @import("link/wasm/shared-memory/build.zig"),
85 },
8486 .{
8587 .build_root = "test/link/wasm/stack_pointer",
8688 .import = @import("link/wasm/stack_pointer/build.zig"),
test/link/wasm/archive/build.zig+2-1
......@@ -19,12 +19,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
1919 .name = "main",
2020 .root_source_file = .{ .path = "main.zig" },
2121 .optimize = optimize,
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
22 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
2323 .strip = false,
2424 });
2525 lib.entry = .disabled;
2626 lib.use_llvm = false;
2727 lib.use_lld = false;
28 lib.root_module.export_symbol_names = &.{"foo"};
2829
2930 const check = lib.checkObject();
3031 check.checkInHeaders();
test/link/wasm/export-data/build.zig+1-1
......@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
1313 .name = "lib",
1414 .root_source_file = .{ .path = "lib.zig" },
1515 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
16 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
16 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
1717 });
1818 lib.entry = .disabled;
1919 lib.use_lld = false;
test/link/wasm/shared-memory/build.zig+40-45
......@@ -11,37 +11,39 @@ pub fn build(b: *std.Build) void {
1111}
1212
1313fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
14 const lib = b.addExecutable(.{
14 const exe = b.addExecutable(.{
1515 .name = "lib",
1616 .root_source_file = .{ .path = "lib.zig" },
17 .target = .{
17 .target = b.resolveTargetQuery(.{
1818 .cpu_arch = .wasm32,
1919 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
2020 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
2121 .os_tag = .freestanding,
22 },
22 }),
2323 .optimize = optimize_mode,
2424 .strip = false,
2525 .single_threaded = false,
2626 });
27 lib.entry = .disabled;
28 lib.use_lld = false;
29 lib.import_memory = true;
30 lib.export_memory = true;
31 lib.shared_memory = true;
32 lib.max_memory = 67108864;
33 lib.root_module.export_symbol_names = &.{"foo"};
27 exe.entry = .disabled;
28 exe.use_lld = false;
29 exe.import_memory = true;
30 exe.export_memory = true;
31 exe.shared_memory = true;
32 exe.max_memory = 67108864;
33 exe.root_module.export_symbol_names = &.{"foo"};
3434
35 const check_lib = lib.checkObject();
35 const check_exe = exe.checkObject();
3636
37 check_lib.checkStart("Section import");
38 check_lib.checkNext("entries 1");
39 check_lib.checkNext("module env");
40 check_lib.checkNext("name memory"); // ensure we are importing memory
37 check_exe.checkInHeaders();
38 check_exe.checkExact("Section import");
39 check_exe.checkExact("entries 1");
40 check_exe.checkExact("module env");
41 check_exe.checkExact("name memory"); // ensure we are importing memory
4142
42 check_lib.checkStart("Section export");
43 check_lib.checkNext("entries 2");
44 check_lib.checkNext("name memory"); // ensure we also export memory again
43 check_exe.checkInHeaders();
44 check_exe.checkExact("Section export");
45 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name memory"); // ensure we also export memory again
4547
4648 // This section *must* be emit as the start function is set to the index
4749 // of __wasm_init_memory
......@@ -49,49 +51,42 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
4951 // This means we won't have __wasm_init_memory in such case, and therefore
5052 // should also not have a section "start"
5153 if (optimize_mode == .Debug) {
52 check_lib.checkStart("Section start");
54 check_exe.checkInHeaders();
55 check_exe.checkExact("Section start");
5356 }
5457
5558 // This section is only and *must* be emit when shared-memory is enabled
5659 // release modes will have the TLS segment optimized out in our test-case.
5760 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");
59 check_lib.checkNext("count 3");
61 check_exe.checkInHeaders();
62 check_exe.checkExact("Section data_count");
63 check_exe.checkExact("count 1");
6064 }
6165
62 check_lib.checkStart("Section custom");
63 check_lib.checkNext("name name");
64 check_lib.checkNext("type function");
66 check_exe.checkInHeaders();
67 check_exe.checkExact("Section custom");
68 check_exe.checkExact("name name");
69 check_exe.checkExact("type function");
6570 if (optimize_mode == .Debug) {
66 check_lib.checkNext("name __wasm_init_memory");
71 check_exe.checkExact("name __wasm_init_memory");
6772 }
68 check_lib.checkNext("name __wasm_init_tls");
69 check_lib.checkNext("type global");
73 check_exe.checkExact("name __wasm_init_tls");
74 check_exe.checkExact("type global");
7075
7176 // In debug mode the symbol __tls_base is resolved to an undefined symbol
7277 // from the object file, hence its placement differs than in release modes
7378 // where the entire tls segment is optimized away, and tls_base will have
7479 // its original position.
75 if (optimize_mode == .Debug) {
76 check_lib.checkNext("name __tls_size");
77 check_lib.checkNext("name __tls_align");
78 check_lib.checkNext("name __tls_base");
79 } else {
80 check_lib.checkNext("name __tls_base");
81 check_lib.checkNext("name __tls_size");
82 check_lib.checkNext("name __tls_align");
83 }
80 check_exe.checkExact("name __tls_base");
81 check_exe.checkExact("name __tls_size");
82 check_exe.checkExact("name __tls_align");
8483
85 check_lib.checkNext("type data_segment");
84 check_exe.checkExact("type data_segment");
8685 if (optimize_mode == .Debug) {
87 check_lib.checkNext("names 3");
88 check_lib.checkNext("index 0");
89 check_lib.checkNext("name .rodata");
90 check_lib.checkNext("index 1");
91 check_lib.checkNext("name .bss");
92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
86 check_exe.checkExact("names 1");
87 check_exe.checkExact("index 0");
88 check_exe.checkExact("name .tdata");
9489 }
9590
96 test_step.dependOn(&check_lib.step);
91 test_step.dependOn(&check_exe.step);
9792}
test/link/wasm/type/build.zig+17-16
......@@ -13,31 +13,32 @@ pub fn build(b: *std.Build) void {
1313}
1414
1515fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addExecutable(.{
16 const exe = b.addExecutable(.{
1717 .name = "lib",
1818 .root_source_file = .{ .path = "lib.zig" },
1919 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
2020 .optimize = optimize,
2121 .strip = false,
2222 });
23 lib.entry = .disabled;
24 lib.use_llvm = false;
25 lib.use_lld = false;
26 b.installArtifact(lib);
23 exe.entry = .disabled;
24 exe.use_llvm = false;
25 exe.use_lld = false;
26 exe.root_module.export_symbol_names = &.{"foo"};
27 b.installArtifact(exe);
2728
28 const check_lib = lib.checkObject();
29 check_lib.checkInHeaders();
30 check_lib.checkExact("Section type");
29 const check_exe = exe.checkObject();
30 check_exe.checkInHeaders();
31 check_exe.checkExact("Section type");
3132 // only 2 entries, although we have more functions.
3233 // This is to test functions with the same function signature
3334 // have their types deduplicated.
34 check_lib.checkExact("entries 2");
35 check_lib.checkExact("params 1");
36 check_lib.checkExact("type i32");
37 check_lib.checkExact("returns 1");
38 check_lib.checkExact("type i64");
39 check_lib.checkExact("params 0");
40 check_lib.checkExact("returns 0");
35 check_exe.checkExact("entries 2");
36 check_exe.checkExact("params 1");
37 check_exe.checkExact("type i32");
38 check_exe.checkExact("returns 1");
39 check_exe.checkExact("type i64");
40 check_exe.checkExact("params 0");
41 check_exe.checkExact("returns 0");
4142
42 test_step.dependOn(&check_lib.step);
43 test_step.dependOn(&check_exe.step);
4344}