Commit 4be3cd2754

Luuk de Gram <luuk@degram.dev>
2023-11-25 18:03:23
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 parent 8447d4f
Changed files (1)
src
src/link/Wasm.zig
@@ -1333,6 +1333,10 @@ pub fn deinit(wasm: *Wasm) void {
             atom.deinit(gpa);
         }
     }
+    for (wasm.synthetic_functions.items) |atom_index| {
+        const atom = wasm.getAtomPtr(atom_index);
+        atom.deinit(gpa);
+    }
 
     wasm.decls.deinit(gpa);
     wasm.anon_decls.deinit(gpa);
@@ -1364,10 +1368,6 @@ pub fn deinit(wasm: *Wasm) void {
     wasm.exports.deinit(gpa);
 
     wasm.string_table.deinit(gpa);
-    for (wasm.synthetic_functions.items) |atom_index| {
-        const atom = wasm.getAtomPtr(atom_index);
-        atom.deinit(gpa);
-    }
     wasm.synthetic_functions.deinit(gpa);
 
     if (wasm.dwarf) |*dwarf| {
@@ -2134,9 +2134,13 @@ const Kind = union(enum) {
 fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
     const atom = wasm.getAtomPtr(atom_index);
     const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
+    if (symbol.isDead()) {
+        // Prevent unreferenced symbols from being parsed.
+        return;
+    }
     const final_index: u32 = switch (kind) {
         .function => result: {
-            const index = @as(u32, @intCast(wasm.functions.count() + wasm.imported_functions_count));
+            const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
             const type_index = wasm.atom_types.get(atom_index).?;
             try wasm.functions.putNoClobber(
                 wasm.base.allocator,
@@ -2147,7 +2151,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
             symbol.index = index;
 
             if (wasm.code_section_index == null) {
-                wasm.code_section_index = @as(u32, @intCast(wasm.segments.items.len));
+                wasm.code_section_index = @intCast(wasm.segments.items.len);
                 try wasm.segments.append(wasm.base.allocator, .{
                     .alignment = atom.alignment,
                     .size = atom.size,
@@ -2185,12 +2189,12 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
                 const index = gop.value_ptr.*;
                 wasm.segments.items[index].size += atom.size;
 
-                symbol.index = @as(u32, @intCast(wasm.segment_info.getIndex(index).?));
+                symbol.index = @intCast(wasm.segment_info.getIndex(index).?);
                 // segment info already exists, so free its memory
                 wasm.base.allocator.free(segment_name);
                 break :result index;
             } else {
-                const index = @as(u32, @intCast(wasm.segments.items.len));
+                const index: u32 = @intCast(wasm.segments.items.len);
                 var flags: u32 = 0;
                 if (wasm.base.options.shared_memory) {
                     flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
@@ -2203,7 +2207,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
                 });
                 gop.value_ptr.* = index;
 
-                const info_index = @as(u32, @intCast(wasm.segment_info.count()));
+                const info_index: u32 = @intCast(wasm.segment_info.count());
                 try wasm.segment_info.put(wasm.base.allocator, index, segment_info);
                 symbol.index = info_index;
                 break :result index;
@@ -2318,8 +2322,10 @@ fn allocateAtoms(wasm: *Wasm) !void {
 fn allocateVirtualAddresses(wasm: *Wasm) void {
     for (wasm.resolved_symbols.keys()) |loc| {
         const symbol = loc.getSymbol(wasm);
-        if (symbol.tag != .data) {
-            continue; // only data symbols have virtual addresses
+        if (symbol.tag != .data or symbol.isDead()) {
+            // Only data symbols have virtual addresses.
+            // Dead symbols do not get allocated, so we don't need to set their virtual address either.
+            continue;
         }
         const atom_index = wasm.symbol_atom.get(loc) orelse {
             // synthetic symbol that does not contain an atom
@@ -2681,10 +2687,10 @@ fn setupImports(wasm: *Wasm) !void {
     }
 
     for (wasm.resolved_symbols.keys()) |symbol_loc| {
-        if (symbol_loc.file == null) {
+        const file_index = symbol_loc.file orelse {
             // imports generated by Zig code are already in the `import` section
             continue;
-        }
+        };
 
         const symbol = symbol_loc.getSymbol(wasm);
         if (symbol.isDead() or
@@ -2695,7 +2701,7 @@ fn setupImports(wasm: *Wasm) !void {
         }
 
         log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
-        const object = wasm.objects.items[symbol_loc.file.?];
+        const object = wasm.objects.items[file_index];
         const import = object.findImport(symbol.tag.externalType(), symbol.index);
 
         // 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
                     .offset = 0,
                     .flags = flags,
                 });
+                try wasm.segment_info.putNoClobber(wasm.base.allocator, index, .{
+                    .name = try wasm.base.allocator.dupe(u8, segment_info.name),
+                    .alignment = segment_info.alignment,
+                    .flags = segment_info.flags,
+                });
                 return index;
             } else return result.value_ptr.*;
         },
@@ -3198,6 +3209,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
         .virtual_address = undefined,
     };
     symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
+    symbol.mark();
 
     try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
 
@@ -3230,6 +3242,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
         .virtual_address = undefined,
     };
     names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
+    names_symbol.mark();
 
     log.debug("Populating error names", .{});
 
@@ -3606,9 +3619,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
     // So we can rebuild the binary file on each incremental update
     defer wasm.resetState();
     try wasm.setupInitFunctions();
-    try wasm.setupErrorsLen();
     try wasm.setupStart();
     try wasm.markReferences();
+    try wasm.setupErrorsLen();
     try wasm.setupImports();
     if (wasm.base.options.module) |mod| {
         var decl_it = wasm.decls.iterator();
@@ -5152,14 +5165,15 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
         return;
     }
 
-    const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately
-    const object = &wasm.objects.items[file];
-    const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm);
+    const atom_index = if (loc.file) |file_index| idx: {
+        const object = &wasm.objects.items[file_index];
+        const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);
+        break :idx atom_index;
+    } else wasm.symbol_atom.get(loc) orelse return;
 
     const atom = wasm.getAtom(atom_index);
-    const relocations: []const types.Relocation = atom.relocs.items;
-    for (relocations) |reloc| {
-        const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file };
+    for (atom.relocs.items) |reloc| {
+        const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
         try wasm.mark(target_loc.finalLoc(wasm));
     }
 }