From a01b1448e2927146cde9f7861605585f96b5084d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 10 Sep 2022 13:49:39 +0200 Subject: [PATCH 1/4] wasm-linker: write magic bytes only on success By writing them at the very end, we can easily detect where the writing of the binary went wrong as tools will indicate the missing of the magic bytes. --- src/link/Wasm.zig | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index babed64755..6ff554de18 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -324,8 +324,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option wasm_bin.base.file = file; wasm_bin.name = sub_path; - try file.writeAll(&(wasm.magic ++ wasm.version)); - // As sym_index '0' is reserved, we use it for our stack pointer symbol const sym_name = try wasm_bin.string_table.put(allocator, "__stack_pointer"); const symbol = try wasm_bin.symbols.addOne(allocator); @@ -363,7 +361,11 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option }; } - try wasm_bin.initDebugSections(); + if (!options.strip and options.module != null) { + wasm_bin.dwarf = Dwarf.init(allocator, .wasm, options.target); + try wasm_bin.initDebugSections(); + } + return wasm_bin; } @@ -380,10 +382,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { .name = undefined, }; - if (!options.strip and options.module != null) { - self.dwarf = Dwarf.init(gpa, .wasm, options.target); - } - const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.have_stage1 and options.use_stage1; if (use_llvm and !use_stage1) { @@ -2213,7 +2211,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const header_size = 5 + 1; const is_obj = self.base.options.output_mode == .Obj; - // No need to rewrite the magic/version header + // We write the magic bytes at the end so they will only be written + // if everything succeeded as expected. try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); @@ -2599,6 +2598,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try self.emitNameSection(file, arena); } + + // Only when writing all sections executed properly we write the magic + // bytes. This allows us to easily detect what went wrong while generating + // the final binary. + try file.pwriteAll(&(wasm.magic ++ wasm.version), 0); } fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { From 6dbf5f1d8686c1f5b31f3dfce9b1ac0944f8e3a5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 10 Sep 2022 19:38:19 +0200 Subject: [PATCH 2/4] wasm-linker: write to file at once Rather than writing to the file using a writer, we now first write to an arraylist and store the binary in memory. Once the full binary data was written, we write all data to disk at once. This reduces the amount of syscalls tremendously, increasing the performance of the linker in exchange for increased memory usage during flush. --- src/link/Wasm.zig | 358 ++++++++++++++++++++-------------------------- 1 file changed, 158 insertions(+), 200 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 6ff554de18..10df96499d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -653,6 +653,8 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { } fn checkUndefinedSymbols(self: *const Wasm) !void { + if (self.base.options.output_mode == .Obj) return; + var found_undefined_symbols = false; for (self.undefs.values()) |undef| { const symbol = undef.getSymbol(self); @@ -2207,33 +2209,38 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try self.mergeTypes(); try self.setupExports(); - const file = self.base.file.?; const header_size = 5 + 1; const is_obj = self.base.options.output_mode == .Obj; + var binary_bytes = std.ArrayList(u8).init(self.base.allocator); + defer binary_bytes.deinit(); + const binary_writer = binary_bytes.writer(); + // We write the magic bytes at the end so they will only be written - // if everything succeeded as expected. - try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); - try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); + // if everything succeeded as expected. So populate with 0's for now. + try binary_writer.writeAll(&[_]u8{0} ** 8); // Type section if (self.func_types.items.len != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); for (self.func_types.items) |func_type| { - try leb.writeULEB128(writer, wasm.function_type); - try leb.writeULEB128(writer, @intCast(u32, func_type.params.len)); - for (func_type.params) |param_ty| try leb.writeULEB128(writer, wasm.valtype(param_ty)); - try leb.writeULEB128(writer, @intCast(u32, func_type.returns.len)); - for (func_type.returns) |ret_ty| try leb.writeULEB128(writer, wasm.valtype(ret_ty)); + try leb.writeULEB128(binary_writer, wasm.function_type); + try leb.writeULEB128(binary_writer, @intCast(u32, func_type.params.len)); + for (func_type.params) |param_ty| { + try leb.writeULEB128(binary_writer, wasm.valtype(param_ty)); + } + try leb.writeULEB128(binary_writer, @intCast(u32, func_type.returns.len)); + for (func_type.returns) |ret_ty| { + try leb.writeULEB128(binary_writer, wasm.valtype(ret_ty)); + } } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .type, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.func_types.items.len), ); section_count += 1; @@ -2243,8 +2250,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const import_memory = self.base.options.import_memory or is_obj; const import_table = self.base.options.import_table or is_obj; if (self.imports.count() != 0 or import_memory or import_table) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); // import table is always first table so emit that first if (import_table) { @@ -2261,14 +2267,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod }, }, }; - try self.emitImport(writer, table_imp); + try self.emitImport(binary_writer, table_imp); } var it = self.imports.iterator(); while (it.next()) |entry| { assert(entry.key_ptr.*.getSymbol(self).isUndefined()); const import = entry.value_ptr.*; - try self.emitImport(writer, import); + try self.emitImport(binary_writer, import); } if (import_memory) { @@ -2278,14 +2284,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod .name = try self.string_table.put(self.base.allocator, mem_name), .kind = .{ .memory = self.memories.limits }, }; - try self.emitImport(writer, mem_imp); + try self.emitImport(binary_writer, mem_imp); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .import, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), ); section_count += 1; @@ -2293,17 +2299,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Function section if (self.functions.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); for (self.functions.values()) |function| { - try leb.writeULEB128(writer, function.type_index); + try leb.writeULEB128(binary_writer, function.type_index); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .function, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.functions.count()), ); section_count += 1; @@ -2312,20 +2317,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Table section const export_table = self.base.options.export_table; if (!import_table and self.function_table.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); - try leb.writeULEB128(writer, wasm.reftype(.funcref)); - try emitLimits(writer, .{ + try leb.writeULEB128(binary_writer, wasm.reftype(.funcref)); + try emitLimits(binary_writer, .{ .min = @intCast(u32, self.function_table.count()) + 1, .max = null, }); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .table, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), ); section_count += 1; @@ -2333,15 +2337,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Memory section if (!import_memory) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); - try emitLimits(writer, self.memories.limits); + try emitLimits(binary_writer, self.memories.limits); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .memory, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), // wasm currently only supports 1 linear memory segment ); section_count += 1; @@ -2349,20 +2352,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Global section (used to emit stack pointer) if (self.wasm_globals.items.len > 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); for (self.wasm_globals.items) |global| { - try writer.writeByte(wasm.valtype(global.global_type.valtype)); - try writer.writeByte(@boolToInt(global.global_type.mutable)); - try emitInit(writer, global.init); + try binary_writer.writeByte(wasm.valtype(global.global_type.valtype)); + try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); + try emitInit(binary_writer, global.init); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .global, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.wasm_globals.items.len), ); section_count += 1; @@ -2370,35 +2372,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Export section if (self.exports.items.len != 0 or export_table or !import_memory) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); + for (self.exports.items) |exp| { const name = self.string_table.get(exp.name); - try leb.writeULEB128(writer, @intCast(u32, name.len)); - try writer.writeAll(name); - try leb.writeULEB128(writer, @enumToInt(exp.kind)); - try leb.writeULEB128(writer, exp.index); + try leb.writeULEB128(binary_writer, @intCast(u32, name.len)); + try binary_writer.writeAll(name); + try leb.writeULEB128(binary_writer, @enumToInt(exp.kind)); + try leb.writeULEB128(binary_writer, exp.index); } if (export_table) { - try leb.writeULEB128(writer, @intCast(u32, "__indirect_function_table".len)); - try writer.writeAll("__indirect_function_table"); - try writer.writeByte(wasm.externalKind(.table)); - try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table + try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len)); + try binary_writer.writeAll("__indirect_function_table"); + try binary_writer.writeByte(wasm.externalKind(.table)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table } if (!import_memory) { - try leb.writeULEB128(writer, @intCast(u32, "memory".len)); - try writer.writeAll("memory"); - try writer.writeByte(wasm.externalKind(.memory)); - try leb.writeULEB128(writer, @as(u32, 0)); + try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); + try binary_writer.writeAll("memory"); + try binary_writer.writeByte(wasm.externalKind(.memory)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .@"export", - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), ); section_count += 1; @@ -2406,25 +2408,24 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // element section (function table) if (self.function_table.count() > 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var flags: u32 = 0x2; // Yes we have a table - try leb.writeULEB128(writer, flags); - try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols - try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid - try leb.writeULEB128(writer, @as(u8, 0)); - try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); + try leb.writeULEB128(binary_writer, flags); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols + try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid + try leb.writeULEB128(binary_writer, @as(u8, 0)); + try leb.writeULEB128(binary_writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator(); while (symbol_it.next()) |symbol_loc_ptr| { - try leb.writeULEB128(writer, symbol_loc_ptr.*.getSymbol(self).index); + try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(self).index); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .element, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), ); section_count += 1; @@ -2433,8 +2434,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Code section var code_section_size: u32 = 0; if (self.code_section_index) |code_index| { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var atom: *Atom = self.atoms.get(code_index).?.getFirst(); // The code section must be sorted in line with the function order. @@ -2460,13 +2460,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn); for (sorted_atoms.items) |sorted_atom| { - try leb.writeULEB128(writer, sorted_atom.size); - try writer.writeAll(sorted_atom.code.items); + try leb.writeULEB128(binary_writer, sorted_atom.size); + try binary_writer.writeAll(sorted_atom.code.items); } - code_section_size = @intCast(u32, (try file.getPos()) - header_offset - header_size); + code_section_size = @intCast(u32, binary_bytes.items.len - header_offset - header_size); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .code, code_section_size, @@ -2478,8 +2478,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Data section if (self.data_segments.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var it = self.data_segments.iterator(); var segment_count: u32 = 0; @@ -2493,10 +2492,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const segment = self.segments.items[atom_index]; // flag and index to memory section (currently, there can only be 1 memory section in wasm) - try leb.writeULEB128(writer, @as(u32, 0)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // offset into data section - try emitInit(writer, .{ .i32_const = @bitCast(i32, segment.offset) }); - try leb.writeULEB128(writer, segment.size); + try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) }); + try leb.writeULEB128(binary_writer, segment.size); // fill in the offset table and the data segments var current_offset: u32 = 0; @@ -2508,12 +2507,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Pad with zeroes to ensure all segments are aligned if (current_offset != atom.offset) { const diff = atom.offset - current_offset; - try writer.writeByteNTimes(0, diff); + try binary_writer.writeByteNTimes(0, diff); current_offset += diff; } assert(current_offset == atom.offset); assert(atom.code.items.len == atom.size); - try writer.writeAll(atom.code.items); + try binary_writer.writeAll(atom.code.items); current_offset += atom.size; if (atom.next) |next| { @@ -2522,7 +2521,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // also pad with zeroes when last atom to ensure // segments are aligned. if (current_offset != segment.size) { - try writer.writeByteNTimes(0, segment.size - current_offset); + try binary_writer.writeByteNTimes(0, segment.size - current_offset); current_offset += segment.size - current_offset; } break; @@ -2532,10 +2531,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .data, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, segment_count), ); data_section_index = section_count; @@ -2547,12 +2546,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // we never store all symbols in a single table, but store a location reference instead. // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); - try self.emitLinkSection(file, arena, &symbol_table); + try self.emitLinkSection(&binary_bytes, &symbol_table); if (code_section_index) |code_index| { - try self.emitCodeRelocations(file, arena, code_index, symbol_table); + try self.emitCodeRelocations(&binary_bytes, code_index, symbol_table); } if (data_section_index) |data_index| { - try self.emitDataRelocations(file, arena, data_index, symbol_table); + try self.emitDataRelocations(&binary_bytes, data_index, symbol_table); } } else if (!self.base.options.strip) { if (self.dwarf) |*dwarf| { @@ -2592,41 +2591,45 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try debug_bytes.appendSlice(atom.code.items); atom = atom.next orelse break; } - try emitDebugSection(file, debug_bytes.items, item.name); + try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); debug_bytes.clearRetainingCapacity(); } } - try self.emitNameSection(file, arena); + try self.emitNameSection(&binary_bytes, arena); } // Only when writing all sections executed properly we write the magic // bytes. This allows us to easily detect what went wrong while generating // the final binary. - try file.pwriteAll(&(wasm.magic ++ wasm.version), 0); + mem.copy(u8, binary_bytes.items, &(wasm.magic ++ wasm.version)); + + // finally, write the entire binary into the file. + var iovec = [_]std.os.iovec_const{.{ + .iov_base = binary_bytes.items.ptr, + .iov_len = binary_bytes.items.len, + }}; + try self.base.file.?.writevAll(&iovec); } -fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { +fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void { if (data.len == 0) return; - const header_offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); - try file.writevAll(&[_]std.os.iovec_const{.{ - .iov_base = data.ptr, - .iov_len = data.len, - }}); - const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len)); + const start = binary_bytes.items.len - header_offset; log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len }); + try writer.writeAll(data); try writeCustomSectionHeader( - file, + binary_bytes.items, header_offset, - @intCast(u32, (try file.getPos()) - header_offset - 6), + @intCast(u32, binary_bytes.items.len - header_offset - 6), ); } -fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { +fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { const Name = struct { index: u32, name: []const u8, @@ -2672,8 +2675,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { std.sort.sort(Name, funcs.values(), {}, Name.lessThan); std.sort.sort(Name, globals.items, {}, Name.lessThan); - const header_offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @intCast(u32, "name".len)); try writer.writeAll("name"); @@ -2682,9 +2685,9 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { try self.emitNameSubsection(.data_segment, segments.items, writer); try writeCustomSectionHeader( - file, + binary_bytes.items, header_offset, - @intCast(u32, (try file.getPos()) - header_offset - 6), + @intCast(u32, binary_bytes.items.len - header_offset - 6), ); } @@ -3197,54 +3200,40 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } -fn reserveVecSectionHeader(file: fs.File) !u64 { +fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { // section id + fixed leb contents size + fixed leb vector length const header_size = 1 + 5 + 5; - // TODO: this should be a single lseek(2) call, but fs.File does not - // currently provide a way to do this. - try file.seekBy(header_size); - return (try file.getPos()) - header_size; + const offset = @intCast(u32, bytes.items.len); + try bytes.appendSlice(&[_]u8{0} ** header_size); + return offset; } -fn reserveCustomSectionHeader(file: fs.File) !u64 { +fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 { // unlike regular section, we don't emit the count const header_size = 1 + 5; - // TODO: this should be a single lseek(2) call, but fs.File does not - // currently provide a way to do this. - try file.seekBy(header_size); - return (try file.getPos()) - header_size; + const offset = @intCast(u32, bytes.items.len); + try bytes.appendSlice(&[_]u8{0} ** header_size); + return offset; } -fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void { +fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: u32, items: u32) !void { var buf: [1 + 5 + 5]u8 = undefined; buf[0] = @enumToInt(section); leb.writeUnsignedFixed(5, buf[1..6], size); leb.writeUnsignedFixed(5, buf[6..], items); - - if (builtin.target.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12783 - const curr_pos = try file.getPos(); - try file.pwriteAll(&buf, offset); - try file.seekTo(curr_pos); - } else try file.pwriteAll(&buf, offset); + mem.copy(u8, buffer[offset..], &buf); } -fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { +fn writeCustomSectionHeader(buffer: []u8, offset: u64, size: u32) !void { var buf: [1 + 5]u8 = undefined; buf[0] = 0; // 0 = 'custom' section leb.writeUnsignedFixed(5, buf[1..6], size); - - if (builtin.target.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12783 - const curr_pos = try file.getPos(); - try file.pwriteAll(&buf, offset); - try file.seekTo(curr_pos); - } else try file.pwriteAll(&buf, offset); + mem.copy(u8, buffer[offset..], &buf); } -fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { - const offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); +fn emitLinkSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { + const offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); // emit "linking" custom section name const section_name = "linking"; try leb.writeULEB128(writer, section_name.len); @@ -3255,21 +3244,18 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * // For each subsection type (found in types.Subsection) we can emit a section. // Currently, we only support emitting segment info and the symbol table. - try self.emitSymbolTable(file, arena, symbol_table); - try self.emitSegmentInfo(file, arena); + try self.emitSymbolTable(binary_bytes, symbol_table); + try self.emitSegmentInfo(binary_bytes); - const size = @intCast(u32, (try file.getPos()) - offset - 6); - try writeCustomSectionHeader(file, offset, size); + const size = @intCast(u32, binary_bytes.items.len - offset - 6); + try writeCustomSectionHeader(binary_bytes.items, offset, size); } -fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { - // After emitting the subtype, we must emit the subsection's length - // so first write it to a temporary arraylist to calculate the length - // and then write all data at once. - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); +fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { + const writer = binary_bytes.writer(); - try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE)); + try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE)); + const table_offset = binary_bytes.items.len; var symbol_count: u32 = 0; for (self.resolved_symbols.keys()) |sym_loc| { @@ -3307,23 +3293,17 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * } } - var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, symbol_count); - try payload.insertSlice(0, &buf); - try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len)); - - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }; - var iovecs = [_]std.os.iovec_const{iovec}; - try file.writevAll(&iovecs); + var buf: [10]u8 = undefined; + leb.writeUnsignedFixed(5, buf[0..5], @intCast(u32, binary_bytes.items.len - table_offset + 5)); + leb.writeUnsignedFixed(5, buf[5..], symbol_count); + try binary_bytes.insertSlice(table_offset, &buf); } -fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); - try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); +fn emitSegmentInfo(self: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { + const writer = binary_bytes.writer(); + try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); + const segment_offset = binary_bytes.items.len; + try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); for (self.segment_info.values()) |segment_info| { log.debug("Emit segment: {s} align({d}) flags({b})", .{ @@ -3337,13 +3317,9 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { try leb.writeULEB128(writer, segment_info.flags); } - try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len)); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }; - var iovecs = [_]std.os.iovec_const{iovec}; - try file.writevAll(&iovecs); + var buf: [5]u8 = undefined; + leb.writeUnsignedFixed(5, &buf, @intCast(u32, binary_bytes.items.len - segment_offset)); + try binary_bytes.insertSlice(segment_offset, &buf); } pub fn getULEB128Size(uint_value: anytype) u32 { @@ -3361,21 +3337,20 @@ pub fn getULEB128Size(uint_value: anytype) u32 { /// For each relocatable section, emits a custom "relocation." section fn emitCodeRelocations( self: *Wasm, - file: fs.File, - arena: Allocator, + binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { const code_index = self.code_section_index orelse return; - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); + const writer = binary_bytes.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); // write custom section information const name = "reloc.CODE"; try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); try leb.writeULEB128(writer, section_index); - const reloc_start = payload.items.len; + const reloc_start = binary_bytes.items.len; var count: u32 = 0; var atom: *Atom = self.atoms.get(code_index).?.getFirst(); @@ -3401,36 +3376,27 @@ fn emitCodeRelocations( if (count == 0) return; var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); - try payload.insertSlice(reloc_start, &buf); - var iovecs = [_]std.os.iovec_const{ - .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }, - }; - const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&iovecs); - const size = @intCast(u32, payload.items.len); - try writeCustomSectionHeader(file, header_offset, size); + try binary_bytes.insertSlice(reloc_start, &buf); + const size = @intCast(u32, binary_bytes.items.len - header_offset - 6); + try writeCustomSectionHeader(binary_bytes.items, header_offset, size); } fn emitDataRelocations( self: *Wasm, - file: fs.File, - arena: Allocator, + binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { if (self.data_segments.count() == 0) return; - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); + const writer = binary_bytes.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); // write custom section information const name = "reloc.DATA"; try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); try leb.writeULEB128(writer, section_index); - const reloc_start = payload.items.len; + const reloc_start = binary_bytes.items.len; var count: u32 = 0; // for each atom, we calculate the uleb size and append that @@ -3462,17 +3428,9 @@ fn emitDataRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); - try payload.insertSlice(reloc_start, &buf); - var iovecs = [_]std.os.iovec_const{ - .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }, - }; - const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&iovecs); - const size = @intCast(u32, payload.items.len); - try writeCustomSectionHeader(file, header_offset, size); + try binary_bytes.insertSlice(reloc_start, &buf); + const size = @intCast(u32, binary_bytes.items.len - header_offset - 6); + try writeCustomSectionHeader(binary_bytes.items, header_offset, size); } /// Searches for an a matching function signature, when not found From 61f317e3862101a22411ac1f6005ef4ef5b8ab56 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 11 Sep 2022 17:41:56 +0200 Subject: [PATCH 3/4] wasm-linker: rename self to descriptive name --- src/link/Wasm.zig | 1517 ++++++++++++++++++------------------- src/link/Wasm/Archive.zig | 1 - src/link/Wasm/Atom.zig | 72 +- src/link/Wasm/Object.zig | 228 +++--- src/link/Wasm/Symbol.zig | 88 +-- src/link/Wasm/types.zig | 10 +- 6 files changed, 957 insertions(+), 959 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 10df96499d..14d864c330 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -8,7 +8,6 @@ const assert = std.debug.assert; const fs = std.fs; const leb = std.leb; const log = std.log.scoped(.link); -const wasm = std.wasm; const Atom = @import("Wasm/Atom.zig"); const Dwarf = @import("Dwarf.zig"); @@ -106,17 +105,17 @@ dwarf: ?Dwarf = null, // Output sections /// Output type section -func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, +func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, /// Output function section where the key is the original /// function index and the value is function. /// This allows us to map multiple symbols to the same function. -functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, wasm.Func) = .{}, +functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.wasm.Func) = .{}, /// Output global section -wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{}, +wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, /// Memory section -memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, +memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, /// Output table section -tables: std.ArrayListUnmanaged(wasm.Table) = .{}, +tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, /// Output export section exports: std.ArrayListUnmanaged(types.Export) = .{}, @@ -203,39 +202,39 @@ pub const SymbolLoc = struct { file: ?u16, /// From a given location, returns the corresponding symbol in the wasm binary - pub fn getSymbol(self: SymbolLoc, wasm_bin: *const Wasm) *Symbol { - if (wasm_bin.discarded.get(self)) |new_loc| { + pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol { + if (wasm_bin.discarded.get(loc)) |new_loc| { return new_loc.getSymbol(wasm_bin); } - if (self.file) |object_index| { + if (loc.file) |object_index| { const object = wasm_bin.objects.items[object_index]; - return &object.symtable[self.index]; + return &object.symtable[loc.index]; } - return &wasm_bin.symbols.items[self.index]; + return &wasm_bin.symbols.items[loc.index]; } /// From a given location, returns the name of the symbol. - pub fn getName(self: SymbolLoc, wasm_bin: *const Wasm) []const u8 { - if (wasm_bin.discarded.get(self)) |new_loc| { + pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 { + if (wasm_bin.discarded.get(loc)) |new_loc| { return new_loc.getName(wasm_bin); } - if (self.file) |object_index| { + if (loc.file) |object_index| { const object = wasm_bin.objects.items[object_index]; - return object.string_table.get(object.symtable[self.index].name); + return object.string_table.get(object.symtable[loc.index].name); } - return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name); + return wasm_bin.string_table.get(wasm_bin.symbols.items[loc.index].name); } /// From a given symbol location, returns the final location. /// e.g. when a symbol was resolved and replaced by the symbol /// in a different file, this will return said location. /// If the symbol wasn't replaced by another, this will return - /// the given location itself. - pub fn finalLoc(self: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { - if (wasm_bin.discarded.get(self)) |new_loc| { + /// the given location itwasm. + pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { + if (wasm_bin.discarded.get(loc)) |new_loc| { return new_loc.finalLoc(wasm_bin); } - return self; + return loc; } }; @@ -258,12 +257,12 @@ pub const StringTable = struct { /// When found, de-duplicates the string and returns the existing offset instead. /// When the string is not found in the `string_table`, a new entry will be inserted /// and the new offset to its data will be returned. - pub fn put(self: *StringTable, allocator: Allocator, string: []const u8) !u32 { - const gop = try self.string_table.getOrPutContextAdapted( + pub fn put(table: *StringTable, allocator: Allocator, string: []const u8) !u32 { + const gop = try table.string_table.getOrPutContextAdapted( allocator, string, - std.hash_map.StringIndexAdapter{ .bytes = &self.string_data }, - .{ .bytes = &self.string_data }, + std.hash_map.StringIndexAdapter{ .bytes = &table.string_data }, + .{ .bytes = &table.string_data }, ); if (gop.found_existing) { const off = gop.key_ptr.*; @@ -271,13 +270,13 @@ pub const StringTable = struct { return off; } - try self.string_data.ensureUnusedCapacity(allocator, string.len + 1); - const offset = @intCast(u32, self.string_data.items.len); + try table.string_data.ensureUnusedCapacity(allocator, string.len + 1); + const offset = @intCast(u32, table.string_data.items.len); log.debug("writing new string '{s}' at offset 0x{x}", .{ string, offset }); - self.string_data.appendSliceAssumeCapacity(string); - self.string_data.appendAssumeCapacity(0); + table.string_data.appendSliceAssumeCapacity(string); + table.string_data.appendAssumeCapacity(0); gop.key_ptr.* = offset; @@ -286,26 +285,26 @@ pub const StringTable = struct { /// From a given offset, returns its corresponding string value. /// Asserts offset does not exceed bounds. - pub fn get(self: StringTable, off: u32) []const u8 { - assert(off < self.string_data.items.len); - return mem.sliceTo(@ptrCast([*:0]const u8, self.string_data.items.ptr + off), 0); + pub fn get(table: StringTable, off: u32) []const u8 { + assert(off < table.string_data.items.len); + return mem.sliceTo(@ptrCast([*:0]const u8, table.string_data.items.ptr + off), 0); } /// Returns the offset of a given string when it exists. /// Will return null if the given string does not yet exist within the string table. - pub fn getOffset(self: *StringTable, string: []const u8) ?u32 { - return self.string_table.getKeyAdapted( + pub fn getOffset(table: *StringTable, string: []const u8) ?u32 { + return table.string_table.getKeyAdapted( string, - std.hash_map.StringIndexAdapter{ .bytes = &self.string_data }, + std.hash_map.StringIndexAdapter{ .bytes = &table.string_data }, ); } /// Frees all resources of the string table. Any references pointing /// to the strings will be invalid. - pub fn deinit(self: *StringTable, allocator: Allocator) void { - self.string_data.deinit(allocator); - self.string_table.deinit(allocator); - self.* = undefined; + pub fn deinit(table: *StringTable, allocator: Allocator) void { + table.string_data.deinit(allocator); + table.string_table.deinit(allocator); + table.* = undefined; } }; @@ -370,9 +369,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option } pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { - const self = try gpa.create(Wasm); - errdefer gpa.destroy(self); - self.* = .{ + const wasm = try gpa.create(Wasm); + errdefer gpa.destroy(wasm); + wasm.* = .{ .base = .{ .tag = .wasm, .options = options, @@ -385,33 +384,33 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.have_stage1 and options.use_stage1; if (use_llvm and !use_stage1) { - self.llvm_object = try LlvmObject.create(gpa, options); + wasm.llvm_object = try LlvmObject.create(gpa, options); } - return self; + return wasm; } /// Initializes symbols and atoms for the debug sections /// Initialization is only done when compiling Zig code. /// When Zig is invoked as a linker instead, the atoms /// and symbols come from the object files instead. -pub fn initDebugSections(self: *Wasm) !void { - if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections - assert(self.debug_info_index == null); +pub fn initDebugSections(wasm: *Wasm) !void { + if (wasm.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections + assert(wasm.debug_info_index == null); // this will create an Atom and set the index for us. - self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info"); - self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line"); - self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc"); - self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev"); - self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges"); - self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str"); - self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames"); - self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes"); + wasm.debug_info_atom = try wasm.createDebugSectionForIndex(&wasm.debug_info_index, ".debug_info"); + wasm.debug_line_atom = try wasm.createDebugSectionForIndex(&wasm.debug_line_index, ".debug_line"); + wasm.debug_loc_atom = try wasm.createDebugSectionForIndex(&wasm.debug_loc_index, ".debug_loc"); + wasm.debug_abbrev_atom = try wasm.createDebugSectionForIndex(&wasm.debug_abbrev_index, ".debug_abbrev"); + wasm.debug_ranges_atom = try wasm.createDebugSectionForIndex(&wasm.debug_ranges_index, ".debug_ranges"); + wasm.debug_str_atom = try wasm.createDebugSectionForIndex(&wasm.debug_str_index, ".debug_str"); + wasm.debug_pubnames_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubnames_index, ".debug_pubnames"); + wasm.debug_pubtypes_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubtypes_index, ".debug_pubtypes"); } -fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { +fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void { for (files) |path| { - if (try self.parseObjectFile(path)) continue; - if (try self.parseArchive(path, false)) continue; // load archives lazily + if (try wasm.parseObjectFile(path)) continue; + if (try wasm.parseArchive(path, false)) continue; // load archives lazily log.warn("Unexpected file format at path: '{s}'", .{path}); } } @@ -419,16 +418,16 @@ fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { /// Parses the object file from given path. Returns true when the given file was an object /// file and parsed successfully. Returns false when file is not an object file. /// May return an error instead when parsing failed. -fn parseObjectFile(self: *Wasm, path: []const u8) !bool { +fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { const file = try fs.cwd().openFile(path, .{}); errdefer file.close(); - var object = Object.create(self.base.allocator, file, path, null) catch |err| switch (err) { + var object = Object.create(wasm.base.allocator, file, path, null) catch |err| switch (err) { error.InvalidMagicByte, error.NotObjectFile => return false, else => |e| return e, }; - errdefer object.deinit(self.base.allocator); - try self.objects.append(self.base.allocator, object); + errdefer object.deinit(wasm.base.allocator); + try wasm.objects.append(wasm.base.allocator, object); return true; } @@ -440,7 +439,7 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool { /// When `force_load` is `true`, it will for link all object files in the archive. /// When false, it will only link with object files that contain symbols that /// are referenced by other object files or Zig code. -fn parseArchive(self: *Wasm, path: []const u8, force_load: bool) !bool { +fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { const file = try fs.cwd().openFile(path, .{}); errdefer file.close(); @@ -448,25 +447,25 @@ fn parseArchive(self: *Wasm, path: []const u8, force_load: bool) !bool { .file = file, .name = path, }; - archive.parse(self.base.allocator) catch |err| switch (err) { + archive.parse(wasm.base.allocator) catch |err| switch (err) { error.EndOfStream, error.NotArchive => { - archive.deinit(self.base.allocator); + archive.deinit(wasm.base.allocator); return false; }, else => |e| return e, }; if (!force_load) { - errdefer archive.deinit(self.base.allocator); - try self.archives.append(self.base.allocator, archive); + errdefer archive.deinit(wasm.base.allocator); + try wasm.archives.append(wasm.base.allocator, archive); return true; } - defer archive.deinit(self.base.allocator); + defer archive.deinit(wasm.base.allocator); // In this case we must force link all embedded object files within the archive // We loop over all symbols, and then group them by offset as the offset // notates where the object file starts. - var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator); + var offsets = std.AutoArrayHashMap(u32, void).init(wasm.base.allocator); defer offsets.deinit(); for (archive.toc.values()) |symbol_offsets| { for (symbol_offsets.items) |sym_offset| { @@ -475,15 +474,15 @@ fn parseArchive(self: *Wasm, path: []const u8, force_load: bool) !bool { } for (offsets.keys()) |file_offset| { - const object = try self.objects.addOne(self.base.allocator); - object.* = try archive.parseObject(self.base.allocator, file_offset); + const object = try wasm.objects.addOne(wasm.base.allocator); + object.* = try archive.parseObject(wasm.base.allocator, file_offset); } return true; } -fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { - const object: Object = self.objects.items[object_index]; +fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { + const object: Object = wasm.objects.items[object_index]; log.debug("Resolving symbols in object: '{s}'", .{object.name}); for (object.symtable) |symbol, i| { @@ -496,7 +495,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { if (mem.eql(u8, sym_name, "__indirect_function_table")) { continue; } - const sym_name_index = try self.string_table.put(self.base.allocator, sym_name); + const sym_name_index = try wasm.string_table.put(wasm.base.allocator, sym_name); if (symbol.isLocal()) { if (symbol.isUndefined()) { @@ -504,27 +503,27 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name }); return error.undefinedLocal; } - try self.resolved_symbols.putNoClobber(self.base.allocator, location, {}); + try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, location, {}); continue; } - const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index); + const maybe_existing = try wasm.globals.getOrPut(wasm.base.allocator, sym_name_index); if (!maybe_existing.found_existing) { maybe_existing.value_ptr.* = location; - try self.resolved_symbols.putNoClobber(self.base.allocator, location, {}); + try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, location, {}); if (symbol.isUndefined()) { - try self.undefs.putNoClobber(self.base.allocator, sym_name, location); + try wasm.undefs.putNoClobber(wasm.base.allocator, sym_name, location); } continue; } const existing_loc = maybe_existing.value_ptr.*; - const existing_sym: *Symbol = existing_loc.getSymbol(self); + const existing_sym: *Symbol = existing_loc.getSymbol(wasm); const existing_file_path = if (existing_loc.file) |file| blk: { - break :blk self.objects.items[file].name; - } else self.name; + break :blk wasm.objects.items[file].name; + } else wasm.name; if (!existing_sym.isUndefined()) outer: { if (!symbol.isUndefined()) inner: { @@ -541,7 +540,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { return error.SymbolCollision; } - try self.discarded.put(self.base.allocator, location, existing_loc); + try wasm.discarded.put(wasm.base.allocator, location, existing_loc); continue; // Do not overwrite defined symbols with undefined symbols } @@ -554,12 +553,12 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { if (existing_sym.isUndefined() and symbol.isUndefined()) { const existing_name = if (existing_loc.file) |file_index| blk: { - const obj = self.objects.items[file_index]; + const obj = wasm.objects.items[file_index]; const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; break :blk obj.string_table.get(name_index); } else blk: { - const name_index = self.imports.get(existing_loc).?.module_name; - break :blk self.string_table.get(name_index); + const name_index = wasm.imports.get(existing_loc).?.module_name; + break :blk wasm.string_table.get(name_index); }; const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; @@ -577,8 +576,8 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { } if (existing_sym.tag == .global) { - const existing_ty = self.getGlobalType(existing_loc); - const new_ty = self.getGlobalType(location); + const existing_ty = wasm.getGlobalType(existing_loc); + const new_ty = wasm.getGlobalType(location); if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) { log.err("symbol '{s}' mismatching global types", .{sym_name}); log.err(" first definition in '{s}'", .{existing_file_path}); @@ -588,8 +587,8 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { } if (existing_sym.tag == .function) { - const existing_ty = self.getFunctionSignature(existing_loc); - const new_ty = self.getFunctionSignature(location); + const existing_ty = wasm.getFunctionSignature(existing_loc); + const new_ty = wasm.getFunctionSignature(location); if (!existing_ty.eql(new_ty)) { log.err("symbol '{s}' mismatching function signatures.", .{sym_name}); log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty }); @@ -601,7 +600,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { // when both symbols are weak, we skip overwriting if (existing_sym.isWeak() and symbol.isWeak()) { - try self.discarded.put(self.base.allocator, location, existing_loc); + try wasm.discarded.put(wasm.base.allocator, location, existing_loc); continue; } @@ -609,27 +608,27 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { log.debug("Overwriting symbol '{s}'", .{sym_name}); log.debug(" old definition in '{s}'", .{existing_file_path}); log.debug(" new definition in '{s}'", .{object.name}); - try self.discarded.putNoClobber(self.base.allocator, existing_loc, location); + try wasm.discarded.putNoClobber(wasm.base.allocator, existing_loc, location); maybe_existing.value_ptr.* = location; - try self.globals.put(self.base.allocator, sym_name_index, location); - try self.resolved_symbols.put(self.base.allocator, location, {}); - assert(self.resolved_symbols.swapRemove(existing_loc)); + try wasm.globals.put(wasm.base.allocator, sym_name_index, location); + try wasm.resolved_symbols.put(wasm.base.allocator, location, {}); + assert(wasm.resolved_symbols.swapRemove(existing_loc)); if (existing_sym.isUndefined()) { - assert(self.undefs.swapRemove(sym_name)); + assert(wasm.undefs.swapRemove(sym_name)); } } } -fn resolveSymbolsInArchives(self: *Wasm) !void { - if (self.archives.items.len == 0) return; +fn resolveSymbolsInArchives(wasm: *Wasm) !void { + if (wasm.archives.items.len == 0) return; log.debug("Resolving symbols in archives", .{}); var index: u32 = 0; - undef_loop: while (index < self.undefs.count()) { - const undef_sym_loc = self.undefs.values()[index]; - const sym_name = undef_sym_loc.getName(self); + undef_loop: while (index < wasm.undefs.count()) { + const undef_sym_loc = wasm.undefs.values()[index]; + const sym_name = undef_sym_loc.getName(wasm); - for (self.archives.items) |archive| { + for (wasm.archives.items) |archive| { const offset = archive.toc.get(sym_name) orelse { // symbol does not exist in this archive continue; @@ -639,10 +638,10 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { // Symbol is found in unparsed object file within current archive. // Parse object and and resolve symbols again before we check remaining // undefined symbols. - const object_file_index = @intCast(u16, self.objects.items.len); - var object = try archive.parseObject(self.base.allocator, offset.items[0]); - try self.objects.append(self.base.allocator, object); - try self.resolveSymbolsInObject(object_file_index); + const object_file_index = @intCast(u16, wasm.objects.items.len); + var object = try archive.parseObject(wasm.base.allocator, offset.items[0]); + try wasm.objects.append(wasm.base.allocator, object); + try wasm.resolveSymbolsInObject(object_file_index); // continue loop for any remaining undefined symbols that still exist // after resolving last object file @@ -652,18 +651,18 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { } } -fn checkUndefinedSymbols(self: *const Wasm) !void { - if (self.base.options.output_mode == .Obj) return; +fn checkUndefinedSymbols(wasm: *const Wasm) !void { + if (wasm.base.options.output_mode == .Obj) return; var found_undefined_symbols = false; - for (self.undefs.values()) |undef| { - const symbol = undef.getSymbol(self); + for (wasm.undefs.values()) |undef| { + const symbol = undef.getSymbol(wasm); if (symbol.tag == .data) { found_undefined_symbols = true; const file_name = if (undef.file) |file_index| name: { - break :name self.objects.items[file_index].name; - } else self.name; - log.err("could not resolve undefined symbol '{s}'", .{undef.getName(self)}); + break :name wasm.objects.items[file_index].name; + } else wasm.name; + log.err("could not resolve undefined symbol '{s}'", .{undef.getName(wasm)}); log.err(" defined in '{s}'", .{file_name}); } } @@ -672,80 +671,80 @@ fn checkUndefinedSymbols(self: *const Wasm) !void { } } -pub fn deinit(self: *Wasm) void { - const gpa = self.base.allocator; +pub fn deinit(wasm: *Wasm) void { + const gpa = wasm.base.allocator; if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa); } - if (self.base.options.module) |mod| { - var decl_it = self.decls.keyIterator(); + if (wasm.base.options.module) |mod| { + var decl_it = wasm.decls.keyIterator(); while (decl_it.next()) |decl_index_ptr| { const decl = mod.declPtr(decl_index_ptr.*); decl.link.wasm.deinit(gpa); } } else { - assert(self.decls.count() == 0); + assert(wasm.decls.count() == 0); } - for (self.func_types.items) |*func_type| { + for (wasm.func_types.items) |*func_type| { func_type.deinit(gpa); } - for (self.segment_info.values()) |segment_info| { + for (wasm.segment_info.values()) |segment_info| { gpa.free(segment_info.name); } - for (self.objects.items) |*object| { + for (wasm.objects.items) |*object| { object.deinit(gpa); } - for (self.archives.items) |*archive| { + for (wasm.archives.items) |*archive| { archive.deinit(gpa); } - self.decls.deinit(gpa); - self.symbols.deinit(gpa); - self.symbols_free_list.deinit(gpa); - self.globals.deinit(gpa); - self.resolved_symbols.deinit(gpa); - self.undefs.deinit(gpa); - self.discarded.deinit(gpa); - self.symbol_atom.deinit(gpa); - self.export_names.deinit(gpa); - self.atoms.deinit(gpa); - for (self.managed_atoms.items) |managed_atom| { + wasm.decls.deinit(gpa); + wasm.symbols.deinit(gpa); + wasm.symbols_free_list.deinit(gpa); + wasm.globals.deinit(gpa); + wasm.resolved_symbols.deinit(gpa); + wasm.undefs.deinit(gpa); + wasm.discarded.deinit(gpa); + wasm.symbol_atom.deinit(gpa); + wasm.export_names.deinit(gpa); + wasm.atoms.deinit(gpa); + for (wasm.managed_atoms.items) |managed_atom| { managed_atom.deinit(gpa); gpa.destroy(managed_atom); } - self.managed_atoms.deinit(gpa); - self.segments.deinit(gpa); - self.data_segments.deinit(gpa); - self.segment_info.deinit(gpa); - self.objects.deinit(gpa); - self.archives.deinit(gpa); + wasm.managed_atoms.deinit(gpa); + wasm.segments.deinit(gpa); + wasm.data_segments.deinit(gpa); + wasm.segment_info.deinit(gpa); + wasm.objects.deinit(gpa); + wasm.archives.deinit(gpa); // free output sections - self.imports.deinit(gpa); - self.func_types.deinit(gpa); - self.functions.deinit(gpa); - self.wasm_globals.deinit(gpa); - self.function_table.deinit(gpa); - self.tables.deinit(gpa); - self.exports.deinit(gpa); + wasm.imports.deinit(gpa); + wasm.func_types.deinit(gpa); + wasm.functions.deinit(gpa); + wasm.wasm_globals.deinit(gpa); + wasm.function_table.deinit(gpa); + wasm.tables.deinit(gpa); + wasm.exports.deinit(gpa); - self.string_table.deinit(gpa); + wasm.string_table.deinit(gpa); - if (self.dwarf) |*dwarf| { + if (wasm.dwarf) |*dwarf| { dwarf.deinit(); } } -pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { - if (self.llvm_object) |_| return; - const decl = self.base.options.module.?.declPtr(decl_index); +pub fn allocateDeclIndexes(wasm: *Wasm, decl_index: Module.Decl.Index) !void { + if (wasm.llvm_object) |_| return; + const decl = wasm.base.options.module.?.declPtr(decl_index); if (decl.link.wasm.sym_index != 0) return; - try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); - try self.decls.putNoClobber(self.base.allocator, decl_index, {}); + try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); + try wasm.decls.putNoClobber(wasm.base.allocator, decl_index, {}); const atom = &decl.link.wasm; @@ -756,22 +755,22 @@ pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { .index = undefined, // will be set after updateDecl }; - if (self.symbols_free_list.popOrNull()) |index| { + if (wasm.symbols_free_list.popOrNull()) |index| { atom.sym_index = index; - self.symbols.items[index] = symbol; + wasm.symbols.items[index] = symbol; } else { - atom.sym_index = @intCast(u32, self.symbols.items.len); - self.symbols.appendAssumeCapacity(symbol); + atom.sym_index = @intCast(u32, wasm.symbols.items.len); + wasm.symbols.appendAssumeCapacity(symbol); } - try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom); + try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom); } -pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { +pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness); + if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness); } const tracy = trace(@src()); @@ -783,13 +782,13 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes decl.link.wasm.clear(); - var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl) else null; + var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl) else null; defer if (decl_state) |*ds| ds.deinit(); - var code_writer = std.ArrayList(u8).init(self.base.allocator); + var code_writer = std.ArrayList(u8).init(wasm.base.allocator); defer code_writer.deinit(); const result = try codegen.generateFunction( - &self.base, + &wasm.base, decl.srcLoc(), func, air, @@ -807,9 +806,9 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes }, }; - if (self.dwarf) |*dwarf| { + if (wasm.dwarf) |*dwarf| { try dwarf.commitDeclState( - &self.base, + &wasm.base, mod, decl, // Actual value will be written after relocation. @@ -820,17 +819,17 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes &decl_state.?, ); } - return self.finishUpdateDecl(decl, code); + return wasm.finishUpdateDecl(decl, code); } // Generate code for the Decl, storing it in memory to be later written to // the file on flush(). -pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void { +pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); + if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); } const tracy = trace(@src()); @@ -850,15 +849,15 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi if (decl.isExtern()) { const variable = decl.getVariable().?; const name = mem.sliceTo(decl.name, 0); - return self.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null); + return wasm.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null); } const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - var code_writer = std.ArrayList(u8).init(self.base.allocator); + var code_writer = std.ArrayList(u8).init(wasm.base.allocator); defer code_writer.deinit(); const res = try codegen.generateSymbol( - &self.base, + &wasm.base, decl.srcLoc(), .{ .ty = decl.ty, .val = val }, &code_writer, @@ -876,46 +875,46 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi }, }; - return self.finishUpdateDecl(decl, code); + return wasm.finishUpdateDecl(decl, code); } -pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl) !void { - if (self.llvm_object) |_| return; - if (self.dwarf) |*dw| { +pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void { + if (wasm.llvm_object) |_| return; + if (wasm.dwarf) |*dw| { const tracy = trace(@src()); defer tracy.end(); const decl_name = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(decl_name); + defer wasm.base.allocator.free(decl_name); log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); - try dw.updateDeclLineNumber(&self.base, decl); + try dw.updateDeclLineNumber(&wasm.base, decl); } } -fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { - const mod = self.base.options.module.?; +fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void { + const mod = wasm.base.options.module.?; const atom: *Atom = &decl.link.wasm; - const symbol = &self.symbols.items[atom.sym_index]; + const symbol = &wasm.symbols.items[atom.sym_index]; const full_name = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(full_name); - symbol.name = try self.string_table.put(self.base.allocator, full_name); - try atom.code.appendSlice(self.base.allocator, code); - try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {}); + defer wasm.base.allocator.free(full_name); + symbol.name = try wasm.string_table.put(wasm.base.allocator, full_name); + try atom.code.appendSlice(wasm.base.allocator, code); + try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); if (code.len == 0) return; atom.size = @intCast(u32, code.len); - atom.alignment = decl.ty.abiAlignment(self.base.options.target); + atom.alignment = decl.ty.abiAlignment(wasm.base.options.target); } /// From a given symbol location, returns its `wasm.GlobalType`. /// Asserts the Symbol represents a global. -fn getGlobalType(self: *const Wasm, loc: SymbolLoc) wasm.GlobalType { - const symbol = loc.getSymbol(self); +fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { + const symbol = loc.getSymbol(wasm); assert(symbol.tag == .global); const is_undefined = symbol.isUndefined(); if (loc.file) |file_index| { - const obj: Object = self.objects.items[file_index]; + const obj: Object = wasm.objects.items[file_index]; if (is_undefined) { return obj.findImport(.global, symbol.index).kind.global; } @@ -923,19 +922,19 @@ fn getGlobalType(self: *const Wasm, loc: SymbolLoc) wasm.GlobalType { return obj.globals[symbol.index - import_global_count].global_type; } if (is_undefined) { - return self.imports.get(loc).?.kind.global; + return wasm.imports.get(loc).?.kind.global; } - return self.wasm_globals.items[symbol.index].global_type; + return wasm.wasm_globals.items[symbol.index].global_type; } /// From a given symbol location, returns its `wasm.Type`. /// Asserts the Symbol represents a function. -fn getFunctionSignature(self: *const Wasm, loc: SymbolLoc) wasm.Type { - const symbol = loc.getSymbol(self); +fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { + const symbol = loc.getSymbol(wasm); assert(symbol.tag == .function); const is_undefined = symbol.isUndefined(); if (loc.file) |file_index| { - const obj: Object = self.objects.items[file_index]; + const obj: Object = wasm.objects.items[file_index]; if (is_undefined) { const ty_index = obj.findImport(.function, symbol.index).kind.function; return obj.func_types[ty_index]; @@ -945,55 +944,55 @@ fn getFunctionSignature(self: *const Wasm, loc: SymbolLoc) wasm.Type { return obj.func_types[type_index]; } if (is_undefined) { - const ty_index = self.imports.get(loc).?.kind.function; - return self.func_types.items[ty_index]; + const ty_index = wasm.imports.get(loc).?.kind.function; + return wasm.func_types.items[ty_index]; } - return self.func_types.items[self.functions.get(.{ .file = loc.file, .index = loc.index }).?.type_index]; + return wasm.func_types.items[wasm.functions.get(.{ .file = loc.file, .index = loc.index }).?.type_index]; } /// Lowers a constant typed value to a local symbol and atom. /// Returns the symbol index of the local /// The given `decl` is the parent decl whom owns the constant. -pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { +pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { assert(tv.ty.zigTypeTag() != .Fn); // cannot create local symbols for functions - const mod = self.base.options.module.?; + const mod = wasm.base.options.module.?; const decl = mod.declPtr(decl_index); // Create and initialize a new local symbol and atom const local_index = decl.link.wasm.locals.items.len; const fqdn = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(fqdn); - const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); - defer self.base.allocator.free(name); + defer wasm.base.allocator.free(fqdn); + const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); + defer wasm.base.allocator.free(name); var symbol: Symbol = .{ - .name = try self.string_table.put(self.base.allocator, name), + .name = try wasm.string_table.put(wasm.base.allocator, name), .flags = 0, .tag = .data, .index = undefined, }; symbol.setFlag(.WASM_SYM_BINDING_LOCAL); - const atom = try decl.link.wasm.locals.addOne(self.base.allocator); + const atom = try decl.link.wasm.locals.addOne(wasm.base.allocator); atom.* = Atom.empty; - atom.alignment = tv.ty.abiAlignment(self.base.options.target); - try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); + atom.alignment = tv.ty.abiAlignment(wasm.base.options.target); + try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); - if (self.symbols_free_list.popOrNull()) |index| { + if (wasm.symbols_free_list.popOrNull()) |index| { atom.sym_index = index; - self.symbols.items[index] = symbol; + wasm.symbols.items[index] = symbol; } else { - atom.sym_index = @intCast(u32, self.symbols.items.len); - self.symbols.appendAssumeCapacity(symbol); + atom.sym_index = @intCast(u32, wasm.symbols.items.len); + wasm.symbols.appendAssumeCapacity(symbol); } - try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {}); - try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom); + try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); + try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom); - var value_bytes = std.ArrayList(u8).init(self.base.allocator); + var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); defer value_bytes.deinit(); const result = try codegen.generateSymbol( - &self.base, + &wasm.base, decl.srcLoc(), tv, &value_bytes, @@ -1014,7 +1013,7 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In }; atom.size = @intCast(u32, code.len); - try atom.code.appendSlice(self.base.allocator, code); + try atom.code.appendSlice(wasm.base.allocator, code); return atom.sym_index; } @@ -1022,9 +1021,9 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In /// such as an exported or imported symbol. /// If the symbol does not yet exist, creates a new one symbol instead /// and then returns the index to it. -pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 { - const name_index = try self.string_table.put(self.base.allocator, name); - const gop = try self.globals.getOrPut(self.base.allocator, name_index); +pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 { + const name_index = try wasm.string_table.put(wasm.base.allocator, name); + const gop = try wasm.globals.getOrPut(wasm.base.allocator, name_index); if (gop.found_existing) { return gop.value_ptr.*.index; } @@ -1038,46 +1037,46 @@ pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 { symbol.setGlobal(true); symbol.setUndefined(true); - const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: { - var index = @intCast(u32, self.symbols.items.len); - try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); - self.symbols.items.len += 1; + const sym_index = if (wasm.symbols_free_list.popOrNull()) |index| index else blk: { + var index = @intCast(u32, wasm.symbols.items.len); + try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); + wasm.symbols.items.len += 1; break :blk index; }; - self.symbols.items[sym_index] = symbol; + wasm.symbols.items[sym_index] = symbol; gop.value_ptr.* = .{ .index = sym_index, .file = null }; - try self.resolved_symbols.put(self.base.allocator, gop.value_ptr.*, {}); - try self.undefs.putNoClobber(self.base.allocator, name, gop.value_ptr.*); + try wasm.resolved_symbols.put(wasm.base.allocator, gop.value_ptr.*, {}); + try wasm.undefs.putNoClobber(wasm.base.allocator, name, gop.value_ptr.*); return sym_index; } /// For a given decl, find the given symbol index's atom, and create a relocation for the type. /// Returns the given pointer address pub fn getDeclVAddr( - self: *Wasm, + wasm: *Wasm, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo, ) !u64 { - const mod = self.base.options.module.?; + const mod = wasm.base.options.module.?; const decl = mod.declPtr(decl_index); const target_symbol_index = decl.link.wasm.sym_index; assert(target_symbol_index != 0); assert(reloc_info.parent_atom_index != 0); - const atom = self.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; - const is_wasm32 = self.base.options.target.cpu.arch == .wasm32; + const atom = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; + const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; if (decl.ty.zigTypeTag() == .Fn) { assert(reloc_info.addend == 0); // addend not allowed for function relocations // We found a function pointer, so add it to our table, // as function pointers are not allowed to be stored inside the data section. // They are instead stored in a function table which are called by index. - try self.addTableFunction(target_symbol_index); - try atom.relocs.append(self.base.allocator, .{ + try wasm.addTableFunction(target_symbol_index); + try atom.relocs.append(wasm.base.allocator, .{ .index = target_symbol_index, .offset = @intCast(u32, reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, }); } else { - try atom.relocs.append(self.base.allocator, .{ + try atom.relocs.append(wasm.base.allocator, .{ .index = target_symbol_index, .offset = @intCast(u32, reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, @@ -1091,22 +1090,22 @@ pub fn getDeclVAddr( return target_symbol_index; } -pub fn deleteExport(self: *Wasm, exp: Export) void { - if (self.llvm_object) |_| return; +pub fn deleteExport(wasm: *Wasm, exp: Export) void { + if (wasm.llvm_object) |_| return; const sym_index = exp.sym_index orelse return; const loc: SymbolLoc = .{ .file = null, .index = sym_index }; - const symbol = loc.getSymbol(self); - const symbol_name = self.string_table.get(symbol.name); + const symbol = loc.getSymbol(wasm); + const symbol_name = wasm.string_table.get(symbol.name); log.debug("Deleting export for decl '{s}'", .{symbol_name}); - if (self.export_names.fetchRemove(loc)) |kv| { - assert(self.globals.remove(kv.value)); + if (wasm.export_names.fetchRemove(loc)) |kv| { + assert(wasm.globals.remove(kv.value)); } else { - assert(self.globals.remove(symbol.name)); + assert(wasm.globals.remove(symbol.name)); } } pub fn updateDeclExports( - self: *Wasm, + wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index, exports: []const *Module.Export, @@ -1115,7 +1114,7 @@ pub fn updateDeclExports( @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports); + if (wasm.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports); } const decl = mod.declPtr(decl_index); @@ -1131,10 +1130,10 @@ pub fn updateDeclExports( continue; } - const export_name = try self.string_table.put(self.base.allocator, exp.options.name); - if (self.globals.getPtr(export_name)) |existing_loc| { + const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name); + if (wasm.globals.getPtr(export_name)) |existing_loc| { if (existing_loc.index == decl.link.wasm.sym_index) continue; - const existing_sym: Symbol = existing_loc.getSymbol(self).*; + const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; // When both the to-bo-exported symbol and the already existing symbol @@ -1148,7 +1147,7 @@ pub fn updateDeclExports( \\ first definition in '{s}' \\ next definition in '{s}' , - .{ exp.options.name, self.name, self.name }, + .{ exp.options.name, wasm.name, wasm.name }, )); continue; } else if (exp_is_weak) { @@ -1163,7 +1162,7 @@ pub fn updateDeclExports( const exported_decl = mod.declPtr(exp.exported_decl); const sym_index = exported_decl.link.wasm.sym_index; const sym_loc = exported_decl.link.wasm.symbolLoc(); - const symbol = sym_loc.getSymbol(self); + const symbol = sym_loc.getSymbol(wasm); switch (exp.options.linkage) { .Internal => { symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); @@ -1183,68 +1182,68 @@ pub fn updateDeclExports( }, } // Ensure the symbol will be exported using the given name - if (!mem.eql(u8, exp.options.name, sym_loc.getName(self))) { - try self.export_names.put(self.base.allocator, sym_loc, export_name); + if (!mem.eql(u8, exp.options.name, sym_loc.getName(wasm))) { + try wasm.export_names.put(wasm.base.allocator, sym_loc, export_name); } symbol.setGlobal(true); symbol.setUndefined(false); - try self.globals.put( - self.base.allocator, + try wasm.globals.put( + wasm.base.allocator, export_name, sym_loc, ); // if the symbol was previously undefined, remove it as an import - _ = self.imports.remove(sym_loc); - _ = self.undefs.swapRemove(exp.options.name); + _ = wasm.imports.remove(sym_loc); + _ = wasm.undefs.swapRemove(exp.options.name); exp.link.wasm.sym_index = sym_index; } } -pub fn freeDecl(self: *Wasm, decl_index: Module.Decl.Index) void { +pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); + if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); } - const mod = self.base.options.module.?; + const mod = wasm.base.options.module.?; const decl = mod.declPtr(decl_index); const atom = &decl.link.wasm; - self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {}; - _ = self.decls.remove(decl_index); - self.symbols.items[atom.sym_index].tag = .dead; + wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {}; + _ = wasm.decls.remove(decl_index); + wasm.symbols.items[atom.sym_index].tag = .dead; for (atom.locals.items) |local_atom| { - const local_symbol = &self.symbols.items[local_atom.sym_index]; + const local_symbol = &wasm.symbols.items[local_atom.sym_index]; local_symbol.tag = .dead; // also for any local symbol - self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {}; - assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc())); - assert(self.symbol_atom.remove(local_atom.symbolLoc())); + wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {}; + assert(wasm.resolved_symbols.swapRemove(local_atom.symbolLoc())); + assert(wasm.symbol_atom.remove(local_atom.symbolLoc())); } if (decl.isExtern()) { - _ = self.imports.remove(atom.symbolLoc()); + _ = wasm.imports.remove(atom.symbolLoc()); } - _ = self.resolved_symbols.swapRemove(atom.symbolLoc()); - _ = self.symbol_atom.remove(atom.symbolLoc()); + _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc()); + _ = wasm.symbol_atom.remove(atom.symbolLoc()); - if (self.dwarf) |*dwarf| { + if (wasm.dwarf) |*dwarf| { dwarf.freeDecl(decl); dwarf.freeAtom(&atom.dbg_info_atom); } - atom.deinit(self.base.allocator); + atom.deinit(wasm.base.allocator); } /// Appends a new entry to the indirect function table -pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void { - const index = @intCast(u32, self.function_table.count()); - try self.function_table.put(self.base.allocator, .{ .file = null, .index = symbol_index }, index); +pub fn addTableFunction(wasm: *Wasm, symbol_index: u32) !void { + const index = @intCast(u32, wasm.function_table.count()); + try wasm.function_table.put(wasm.base.allocator, .{ .file = null, .index = symbol_index }, index); } /// Assigns indexes to all indirect functions. /// Starts at offset 1, where the value `0` represents an unresolved function pointer /// or null-pointer -fn mapFunctionTable(self: *Wasm) void { - var it = self.function_table.valueIterator(); +fn mapFunctionTable(wasm: *Wasm) void { + var it = wasm.function_table.valueIterator(); var index: u32 = 1; while (it.next()) |value_ptr| : (index += 1) { value_ptr.* = index; @@ -1255,7 +1254,7 @@ fn mapFunctionTable(self: *Wasm) void { /// When `type_index` is non-null, we assume an external function. /// In all other cases, a data-symbol will be created instead. pub fn addOrUpdateImport( - self: *Wasm, + wasm: *Wasm, /// Name of the import name: []const u8, /// Symbol index that is external @@ -1268,28 +1267,28 @@ pub fn addOrUpdateImport( type_index: ?u32, ) !void { assert(symbol_index != 0); - // For the import name itself, we use the decl's name, rather than the fully qualified name - const decl_name_index = try self.string_table.put(self.base.allocator, name); - const symbol: *Symbol = &self.symbols.items[symbol_index]; + // For the import name itwasm, we use the decl's name, rather than the fully qualified name + const decl_name_index = try wasm.string_table.put(wasm.base.allocator, name); + const symbol: *Symbol = &wasm.symbols.items[symbol_index]; symbol.setUndefined(true); symbol.setGlobal(true); symbol.name = decl_name_index; - const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index); + const global_gop = try wasm.globals.getOrPut(wasm.base.allocator, decl_name_index); if (!global_gop.found_existing) { const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; global_gop.value_ptr.* = loc; - try self.resolved_symbols.put(self.base.allocator, loc, {}); - try self.undefs.putNoClobber(self.base.allocator, name, loc); + try wasm.resolved_symbols.put(wasm.base.allocator, loc, {}); + try wasm.undefs.putNoClobber(wasm.base.allocator, name, loc); } if (type_index) |ty_index| { - const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null }); + const gop = try wasm.imports.getOrPut(wasm.base.allocator, .{ .index = symbol_index, .file = null }); const module_name = if (lib_name) |l_name| blk: { break :blk mem.sliceTo(l_name, 0); - } else self.host_name; + } else wasm.host_name; if (!gop.found_existing) { gop.value_ptr.* = .{ - .module_name = try self.string_table.put(self.base.allocator, module_name), + .module_name = try wasm.string_table.put(wasm.base.allocator, module_name), .name = decl_name_index, .kind = .{ .function = ty_index }, }; @@ -1326,36 +1325,36 @@ const Kind = union(enum) { }; /// Parses an Atom and inserts its metadata into the corresponding sections. -fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { - const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(self); +fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void { + const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); const final_index: u32 = switch (kind) { .function => |fn_data| result: { - const index = @intCast(u32, self.functions.count() + self.imported_functions_count); - try self.functions.putNoClobber( - self.base.allocator, + const index = @intCast(u32, wasm.functions.count() + wasm.imported_functions_count); + try wasm.functions.putNoClobber( + wasm.base.allocator, .{ .file = null, .index = index }, .{ .type_index = fn_data.type_index }, ); symbol.tag = .function; symbol.index = index; - if (self.code_section_index == null) { - self.code_section_index = @intCast(u32, self.segments.items.len); - try self.segments.append(self.base.allocator, .{ + if (wasm.code_section_index == null) { + wasm.code_section_index = @intCast(u32, wasm.segments.items.len); + try wasm.segments.append(wasm.base.allocator, .{ .alignment = atom.alignment, .size = atom.size, .offset = 0, }); } - break :result self.code_section_index.?; + break :result wasm.code_section_index.?; }, .data => result: { - const segment_name = try std.mem.concat(self.base.allocator, u8, &.{ + const segment_name = try std.mem.concat(wasm.base.allocator, u8, &.{ kind.segmentName(), - self.string_table.get(symbol.name), + wasm.string_table.get(symbol.name), }); - errdefer self.base.allocator.free(segment_name); + errdefer wasm.base.allocator.free(segment_name); const segment_info: types.Segment = .{ .name = segment_name, .alignment = atom.alignment, @@ -1367,59 +1366,59 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { // we set the entire region of it to zeroes. // We do not have to do this when exporting the memory (the default) because the runtime // will do it for us, and we do not emit the bss segment at all. - if ((self.base.options.output_mode == .Obj or self.base.options.import_memory) and kind.data == .uninitialized) { + if ((wasm.base.options.output_mode == .Obj or wasm.base.options.import_memory) and kind.data == .uninitialized) { std.mem.set(u8, atom.code.items, 0); } - const should_merge = self.base.options.output_mode != .Obj; - const gop = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(should_merge)); + const should_merge = wasm.base.options.output_mode != .Obj; + const gop = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(should_merge)); if (gop.found_existing) { const index = gop.value_ptr.*; - self.segments.items[index].size += atom.size; + wasm.segments.items[index].size += atom.size; - symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); + symbol.index = @intCast(u32, wasm.segment_info.getIndex(index).?); // segment info already exists, so free its memory - self.base.allocator.free(segment_name); + wasm.base.allocator.free(segment_name); break :result index; } else { - const index = @intCast(u32, self.segments.items.len); - try self.segments.append(self.base.allocator, .{ + const index = @intCast(u32, wasm.segments.items.len); + try wasm.segments.append(wasm.base.allocator, .{ .alignment = atom.alignment, .size = 0, .offset = 0, }); gop.value_ptr.* = index; - const info_index = @intCast(u32, self.segment_info.count()); - try self.segment_info.put(self.base.allocator, index, segment_info); + const info_index = @intCast(u32, wasm.segment_info.count()); + try wasm.segment_info.put(wasm.base.allocator, index, segment_info); symbol.index = info_index; break :result index; } }, }; - const segment: *Segment = &self.segments.items[final_index]; + const segment: *Segment = &wasm.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - try self.appendAtomAtIndex(final_index, atom); + try wasm.appendAtomAtIndex(final_index, atom); } /// From a given index, append the given `Atom` at the back of the linked list. /// Simply inserts it into the map of atoms when it doesn't exist yet. -pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void { - if (self.atoms.getPtr(index)) |last| { +pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void { + if (wasm.atoms.getPtr(index)) |last| { last.*.next = atom; atom.prev = last.*; last.* = atom; } else { - try self.atoms.putNoClobber(self.base.allocator, index, atom); + try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom); } } /// Allocates debug atoms into their respective debug sections /// to merge them with maybe-existing debug atoms from object files. -fn allocateDebugAtoms(self: *Wasm) !void { - if (self.dwarf == null) return; +fn allocateDebugAtoms(wasm: *Wasm) !void { + if (wasm.dwarf == null) return; const allocAtom = struct { fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { @@ -1435,24 +1434,24 @@ fn allocateDebugAtoms(self: *Wasm) !void { } }.f; - try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?); - try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?); - try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?); - try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?); - try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?); - try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?); - try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?); - try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?); + try allocAtom(wasm, &wasm.debug_info_index, wasm.debug_info_atom.?); + try allocAtom(wasm, &wasm.debug_line_index, wasm.debug_line_atom.?); + try allocAtom(wasm, &wasm.debug_loc_index, wasm.debug_loc_atom.?); + try allocAtom(wasm, &wasm.debug_str_index, wasm.debug_str_atom.?); + try allocAtom(wasm, &wasm.debug_ranges_index, wasm.debug_ranges_atom.?); + try allocAtom(wasm, &wasm.debug_abbrev_index, wasm.debug_abbrev_atom.?); + try allocAtom(wasm, &wasm.debug_pubnames_index, wasm.debug_pubnames_atom.?); + try allocAtom(wasm, &wasm.debug_pubtypes_index, wasm.debug_pubtypes_atom.?); } -fn allocateAtoms(self: *Wasm) !void { +fn allocateAtoms(wasm: *Wasm) !void { // first sort the data segments - try sortDataSegments(self); - try allocateDebugAtoms(self); + try sortDataSegments(wasm); + try allocateDebugAtoms(wasm); - var it = self.atoms.iterator(); + var it = wasm.atoms.iterator(); while (it.next()) |entry| { - const segment = &self.segments.items[entry.key_ptr.*]; + const segment = &wasm.segments.items[entry.key_ptr.*]; var atom: *Atom = entry.value_ptr.*.getFirst(); var offset: u32 = 0; while (true) { @@ -1460,26 +1459,26 @@ fn allocateAtoms(self: *Wasm) !void { atom.offset = offset; const symbol_loc = atom.symbolLoc(); log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ - symbol_loc.getName(self), + symbol_loc.getName(wasm), offset, offset + atom.size, atom.size, }); offset += atom.size; - try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers + try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); // Update atom pointers atom = atom.next orelse break; } segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); } } -fn sortDataSegments(self: *Wasm) !void { +fn sortDataSegments(wasm: *Wasm) !void { var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; - try new_mapping.ensureUnusedCapacity(self.base.allocator, self.data_segments.count()); - errdefer new_mapping.deinit(self.base.allocator); + try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count()); + errdefer new_mapping.deinit(wasm.base.allocator); - const keys = try self.base.allocator.dupe([]const u8, self.data_segments.keys()); - defer self.base.allocator.free(keys); + const keys = try wasm.base.allocator.dupe([]const u8, wasm.data_segments.keys()); + defer wasm.base.allocator.free(keys); const SortContext = struct { fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { @@ -1496,63 +1495,63 @@ fn sortDataSegments(self: *Wasm) !void { std.sort.sort([]const u8, keys, {}, SortContext.sort); for (keys) |key| { - const segment_index = self.data_segments.get(key).?; + const segment_index = wasm.data_segments.get(key).?; new_mapping.putAssumeCapacity(key, segment_index); } - self.data_segments.deinit(self.base.allocator); - self.data_segments = new_mapping; + wasm.data_segments.deinit(wasm.base.allocator); + wasm.data_segments = new_mapping; } -fn setupImports(self: *Wasm) !void { +fn setupImports(wasm: *Wasm) !void { log.debug("Merging imports", .{}); - var discarded_it = self.discarded.keyIterator(); + var discarded_it = wasm.discarded.keyIterator(); while (discarded_it.next()) |discarded| { if (discarded.file == null) { // remove an import if it was resolved - if (self.imports.remove(discarded.*)) { + if (wasm.imports.remove(discarded.*)) { log.debug("Removed symbol '{s}' as an import", .{ - discarded.getName(self), + discarded.getName(wasm), }); } } } - for (self.resolved_symbols.keys()) |symbol_loc| { + for (wasm.resolved_symbols.keys()) |symbol_loc| { if (symbol_loc.file == null) { // imports generated by Zig code are already in the `import` section continue; } - const symbol = symbol_loc.getSymbol(self); - if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { + const symbol = symbol_loc.getSymbol(wasm); + if (std.mem.eql(u8, symbol_loc.getName(wasm), "__indirect_function_table")) { continue; } if (!symbol.requiresImport()) { continue; } - log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(self)}); - const object = self.objects.items[symbol_loc.file.?]; + log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)}); + const object = wasm.objects.items[symbol_loc.file.?]; const import = object.findImport(symbol.tag.externalType(), symbol.index); // We copy the import to a new import to ensure the names contain references // to the internal string table, rather than of the object file. var new_imp: types.Import = .{ - .module_name = try self.string_table.put(self.base.allocator, object.string_table.get(import.module_name)), - .name = try self.string_table.put(self.base.allocator, object.string_table.get(import.name)), + .module_name = try wasm.string_table.put(wasm.base.allocator, object.string_table.get(import.module_name)), + .name = try wasm.string_table.put(wasm.base.allocator, object.string_table.get(import.name)), .kind = import.kind, }; // TODO: De-duplicate imports when they contain the same names and type - try self.imports.putNoClobber(self.base.allocator, symbol_loc, new_imp); + try wasm.imports.putNoClobber(wasm.base.allocator, symbol_loc, new_imp); } // Assign all indexes of the imports to their representing symbols var function_index: u32 = 0; var global_index: u32 = 0; var table_index: u32 = 0; - var it = self.imports.iterator(); + var it = wasm.imports.iterator(); while (it.next()) |entry| { - const symbol = entry.key_ptr.*.getSymbol(self); + const symbol = entry.key_ptr.*.getSymbol(wasm); const import: types.Import = entry.value_ptr.*; switch (import.kind) { .function => { @@ -1570,9 +1569,9 @@ fn setupImports(self: *Wasm) !void { else => unreachable, } } - self.imported_functions_count = function_index; - self.imported_globals_count = global_index; - self.imported_tables_count = table_index; + wasm.imported_functions_count = function_index; + wasm.imported_globals_count = global_index; + wasm.imported_tables_count = table_index; log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{ function_index, @@ -1583,26 +1582,26 @@ fn setupImports(self: *Wasm) !void { /// Takes the global, function and table section from each linked object file /// and merges it into a single section for each. -fn mergeSections(self: *Wasm) !void { +fn mergeSections(wasm: *Wasm) !void { // append the indirect function table if initialized - if (self.string_table.getOffset("__indirect_function_table")) |offset| { - const sym_loc = self.globals.get(offset).?; - const table: wasm.Table = .{ - .limits = .{ .min = @intCast(u32, self.function_table.count()), .max = null }, + if (wasm.string_table.getOffset("__indirect_function_table")) |offset| { + const sym_loc = wasm.globals.get(offset).?; + const table: std.wasm.Table = .{ + .limits = .{ .min = @intCast(u32, wasm.function_table.count()), .max = null }, .reftype = .funcref, }; - sym_loc.getSymbol(self).index = @intCast(u32, self.tables.items.len) + self.imported_tables_count; - try self.tables.append(self.base.allocator, table); + sym_loc.getSymbol(wasm).index = @intCast(u32, wasm.tables.items.len) + wasm.imported_tables_count; + try wasm.tables.append(wasm.base.allocator, table); } - for (self.resolved_symbols.keys()) |sym_loc| { + for (wasm.resolved_symbols.keys()) |sym_loc| { if (sym_loc.file == null) { // Zig code-generated symbols are already within the sections and do not // require to be merged continue; } - const object = self.objects.items[sym_loc.file.?]; + const object = wasm.objects.items[sym_loc.file.?]; const symbol = &object.symtable[sym_loc.index]; if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) { // Skip undefined symbols as they go in the `import` section @@ -1615,51 +1614,51 @@ fn mergeSections(self: *Wasm) !void { switch (symbol.tag) { .function => { const original_func = object.functions[index]; - const gop = try self.functions.getOrPut( - self.base.allocator, + const gop = try wasm.functions.getOrPut( + wasm.base.allocator, .{ .file = sym_loc.file, .index = symbol.index }, ); if (!gop.found_existing) { gop.value_ptr.* = original_func; } - symbol.index = @intCast(u32, gop.index) + self.imported_functions_count; + symbol.index = @intCast(u32, gop.index) + wasm.imported_functions_count; }, .global => { const original_global = object.globals[index]; - symbol.index = @intCast(u32, self.wasm_globals.items.len) + self.imported_globals_count; - try self.wasm_globals.append(self.base.allocator, original_global); + symbol.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count; + try wasm.wasm_globals.append(wasm.base.allocator, original_global); }, .table => { const original_table = object.tables[index]; - symbol.index = @intCast(u32, self.tables.items.len) + self.imported_tables_count; - try self.tables.append(self.base.allocator, original_table); + symbol.index = @intCast(u32, wasm.tables.items.len) + wasm.imported_tables_count; + try wasm.tables.append(wasm.base.allocator, original_table); }, else => unreachable, } } - log.debug("Merged ({d}) functions", .{self.functions.count()}); - log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len}); - log.debug("Merged ({d}) tables", .{self.tables.items.len}); + log.debug("Merged ({d}) functions", .{wasm.functions.count()}); + log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len}); + log.debug("Merged ({d}) tables", .{wasm.tables.items.len}); } /// Merges function types of all object files into the final /// 'types' section, while assigning the type index to the representing /// section (import, export, function). -fn mergeTypes(self: *Wasm) !void { +fn mergeTypes(wasm: *Wasm) !void { // A map to track which functions have already had their // type inserted. If we do this for the same function multiple times, // it will be overwritten with the incorrect type. - var dirty = std.AutoHashMap(u32, void).init(self.base.allocator); - try dirty.ensureUnusedCapacity(@intCast(u32, self.functions.count())); + var dirty = std.AutoHashMap(u32, void).init(wasm.base.allocator); + try dirty.ensureUnusedCapacity(@intCast(u32, wasm.functions.count())); defer dirty.deinit(); - for (self.resolved_symbols.keys()) |sym_loc| { + for (wasm.resolved_symbols.keys()) |sym_loc| { if (sym_loc.file == null) { // zig code-generated symbols are already present in final type section continue; } - const object = self.objects.items[sym_loc.file.?]; + const object = wasm.objects.items[sym_loc.file.?]; const symbol = object.symtable[sym_loc.index]; if (symbol.tag != .function) { // Only functions have types @@ -1667,32 +1666,32 @@ fn mergeTypes(self: *Wasm) !void { } if (symbol.isUndefined()) { - log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(self)}); - const import: *types.Import = self.imports.getPtr(sym_loc).?; + log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); + const import: *types.Import = wasm.imports.getPtr(sym_loc).?; const original_type = object.func_types[import.kind.function]; - import.kind.function = try self.putOrGetFuncType(original_type); + import.kind.function = try wasm.putOrGetFuncType(original_type); } else if (!dirty.contains(symbol.index)) { - log.debug("Adding type from function '{s}'", .{sym_loc.getName(self)}); - const func = &self.functions.values()[symbol.index - self.imported_functions_count]; - func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]); + log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)}); + const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count]; + func.type_index = try wasm.putOrGetFuncType(object.func_types[func.type_index]); dirty.putAssumeCapacityNoClobber(symbol.index, {}); } } - log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len}); + log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len}); } -fn setupExports(self: *Wasm) !void { - if (self.base.options.output_mode == .Obj) return; +fn setupExports(wasm: *Wasm) !void { + if (wasm.base.options.output_mode == .Obj) return; log.debug("Building exports from symbols", .{}); - for (self.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(self); + for (wasm.resolved_symbols.keys()) |sym_loc| { + const symbol = sym_loc.getSymbol(wasm); if (!symbol.isExported()) continue; - const sym_name = sym_loc.getName(self); - const export_name = if (self.export_names.get(sym_loc)) |name| name else blk: { + const sym_name = sym_loc.getName(wasm); + const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: { if (sym_loc.file == null) break :blk symbol.name; - break :blk try self.string_table.put(self.base.allocator, sym_name); + break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); }; const exp: types.Export = .{ .name = export_name, @@ -1701,21 +1700,21 @@ fn setupExports(self: *Wasm) !void { }; log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, - self.string_table.get(exp.name), + wasm.string_table.get(exp.name), exp.index, }); - try self.exports.append(self.base.allocator, exp); + try wasm.exports.append(wasm.base.allocator, exp); } - log.debug("Completed building exports. Total count: ({d})", .{self.exports.items.len}); + log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len}); } -fn setupStart(self: *Wasm) !void { - const entry_name = self.base.options.entry orelse "_start"; +fn setupStart(wasm: *Wasm) !void { + const entry_name = wasm.base.options.entry orelse "_start"; - const symbol_name_offset = self.string_table.getOffset(entry_name) orelse { - if (self.base.options.output_mode == .Exe) { - if (self.base.options.wasi_exec_model == .reactor) return; // Not required for reactors + const symbol_name_offset = wasm.string_table.getOffset(entry_name) orelse { + if (wasm.base.options.output_mode == .Exe) { + if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors } else { return; // No entry point needed for non-executable wasm files } @@ -1723,45 +1722,45 @@ fn setupStart(self: *Wasm) !void { return error.MissingSymbol; }; - const symbol_loc = self.globals.get(symbol_name_offset).?; - const symbol = symbol_loc.getSymbol(self); + const symbol_loc = wasm.globals.get(symbol_name_offset).?; + const symbol = symbol_loc.getSymbol(wasm); if (symbol.tag != .function) { log.err("Entry symbol '{s}' is not a function", .{entry_name}); return error.InvalidEntryKind; } // Ensure the symbol is exported so host environment can access it - if (self.base.options.output_mode != .Obj) { + if (wasm.base.options.output_mode != .Obj) { symbol.setFlag(.WASM_SYM_EXPORTED); } } /// Sets up the memory section of the wasm module, as well as the stack. -fn setupMemory(self: *Wasm) !void { +fn setupMemory(wasm: *Wasm) !void { log.debug("Setting up memory layout", .{}); const page_size = 64 * 1024; - const stack_size = self.base.options.stack_size_override orelse page_size * 1; + const stack_size = wasm.base.options.stack_size_override orelse page_size * 1; const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention // Always place the stack at the start by default // unless the user specified the global-base flag var place_stack_first = true; - var memory_ptr: u64 = if (self.base.options.global_base) |base| blk: { + var memory_ptr: u64 = if (wasm.base.options.global_base) |base| blk: { place_stack_first = false; break :blk base; } else 0; - const is_obj = self.base.options.output_mode == .Obj; + const is_obj = wasm.base.options.output_mode == .Obj; if (place_stack_first and !is_obj) { memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); memory_ptr += stack_size; // We always put the stack pointer global at index 0 - self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); + wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); } var offset: u32 = @intCast(u32, memory_ptr); - for (self.data_segments.values()) |segment_index| { - const segment = &self.segments.items[segment_index]; + for (wasm.data_segments.values()) |segment_index| { + const segment = &wasm.segments.items[segment_index]; memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); memory_ptr += segment.size; segment.offset = offset; @@ -1771,14 +1770,14 @@ fn setupMemory(self: *Wasm) !void { if (!place_stack_first and !is_obj) { memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); memory_ptr += stack_size; - self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); + wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); } // Setup the max amount of pages // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 const max_memory_allowed: u64 = (1 << 32) - 1; - if (self.base.options.initial_memory) |initial_memory| { + if (wasm.base.options.initial_memory) |initial_memory| { if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { log.err("Initial memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -1796,10 +1795,10 @@ fn setupMemory(self: *Wasm) !void { // In case we do not import memory, but define it ourselves, // set the minimum amount of pages on the memory section. - self.memories.limits.min = @intCast(u32, std.mem.alignForwardGeneric(u64, memory_ptr, page_size) / page_size); - log.debug("Total memory pages: {d}", .{self.memories.limits.min}); + wasm.memories.limits.min = @intCast(u32, std.mem.alignForwardGeneric(u64, memory_ptr, page_size) / page_size); + log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); - if (self.base.options.max_memory) |max_memory| { + if (wasm.base.options.max_memory) |max_memory| { if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { log.err("Maximum memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -1812,83 +1811,83 @@ fn setupMemory(self: *Wasm) !void { log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed}); return error.MemoryTooBig; } - self.memories.limits.max = @intCast(u32, max_memory / page_size); - log.debug("Maximum memory pages: {?d}", .{self.memories.limits.max}); + wasm.memories.limits.max = @intCast(u32, max_memory / page_size); + log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); } } /// From a given object's index and the index of the segment, returns the corresponding /// index of the segment within the final data section. When the segment does not yet /// exist, a new one will be initialized and appended. The new index will be returned in that case. -pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { - const object: Object = self.objects.items[object_index]; +pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { + const object: Object = wasm.objects.items[object_index]; const relocatable_data = object.relocatable_data[relocatable_index]; - const index = @intCast(u32, self.segments.items.len); + const index = @intCast(u32, wasm.segments.items.len); switch (relocatable_data.type) { .data => { const segment_info = object.segment_info[relocatable_data.index]; - const merge_segment = self.base.options.output_mode != .Obj; - const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); + const merge_segment = wasm.base.options.output_mode != .Obj; + const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); if (!result.found_existing) { result.value_ptr.* = index; - try self.appendDummySegment(); + try wasm.appendDummySegment(); return index; } else return result.value_ptr.*; }, - .code => return self.code_section_index orelse blk: { - self.code_section_index = index; - try self.appendDummySegment(); + .code => return wasm.code_section_index orelse blk: { + wasm.code_section_index = index; + try wasm.appendDummySegment(); break :blk index; }, .debug => { const debug_name = object.getDebugName(relocatable_data); if (mem.eql(u8, debug_name, ".debug_info")) { - return self.debug_info_index orelse blk: { - self.debug_info_index = index; - try self.appendDummySegment(); + return wasm.debug_info_index orelse blk: { + wasm.debug_info_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_line")) { - return self.debug_line_index orelse blk: { - self.debug_line_index = index; - try self.appendDummySegment(); + return wasm.debug_line_index orelse blk: { + wasm.debug_line_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_loc")) { - return self.debug_loc_index orelse blk: { - self.debug_loc_index = index; - try self.appendDummySegment(); + return wasm.debug_loc_index orelse blk: { + wasm.debug_loc_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_ranges")) { - return self.debug_line_index orelse blk: { - self.debug_ranges_index = index; - try self.appendDummySegment(); + return wasm.debug_line_index orelse blk: { + wasm.debug_ranges_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { - return self.debug_pubnames_index orelse blk: { - self.debug_pubnames_index = index; - try self.appendDummySegment(); + return wasm.debug_pubnames_index orelse blk: { + wasm.debug_pubnames_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { - return self.debug_pubtypes_index orelse blk: { - self.debug_pubtypes_index = index; - try self.appendDummySegment(); + return wasm.debug_pubtypes_index orelse blk: { + wasm.debug_pubtypes_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { - return self.debug_abbrev_index orelse blk: { - self.debug_abbrev_index = index; - try self.appendDummySegment(); + return wasm.debug_abbrev_index orelse blk: { + wasm.debug_abbrev_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else if (mem.eql(u8, debug_name, ".debug_str")) { - return self.debug_str_index orelse blk: { - self.debug_str_index = index; - try self.appendDummySegment(); + return wasm.debug_str_index orelse blk: { + wasm.debug_str_index = index; + try wasm.appendDummySegment(); break :blk index; }; } else { @@ -1901,8 +1900,8 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 } /// Appends a new segment with default field values -fn appendDummySegment(self: *Wasm) !void { - try self.segments.append(self.base.allocator, .{ +fn appendDummySegment(wasm: *Wasm) !void { + try wasm.segments.append(wasm.base.allocator, .{ .alignment = 1, .size = 0, .offset = 0, @@ -1912,8 +1911,8 @@ fn appendDummySegment(self: *Wasm) !void { /// Returns the symbol index of the error name table. /// /// When the symbol does not yet exist, it will create a new one instead. -pub fn getErrorTableSymbol(self: *Wasm) !u32 { - if (self.error_table_symbol) |symbol| { +pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { + if (wasm.error_table_symbol) |symbol| { return symbol; } @@ -1922,14 +1921,14 @@ pub fn getErrorTableSymbol(self: *Wasm) !u32 { // during `flush` when we know all possible error names. // As sym_index '0' is reserved, we use it for our stack pointer symbol - const symbol_index = self.symbols_free_list.popOrNull() orelse blk: { - const index = @intCast(u32, self.symbols.items.len); - _ = try self.symbols.addOne(self.base.allocator); + const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: { + const index = @intCast(u32, wasm.symbols.items.len); + _ = try wasm.symbols.addOne(wasm.base.allocator); break :blk index; }; - const sym_name = try self.string_table.put(self.base.allocator, "__zig_err_name_table"); - const symbol = &self.symbols.items[symbol_index]; + const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table"); + const symbol = &wasm.symbols.items[symbol_index]; symbol.* = .{ .name = sym_name, .tag = .data, @@ -1940,17 +1939,17 @@ pub fn getErrorTableSymbol(self: *Wasm) !u32 { const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); - const atom = try self.base.allocator.create(Atom); + const atom = try wasm.base.allocator.create(Atom); atom.* = Atom.empty; atom.sym_index = symbol_index; - atom.alignment = slice_ty.abiAlignment(self.base.options.target); - try self.managed_atoms.append(self.base.allocator, atom); + atom.alignment = slice_ty.abiAlignment(wasm.base.options.target); + try wasm.managed_atoms.append(wasm.base.allocator, atom); const loc = atom.symbolLoc(); - try self.resolved_symbols.put(self.base.allocator, loc, {}); - try self.symbol_atom.put(self.base.allocator, loc, atom); + try wasm.resolved_symbols.put(wasm.base.allocator, loc, {}); + try wasm.symbol_atom.put(wasm.base.allocator, loc, atom); log.debug("Error name table was created with symbol index: ({d})", .{symbol_index}); - self.error_table_symbol = symbol_index; + wasm.error_table_symbol = symbol_index; return symbol_index; } @@ -1958,24 +1957,24 @@ pub fn getErrorTableSymbol(self: *Wasm) !u32 { /// /// This creates a table that consists of pointers and length to each error name. /// The table is what is being pointed to within the runtime bodies that are generated. -fn populateErrorNameTable(self: *Wasm) !void { - const symbol_index = self.error_table_symbol orelse return; - const atom: *Atom = self.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; +fn populateErrorNameTable(wasm: *Wasm) !void { + const symbol_index = wasm.error_table_symbol orelse return; + const atom: *Atom = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; // Rather than creating a symbol for each individual error name, // we create a symbol for the entire region of error names. We then calculate // the pointers into the list using addends which are appended to the relocation. - const names_atom = try self.base.allocator.create(Atom); + const names_atom = try wasm.base.allocator.create(Atom); names_atom.* = Atom.empty; - try self.managed_atoms.append(self.base.allocator, names_atom); - const names_symbol_index = self.symbols_free_list.popOrNull() orelse blk: { - const index = @intCast(u32, self.symbols.items.len); - _ = try self.symbols.addOne(self.base.allocator); + try wasm.managed_atoms.append(wasm.base.allocator, names_atom); + const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: { + const index = @intCast(u32, wasm.symbols.items.len); + _ = try wasm.symbols.addOne(wasm.base.allocator); break :blk index; }; names_atom.sym_index = names_symbol_index; names_atom.alignment = 1; - const sym_name = try self.string_table.put(self.base.allocator, "__zig_err_names"); - const names_symbol = &self.symbols.items[names_symbol_index]; + const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names"); + const names_symbol = &wasm.symbols.items[names_symbol_index]; names_symbol.* = .{ .name = sym_name, .tag = .data, @@ -1988,27 +1987,27 @@ fn populateErrorNameTable(self: *Wasm) !void { // Addend for each relocation to the table var addend: u32 = 0; - const mod = self.base.options.module.?; + const mod = wasm.base.options.module.?; for (mod.error_name_list.items) |error_name| { const len = @intCast(u32, error_name.len + 1); // names are 0-termianted const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); const offset = @intCast(u32, atom.code.items.len); // first we create the data for the slice of the name - try atom.code.appendNTimes(self.base.allocator, 0, 4); // ptr to name, will be relocated - try atom.code.writer(self.base.allocator).writeIntLittle(u32, len - 1); + try atom.code.appendNTimes(wasm.base.allocator, 0, 4); // ptr to name, will be relocated + try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1); // create relocation to the error name - try atom.relocs.append(self.base.allocator, .{ + try atom.relocs.append(wasm.base.allocator, .{ .index = names_symbol_index, .relocation_type = .R_WASM_MEMORY_ADDR_I32, .offset = offset, .addend = addend, }); - atom.size += @intCast(u32, slice_ty.abiSize(self.base.options.target)); + atom.size += @intCast(u32, slice_ty.abiSize(wasm.base.options.target)); addend += len; // as we updated the error name table, we now store the actual name within the names atom - try names_atom.code.ensureUnusedCapacity(self.base.allocator, len); + try names_atom.code.ensureUnusedCapacity(wasm.base.allocator, len); names_atom.code.appendSliceAssumeCapacity(error_name); names_atom.code.appendAssumeCapacity(0); @@ -2017,51 +2016,51 @@ fn populateErrorNameTable(self: *Wasm) !void { names_atom.size = addend; const name_loc = names_atom.symbolLoc(); - try self.resolved_symbols.put(self.base.allocator, name_loc, {}); - try self.symbol_atom.put(self.base.allocator, name_loc, names_atom); + try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {}); + try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom); // link the atoms with the rest of the binary so they can be allocated // and relocations will be performed. - try self.parseAtom(atom, .{ .data = .read_only }); - try self.parseAtom(names_atom, .{ .data = .read_only }); + try wasm.parseAtom(atom, .{ .data = .read_only }); + try wasm.parseAtom(names_atom, .{ .data = .read_only }); } /// From a given index variable, creates a new debug section. /// This initializes the index, appends a new segment, /// and finally, creates a managed `Atom`. -pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { - const new_index = @intCast(u32, self.segments.items.len); +pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom { + const new_index = @intCast(u32, wasm.segments.items.len); index.* = new_index; - try self.appendDummySegment(); + try wasm.appendDummySegment(); // _ = index; - const sym_index = self.symbols_free_list.popOrNull() orelse idx: { - const tmp_index = @intCast(u32, self.symbols.items.len); - _ = try self.symbols.addOne(self.base.allocator); + const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { + const tmp_index = @intCast(u32, wasm.symbols.items.len); + _ = try wasm.symbols.addOne(wasm.base.allocator); break :idx tmp_index; }; - self.symbols.items[sym_index] = .{ + wasm.symbols.items[sym_index] = .{ .tag = .section, - .name = try self.string_table.put(self.base.allocator, name), + .name = try wasm.string_table.put(wasm.base.allocator, name), .index = 0, .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), }; - const atom = try self.base.allocator.create(Atom); + const atom = try wasm.base.allocator.create(Atom); atom.* = Atom.empty; atom.alignment = 1; // debug sections are always 1-byte-aligned atom.sym_index = sym_index; - try self.managed_atoms.append(self.base.allocator, atom); - try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); + try wasm.managed_atoms.append(wasm.base.allocator, atom); + try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); return atom; } -fn resetState(self: *Wasm) void { - for (self.segment_info.values()) |segment_info| { - self.base.allocator.free(segment_info.name); +fn resetState(wasm: *Wasm) void { + for (wasm.segment_info.values()) |segment_info| { + wasm.base.allocator.free(segment_info.name); } - if (self.base.options.module) |mod| { - var decl_it = self.decls.keyIterator(); + if (wasm.base.options.module) |mod| { + var decl_it = wasm.decls.keyIterator(); while (decl_it.next()) |decl_index_ptr| { const decl = mod.declPtr(decl_index_ptr.*); const atom = &decl.link.wasm; @@ -2074,46 +2073,46 @@ fn resetState(self: *Wasm) void { } } } - self.functions.clearRetainingCapacity(); - self.exports.clearRetainingCapacity(); - self.segments.clearRetainingCapacity(); - self.segment_info.clearRetainingCapacity(); - self.data_segments.clearRetainingCapacity(); - self.atoms.clearRetainingCapacity(); - self.symbol_atom.clearRetainingCapacity(); - self.code_section_index = null; - self.debug_info_index = null; - self.debug_line_index = null; - self.debug_loc_index = null; - self.debug_str_index = null; - self.debug_ranges_index = null; - self.debug_abbrev_index = null; - self.debug_pubnames_index = null; - self.debug_pubtypes_index = null; + wasm.functions.clearRetainingCapacity(); + wasm.exports.clearRetainingCapacity(); + wasm.segments.clearRetainingCapacity(); + wasm.segment_info.clearRetainingCapacity(); + wasm.data_segments.clearRetainingCapacity(); + wasm.atoms.clearRetainingCapacity(); + wasm.symbol_atom.clearRetainingCapacity(); + wasm.code_section_index = null; + wasm.debug_info_index = null; + wasm.debug_line_index = null; + wasm.debug_loc_index = null; + wasm.debug_str_index = null; + wasm.debug_ranges_index = null; + wasm.debug_abbrev_index = null; + wasm.debug_pubnames_index = null; + wasm.debug_pubtypes_index = null; } -pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { - if (self.base.options.emit == null) { +pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { + if (wasm.base.options.emit == null) { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| { + if (wasm.llvm_object) |llvm_object| { return try llvm_object.flushModule(comp, prog_node); } } return; } - if (build_options.have_llvm and self.base.options.use_lld) { - return self.linkWithLLD(comp, prog_node); + if (build_options.have_llvm and wasm.base.options.use_lld) { + return wasm.linkWithLLD(comp, prog_node); } else { - return self.flushModule(comp, prog_node); + return wasm.flushModule(comp, prog_node); } } -pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { +pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| { + if (wasm.llvm_object) |llvm_object| { return try llvm_object.flushModule(comp, prog_node); } } @@ -2123,7 +2122,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod defer sub_prog_node.end(); // ensure the error names table is populated when an error name is referenced - try self.populateErrorNameTable(); + try wasm.populateErrorNameTable(); // The amount of sections that will be written var section_count: u32 = 0; @@ -2133,15 +2132,15 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod var data_section_index: ?u32 = null; // Used for all temporary memory allocated during flushin - var arena_instance = std.heap.ArenaAllocator.init(self.base.allocator); + var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator); defer arena_instance.deinit(); const arena = arena_instance.allocator(); // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList([]const u8).init(arena); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); + try positionals.ensureUnusedCapacity(wasm.base.options.objects.len); - for (self.base.options.objects) |object| { + for (wasm.base.options.objects) |object| { positionals.appendAssumeCapacity(object.path); } @@ -2153,66 +2152,66 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try positionals.append(lib.full_object_path); } - try self.parseInputFiles(positionals.items); + try wasm.parseInputFiles(positionals.items); - for (self.objects.items) |_, object_index| { - try self.resolveSymbolsInObject(@intCast(u16, object_index)); + for (wasm.objects.items) |_, object_index| { + try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); } - try self.resolveSymbolsInArchives(); - try self.checkUndefinedSymbols(); + try wasm.resolveSymbolsInArchives(); + try wasm.checkUndefinedSymbols(); // When we finish/error we reset the state of the linker // So we can rebuild the binary file on each incremental update - defer self.resetState(); - try self.setupStart(); - try self.setupImports(); - if (self.base.options.module) |mod| { - var decl_it = self.decls.keyIterator(); + defer wasm.resetState(); + try wasm.setupStart(); + try wasm.setupImports(); + if (wasm.base.options.module) |mod| { + var decl_it = wasm.decls.keyIterator(); while (decl_it.next()) |decl_index_ptr| { const decl = mod.declPtr(decl_index_ptr.*); if (decl.isExtern()) continue; const atom = &decl.*.link.wasm; if (decl.ty.zigTypeTag() == .Fn) { - try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); + try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm }); } else if (decl.getVariable()) |variable| { if (!variable.is_mutable) { - try self.parseAtom(atom, .{ .data = .read_only }); + try wasm.parseAtom(atom, .{ .data = .read_only }); } else if (variable.init.isUndefDeep()) { - try self.parseAtom(atom, .{ .data = .uninitialized }); + try wasm.parseAtom(atom, .{ .data = .uninitialized }); } else { - try self.parseAtom(atom, .{ .data = .initialized }); + try wasm.parseAtom(atom, .{ .data = .initialized }); } } else { - try self.parseAtom(atom, .{ .data = .read_only }); + try wasm.parseAtom(atom, .{ .data = .read_only }); } // also parse atoms for a decl's locals for (atom.locals.items) |*local_atom| { - try self.parseAtom(local_atom, .{ .data = .read_only }); + try wasm.parseAtom(local_atom, .{ .data = .read_only }); } } - if (self.dwarf) |*dwarf| { - try dwarf.flushModule(&self.base, self.base.options.module.?); + if (wasm.dwarf) |*dwarf| { + try dwarf.flushModule(&wasm.base, wasm.base.options.module.?); } } - for (self.objects.items) |*object, object_index| { - try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); + for (wasm.objects.items) |*object, object_index| { + try object.parseIntoAtoms(wasm.base.allocator, @intCast(u16, object_index), wasm); } - try self.allocateAtoms(); - try self.setupMemory(); - self.mapFunctionTable(); - try self.mergeSections(); - try self.mergeTypes(); - try self.setupExports(); + try wasm.allocateAtoms(); + try wasm.setupMemory(); + wasm.mapFunctionTable(); + try wasm.mergeSections(); + try wasm.mergeTypes(); + try wasm.setupExports(); const header_size = 5 + 1; - const is_obj = self.base.options.output_mode == .Obj; + const is_obj = wasm.base.options.output_mode == .Obj; - var binary_bytes = std.ArrayList(u8).init(self.base.allocator); + var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator); defer binary_bytes.deinit(); const binary_writer = binary_bytes.writer(); @@ -2221,18 +2220,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try binary_writer.writeAll(&[_]u8{0} ** 8); // Type section - if (self.func_types.items.len != 0) { + if (wasm.func_types.items.len != 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); - for (self.func_types.items) |func_type| { - try leb.writeULEB128(binary_writer, wasm.function_type); + log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len}); + for (wasm.func_types.items) |func_type| { + try leb.writeULEB128(binary_writer, std.wasm.function_type); try leb.writeULEB128(binary_writer, @intCast(u32, func_type.params.len)); for (func_type.params) |param_ty| { - try leb.writeULEB128(binary_writer, wasm.valtype(param_ty)); + try leb.writeULEB128(binary_writer, std.wasm.valtype(param_ty)); } try leb.writeULEB128(binary_writer, @intCast(u32, func_type.returns.len)); for (func_type.returns) |ret_ty| { - try leb.writeULEB128(binary_writer, wasm.valtype(ret_ty)); + try leb.writeULEB128(binary_writer, std.wasm.valtype(ret_ty)); } } @@ -2241,50 +2240,50 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .type, @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, self.func_types.items.len), + @intCast(u32, wasm.func_types.items.len), ); section_count += 1; } // Import section - const import_memory = self.base.options.import_memory or is_obj; - const import_table = self.base.options.import_table or is_obj; - if (self.imports.count() != 0 or import_memory or import_table) { + const import_memory = wasm.base.options.import_memory or is_obj; + const import_table = wasm.base.options.import_table or is_obj; + if (wasm.imports.count() != 0 or import_memory or import_table) { const header_offset = try reserveVecSectionHeader(&binary_bytes); // import table is always first table so emit that first if (import_table) { const table_imp: types.Import = .{ - .module_name = try self.string_table.put(self.base.allocator, self.host_name), - .name = try self.string_table.put(self.base.allocator, "__indirect_function_table"), + .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name), + .name = try wasm.string_table.put(wasm.base.allocator, "__indirect_function_table"), .kind = .{ .table = .{ .limits = .{ - .min = @intCast(u32, self.function_table.count()), + .min = @intCast(u32, wasm.function_table.count()), .max = null, }, .reftype = .funcref, }, }, }; - try self.emitImport(binary_writer, table_imp); + try wasm.emitImport(binary_writer, table_imp); } - var it = self.imports.iterator(); + var it = wasm.imports.iterator(); while (it.next()) |entry| { - assert(entry.key_ptr.*.getSymbol(self).isUndefined()); + assert(entry.key_ptr.*.getSymbol(wasm).isUndefined()); const import = entry.value_ptr.*; - try self.emitImport(binary_writer, import); + try wasm.emitImport(binary_writer, import); } if (import_memory) { const mem_name = if (is_obj) "__linear_memory" else "memory"; const mem_imp: types.Import = .{ - .module_name = try self.string_table.put(self.base.allocator, self.host_name), - .name = try self.string_table.put(self.base.allocator, mem_name), - .kind = .{ .memory = self.memories.limits }, + .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name), + .name = try wasm.string_table.put(wasm.base.allocator, mem_name), + .kind = .{ .memory = wasm.memories.limits }, }; - try self.emitImport(binary_writer, mem_imp); + try wasm.emitImport(binary_writer, mem_imp); } try writeVecSectionHeader( @@ -2292,15 +2291,15 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .import, @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), + @intCast(u32, wasm.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), ); section_count += 1; } // Function section - if (self.functions.count() != 0) { + if (wasm.functions.count() != 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - for (self.functions.values()) |function| { + for (wasm.functions.values()) |function| { try leb.writeULEB128(binary_writer, function.type_index); } @@ -2309,19 +2308,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .function, @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, self.functions.count()), + @intCast(u32, wasm.functions.count()), ); section_count += 1; } // Table section - const export_table = self.base.options.export_table; - if (!import_table and self.function_table.count() != 0) { + const export_table = wasm.base.options.export_table; + if (!import_table and wasm.function_table.count() != 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - try leb.writeULEB128(binary_writer, wasm.reftype(.funcref)); + try leb.writeULEB128(binary_writer, std.wasm.reftype(.funcref)); try emitLimits(binary_writer, .{ - .min = @intCast(u32, self.function_table.count()) + 1, + .min = @intCast(u32, wasm.function_table.count()) + 1, .max = null, }); @@ -2339,7 +2338,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod if (!import_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - try emitLimits(binary_writer, self.memories.limits); + try emitLimits(binary_writer, wasm.memories.limits); try writeVecSectionHeader( binary_bytes.items, header_offset, @@ -2351,11 +2350,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } // Global section (used to emit stack pointer) - if (self.wasm_globals.items.len > 0) { + if (wasm.wasm_globals.items.len > 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - for (self.wasm_globals.items) |global| { - try binary_writer.writeByte(wasm.valtype(global.global_type.valtype)); + for (wasm.wasm_globals.items) |global| { + try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype)); try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); try emitInit(binary_writer, global.init); } @@ -2365,17 +2364,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .global, @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, self.wasm_globals.items.len), + @intCast(u32, wasm.wasm_globals.items.len), ); section_count += 1; } // Export section - if (self.exports.items.len != 0 or export_table or !import_memory) { + if (wasm.exports.items.len != 0 or export_table or !import_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - for (self.exports.items) |exp| { - const name = self.string_table.get(exp.name); + for (wasm.exports.items) |exp| { + const name = wasm.string_table.get(exp.name); try leb.writeULEB128(binary_writer, @intCast(u32, name.len)); try binary_writer.writeAll(name); try leb.writeULEB128(binary_writer, @enumToInt(exp.kind)); @@ -2385,14 +2384,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod if (export_table) { try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len)); try binary_writer.writeAll("__indirect_function_table"); - try binary_writer.writeByte(wasm.externalKind(.table)); + try binary_writer.writeByte(std.wasm.externalKind(.table)); try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table } if (!import_memory) { try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); try binary_writer.writeAll("memory"); - try binary_writer.writeByte(wasm.externalKind(.memory)); + try binary_writer.writeByte(std.wasm.externalKind(.memory)); try leb.writeULEB128(binary_writer, @as(u32, 0)); } @@ -2401,13 +2400,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .@"export", @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), + @intCast(u32, wasm.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), ); section_count += 1; } // element section (function table) - if (self.function_table.count() > 0) { + if (wasm.function_table.count() > 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); var flags: u32 = 0x2; // Yes we have a table @@ -2415,10 +2414,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid try leb.writeULEB128(binary_writer, @as(u8, 0)); - try leb.writeULEB128(binary_writer, @intCast(u32, self.function_table.count())); - var symbol_it = self.function_table.keyIterator(); + try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count())); + var symbol_it = wasm.function_table.keyIterator(); while (symbol_it.next()) |symbol_loc_ptr| { - try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(self).index); + try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(wasm).index); } try writeVecSectionHeader( @@ -2433,17 +2432,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Code section var code_section_size: u32 = 0; - if (self.code_section_index) |code_index| { + if (wasm.code_section_index) |code_index| { const header_offset = try reserveVecSectionHeader(&binary_bytes); - var atom: *Atom = self.atoms.get(code_index).?.getFirst(); + var atom: *Atom = wasm.atoms.get(code_index).?.getFirst(); // The code section must be sorted in line with the function order. - var sorted_atoms = try std.ArrayList(*Atom).initCapacity(self.base.allocator, self.functions.count()); + var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); defer sorted_atoms.deinit(); while (true) { if (!is_obj) { - atom.resolveRelocs(self); + atom.resolveRelocs(wasm); } sorted_atoms.appendAssumeCapacity(atom); atom = atom.next orelse break; @@ -2457,7 +2456,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } }.sort; - std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn); + std.sort.sort(*Atom, sorted_atoms.items, wasm, atom_sort_fn); for (sorted_atoms.items) |sorted_atom| { try leb.writeULEB128(binary_writer, sorted_atom.size); @@ -2470,17 +2469,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .code, code_section_size, - @intCast(u32, self.functions.count()), + @intCast(u32, wasm.functions.count()), ); code_section_index = section_count; section_count += 1; } // Data section - if (self.data_segments.count() != 0) { + if (wasm.data_segments.count() != 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); - var it = self.data_segments.iterator(); + var it = wasm.data_segments.iterator(); var segment_count: u32 = 0; while (it.next()) |entry| { // do not output 'bss' section unless we import memory and therefore @@ -2488,8 +2487,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; segment_count += 1; const atom_index = entry.value_ptr.*; - var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); - const segment = self.segments.items[atom_index]; + var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); + const segment = wasm.segments.items[atom_index]; // flag and index to memory section (currently, there can only be 1 memory section in wasm) try leb.writeULEB128(binary_writer, @as(u32, 0)); @@ -2501,7 +2500,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod var current_offset: u32 = 0; while (true) { if (!is_obj) { - atom.resolveRelocs(self); + atom.resolveRelocs(wasm); } // Pad with zeroes to ensure all segments are aligned @@ -2546,25 +2545,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // we never store all symbols in a single table, but store a location reference instead. // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); - try self.emitLinkSection(&binary_bytes, &symbol_table); + try wasm.emitLinkSection(&binary_bytes, &symbol_table); if (code_section_index) |code_index| { - try self.emitCodeRelocations(&binary_bytes, code_index, symbol_table); + try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table); } if (data_section_index) |data_index| { - try self.emitDataRelocations(&binary_bytes, data_index, symbol_table); + try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); } - } else if (!self.base.options.strip) { - if (self.dwarf) |*dwarf| { - const mod = self.base.options.module.?; - try dwarf.writeDbgAbbrev(&self.base); + } else if (!wasm.base.options.strip) { + if (wasm.dwarf) |*dwarf| { + const mod = wasm.base.options.module.?; + try dwarf.writeDbgAbbrev(&wasm.base); // for debug info and ranges, the address is always 0, // as locations are always offsets relative to 'code' section. - try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); - try dwarf.writeDbgAranges(&self.base, 0, code_section_size); - try dwarf.writeDbgLineHeader(&self.base, mod); + try dwarf.writeDbgInfoHeader(&wasm.base, mod, 0, code_section_size); + try dwarf.writeDbgAranges(&wasm.base, 0, code_section_size); + try dwarf.writeDbgLineHeader(&wasm.base, mod); } - var debug_bytes = std.ArrayList(u8).init(self.base.allocator); + var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); defer debug_bytes.deinit(); const DebugSection = struct { @@ -2573,21 +2572,21 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod }; const debug_sections: []const DebugSection = &.{ - .{ .name = ".debug_info", .index = self.debug_info_index }, - .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index }, - .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index }, - .{ .name = ".debug_line", .index = self.debug_line_index }, - .{ .name = ".debug_str", .index = self.debug_str_index }, - .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index }, - .{ .name = ".debug_loc", .index = self.debug_loc_index }, - .{ .name = ".debug_ranges", .index = self.debug_ranges_index }, + .{ .name = ".debug_info", .index = wasm.debug_info_index }, + .{ .name = ".debug_pubtypes", .index = wasm.debug_pubtypes_index }, + .{ .name = ".debug_abbrev", .index = wasm.debug_abbrev_index }, + .{ .name = ".debug_line", .index = wasm.debug_line_index }, + .{ .name = ".debug_str", .index = wasm.debug_str_index }, + .{ .name = ".debug_pubnames", .index = wasm.debug_pubnames_index }, + .{ .name = ".debug_loc", .index = wasm.debug_loc_index }, + .{ .name = ".debug_ranges", .index = wasm.debug_ranges_index }, }; for (debug_sections) |item| { if (item.index) |index| { - var atom = self.atoms.get(index).?.getFirst(); + var atom = wasm.atoms.get(index).?.getFirst(); while (true) { - atom.resolveRelocs(self); + atom.resolveRelocs(wasm); try debug_bytes.appendSlice(atom.code.items); atom = atom.next orelse break; } @@ -2595,20 +2594,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod debug_bytes.clearRetainingCapacity(); } } - try self.emitNameSection(&binary_bytes, arena); + try wasm.emitNameSection(&binary_bytes, arena); } // Only when writing all sections executed properly we write the magic // bytes. This allows us to easily detect what went wrong while generating // the final binary. - mem.copy(u8, binary_bytes.items, &(wasm.magic ++ wasm.version)); + mem.copy(u8, binary_bytes.items, &(std.wasm.magic ++ std.wasm.version)); // finally, write the entire binary into the file. var iovec = [_]std.os.iovec_const{.{ .iov_base = binary_bytes.items.ptr, .iov_len = binary_bytes.items.len, }}; - try self.base.file.?.writevAll(&iovec); + try wasm.base.file.?.writevAll(&iovec); } fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void { @@ -2629,7 +2628,7 @@ fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: [] ); } -fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { +fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { const Name = struct { index: u32, name: []const u8, @@ -2642,15 +2641,15 @@ fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem // we must de-duplicate symbols that point to the same function var funcs = std.AutoArrayHashMap(u32, Name).init(arena); - try funcs.ensureUnusedCapacity(self.functions.count() + self.imported_functions_count); - var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len + self.imported_globals_count); - var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count()); + try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count); + var globals = try std.ArrayList(Name).initCapacity(arena, wasm.wasm_globals.items.len + wasm.imported_globals_count); + var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count()); - for (self.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(self).*; + for (wasm.resolved_symbols.keys()) |sym_loc| { + const symbol = sym_loc.getSymbol(wasm).*; const name = if (symbol.isUndefined()) blk: { - break :blk self.string_table.get(self.imports.get(sym_loc).?.name); - } else sym_loc.getName(self); + break :blk wasm.string_table.get(wasm.imports.get(sym_loc).?.name); + } else sym_loc.getName(wasm); switch (symbol.tag) { .function => { const gop = funcs.getOrPutAssumeCapacity(symbol.index); @@ -2664,10 +2663,10 @@ fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem } // data segments are already 'ordered' var data_segment_index: u32 = 0; - for (self.data_segments.keys()) |key| { + for (wasm.data_segments.keys()) |key| { // bss section is not emitted when this condition holds true, so we also // do not output a name for it. - if (!self.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; + if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); data_segment_index += 1; } @@ -2680,9 +2679,9 @@ fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem try leb.writeULEB128(writer, @intCast(u32, "name".len)); try writer.writeAll("name"); - try self.emitNameSubsection(.function, funcs.values(), writer); - try self.emitNameSubsection(.global, globals.items, writer); - try self.emitNameSubsection(.data_segment, segments.items, writer); + try wasm.emitNameSubsection(.function, funcs.values(), writer); + try wasm.emitNameSubsection(.global, globals.items, writer); + try wasm.emitNameSubsection(.data_segment, segments.items, writer); try writeCustomSectionHeader( binary_bytes.items, @@ -2691,9 +2690,9 @@ fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem ); } -fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { +fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { // We must emit subsection size, so first write to a temporary list - var section_list = std.ArrayList(u8).init(self.base.allocator); + var section_list = std.ArrayList(u8).init(wasm.base.allocator); defer section_list.deinit(); const sub_writer = section_list.writer(); @@ -2711,7 +2710,7 @@ fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: a try writer.writeAll(section_list.items); } -fn emitLimits(writer: anytype, limits: wasm.Limits) !void { +fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void { try leb.writeULEB128(writer, @boolToInt(limits.max != null)); try leb.writeULEB128(writer, limits.min); if (limits.max) |max| { @@ -2719,38 +2718,38 @@ fn emitLimits(writer: anytype, limits: wasm.Limits) !void { } } -fn emitInit(writer: anytype, init_expr: wasm.InitExpression) !void { +fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { switch (init_expr) { .i32_const => |val| { - try writer.writeByte(wasm.opcode(.i32_const)); + try writer.writeByte(std.wasm.opcode(.i32_const)); try leb.writeILEB128(writer, val); }, .i64_const => |val| { - try writer.writeByte(wasm.opcode(.i64_const)); + try writer.writeByte(std.wasm.opcode(.i64_const)); try leb.writeILEB128(writer, val); }, .f32_const => |val| { - try writer.writeByte(wasm.opcode(.f32_const)); + try writer.writeByte(std.wasm.opcode(.f32_const)); try writer.writeIntLittle(u32, @bitCast(u32, val)); }, .f64_const => |val| { - try writer.writeByte(wasm.opcode(.f64_const)); + try writer.writeByte(std.wasm.opcode(.f64_const)); try writer.writeIntLittle(u64, @bitCast(u64, val)); }, .global_get => |val| { - try writer.writeByte(wasm.opcode(.global_get)); + try writer.writeByte(std.wasm.opcode(.global_get)); try leb.writeULEB128(writer, val); }, } - try writer.writeByte(wasm.opcode(.end)); + try writer.writeByte(std.wasm.opcode(.end)); } -fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void { - const module_name = self.string_table.get(import.module_name); +fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void { + const module_name = wasm.string_table.get(import.module_name); try leb.writeULEB128(writer, @intCast(u32, module_name.len)); try writer.writeAll(module_name); - const name = self.string_table.get(import.name); + const name = wasm.string_table.get(import.name); try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); @@ -2758,11 +2757,11 @@ fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void { switch (import.kind) { .function => |type_index| try leb.writeULEB128(writer, type_index), .global => |global_type| { - try leb.writeULEB128(writer, wasm.valtype(global_type.valtype)); + try leb.writeULEB128(writer, std.wasm.valtype(global_type.valtype)); try writer.writeByte(@boolToInt(global_type.mutable)); }, .table => |table| { - try leb.writeULEB128(writer, wasm.reftype(table.reftype)); + try leb.writeULEB128(writer, std.wasm.reftype(table.reftype)); try emitLimits(writer, table.limits); }, .memory => |limits| { @@ -2771,28 +2770,28 @@ fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void { } } -fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { +fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + var arena_allocator = std.heap.ArenaAllocator.init(wasm.base.allocator); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + const directory = wasm.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.options.emit.?.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (self.base.options.module) |mod| blk: { - const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1; + const module_obj_path: ?[]const u8 = if (wasm.base.options.module) |mod| blk: { + const use_stage1 = build_options.have_stage1 and wasm.base.options.use_stage1; if (use_stage1) { const obj_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = self.base.options.root_name, - .target = self.base.options.target, + .root_name = wasm.base.options.root_name, + .target = wasm.base.options.target, .output_mode = .Obj, }); - switch (self.base.options.cache_mode) { + switch (wasm.base.options.cache_mode) { .incremental => break :blk try mod.zig_cache_artifact_directory.join( arena, &[_][]const u8{obj_basename}, @@ -2803,12 +2802,12 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } - try self.flushModule(comp, prog_node); + try wasm.flushModule(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? }); } else { - break :blk self.base.intermediary_basename.?; + break :blk wasm.base.intermediary_basename.?; } } else null; @@ -2817,31 +2816,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! sub_prog_node.context.refresh(); defer sub_prog_node.end(); - const is_obj = self.base.options.output_mode == .Obj; + const is_obj = wasm.base.options.output_mode == .Obj; - const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) + const compiler_rt_path: ?[]const u8 = if (wasm.base.options.include_compiler_rt and !is_obj) comp.compiler_rt_lib.?.full_object_path else null; - const target = self.base.options.target; + const target = wasm.base.options.target; const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!self.base.options.disable_lld_caching) man.deinit(); + defer if (!wasm.base.options.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!self.base.options.disable_lld_caching) { + if (!wasm.base.options.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. - self.base.releaseLock(); + wasm.base.releaseLock(); comptime assert(Compilation.link_hash_implementation_version == 7); - for (self.base.options.objects) |obj| { + for (wasm.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -2850,18 +2849,18 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } try man.addOptionalFile(module_obj_path); try man.addOptionalFile(compiler_rt_path); - man.hash.addOptionalBytes(self.base.options.entry); - man.hash.addOptional(self.base.options.stack_size_override); - man.hash.add(self.base.options.import_memory); - man.hash.add(self.base.options.import_table); - man.hash.add(self.base.options.export_table); - man.hash.addOptional(self.base.options.initial_memory); - man.hash.addOptional(self.base.options.max_memory); - man.hash.add(self.base.options.shared_memory); - man.hash.addOptional(self.base.options.global_base); - man.hash.add(self.base.options.export_symbol_names.len); + man.hash.addOptionalBytes(wasm.base.options.entry); + man.hash.addOptional(wasm.base.options.stack_size_override); + man.hash.add(wasm.base.options.import_memory); + man.hash.add(wasm.base.options.import_table); + man.hash.add(wasm.base.options.export_table); + man.hash.addOptional(wasm.base.options.initial_memory); + man.hash.addOptional(wasm.base.options.max_memory); + man.hash.add(wasm.base.options.shared_memory); + man.hash.addOptional(wasm.base.options.global_base); + man.hash.add(wasm.base.options.export_symbol_names.len); // strip does not need to go into the linker hash because it is part of the hash namespace - for (self.base.options.export_symbol_names) |symbol_name| { + for (wasm.base.options.export_symbol_names) |symbol_name| { man.hash.addBytes(symbol_name); } @@ -2882,7 +2881,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! if (mem.eql(u8, prev_digest, &digest)) { log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); // Hot diggity dog! The output binary is already there. - self.base.lock = man.toOwnedLock(); + wasm.base.lock = man.toOwnedLock(); return; } log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); @@ -2899,8 +2898,8 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (self.base.options.objects.len != 0) - break :blk self.base.options.objects[0].path; + if (wasm.base.options.objects.len != 0) + break :blk wasm.base.options.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -2919,7 +2918,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } else { // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(self.base.allocator); + var argv = std.ArrayList([]const u8).init(wasm.base.allocator); defer argv.deinit(); // We will invoke ourselves as a child process to gain access to LLD. // This is necessary because LLD does not behave properly as a library - @@ -2927,47 +2926,47 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" }); try argv.append("-error-limit=0"); - if (self.base.options.lto) { - switch (self.base.options.optimize_mode) { + if (wasm.base.options.lto) { + switch (wasm.base.options.optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("-O2"), .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), } } - if (self.base.options.import_memory) { + if (wasm.base.options.import_memory) { try argv.append("--import-memory"); } - if (self.base.options.import_table) { - assert(!self.base.options.export_table); + if (wasm.base.options.import_table) { + assert(!wasm.base.options.export_table); try argv.append("--import-table"); } - if (self.base.options.export_table) { - assert(!self.base.options.import_table); + if (wasm.base.options.export_table) { + assert(!wasm.base.options.import_table); try argv.append("--export-table"); } - if (self.base.options.strip) { + if (wasm.base.options.strip) { try argv.append("-s"); } - if (self.base.options.initial_memory) |initial_memory| { + if (wasm.base.options.initial_memory) |initial_memory| { const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); try argv.append(arg); } - if (self.base.options.max_memory) |max_memory| { + if (wasm.base.options.max_memory) |max_memory| { const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); try argv.append(arg); } - if (self.base.options.shared_memory) { + if (wasm.base.options.shared_memory) { try argv.append("--shared-memory"); } - if (self.base.options.global_base) |global_base| { + if (wasm.base.options.global_base) |global_base| { const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); try argv.append(arg); } else { @@ -2980,29 +2979,29 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! var auto_export_symbols = true; // Users are allowed to specify which symbols they want to export to the wasm host. - for (self.base.options.export_symbol_names) |symbol_name| { + for (wasm.base.options.export_symbol_names) |symbol_name| { const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); auto_export_symbols = false; } - if (self.base.options.rdynamic) { + if (wasm.base.options.rdynamic) { try argv.append("--export-dynamic"); auto_export_symbols = false; } if (auto_export_symbols) { - if (self.base.options.module) |mod| { + if (wasm.base.options.module) |mod| { // when we use stage1, we use the exports that stage1 provided us. // For stage2, we can directly retrieve them from the module. - const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1; + const use_stage1 = build_options.have_stage1 and wasm.base.options.use_stage1; if (use_stage1) { for (comp.export_symbol_names.items) |symbol_name| { try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name})); } } else { const skip_export_non_fn = target.os.tag == .wasi and - self.base.options.wasi_exec_model == .command; + wasm.base.options.wasi_exec_model == .command; for (mod.decl_exports.values()) |exports| { for (exports) |exprt| { const exported_decl = mod.declPtr(exprt.exported_decl); @@ -3020,7 +3019,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } - if (self.base.options.entry) |entry| { + if (wasm.base.options.entry) |entry| { try argv.append("--entry"); try argv.append(entry); } @@ -3028,16 +3027,16 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // Increase the default stack size to a more reasonable value of 1MB instead of // the default of 1 Wasm page being 64KB, unless overridden by the user. try argv.append("-z"); - const stack_size = self.base.options.stack_size_override orelse wasm.page_size * 16; + const stack_size = wasm.base.options.stack_size_override orelse std.wasm.page_size * 16; const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); try argv.append(arg); - if (self.base.options.output_mode == .Exe) { - if (self.base.options.wasi_exec_model == .reactor) { + if (wasm.base.options.output_mode == .Exe) { + if (wasm.base.options.wasi_exec_model == .reactor) { // Reactor execution model does not have _start so lld doesn't look for it. try argv.append("--no-entry"); } - } else if (self.base.options.entry == null) { + } else if (wasm.base.options.entry == null) { try argv.append("--no-entry"); // So lld doesn't look for _start. } try argv.appendSlice(&[_][]const u8{ @@ -3051,10 +3050,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } if (target.os.tag == .wasi) { - const is_exe_or_dyn_lib = self.base.options.output_mode == .Exe or - (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic); + const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or + (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic); if (is_exe_or_dyn_lib) { - const wasi_emulated_libs = self.base.options.wasi_emulated_libs; + const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs; for (wasi_emulated_libs) |crt_file| { try argv.append(try comp.get_libc_crt_file( arena, @@ -3062,15 +3061,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! )); } - if (self.base.options.link_libc) { + if (wasm.base.options.link_libc) { try argv.append(try comp.get_libc_crt_file( arena, - wasi_libc.execModelCrtFileFullName(self.base.options.wasi_exec_model), + wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), )); try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); } - if (self.base.options.link_libcpp) { + if (wasm.base.options.link_libcpp) { try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(comp.libcxxabi_static_lib.?.full_object_path); } @@ -3079,7 +3078,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // Positional arguments to the linker such as object files. var whole_archive = false; - for (self.base.options.objects) |obj| { + for (wasm.base.options.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -3101,9 +3100,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append(p); } - if (self.base.options.output_mode != .Obj and - !self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) + if (wasm.base.options.output_mode != .Obj and + !wasm.base.options.skip_linker_dependencies and + !wasm.base.options.link_libc) { try argv.append(comp.libc_static_lib.?.full_object_path); } @@ -3112,7 +3111,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append(p); } - if (self.base.options.verbose_link) { + if (wasm.base.options.verbose_link) { // Skip over our own name so that the LLD linker name is the first argv item. Compilation.dump_argv(argv.items[1..]); } @@ -3129,7 +3128,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const term = child.spawnAndWait() catch |err| { log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; + return error.UnableToSpawnwasm; }; switch (term) { .Exited => |code| { @@ -3150,7 +3149,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const term = child.wait() catch |err| { log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; + return error.UnableToSpawnwasm; }; switch (term) { @@ -3184,7 +3183,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } - if (!self.base.options.disable_lld_caching) { + if (!wasm.base.options.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -3196,7 +3195,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. - self.base.lock = man.toOwnedLock(); + wasm.base.lock = man.toOwnedLock(); } } @@ -3208,7 +3207,7 @@ fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { return offset; } -fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 { +fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 { // unlike regular section, we don't emit the count const header_size = 1 + 5; const offset = @intCast(u32, bytes.items.len); @@ -3216,7 +3215,7 @@ fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 { return offset; } -fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: u32, items: u32) !void { +fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void { var buf: [1 + 5 + 5]u8 = undefined; buf[0] = @enumToInt(section); leb.writeUnsignedFixed(5, buf[1..6], size); @@ -3224,14 +3223,14 @@ fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: mem.copy(u8, buffer[offset..], &buf); } -fn writeCustomSectionHeader(buffer: []u8, offset: u64, size: u32) !void { +fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void { var buf: [1 + 5]u8 = undefined; buf[0] = 0; // 0 = 'custom' section leb.writeUnsignedFixed(5, buf[1..6], size); mem.copy(u8, buffer[offset..], &buf); } -fn emitLinkSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { +fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { const offset = try reserveCustomSectionHeader(binary_bytes); const writer = binary_bytes.writer(); // emit "linking" custom section name @@ -3244,22 +3243,22 @@ fn emitLinkSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: // For each subsection type (found in types.Subsection) we can emit a section. // Currently, we only support emitting segment info and the symbol table. - try self.emitSymbolTable(binary_bytes, symbol_table); - try self.emitSegmentInfo(binary_bytes); + try wasm.emitSymbolTable(binary_bytes, symbol_table); + try wasm.emitSegmentInfo(binary_bytes); const size = @intCast(u32, binary_bytes.items.len - offset - 6); try writeCustomSectionHeader(binary_bytes.items, offset, size); } -fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { +fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE)); const table_offset = binary_bytes.items.len; var symbol_count: u32 = 0; - for (self.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(self).*; + for (wasm.resolved_symbols.keys()) |sym_loc| { + const symbol = sym_loc.getSymbol(wasm).*; if (symbol.tag == .dead) continue; // Do not emit dead symbols try symbol_table.putNoClobber(sym_loc, symbol_count); symbol_count += 1; @@ -3267,7 +3266,7 @@ fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: try leb.writeULEB128(writer, @enumToInt(symbol.tag)); try leb.writeULEB128(writer, symbol.flags); - const sym_name = if (self.export_names.get(sym_loc)) |exp_name| self.string_table.get(exp_name) else sym_loc.getName(self); + const sym_name = if (wasm.export_names.get(sym_loc)) |exp_name| wasm.string_table.get(exp_name) else sym_loc.getName(wasm); switch (symbol.tag) { .data => { try leb.writeULEB128(writer, @intCast(u32, sym_name.len)); @@ -3275,7 +3274,7 @@ fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: if (symbol.isDefined()) { try leb.writeULEB128(writer, symbol.index); - const atom = self.symbol_atom.get(sym_loc).?; + const atom = wasm.symbol_atom.get(sym_loc).?; try leb.writeULEB128(writer, @as(u32, atom.offset)); try leb.writeULEB128(writer, @as(u32, atom.size)); } @@ -3299,13 +3298,13 @@ fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: try binary_bytes.insertSlice(table_offset, &buf); } -fn emitSegmentInfo(self: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { +fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); const segment_offset = binary_bytes.items.len; - try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); - for (self.segment_info.values()) |segment_info| { + try leb.writeULEB128(writer, @intCast(u32, wasm.segment_info.count())); + for (wasm.segment_info.values()) |segment_info| { log.debug("Emit segment: {s} align({d}) flags({b})", .{ segment_info.name, @ctz(segment_info.alignment), @@ -3336,12 +3335,12 @@ pub fn getULEB128Size(uint_value: anytype) u32 { /// For each relocatable section, emits a custom "relocation." section fn emitCodeRelocations( - self: *Wasm, + wasm: *Wasm, binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { - const code_index = self.code_section_index orelse return; + const code_index = wasm.code_section_index orelse return; const writer = binary_bytes.writer(); const header_offset = try reserveCustomSectionHeader(binary_bytes); @@ -3353,7 +3352,7 @@ fn emitCodeRelocations( const reloc_start = binary_bytes.items.len; var count: u32 = 0; - var atom: *Atom = self.atoms.get(code_index).?.getFirst(); + var atom: *Atom = wasm.atoms.get(code_index).?.getFirst(); // for each atom, we calculate the uleb size and append that var size_offset: u32 = 5; // account for code section size leb128 while (true) { @@ -3382,12 +3381,12 @@ fn emitCodeRelocations( } fn emitDataRelocations( - self: *Wasm, + wasm: *Wasm, binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { - if (self.data_segments.count() == 0) return; + if (wasm.data_segments.count() == 0) return; const writer = binary_bytes.writer(); const header_offset = try reserveCustomSectionHeader(binary_bytes); @@ -3401,8 +3400,8 @@ fn emitDataRelocations( var count: u32 = 0; // for each atom, we calculate the uleb size and append that var size_offset: u32 = 5; // account for code section size leb128 - for (self.data_segments.values()) |segment_index| { - var atom: *Atom = self.atoms.get(segment_index).?.getFirst(); + for (wasm.data_segments.values()) |segment_index| { + var atom: *Atom = wasm.atoms.get(segment_index).?.getFirst(); while (true) { size_offset += getULEB128Size(atom.size); for (atom.relocs.items) |relocation| { @@ -3435,18 +3434,18 @@ fn emitDataRelocations( /// Searches for an a matching function signature, when not found /// a new entry will be made. The index of the existing/new signature will be returned. -pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 { +pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { var index: u32 = 0; - while (index < self.func_types.items.len) : (index += 1) { - if (self.func_types.items[index].eql(func_type)) return index; + while (index < wasm.func_types.items.len) : (index += 1) { + if (wasm.func_types.items[index].eql(func_type)) return index; } // functype does not exist. - const params = try self.base.allocator.dupe(wasm.Valtype, func_type.params); - errdefer self.base.allocator.free(params); - const returns = try self.base.allocator.dupe(wasm.Valtype, func_type.returns); - errdefer self.base.allocator.free(returns); - try self.func_types.append(self.base.allocator, .{ + const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params); + errdefer wasm.base.allocator.free(params); + const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns); + errdefer wasm.base.allocator.free(returns); + try wasm.func_types.append(wasm.base.allocator, .{ .params = params, .returns = returns, }); diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index b1cce15b1d..2fa1e07915 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -4,7 +4,6 @@ const std = @import("std"); const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.archive); -const macho = std.macho; const mem = std.mem; const Allocator = mem.Allocator; diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 3e288fa018..5af07d891a 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -55,37 +55,37 @@ pub const empty: Atom = .{ }; /// Frees all resources owned by this `Atom`. -pub fn deinit(self: *Atom, gpa: Allocator) void { - self.relocs.deinit(gpa); - self.code.deinit(gpa); +pub fn deinit(atom: *Atom, gpa: Allocator) void { + atom.relocs.deinit(gpa); + atom.code.deinit(gpa); - for (self.locals.items) |*local| { + for (atom.locals.items) |*local| { local.deinit(gpa); } - self.locals.deinit(gpa); + atom.locals.deinit(gpa); } /// Sets the length of relocations and code to '0', /// effectively resetting them and allowing them to be re-populated. -pub fn clear(self: *Atom) void { - self.relocs.clearRetainingCapacity(); - self.code.clearRetainingCapacity(); +pub fn clear(atom: *Atom) void { + atom.relocs.clearRetainingCapacity(); + atom.code.clearRetainingCapacity(); } -pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { +pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ - self.sym_index, - self.alignment, - self.size, - self.offset, + atom.sym_index, + atom.alignment, + atom.size, + atom.offset, }); } /// Returns the first `Atom` from a given atom -pub fn getFirst(self: *Atom) *Atom { - var tmp = self; +pub fn getFirst(atom: *Atom) *Atom { + var tmp = atom; while (tmp.prev) |prev| tmp = prev; return tmp; } @@ -94,9 +94,9 @@ pub fn getFirst(self: *Atom) *Atom { /// produced from Zig code, rather than an object file. /// This is useful for debug sections where we want to extend /// the bytes, and don't want to overwrite existing Atoms. -pub fn getFirstZigAtom(self: *Atom) *Atom { - if (self.file == null) return self; - var tmp = self; +pub fn getFirstZigAtom(atom: *Atom) *Atom { + if (atom.file == null) return atom; + var tmp = atom; return while (tmp.prev) |prev| { if (prev.file == null) break prev; tmp = prev; @@ -104,24 +104,24 @@ pub fn getFirstZigAtom(self: *Atom) *Atom { } /// Returns the location of the symbol that represents this `Atom` -pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { - return .{ .file = self.file, .index = self.sym_index }; +pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { + return .{ .file = atom.file, .index = atom.sym_index }; } /// Resolves the relocations within the atom, writing the new value /// at the calculated offset. -pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { - if (self.relocs.items.len == 0) return; - const symbol_name = self.symbolLoc().getName(wasm_bin); +pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { + if (atom.relocs.items.len == 0) return; + const symbol_name = atom.symbolLoc().getName(wasm_bin); log.debug("Resolving relocs in atom '{s}' count({d})", .{ symbol_name, - self.relocs.items.len, + atom.relocs.items.len, }); - for (self.relocs.items) |reloc| { - const value = self.relocationValue(reloc, wasm_bin); + for (atom.relocs.items) |reloc| { + const value = atom.relocationValue(reloc, wasm_bin); log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{ - (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getName(wasm_bin), + (Wasm.SymbolLoc{ .file = atom.file, .index = reloc.index }).getName(wasm_bin), symbol_name, reloc.offset, value, @@ -133,10 +133,10 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { .R_WASM_GLOBAL_INDEX_I32, .R_WASM_MEMORY_ADDR_I32, .R_WASM_SECTION_OFFSET_I32, - => std.mem.writeIntLittle(u32, self.code.items[reloc.offset..][0..4], @intCast(u32, value)), + => std.mem.writeIntLittle(u32, atom.code.items[reloc.offset..][0..4], @intCast(u32, value)), .R_WASM_TABLE_INDEX_I64, .R_WASM_MEMORY_ADDR_I64, - => std.mem.writeIntLittle(u64, self.code.items[reloc.offset..][0..8], value), + => std.mem.writeIntLittle(u64, atom.code.items[reloc.offset..][0..8], value), .R_WASM_GLOBAL_INDEX_LEB, .R_WASM_EVENT_INDEX_LEB, .R_WASM_FUNCTION_INDEX_LEB, @@ -145,11 +145,11 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_NUMBER_LEB, .R_WASM_TYPE_INDEX_LEB, - => leb.writeUnsignedFixed(5, self.code.items[reloc.offset..][0..5], @intCast(u32, value)), + => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)), .R_WASM_MEMORY_ADDR_LEB64, .R_WASM_MEMORY_ADDR_SLEB64, .R_WASM_TABLE_INDEX_SLEB64, - => leb.writeUnsignedFixed(10, self.code.items[reloc.offset..][0..10], value), + => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value), } } } @@ -157,8 +157,8 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { /// From a given `relocation` will return the new value to be written. /// All values will be represented as a `u64` as all values can fit within it. /// The final value must be casted to the correct size. -fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { - const target_loc = (Wasm.SymbolLoc{ .file = self.file, .index = relocation.index }).finalLoc(wasm_bin); +fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { + const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = relocation.index }).finalLoc(wasm_bin); const symbol = target_loc.getSymbol(wasm_bin).*; switch (relocation.relocation_type) { .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, @@ -203,7 +203,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa }, .R_WASM_FUNCTION_OFFSET_I32 => { const target_atom = wasm_bin.symbol_atom.get(target_loc).?; - var atom = target_atom.getFirst(); + var current_atom = target_atom.getFirst(); var offset: u32 = 0; // TODO: Calculate this during atom allocation, rather than // this linear calculation. For now it's done here as atoms @@ -211,8 +211,8 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa // merged until later. while (true) { offset += 5; // each atom uses 5 bytes to store its body's size - if (atom == target_atom) break; - atom = atom.next.?; + if (current_atom == target_atom) break; + current_atom = current_atom.next.?; } return target_atom.offset + offset + (relocation.addend orelse 0); }, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 808608fac5..97b7da0255 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -88,28 +88,28 @@ const RelocatableData = struct { /// meta data of the given object file. /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's /// alignment to retrieve the natural alignment. - pub fn getAlignment(self: RelocatableData, object: *const Object) u32 { - if (self.type != .data) return 1; - const data_alignment = object.segment_info[self.index].alignment; + pub fn getAlignment(relocatable_data: RelocatableData, object: *const Object) u32 { + if (relocatable_data.type != .data) return 1; + const data_alignment = object.segment_info[relocatable_data.index].alignment; if (data_alignment == 0) return 1; // Decode from power of 2 to natural alignment return @as(u32, 1) << @intCast(u5, data_alignment); } /// Returns the symbol kind that corresponds to the relocatable section - pub fn getSymbolKind(self: RelocatableData) Symbol.Tag { - return switch (self.type) { + pub fn getSymbolKind(relocatable_data: RelocatableData) Symbol.Tag { + return switch (relocatable_data.type) { .data => .data, .code => .function, .debug => .section, }; } - /// Returns the index within a section itself, or in case of a debug section, + /// Returns the index within a section itrelocatable_data, or in case of a debug section, /// returns the section index within the object file. - pub fn getIndex(self: RelocatableData) u32 { - if (self.type == .debug) return self.section_index; - return self.index; + pub fn getIndex(relocatable_data: RelocatableData) u32 { + if (relocatable_data.type == .debug) return relocatable_data.section_index; + return relocatable_data.index; } }; @@ -153,51 +153,51 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz /// Frees all memory of `Object` at once. The given `Allocator` must be /// the same allocator that was used when `init` was called. -pub fn deinit(self: *Object, gpa: Allocator) void { - if (self.file) |file| { +pub fn deinit(object: *Object, gpa: Allocator) void { + if (object.file) |file| { file.close(); } - for (self.func_types) |func_ty| { + for (object.func_types) |func_ty| { gpa.free(func_ty.params); gpa.free(func_ty.returns); } - gpa.free(self.func_types); - gpa.free(self.functions); - gpa.free(self.imports); - gpa.free(self.tables); - gpa.free(self.memories); - gpa.free(self.globals); - gpa.free(self.exports); - for (self.elements) |el| { + gpa.free(object.func_types); + gpa.free(object.functions); + gpa.free(object.imports); + gpa.free(object.tables); + gpa.free(object.memories); + gpa.free(object.globals); + gpa.free(object.exports); + for (object.elements) |el| { gpa.free(el.func_indexes); } - gpa.free(self.elements); - gpa.free(self.features); - for (self.relocations.values()) |val| { + gpa.free(object.elements); + gpa.free(object.features); + for (object.relocations.values()) |val| { gpa.free(val); } - self.relocations.deinit(gpa); - gpa.free(self.symtable); - gpa.free(self.comdat_info); - gpa.free(self.init_funcs); - for (self.segment_info) |info| { + object.relocations.deinit(gpa); + gpa.free(object.symtable); + gpa.free(object.comdat_info); + gpa.free(object.init_funcs); + for (object.segment_info) |info| { gpa.free(info.name); } - gpa.free(self.segment_info); - for (self.relocatable_data) |rel_data| { + gpa.free(object.segment_info); + for (object.relocatable_data) |rel_data| { gpa.free(rel_data.data[0..rel_data.size]); } - gpa.free(self.relocatable_data); - self.string_table.deinit(gpa); - gpa.free(self.name); - self.* = undefined; + gpa.free(object.relocatable_data); + object.string_table.deinit(gpa); + gpa.free(object.name); + object.* = undefined; } /// Finds the import within the list of imports from a given kind and index of that kind. /// Asserts the import exists -pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import { +pub fn findImport(object: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import { var i: u32 = 0; - return for (self.imports) |import| { + return for (object.imports) |import| { if (std.meta.activeTag(import.kind) == import_kind) { if (i == index) return import; i += 1; @@ -206,16 +206,16 @@ pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index } /// Counts the entries of imported `kind` and returns the result -pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 { +pub fn importedCountByKind(object: *const Object, kind: std.wasm.ExternalKind) u32 { var i: u32 = 0; - return for (self.imports) |imp| { + return for (object.imports) |imp| { if (@as(std.wasm.ExternalKind, imp.kind) == kind) i += 1; } else i; } /// From a given `RelocatableDate`, find the corresponding debug section name -pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 { - return self.string_table.get(relocatable_data.index); +pub fn getDebugName(object: *const Object, relocatable_data: RelocatableData) []const u8 { + return object.string_table.get(relocatable_data.index); } /// Checks if the object file is an MVP version. @@ -224,13 +224,13 @@ pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []co /// we initialize a new table symbol that corresponds to that import and return that symbol. /// /// When the object file is *NOT* MVP, we return `null`. -fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol { +fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol { var table_count: usize = 0; - for (self.symtable) |sym| { + for (object.symtable) |sym| { if (sym.tag == .table) table_count += 1; } - const import_table_count = self.importedCountByKind(.table); + const import_table_count = object.importedCountByKind(.table); // For each import table, we also have a symbol so this is not a legacy object file if (import_table_count == table_count) return null; @@ -244,7 +244,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol { } // MVP object files cannot have any table definitions, only imports (for the indirect function table). - if (self.tables.len > 0) { + if (object.tables.len > 0) { log.err("Unexpected table definition without representing table symbols.", .{}); return error.UnexpectedTable; } @@ -254,14 +254,14 @@ fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol { return error.MissingTableSymbols; } - var table_import: types.Import = for (self.imports) |imp| { + var table_import: types.Import = for (object.imports) |imp| { if (imp.kind == .table) { break imp; } } else unreachable; - if (!std.mem.eql(u8, self.string_table.get(table_import.name), "__indirect_function_table")) { - log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{self.string_table.get(table_import.name)}); + if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) { + log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)}); return error.MissingTableSymbols; } @@ -313,41 +313,41 @@ pub const ParseError = error{ UnknownFeature, }; -fn parse(self: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void { - var parser = Parser(@TypeOf(reader)).init(self, reader); +fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void { + var parser = Parser(@TypeOf(reader)).init(object, reader); return parser.parseObject(gpa, is_object_file); } fn Parser(comptime ReaderType: type) type { return struct { - const Self = @This(); + const ObjectParser = @This(); const Error = ReaderType.Error || ParseError; reader: std.io.CountingReader(ReaderType), /// Object file we're building object: *Object, - fn init(object: *Object, reader: ReaderType) Self { + fn init(object: *Object, reader: ReaderType) ObjectParser { return .{ .object = object, .reader = std.io.countingReader(reader) }; } /// Verifies that the first 4 bytes contains \0Asm - fn verifyMagicBytes(self: *Self) Error!void { + fn verifyMagicBytes(parser: *ObjectParser) Error!void { var magic_bytes: [4]u8 = undefined; - try self.reader.reader().readNoEof(&magic_bytes); + try parser.reader.reader().readNoEof(&magic_bytes); if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) { log.debug("Invalid magic bytes '{s}'", .{&magic_bytes}); return error.InvalidMagicByte; } } - fn parseObject(self: *Self, gpa: Allocator, is_object_file: *bool) Error!void { - errdefer self.object.deinit(gpa); - try self.verifyMagicBytes(); - const version = try self.reader.reader().readIntLittle(u32); + fn parseObject(parser: *ObjectParser, gpa: Allocator, is_object_file: *bool) Error!void { + errdefer parser.object.deinit(gpa); + try parser.verifyMagicBytes(); + const version = try parser.reader.reader().readIntLittle(u32); - self.object.version = version; + parser.object.version = version; var relocatable_data = std.ArrayList(RelocatableData).init(gpa); var debug_names = std.ArrayList(u8).init(gpa); @@ -360,9 +360,9 @@ fn Parser(comptime ReaderType: type) type { } var section_index: u32 = 0; - while (self.reader.reader().readByte()) |byte| : (section_index += 1) { - const len = try readLeb(u32, self.reader.reader()); - var limited_reader = std.io.limitedReader(self.reader.reader(), len); + while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { + const len = try readLeb(u32, parser.reader.reader()); + var limited_reader = std.io.limitedReader(parser.reader.reader(), len); const reader = limited_reader.reader(); switch (@intToEnum(std.wasm.Section, byte)) { .custom => { @@ -373,12 +373,12 @@ fn Parser(comptime ReaderType: type) type { if (std.mem.eql(u8, name, "linking")) { is_object_file.* = true; - self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. - try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); + parser.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. + try parser.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); } else if (std.mem.startsWith(u8, name, "reloc")) { - try self.parseRelocations(gpa); + try parser.parseRelocations(gpa); } else if (std.mem.eql(u8, name, "target_features")) { - try self.parseFeatures(gpa); + try parser.parseFeatures(gpa); } else if (std.mem.startsWith(u8, name, ".debug")) { const debug_size = @intCast(u32, reader.context.bytes_left); const debug_content = try gpa.alloc(u8, debug_size); @@ -389,7 +389,7 @@ fn Parser(comptime ReaderType: type) type { .type = .debug, .data = debug_content.ptr, .size = debug_size, - .index = try self.object.string_table.put(gpa, name), + .index = try parser.object.string_table.put(gpa, name), .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset .section_index = section_index, }); @@ -398,7 +398,7 @@ fn Parser(comptime ReaderType: type) type { } }, .type => { - for (try readVec(&self.object.func_types, reader, gpa)) |*type_val| { + for (try readVec(&parser.object.func_types, reader, gpa)) |*type_val| { if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType; for (try readVec(&type_val.params, reader, gpa)) |*param| { @@ -412,7 +412,7 @@ fn Parser(comptime ReaderType: type) type { try assertEnd(reader); }, .import => { - for (try readVec(&self.object.imports, reader, gpa)) |*import| { + for (try readVec(&parser.object.imports, reader, gpa)) |*import| { const module_len = try readLeb(u32, reader); const module_name = try gpa.alloc(u8, module_len); defer gpa.free(module_name); @@ -438,21 +438,21 @@ fn Parser(comptime ReaderType: type) type { }; import.* = .{ - .module_name = try self.object.string_table.put(gpa, module_name), - .name = try self.object.string_table.put(gpa, name), + .module_name = try parser.object.string_table.put(gpa, module_name), + .name = try parser.object.string_table.put(gpa, name), .kind = kind_value, }; } try assertEnd(reader); }, .function => { - for (try readVec(&self.object.functions, reader, gpa)) |*func| { + for (try readVec(&parser.object.functions, reader, gpa)) |*func| { func.* = .{ .type_index = try readLeb(u32, reader) }; } try assertEnd(reader); }, .table => { - for (try readVec(&self.object.tables, reader, gpa)) |*table| { + for (try readVec(&parser.object.tables, reader, gpa)) |*table| { table.* = .{ .reftype = try readEnum(std.wasm.RefType, reader), .limits = try readLimits(reader), @@ -461,13 +461,13 @@ fn Parser(comptime ReaderType: type) type { try assertEnd(reader); }, .memory => { - for (try readVec(&self.object.memories, reader, gpa)) |*memory| { + for (try readVec(&parser.object.memories, reader, gpa)) |*memory| { memory.* = .{ .limits = try readLimits(reader) }; } try assertEnd(reader); }, .global => { - for (try readVec(&self.object.globals, reader, gpa)) |*global| { + for (try readVec(&parser.object.globals, reader, gpa)) |*global| { global.* = .{ .global_type = .{ .valtype = try readEnum(std.wasm.Valtype, reader), @@ -479,13 +479,13 @@ fn Parser(comptime ReaderType: type) type { try assertEnd(reader); }, .@"export" => { - for (try readVec(&self.object.exports, reader, gpa)) |*exp| { + for (try readVec(&parser.object.exports, reader, gpa)) |*exp| { const name_len = try readLeb(u32, reader); const name = try gpa.alloc(u8, name_len); defer gpa.free(name); try reader.readNoEof(name); exp.* = .{ - .name = try self.object.string_table.put(gpa, name), + .name = try parser.object.string_table.put(gpa, name), .kind = try readEnum(std.wasm.ExternalKind, reader), .index = try readLeb(u32, reader), }; @@ -493,11 +493,11 @@ fn Parser(comptime ReaderType: type) type { try assertEnd(reader); }, .start => { - self.object.start = try readLeb(u32, reader); + parser.object.start = try readLeb(u32, reader); try assertEnd(reader); }, .element => { - for (try readVec(&self.object.elements, reader, gpa)) |*elem| { + for (try readVec(&parser.object.elements, reader, gpa)) |*elem| { elem.table_index = try readLeb(u32, reader); elem.offset = try readInit(reader); @@ -521,7 +521,7 @@ fn Parser(comptime ReaderType: type) type { .type = .code, .data = data.ptr, .size = code_len, - .index = self.object.importedCountByKind(.function) + index, + .index = parser.object.importedCountByKind(.function) + index, .offset = offset, .section_index = section_index, }); @@ -551,22 +551,22 @@ fn Parser(comptime ReaderType: type) type { }); } }, - else => try self.reader.reader().skipBytes(len, .{}), + else => try parser.reader.reader().skipBytes(len, .{}), } } else |err| switch (err) { error.EndOfStream => {}, // finished parsing the file else => |e| return e, } - self.object.relocatable_data = relocatable_data.toOwnedSlice(); + parser.object.relocatable_data = relocatable_data.toOwnedSlice(); } /// Based on the "features" custom section, parses it into a list of /// features that tell the linker what features were enabled and may be mandatory /// to be able to link. /// Logs an info message when an undefined feature is detected. - fn parseFeatures(self: *Self, gpa: Allocator) !void { - const reader = self.reader.reader(); - for (try readVec(&self.object.features, reader, gpa)) |*feature| { + fn parseFeatures(parser: *ObjectParser, gpa: Allocator) !void { + const reader = parser.reader.reader(); + for (try readVec(&parser.object.features, reader, gpa)) |*feature| { const prefix = try readEnum(types.Feature.Prefix, reader); const name_len = try leb.readULEB128(u32, reader); const name = try gpa.alloc(u8, name_len); @@ -587,8 +587,8 @@ fn Parser(comptime ReaderType: type) type { /// Parses a "reloc" custom section into a list of relocations. /// The relocations are mapped into `Object` where the key is the section /// they apply to. - fn parseRelocations(self: *Self, gpa: Allocator) !void { - const reader = self.reader.reader(); + fn parseRelocations(parser: *ObjectParser, gpa: Allocator) !void { + const reader = parser.reader.reader(); const section = try leb.readULEB128(u32, reader); const count = try leb.readULEB128(u32, reader); const relocations = try gpa.alloc(types.Relocation, count); @@ -616,15 +616,15 @@ fn Parser(comptime ReaderType: type) type { }); } - try self.object.relocations.putNoClobber(gpa, section, relocations); + try parser.object.relocations.putNoClobber(gpa, section, relocations); } /// Parses the "linking" custom section. Versions that are not /// supported will be an error. `payload_size` is required to be able /// to calculate the subsections we need to parse, as that data is not - /// available within the section itself. - fn parseMetadata(self: *Self, gpa: Allocator, payload_size: usize) !void { - var limited = std.io.limitedReader(self.reader.reader(), payload_size); + /// available within the section itparser. + fn parseMetadata(parser: *ObjectParser, gpa: Allocator, payload_size: usize) !void { + var limited = std.io.limitedReader(parser.reader.reader(), payload_size); const limited_reader = limited.reader(); const version = try leb.readULEB128(u32, limited_reader); @@ -632,7 +632,7 @@ fn Parser(comptime ReaderType: type) type { if (version != 2) return error.UnsupportedVersion; while (limited.bytes_left > 0) { - try self.parseSubsection(gpa, limited_reader); + try parser.parseSubsection(gpa, limited_reader); } } @@ -640,9 +640,9 @@ fn Parser(comptime ReaderType: type) type { /// The `reader` param for this is to provide a `LimitedReader`, which allows /// us to only read until a max length. /// - /// `self` is used to provide access to other sections that may be needed, + /// `parser` is used to provide access to other sections that may be needed, /// such as access to the `import` section to find the name of a symbol. - fn parseSubsection(self: *Self, gpa: Allocator, reader: anytype) !void { + fn parseSubsection(parser: *ObjectParser, gpa: Allocator, reader: anytype) !void { const sub_type = try leb.readULEB128(u8, reader); log.debug("Found subsection: {s}", .{@tagName(@intToEnum(types.SubsectionType, sub_type))}); const payload_len = try leb.readULEB128(u32, reader); @@ -674,7 +674,7 @@ fn Parser(comptime ReaderType: type) type { segment.flags, }); } - self.object.segment_info = segments; + parser.object.segment_info = segments; }, .WASM_INIT_FUNCS => { const funcs = try gpa.alloc(types.InitFunc, count); @@ -686,7 +686,7 @@ fn Parser(comptime ReaderType: type) type { }; log.debug("Found function - prio: {d}, index: {d}", .{ func.priority, func.symbol_index }); } - self.object.init_funcs = funcs; + parser.object.init_funcs = funcs; }, .WASM_COMDAT_INFO => { const comdats = try gpa.alloc(types.Comdat, count); @@ -719,7 +719,7 @@ fn Parser(comptime ReaderType: type) type { }; } - self.object.comdat_info = comdats; + parser.object.comdat_info = comdats; }, .WASM_SYMBOL_TABLE => { var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count); @@ -727,22 +727,22 @@ fn Parser(comptime ReaderType: type) type { var i: usize = 0; while (i < count) : (i += 1) { const symbol = symbols.addOneAssumeCapacity(); - symbol.* = try self.parseSymbol(gpa, reader); + symbol.* = try parser.parseSymbol(gpa, reader); log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{ @tagName(symbol.tag), - self.object.string_table.get(symbol.name), + parser.object.string_table.get(symbol.name), symbol.flags, }); } // we found all symbols, check for indirect function table // in case of an MVP object file - if (try self.object.checkLegacyIndirectFunctionTable()) |symbol| { + if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| { try symbols.append(symbol); log.debug("Found legacy indirect function table. Created symbol", .{}); } - self.object.symtable = symbols.toOwnedSlice(); + parser.object.symtable = symbols.toOwnedSlice(); }, } } @@ -750,7 +750,7 @@ fn Parser(comptime ReaderType: type) type { /// Parses the symbol information based on its kind, /// requires access to `Object` to find the name of a symbol when it's /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set. - fn parseSymbol(self: *Self, gpa: Allocator, reader: anytype) !Symbol { + fn parseSymbol(parser: *ObjectParser, gpa: Allocator, reader: anytype) !Symbol { const tag = @intToEnum(Symbol.Tag, try leb.readULEB128(u8, reader)); const flags = try leb.readULEB128(u32, reader); var symbol: Symbol = .{ @@ -766,7 +766,7 @@ fn Parser(comptime ReaderType: type) type { const name = try gpa.alloc(u8, name_len); defer gpa.free(name); try reader.readNoEof(name); - symbol.name = try self.object.string_table.put(gpa, name); + symbol.name = try parser.object.string_table.put(gpa, name); // Data symbols only have the following fields if the symbol is defined if (symbol.isDefined()) { @@ -778,7 +778,7 @@ fn Parser(comptime ReaderType: type) type { }, .section => { symbol.index = try leb.readULEB128(u32, reader); - for (self.object.relocatable_data) |data| { + for (parser.object.relocatable_data) |data| { if (data.section_index == symbol.index) { symbol.name = data.index; break; @@ -791,7 +791,7 @@ fn Parser(comptime ReaderType: type) type { const is_undefined = symbol.isUndefined(); if (is_undefined) { - maybe_import = self.object.findImport(symbol.tag.externalType(), symbol.index); + maybe_import = parser.object.findImport(symbol.tag.externalType(), symbol.index); } const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME); if (!(is_undefined and !explicit_name)) { @@ -799,7 +799,7 @@ fn Parser(comptime ReaderType: type) type { const name = try gpa.alloc(u8, name_len); defer gpa.free(name); try reader.readNoEof(name); - symbol.name = try self.object.string_table.put(gpa, name); + symbol.name = try parser.object.string_table.put(gpa, name); } else { symbol.name = maybe_import.?.name; } @@ -872,7 +872,7 @@ fn assertEnd(reader: anytype) !void { } /// Parses an object file into atoms, for code and data sections -pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { +pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { const Key = struct { kind: Symbol.Tag, index: u32, @@ -882,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin list.deinit(); } else symbol_for_segment.deinit(); - for (self.symtable) |symbol, symbol_index| { + for (object.symtable) |symbol, symbol_index| { switch (symbol.tag) { .function, .data, .section => if (!symbol.isUndefined()) { const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); @@ -896,7 +896,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin } } - for (self.relocatable_data) |relocatable_data, index| { + for (object.relocatable_data) |relocatable_data, index| { const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse { continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. }; @@ -911,12 +911,12 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin try wasm_bin.managed_atoms.append(gpa, atom); atom.file = object_index; atom.size = relocatable_data.size; - atom.alignment = relocatable_data.getAlignment(self); + atom.alignment = relocatable_data.getAlignment(object); - const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{}; + const relocations: []types.Relocation = object.relocations.get(relocatable_data.section_index) orelse &.{}; for (relocations) |relocation| { if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) { - // set the offset relative to the offset of the segment itself, + // set the offset relative to the offset of the segment itobject, // rather than within the entire section. var reloc = relocation; reloc.offset -= relocatable_data.offset; @@ -942,8 +942,8 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin // symbols referencing the same atom will be added as alias // or as 'parent' when they are global. while (symbols.popOrNull()) |idx| { - const alias_symbol = self.symtable[idx]; - const symbol = self.symtable[atom.sym_index]; + const alias_symbol = object.symtable[idx]; + const symbol = object.symtable[atom.sym_index]; if (alias_symbol.isGlobal() and symbol.isLocal()) { atom.sym_index = idx; } @@ -957,7 +957,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin } try wasm_bin.appendAtomAtIndex(final_index, atom); - log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index }); + log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index }); } } diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index 5e13456605..d857e6de44 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -34,8 +34,8 @@ pub const Tag = enum { /// From a given symbol tag, returns the `ExternalType` /// Asserts the given tag can be represented as an external type. - pub fn externalType(self: Tag) std.wasm.ExternalKind { - return switch (self) { + pub fn externalType(tag: Tag) std.wasm.ExternalKind { + return switch (tag) { .function => .function, .global => .global, .data => .memory, @@ -78,85 +78,85 @@ pub const Flag = enum(u32) { /// Verifies if the given symbol should be imported from the /// host environment or not -pub fn requiresImport(self: Symbol) bool { - if (self.tag == .data) return false; - if (!self.isUndefined()) return false; - if (self.isWeak()) return false; - // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib +pub fn requiresImport(symbol: Symbol) bool { + if (symbol.tag == .data) return false; + if (!symbol.isUndefined()) return false; + if (symbol.isWeak()) return false; + // if (symbol.isDefined() and symbol.isWeak()) return true; //TODO: Only when building shared lib return true; } -pub fn hasFlag(self: Symbol, flag: Flag) bool { - return self.flags & @enumToInt(flag) != 0; +pub fn hasFlag(symbol: Symbol, flag: Flag) bool { + return symbol.flags & @enumToInt(flag) != 0; } -pub fn setFlag(self: *Symbol, flag: Flag) void { - self.flags |= @enumToInt(flag); +pub fn setFlag(symbol: *Symbol, flag: Flag) void { + symbol.flags |= @enumToInt(flag); } -pub fn isUndefined(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_UNDEFINED) != 0; +pub fn isUndefined(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_UNDEFINED) != 0; } -pub fn setUndefined(self: *Symbol, is_undefined: bool) void { +pub fn setUndefined(symbol: *Symbol, is_undefined: bool) void { if (is_undefined) { - self.setFlag(.WASM_SYM_UNDEFINED); + symbol.setFlag(.WASM_SYM_UNDEFINED); } else { - self.flags &= ~@enumToInt(Flag.WASM_SYM_UNDEFINED); + symbol.flags &= ~@enumToInt(Flag.WASM_SYM_UNDEFINED); } } -pub fn setGlobal(self: *Symbol, is_global: bool) void { +pub fn setGlobal(symbol: *Symbol, is_global: bool) void { if (is_global) { - self.flags &= ~@enumToInt(Flag.WASM_SYM_BINDING_LOCAL); + symbol.flags &= ~@enumToInt(Flag.WASM_SYM_BINDING_LOCAL); } else { - self.setFlag(.WASM_SYM_BINDING_LOCAL); + symbol.setFlag(.WASM_SYM_BINDING_LOCAL); } } -pub fn isDefined(self: Symbol) bool { - return !self.isUndefined(); +pub fn isDefined(symbol: Symbol) bool { + return !symbol.isUndefined(); } -pub fn isVisible(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0; +pub fn isVisible(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0; } -pub fn isLocal(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_BINDING_LOCAL) != 0; +pub fn isLocal(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_BINDING_LOCAL) != 0; } -pub fn isGlobal(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_BINDING_LOCAL) == 0; +pub fn isGlobal(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_BINDING_LOCAL) == 0; } -pub fn isHidden(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0; +pub fn isHidden(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0; } -pub fn isNoStrip(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_NO_STRIP) != 0; +pub fn isNoStrip(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_NO_STRIP) != 0; } -pub fn isExported(self: Symbol) bool { - if (self.isUndefined() or self.isLocal()) return false; - if (self.isHidden()) return false; - if (self.hasFlag(.WASM_SYM_EXPORTED)) return true; - if (self.hasFlag(.WASM_SYM_BINDING_WEAK)) return false; +pub fn isExported(symbol: Symbol) bool { + if (symbol.isUndefined() or symbol.isLocal()) return false; + if (symbol.isHidden()) return false; + if (symbol.hasFlag(.WASM_SYM_EXPORTED)) return true; + if (symbol.hasFlag(.WASM_SYM_BINDING_WEAK)) return false; return true; } -pub fn isWeak(self: Symbol) bool { - return self.flags & @enumToInt(Flag.WASM_SYM_BINDING_WEAK) != 0; +pub fn isWeak(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_BINDING_WEAK) != 0; } /// Formats the symbol into human-readable text -pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { +pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; - const kind_fmt: u8 = switch (self.tag) { + const kind_fmt: u8 = switch (symbol.tag) { .function => 'F', .data => 'D', .global => 'G', @@ -165,12 +165,12 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt .table => 'T', .dead => '-', }; - const visible: []const u8 = if (self.isVisible()) "yes" else "no"; - const binding: []const u8 = if (self.isLocal()) "local" else "global"; - const undef: []const u8 = if (self.isUndefined()) "undefined" else ""; + const visible: []const u8 = if (symbol.isVisible()) "yes" else "no"; + const binding: []const u8 = if (symbol.isLocal()) "local" else "global"; + const undef: []const u8 = if (symbol.isUndefined()) "undefined" else ""; try writer.print( "{c} binding={s} visible={s} id={d} name_offset={d} {s}", - .{ kind_fmt, binding, visible, self.index, self.name, undef }, + .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef }, ); } diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index a606e3f7b6..9216f95efa 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -202,22 +202,22 @@ pub const Feature = struct { required = '=', }; - pub fn toString(self: Feature) []const u8 { - return switch (self.tag) { + pub fn toString(feature: Feature) []const u8 { + return switch (feature.tag) { .bulk_memory => "bulk-memory", .exception_handling => "exception-handling", .mutable_globals => "mutable-globals", .nontrapping_fptoint => "nontrapping-fptoint", .sign_ext => "sign-ext", .tail_call => "tail-call", - else => @tagName(self), + else => @tagName(feature), }; } - pub fn format(self: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { + pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { _ = opt; _ = fmt; - try writer.print("{c} {s}", .{ self.prefix, self.toString() }); + try writer.print("{c} {s}", .{ feature.prefix, feature.toString() }); } }; From 3edf8c7a6cb912597bbc97ad97d554e6e4d96cdb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 12 Sep 2022 21:35:28 +0200 Subject: [PATCH 4/4] wasm-linker: reset file pointer for incremental On each invocation of `flush()` the file pointer is moved. This means that rather than overwriting the binary file, we're appending to the file. With this commit, we're resetting said pointer to '0' and overwrite the existing binary in incremental mode. --- src/link/Wasm.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 14d864c330..6dd3bc56ff 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2218,6 +2218,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // We write the magic bytes at the end so they will only be written // if everything succeeded as expected. So populate with 0's for now. try binary_writer.writeAll(&[_]u8{0} ** 8); + // (Re)set file pointer to 0 + try wasm.base.file.?.setEndPos(0); + try wasm.base.file.?.seekTo(0); // Type section if (wasm.func_types.items.len != 0) {