diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c8ed59f307..9de8fdc1d2 100644 --- a/src/link/Wasm.zig +++ b/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 }, }; diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index f6261ba90a..cd5577bd21 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/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 { diff --git a/src/link/Wasm/file.zig b/src/link/Wasm/file.zig index 79b4fd0e36..5e92f3e079 100644 --- a/src/link/Wasm/file.zig +++ b/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, };