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 {...@@ -1333,6 +1333,10 @@ pub fn deinit(wasm: *Wasm) void {
1333 atom.deinit(gpa);1333 atom.deinit(gpa);
1334 }1334 }
1335 }1335 }
1336 for (wasm.synthetic_functions.items) |atom_index| {
1337 const atom = wasm.getAtomPtr(atom_index);
1338 atom.deinit(gpa);
1339 }
13361340
1337 wasm.decls.deinit(gpa);1341 wasm.decls.deinit(gpa);
1338 wasm.anon_decls.deinit(gpa);1342 wasm.anon_decls.deinit(gpa);
...@@ -1364,10 +1368,6 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1364,10 +1368,6 @@ pub fn deinit(wasm: *Wasm) void {
1364 wasm.exports.deinit(gpa);1368 wasm.exports.deinit(gpa);
13651369
1366 wasm.string_table.deinit(gpa);1370 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 }
1371 wasm.synthetic_functions.deinit(gpa);1371 wasm.synthetic_functions.deinit(gpa);
13721372
1373 if (wasm.dwarf) |*dwarf| {1373 if (wasm.dwarf) |*dwarf| {
...@@ -2134,9 +2134,13 @@ const Kind = union(enum) {...@@ -2134,9 +2134,13 @@ const Kind = union(enum) {
2134fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {2134fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2135 const atom = wasm.getAtomPtr(atom_index);2135 const atom = wasm.getAtomPtr(atom_index);
2136 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);2136 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 }
2137 const final_index: u32 = switch (kind) {2141 const final_index: u32 = switch (kind) {
2138 .function => result: {2142 .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);
2140 const type_index = wasm.atom_types.get(atom_index).?;2144 const type_index = wasm.atom_types.get(atom_index).?;
2141 try wasm.functions.putNoClobber(2145 try wasm.functions.putNoClobber(
2142 wasm.base.allocator,2146 wasm.base.allocator,
...@@ -2147,7 +2151,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -2147,7 +2151,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2147 symbol.index = index;2151 symbol.index = index;
21482152
2149 if (wasm.code_section_index == null) {2153 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);
2151 try wasm.segments.append(wasm.base.allocator, .{2155 try wasm.segments.append(wasm.base.allocator, .{
2152 .alignment = atom.alignment,2156 .alignment = atom.alignment,
2153 .size = atom.size,2157 .size = atom.size,
...@@ -2185,12 +2189,12 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -2185,12 +2189,12 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2185 const index = gop.value_ptr.*;2189 const index = gop.value_ptr.*;
2186 wasm.segments.items[index].size += atom.size;2190 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).?);
2189 // segment info already exists, so free its memory2193 // segment info already exists, so free its memory
2190 wasm.base.allocator.free(segment_name);2194 wasm.base.allocator.free(segment_name);
2191 break :result index;2195 break :result index;
2192 } else {2196 } else {
2193 const index = @as(u32, @intCast(wasm.segments.items.len));2197 const index: u32 = @intCast(wasm.segments.items.len);
2194 var flags: u32 = 0;2198 var flags: u32 = 0;
2195 if (wasm.base.options.shared_memory) {2199 if (wasm.base.options.shared_memory) {
2196 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);2200 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
...@@ -2203,7 +2207,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -2203,7 +2207,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2203 });2207 });
2204 gop.value_ptr.* = index;2208 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());
2207 try wasm.segment_info.put(wasm.base.allocator, index, segment_info);2211 try wasm.segment_info.put(wasm.base.allocator, index, segment_info);
2208 symbol.index = info_index;2212 symbol.index = info_index;
2209 break :result index;2213 break :result index;
...@@ -2318,8 +2322,10 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2318,8 +2322,10 @@ fn allocateAtoms(wasm: *Wasm) !void {
2318fn allocateVirtualAddresses(wasm: *Wasm) void {2322fn allocateVirtualAddresses(wasm: *Wasm) void {
2319 for (wasm.resolved_symbols.keys()) |loc| {2323 for (wasm.resolved_symbols.keys()) |loc| {
2320 const symbol = loc.getSymbol(wasm);2324 const symbol = loc.getSymbol(wasm);
2321 if (symbol.tag != .data) {2325 if (symbol.tag != .data or symbol.isDead()) {
2322 continue; // only data symbols have virtual addresses2326 // 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;
2323 }2329 }
2324 const atom_index = wasm.symbol_atom.get(loc) orelse {2330 const atom_index = wasm.symbol_atom.get(loc) orelse {
2325 // synthetic symbol that does not contain an atom2331 // synthetic symbol that does not contain an atom
...@@ -2681,10 +2687,10 @@ fn setupImports(wasm: *Wasm) !void {...@@ -2681,10 +2687,10 @@ fn setupImports(wasm: *Wasm) !void {
2681 }2687 }
26822688
2683 for (wasm.resolved_symbols.keys()) |symbol_loc| {2689 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2684 if (symbol_loc.file == null) {2690 const file_index = symbol_loc.file orelse {
2685 // imports generated by Zig code are already in the `import` section2691 // imports generated by Zig code are already in the `import` section
2686 continue;2692 continue;
2687 }2693 };
26882694
2689 const symbol = symbol_loc.getSymbol(wasm);2695 const symbol = symbol_loc.getSymbol(wasm);
2690 if (symbol.isDead() or2696 if (symbol.isDead() or
...@@ -2695,7 +2701,7 @@ fn setupImports(wasm: *Wasm) !void {...@@ -2695,7 +2701,7 @@ fn setupImports(wasm: *Wasm) !void {
2695 }2701 }
26962702
2697 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});2703 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];
2699 const import = object.findImport(symbol.tag.externalType(), symbol.index);2705 const import = object.findImport(symbol.tag.externalType(), symbol.index);
27002706
2701 // We copy the import to a new import to ensure the names contain references2707 // 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...@@ -3092,6 +3098,11 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
3092 .offset = 0,3098 .offset = 0,
3093 .flags = flags,3099 .flags = flags,
3094 });3100 });
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 });
3095 return index;3106 return index;
3096 } else return result.value_ptr.*;3107 } else return result.value_ptr.*;
3097 },3108 },
...@@ -3198,6 +3209,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -3198,6 +3209,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
3198 .virtual_address = undefined,3209 .virtual_address = undefined,
3199 };3210 };
3200 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);3211 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3212 symbol.mark();
32013213
3202 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});3214 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
32033215
...@@ -3230,6 +3242,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -3230,6 +3242,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
3230 .virtual_address = undefined,3242 .virtual_address = undefined,
3231 };3243 };
3232 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);3244 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
3245 names_symbol.mark();
32333246
3234 log.debug("Populating error names", .{});3247 log.debug("Populating error names", .{});
32353248
...@@ -3606,9 +3619,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -3606,9 +3619,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
3606 // So we can rebuild the binary file on each incremental update3619 // So we can rebuild the binary file on each incremental update
3607 defer wasm.resetState();3620 defer wasm.resetState();
3608 try wasm.setupInitFunctions();3621 try wasm.setupInitFunctions();
3609 try wasm.setupErrorsLen();
3610 try wasm.setupStart();3622 try wasm.setupStart();
3611 try wasm.markReferences();3623 try wasm.markReferences();
3624 try wasm.setupErrorsLen();
3612 try wasm.setupImports();3625 try wasm.setupImports();
3613 if (wasm.base.options.module) |mod| {3626 if (wasm.base.options.module) |mod| {
3614 var decl_it = wasm.decls.iterator();3627 var decl_it = wasm.decls.iterator();
...@@ -5152,14 +5165,15 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {...@@ -5152,14 +5165,15 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
5152 return;5165 return;
5153 }5166 }
51545167
5155 const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately5168 const atom_index = if (loc.file) |file_index| idx: {
5156 const object = &wasm.objects.items[file];5169 const object = &wasm.objects.items[file_index];
5157 const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm);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
5159 const atom = wasm.getAtom(atom_index);5174 const atom = wasm.getAtom(atom_index);
5160 const relocations: []const types.Relocation = atom.relocs.items;5175 for (atom.relocs.items) |reloc| {
5161 for (relocations) |reloc| {5176 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
5162 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file };
5163 try wasm.mark(target_loc.finalLoc(wasm));5177 try wasm.mark(target_loc.finalLoc(wasm));
5164 }5178 }
5165}5179}