Commit c153f94c89

Luuk de Gram <luuk@degram.dev>
2024-01-29 06:52:50
wasm: ensure unique function indexes
We cannot keep function indexes as maxInt(u32) due to functions being dedupliated when they point to the same function. For this reason we now use a regular arraylist which will have new functions appended to, and when deleted, its index is appended to the free list, allowing us to re-use slots in the function list.
1 parent fde8c2f
Changed files (3)
src/link/Wasm/file.zig
@@ -91,7 +91,10 @@ pub const File = union(enum) {
 
     pub fn function(file: File, sym_index: u32) std.wasm.Func {
         switch (file) {
-            .zig_object => |obj| return obj.functions.get(sym_index).?,
+            .zig_object => |obj| {
+                const sym = obj.symbols.items[sym_index];
+                return obj.functions.items[sym.index];
+            },
             .object => |obj| {
                 const sym = obj.symtable[sym_index];
                 return obj.functions[sym.index - obj.imported_functions_count];
src/link/Wasm/ZigObject.zig
@@ -13,7 +13,9 @@ decls_map: std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclInfo) = .{},
 func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
 /// List of `std.wasm.Func`. Each entry contains the function signature,
 /// rather than the actual body.
-functions: std.AutoHashMapUnmanaged(u32, std.wasm.Func) = .{},
+functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
+/// List of indexes pointing to an entry within the `functions` list which has been removed.
+functions_free_list: std.ArrayListUnmanaged(u32) = .{},
 /// Map of symbol locations, represented by its `types.Import`.
 imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{},
 /// List of WebAssembly globals.
@@ -320,11 +322,7 @@ fn finishUpdateDecl(
 
     switch (decl.ty.zigTypeTag(mod)) {
         .Fn => {
-            try zig_object.functions.put(
-                gpa,
-                atom.sym_index,
-                .{ .type_index = zig_object.atom_types.get(atom_index).? },
-            );
+            sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
             sym.tag = .function;
         },
         else => {
@@ -689,6 +687,9 @@ pub fn addOrUpdateImport(
             };
             zig_object.imported_functions_count += 1;
         }
+        sym.tag = .function;
+    } else {
+        sym.tag = .data;
     }
 }
 
@@ -821,10 +822,6 @@ pub fn deleteDeclExport(
         std.debug.assert(zig_object.global_syms.remove(sym.name));
         std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index }));
         zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {};
-
-        if (sym.tag == .function) {
-            std.debug.assert(zig_object.functions.remove(sym_index));
-        }
         sym.tag = .dead;
     }
 }
@@ -867,17 +864,6 @@ pub fn updateExports(
         else index: {
             const sym_index = try zig_object.allocateSymbol(gpa);
             try decl_info.appendExport(gpa, sym_index);
-
-            // For functions, we also need to put the alias in the function section.
-            // We simply copy the aliased function.
-            // The final linakge will deduplicate these functions.
-            if (decl.ty.zigTypeTag(mod) == .Fn) {
-                try zig_object.functions.putNoClobber(
-                    gpa,
-                    sym_index,
-                    zig_object.functions.get(atom.sym_index).?,
-                );
-            }
             break :index sym_index;
         };
 
@@ -969,7 +955,7 @@ pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool
     }
     switch (decl.ty.zigTypeTag(mod)) {
         .Fn => {
-            std.debug.assert(zig_object.functions.remove(atom.sym_index));
+            zig_object.functions_free_list.append(gpa, sym.index) catch {};
             std.debug.assert(zig_object.atom_types.remove(atom_index));
         },
         else => {
@@ -1221,7 +1207,7 @@ pub fn createFunction(
     sym.tag = .function;
     sym.name = try zig_object.string_table.insert(gpa, symbol_name);
     const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
-    try zig_object.functions.putNoClobber(gpa, sym_index, .{ .type_index = type_index });
+    sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
 
     const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
     const atom = wasm_file.getAtomPtr(atom_index);
@@ -1232,6 +1218,20 @@ pub fn createFunction(
     return sym_index;
 }
 
+/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
+fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
+    const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
+        idx
+    else idx: {
+        const len: u32 = @intCast(zig_object.functions.items.len);
+        _ = try zig_object.functions.addOne(gpa);
+        break :idx len;
+    };
+    zig_object.functions.items[index] = func;
+
+    return index;
+}
+
 const build_options = @import("build_options");
 const builtin = @import("builtin");
 const codegen = @import("../../codegen.zig");
src/link/Wasm.zig
@@ -819,7 +819,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
         }
 
         if (symbol.tag != existing_sym.tag) {
-            log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) });
+            log.err("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
             log.err("  first definition in '{s}'", .{existing_file_path});
             log.err("  next definition in '{s}'", .{obj_file.path()});
             return error.SymbolMismatchingType;