authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-25 18:03:23+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-28 15:47:07+01:00
log4be3cd2754f4af4f18b969dd60d954c53a281a83
treee7d11b7bbf7bf3118008a67c2ce81e72caeb0a29
parent8447d4fb1f5b628b3c0c9f2b1254d8ddc7801ab1
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: support gc for wasm backend code

When using the Wasm backend, we will now also perform garbage collection there, to ensure unreferenced symbols do not get parsed nor emit into the final binary.

1 files changed, 35 insertions(+), 21 deletions(-)

src/link/Wasm.zig+35-21
......@@ -1333,6 +1333,10 @@ pub fn deinit(wasm: *Wasm) void {
13331333 atom.deinit(gpa);
13341334 }
13351335 }
1336 for (wasm.synthetic_functions.items) |atom_index| {
1337 const atom = wasm.getAtomPtr(atom_index);
1338 atom.deinit(gpa);
1339 }
13361340
13371341 wasm.decls.deinit(gpa);
13381342 wasm.anon_decls.deinit(gpa);
......@@ -1364,10 +1368,6 @@ pub fn deinit(wasm: *Wasm) void {
13641368 wasm.exports.deinit(gpa);
13651369
13661370 wasm.string_table.deinit(gpa);
1367 for (wasm.synthetic_functions.items) |atom_index| {
1368 const atom = wasm.getAtomPtr(atom_index);
1369 atom.deinit(gpa);
1370 }
13711371 wasm.synthetic_functions.deinit(gpa);
13721372
13731373 if (wasm.dwarf) |*dwarf| {
......@@ -2134,9 +2134,13 @@ const Kind = union(enum) {
21342134fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
21352135 const atom = wasm.getAtomPtr(atom_index);
21362136 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
2137 if (symbol.isDead()) {
2138 // Prevent unreferenced symbols from being parsed.
2139 return;
2140 }
21372141 const final_index: u32 = switch (kind) {
21382142 .function => result: {
2139 const index = @as(u32, @intCast(wasm.functions.count() + wasm.imported_functions_count));
2143 const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
21402144 const type_index = wasm.atom_types.get(atom_index).?;
21412145 try wasm.functions.putNoClobber(
21422146 wasm.base.allocator,
......@@ -2147,7 +2151,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
21472151 symbol.index = index;
21482152
21492153 if (wasm.code_section_index == null) {
2150 wasm.code_section_index = @as(u32, @intCast(wasm.segments.items.len));
2154 wasm.code_section_index = @intCast(wasm.segments.items.len);
21512155 try wasm.segments.append(wasm.base.allocator, .{
21522156 .alignment = atom.alignment,
21532157 .size = atom.size,
......@@ -2185,12 +2189,12 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
21852189 const index = gop.value_ptr.*;
21862190 wasm.segments.items[index].size += atom.size;
21872191
2188 symbol.index = @as(u32, @intCast(wasm.segment_info.getIndex(index).?));
2192 symbol.index = @intCast(wasm.segment_info.getIndex(index).?);
21892193 // segment info already exists, so free its memory
21902194 wasm.base.allocator.free(segment_name);
21912195 break :result index;
21922196 } else {
2193 const index = @as(u32, @intCast(wasm.segments.items.len));
2197 const index: u32 = @intCast(wasm.segments.items.len);
21942198 var flags: u32 = 0;
21952199 if (wasm.base.options.shared_memory) {
21962200 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
......@@ -2203,7 +2207,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
22032207 });
22042208 gop.value_ptr.* = index;
22052209
2206 const info_index = @as(u32, @intCast(wasm.segment_info.count()));
2210 const info_index: u32 = @intCast(wasm.segment_info.count());
22072211 try wasm.segment_info.put(wasm.base.allocator, index, segment_info);
22082212 symbol.index = info_index;
22092213 break :result index;
......@@ -2318,8 +2322,10 @@ fn allocateAtoms(wasm: *Wasm) !void {
23182322fn allocateVirtualAddresses(wasm: *Wasm) void {
23192323 for (wasm.resolved_symbols.keys()) |loc| {
23202324 const symbol = loc.getSymbol(wasm);
2321 if (symbol.tag != .data) {
2322 continue; // only data symbols have virtual addresses
2325 if (symbol.tag != .data or symbol.isDead()) {
2326 // Only data symbols have virtual addresses.
2327 // Dead symbols do not get allocated, so we don't need to set their virtual address either.
2328 continue;
23232329 }
23242330 const atom_index = wasm.symbol_atom.get(loc) orelse {
23252331 // synthetic symbol that does not contain an atom
......@@ -2681,10 +2687,10 @@ fn setupImports(wasm: *Wasm) !void {
26812687 }
26822688
26832689 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2684 if (symbol_loc.file == null) {
2690 const file_index = symbol_loc.file orelse {
26852691 // imports generated by Zig code are already in the `import` section
26862692 continue;
2687 }
2693 };
26882694
26892695 const symbol = symbol_loc.getSymbol(wasm);
26902696 if (symbol.isDead() or
......@@ -2695,7 +2701,7 @@ fn setupImports(wasm: *Wasm) !void {
26952701 }
26962702
26972703 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
2698 const object = wasm.objects.items[symbol_loc.file.?];
2704 const object = wasm.objects.items[file_index];
26992705 const import = object.findImport(symbol.tag.externalType(), symbol.index);
27002706
27012707 // We copy the import to a new import to ensure the names contain references
......@@ -3092,6 +3098,11 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
30923098 .offset = 0,
30933099 .flags = flags,
30943100 });
3101 try wasm.segment_info.putNoClobber(wasm.base.allocator, index, .{
3102 .name = try wasm.base.allocator.dupe(u8, segment_info.name),
3103 .alignment = segment_info.alignment,
3104 .flags = segment_info.flags,
3105 });
30953106 return index;
30963107 } else return result.value_ptr.*;
30973108 },
......@@ -3198,6 +3209,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
31983209 .virtual_address = undefined,
31993210 };
32003211 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3212 symbol.mark();
32013213
32023214 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
32033215
......@@ -3230,6 +3242,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
32303242 .virtual_address = undefined,
32313243 };
32323244 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3245 names_symbol.mark();
32333246
32343247 log.debug("Populating error names", .{});
32353248
......@@ -3606,9 +3619,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
36063619 // So we can rebuild the binary file on each incremental update
36073620 defer wasm.resetState();
36083621 try wasm.setupInitFunctions();
3609 try wasm.setupErrorsLen();
36103622 try wasm.setupStart();
36113623 try wasm.markReferences();
3624 try wasm.setupErrorsLen();
36123625 try wasm.setupImports();
36133626 if (wasm.base.options.module) |mod| {
36143627 var decl_it = wasm.decls.iterator();
......@@ -5152,14 +5165,15 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
51525165 return;
51535166 }
51545167
5155 const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately
5156 const object = &wasm.objects.items[file];
5157 const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm);
5168 const atom_index = if (loc.file) |file_index| idx: {
5169 const object = &wasm.objects.items[file_index];
5170 const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);
5171 break :idx atom_index;
5172 } else wasm.symbol_atom.get(loc) orelse return;
51585173
51595174 const atom = wasm.getAtom(atom_index);
5160 const relocations: []const types.Relocation = atom.relocs.items;
5161 for (relocations) |reloc| {
5162 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file };
5175 for (atom.relocs.items) |reloc| {
5176 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
51635177 try wasm.mark(target_loc.finalLoc(wasm));
51645178 }
51655179}