Commit f9963909a1

Isaac Freund <ifreund@ifreund.xyz>
2020-08-17 23:49:06
stage2/wasm: only free types after func overwrite
Functions which are free'd are not immediately removed from the binary as this would cause a shifting of function indexes. Instead, they hang around until they can be overwritten by a new function. This means that the types associated with these dead functions must also remain until the function is overwritten to avoid a type mismatch.
1 parent 60fb50e
Changed files (1)
src-self-hosted
src-self-hosted/link/Wasm.zig
@@ -33,7 +33,6 @@ pub const base_tag = link.File.Tag.wasm;
 
 pub const FnData = struct {
     funcidx: u32,
-    typeidx: u32,
 };
 
 base: link.File,
@@ -99,7 +98,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
         return error.TODOImplementNonFnDeclsForWasm;
 
     if (decl.fn_link.wasm) |fn_data| {
-        self.types.free(fn_data.typeidx);
         self.funcs.free(fn_data.funcidx);
     }
 
@@ -113,7 +111,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
     try codegen.genCode(&buf, decl);
     const funcidx = try self.funcs.new(typeidx, buf.items);
 
-    decl.fn_link.wasm = .{ .typeidx = typeidx, .funcidx = funcidx };
+    decl.fn_link.wasm = .{ .funcidx = funcidx };
 
     // TODO: we should be more smart and set this only when needed
     self.exports.dirty = true;
@@ -132,7 +130,6 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
     // TODO: remove this assert when non-function Decls are implemented
     assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
     if (decl.fn_link.wasm) |fn_data| {
-        self.types.free(fn_data.typeidx);
         self.funcs.free(fn_data.funcidx);
         decl.fn_link.wasm = null;
     }
@@ -307,21 +304,20 @@ const Funcs = struct {
     /// This section needs special handling to keep the indexes matching with
     /// the codesec, so we cant just use a VecSection.
     funcsec: Section,
-    /// Number of functions listed in the funcsec. Must be kept in sync with
-    /// codesec.entries.items.len.
-    funcs_count: u32,
+    /// The typeidx stored for each function, indexed by funcidx.
+    func_types: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
     codesec: VecSection,
 
     fn init(file: fs.File, funcs_offset: u64, funcs_size: u64, code_offset: u64, code_size: u64) !Funcs {
         return Funcs{
             .funcsec = (try VecSection.init(spec.funcs_id, file, funcs_offset, funcs_size)).section,
-            .funcs_count = 0,
             .codesec = try VecSection.init(spec.code_id, file, code_offset, code_size),
         };
     }
 
     fn deinit(self: *Funcs) void {
         const wasm = @fieldParentPtr(Wasm, "funcs", self);
+        self.func_types.deinit(wasm.base.allocator);
         self.codesec.deinit(wasm.base.allocator);
     }
 
@@ -333,25 +329,30 @@ const Funcs = struct {
         const file = wasm.base.file.?;
         const allocator = wasm.base.allocator;
 
-        assert(self.funcs_count == self.codesec.entries.items.len);
+        assert(self.func_types.items.len == self.codesec.entries.items.len);
 
         // TODO: consider nop-padding the code if there is a close but not perfect fit
         const funcidx = try self.codesec.addEntry(file, allocator, code);
 
-        if (self.funcs_count < self.codesec.entries.items.len) {
+        if (self.func_types.items.len < self.codesec.entries.items.len) {
             // u32 vector length + funcs_count u32s in the vector
-            const current = 5 + self.funcs_count * 5;
+            const current = 5 + @intCast(u32, self.func_types.items.len) * 5;
             try self.funcsec.resize(file, current, current + 5);
-            self.funcs_count += 1;
+            try self.func_types.append(allocator, typeidx);
 
             // Update the size in the section header and the item count of
             // the contents vector.
+            const count = @intCast(u32, self.func_types.items.len);
             var size_and_count: [10]u8 = undefined;
-            leb.writeUnsignedFixed(5, size_and_count[0..5], 5 + self.funcs_count * 5);
-            leb.writeUnsignedFixed(5, size_and_count[5..], self.funcs_count);
+            leb.writeUnsignedFixed(5, size_and_count[0..5], 5 + count * 5);
+            leb.writeUnsignedFixed(5, size_and_count[5..], count);
             try file.pwriteAll(&size_and_count, self.funcsec.offset + 1);
+        } else {
+            // We are overwriting a dead function and may now free the type
+            wasm.types.free(self.func_types.items[funcidx]);
         }
-        assert(self.funcs_count == self.codesec.entries.items.len);
+
+        assert(self.func_types.items.len == self.codesec.entries.items.len);
 
         var typeidx_leb: [5]u8 = undefined;
         leb.writeUnsignedFixed(5, &typeidx_leb, typeidx);