Commit a028b10b9f

Luuk de Gram <luuk@degram.dev>
2024-01-22 07:12:26
wasm: update `freeDecl` and `finishDecl`
We now parse the decls right away into atoms and allocate the corresponding linker-object, such as segment and function, rather than waiting until `flushModule`.
1 parent 4d14374
Changed files (3)
src/link/Wasm/file.zig
@@ -114,7 +114,6 @@ pub const File = union(enum) {
     }
 
     pub const Entry = union(enum) {
-        null: void,
         zig_object: ZigObject,
         object: Object,
     };
src/link/Wasm/ZigObject.zig
@@ -29,6 +29,8 @@ global_syms: std.AutoHashMapUnmanaged(u32, u32) = .{},
 symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
 /// Extra metadata about the linking section, such as alignment of segments and their name.
 segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
+/// List of indexes which contain a free slot in the `segment_info` list.
+segment_free_list: std.ArrayListUnmanaged(u32) = .{},
 /// File encapsulated string table, used to deduplicate strings within the generated file.
 string_table: StringTable = .{},
 /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
@@ -145,6 +147,7 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
     zig_object.symbols.deinit(gpa);
     zig_object.symbols_free_list.deinit(gpa);
     zig_object.segment_info.deinit(gpa);
+    zig_object.segment_free_list.deinit(gpa);
 
     zig_object.string_table.deinit(gpa);
     if (zig_object.dwarf) |*dwarf| {
@@ -223,7 +226,7 @@ pub fn updateDecl(
         },
     };
 
-    return zig_object.finishUpdateDecl(wasm_file, decl_index, code, .data);
+    return zig_object.finishUpdateDecl(wasm_file, decl_index, code);
 }
 
 pub fn updateFunc(
@@ -263,7 +266,7 @@ pub fn updateFunc(
         },
     };
 
-    return zig_object.finishUpdateDecl(wasm_file, decl_index, code, .function);
+    return zig_object.finishUpdateDecl(wasm_file, decl_index, code);
 }
 
 fn finishUpdateDecl(
@@ -271,25 +274,89 @@ fn finishUpdateDecl(
     wasm_file: *Wasm,
     decl_index: InternPool.DeclIndex,
     code: []const u8,
-    symbol_tag: Symbol.Tag,
 ) !void {
     const gpa = wasm_file.base.comp.gpa;
     const mod = wasm_file.base.comp.module.?;
     const decl = mod.declPtr(decl_index);
     const atom_index = zig_object.decls.get(decl_index).?;
     const atom = wasm_file.getAtomPtr(atom_index);
-    const sym = zig_object.symbol(atom.getSymbolIndex().?);
+    const sym = zig_object.symbol(atom.sym_index);
     const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
     sym.name = try zig_object.string_table.insert(gpa, full_name);
-    sym.tag = symbol_tag;
     try atom.code.appendSlice(gpa, code);
-    try wasm_file.resolved_symbols.put(gpa, atom.symbolLoc(), {});
-
     atom.size = @intCast(code.len);
+
+    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.tag = .function;
+        },
+        else => {
+            const segment_name: []const u8 = if (decl.getOwnedVariable(mod)) |variable| name: {
+                if (variable.is_const) {
+                    break :name ".rodata.";
+                } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
+                    const decl_namespace = mod.namespacePtr(decl.src_namespace);
+                    const optimize_mode = decl_namespace.file_scope.mod.optimize_mode;
+                    const is_initialized = switch (optimize_mode) {
+                        .Debug, .ReleaseSafe => true,
+                        .ReleaseFast, .ReleaseSmall => false,
+                    };
+                    if (is_initialized) {
+                        break :name ".data.";
+                    }
+                    break :name ".bss.";
+                }
+                // when the decl is all zeroes, we store the atom in the bss segment,
+                // in all other cases it will be in the data segment.
+                for (atom.code.items) |byte| {
+                    if (byte != 0) break :name ".data.";
+                }
+                break :name ".bss.";
+            } else ".rodata.";
+            if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and
+                std.mem.startsWith(u8, segment_name, ".bss"))
+            {
+                @memset(atom.code.items, 0);
+            }
+            // Will be freed upon freeing of decl or after cleanup of Wasm binary.
+            const full_segment_name = try std.mem.concat(gpa, u8, &.{
+                segment_name,
+                full_name,
+            });
+            errdefer gpa.free(full_segment_name);
+            sym.tag = .data;
+            sym.index = try zig_object.createDataSegment(gpa, full_segment_name, decl.alignment);
+        },
+    }
     if (code.len == 0) return;
     atom.alignment = decl.getAlignment(mod);
 }
 
+fn createDataSegment(
+    zig_object: *ZigObject,
+    gpa: std.mem.Allocator,
+    name: []const u8,
+    alignment: InternPool.Alignment,
+) !u32 {
+    const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index|
+        index
+    else index: {
+        const idx: u32 = @intCast(zig_object.segment_info.items.len);
+        _ = try zig_object.segment_info.addOne(gpa);
+        break :index idx;
+    };
+    zig_object.segment_info.items[segment_index] = .{
+        .alignment = alignment,
+        .flags = 0,
+        .name = name,
+    };
+}
+
 /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
 /// When the index was not found, a new `Atom` will be created, and its index will be returned.
 /// The newly created Atom is empty with default fields as specified by `Atom.empty`.
@@ -840,20 +907,24 @@ pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool
     const atom_index = zig_object.decls.get(decl_index).?;
     const atom = wasm_file.getAtomPtr(atom_index);
     zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
-    _ = zig_object.decls.remove(decl_index);
-    zig_object.symbols.items[atom.sym_index].tag = .dead;
+    std.debug.assert(zig_object.decls.remove(decl_index));
+    const sym = &zig_object.symbols.items[atom.sym_index];
     for (atom.locals.items) |local_atom_index| {
         const local_atom = wasm_file.getAtom(local_atom_index);
         const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
-        local_symbol.tag = .dead; // also for any local symbol
+        std.debug.assert(local_symbol.tag == .data);
         zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
-        std.denug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
+        std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
+        local_symbol.tag = .dead; // also for any local symbol
+        const segment = &zig_object.segment_info.items[local_atom.sym_index];
+        gpa.free(segment.name);
+        segment.name = &.{}; // Ensure no accidental double free
     }
 
     if (decl.isExtern(mod)) {
-        _ = zig_object.imports.remove(atom.getSymbolIndex().?);
+        std.debug.assert(zig_object.imports.remove(atom.sym_index));
     }
-    _ = wasm_file.symbol_atom.remove(atom.symbolLoc());
+    std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc()));
 
     // if (wasm.dwarf) |*dwarf| {
     //     dwarf.freeDecl(decl_index);
@@ -869,6 +940,20 @@ pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool
         prev_atom.next = atom.next;
         atom.prev = null;
     }
+
+    sym.tag = .dead;
+    switch (decl.ty.zigTypeTag(mod)) {
+        .Fn => {
+            std.debug.assert(zig_object.functions.remove(atom.sym_index));
+            std.debug.assert(zig_object.atom_types.remove(atom_index));
+        },
+        else => {
+            zig_object.segment_free_list.append(gpa, sym.index) catch {};
+            const segment = &zig_object.segment_info.items[sym.index];
+            gpa.free(segment.name);
+            segment.name = &.{}; // Prevent accidental double free
+        },
+    }
 }
 
 fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 {
src/link/Wasm.zig
@@ -585,7 +585,6 @@ pub fn file(wasm: *const Wasm, index: File.Index) ?File {
     if (index == .null) return null;
     const tag = wasm.files.items(.tags)[@intFromEnum(index)];
     return switch (tag) {
-        .null => null,
         .zig_object => .{ .zig_object = &wasm.files.items(.data)[@intFromEnum(index)].zig_object },
         .object => .{ .object = &wasm.files.items(.data)[@intFromEnum(index)].object },
     };