diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 94226bea65..fa64c4e627 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -209,7 +209,17 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp /// Tracks the value at the end of prelink, at which point `functions` /// contains only object file functions, and nothing from the Zcu yet. functions_end_prelink: u32 = 0, -/// Entries are deleted as they are satisfied by the Zcu. +/// At the end of prelink, this is populated with needed functions from +/// objects. +/// +/// During the Zcu phase, entries are not deleted from this table +/// because doing so would be irreversible when a `deleteExport` call is +/// handled. However, entries are added during the Zcu phase when extern +/// functions are passed to `updateNav`. +/// +/// `flush` gets a copy of this table, and then Zcu exports are applied to +/// remove elements from the table, and the remainder are either undefined +/// symbol errors, or import section entries depending on the output mode. function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty, /// Ordered list of non-import globals that will appear in the final binary. @@ -1156,11 +1166,6 @@ pub const ObjectTableIndex = enum(u32) { } }; -/// Index into `global_imports`. -pub const GlobalImportIndex = enum(u32) { - _, -}; - /// Index into `Wasm.object_globals`. pub const ObjectGlobalIndex = enum(u32) { _, @@ -1662,6 +1667,10 @@ pub const FunctionImportId = enum(u32) { return pack(.{ .object_function_import = function_import_index }, wasm); } + pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId { + return pack(.{ .zcu_import = zcu_import }, wasm); + } + /// This function is allowed O(N) lookup because it is only called during /// diagnostic generation. pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation { @@ -2297,13 +2306,21 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { .func => return, // global const which is a function alias - .@"extern" => { + .@"extern" => |ext| { if (is_obj) { assert(!wasm.navs_obj.contains(nav_index)); } else { assert(!wasm.navs_exe.contains(nav_index)); } - try wasm.imports.put(gpa, nav_index, {}); + const name = try wasm.internString(ext.name.toSlice(ip)); + try wasm.imports.ensureUnusedCapacity(gpa, 1); + if (ip.isFunctionType(nav.typeOf(ip))) { + try wasm.function_imports.ensureUnusedCapacity(gpa, 1); + const zcu_import = wasm.addZcuImportReserved(ext.owner_nav); + wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); + } else { + @panic("TODO extern data"); + } return; }, .variable => |variable| variable.init, @@ -3464,3 +3481,9 @@ fn pointerAlignment(wasm: *const Wasm) Alignment { else => unreachable, }; } + +fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex { + const gop = wasm.imports.getOrPutAssumeCapacity(nav_index); + gop.value_ptr.* = {}; + return @enumFromInt(gop.index); +} diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index c2a4352910..1df665f0c0 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -76,7 +76,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?, }); _ = f.missing_exports.swapRemove(nav_export.name); - _ = wasm.function_imports.swapRemove(nav_export.name); + _ = f.function_imports.swapRemove(nav_export.name); if (nav_export.name.toOptional() == entry_name) wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index); @@ -86,7 +86,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .global_index = Wasm.GlobalIndex.fromIpNav(wasm, nav_export.nav_index).?, }); _ = f.missing_exports.swapRemove(nav_export.name); - _ = wasm.global_imports.swapRemove(nav_export.name); + _ = f.global_imports.swapRemove(nav_export.name); } } @@ -104,11 +104,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } if (!allow_undefined) { - for (wasm.function_imports.keys(), wasm.function_imports.values()) |name, function_import_id| { + for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| { const src_loc = function_import_id.sourceLocation(wasm); src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)}); } - for (wasm.global_imports.keys(), wasm.global_imports.values()) |name, global_import_id| { + for (f.global_imports.keys(), f.global_imports.values()) |name, global_import_id| { const src_loc = global_import_id.sourceLocation(wasm); src_loc.addError(wasm, "undefined global: {s}", .{name.slice(wasm)}); } @@ -391,12 +391,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { section_index += 1; } + if (!is_obj) { + // TODO: sort function_imports by ref count descending for optimal LEB encodings + // TODO: sort global_imports by ref count descending for optimal LEB encodings + // TODO: sort output functions by ref count descending for optimal LEB encodings + } + // Import section { var total_imports: usize = 0; const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); - for (wasm.function_imports.values()) |id| { + for (f.function_imports.values()) |id| { const module_name = id.moduleName(wasm).slice(wasm); try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); try binary_writer.writeAll(module_name); @@ -408,7 +414,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); try leb.writeUleb128(binary_writer, @intFromEnum(id.functionType(wasm))); } - total_imports += wasm.function_imports.entries.len; + total_imports += f.function_imports.entries.len; for (wasm.table_imports.values()) |id| { const table_import = id.value(wasm); @@ -441,7 +447,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { total_imports += 1; } - for (wasm.global_imports.values()) |id| { + for (f.global_imports.values()) |id| { const module_name = id.moduleName(wasm).slice(wasm); try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); try binary_writer.writeAll(module_name); @@ -455,7 +461,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); try binary_writer.writeByte(@intFromBool(global_type.mutable)); } - total_imports += wasm.global_imports.entries.len; + total_imports += f.global_imports.entries.len; if (total_imports > 0) { replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports)); @@ -757,6 +763,7 @@ fn emitNameSection( data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32), binary_bytes: *std.ArrayListUnmanaged(u8), ) !void { + const f = &wasm.flush_buffer; const comp = wasm.base.comp; const gpa = comp.gpa; @@ -771,16 +778,16 @@ fn emitNameSection( const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function)); - const total_functions: u32 = @intCast(wasm.function_imports.entries.len + wasm.functions.entries.len); + const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len); try leb.writeUleb128(binary_bytes.writer(gpa), total_functions); - for (wasm.function_imports.keys(), 0..) |name_index, function_index| { + for (f.function_imports.keys(), 0..) |name_index, function_index| { const name = name_index.slice(wasm); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } - for (wasm.functions.keys(), wasm.function_imports.entries.len..) |resolution, function_index| { + for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { const name = resolution.name(wasm).?; try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); @@ -792,16 +799,16 @@ fn emitNameSection( const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global)); - const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len); + const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len); try leb.writeUleb128(binary_bytes.writer(gpa), total_globals); - for (wasm.global_imports.keys(), 0..) |name_index, global_index| { + for (f.global_imports.keys(), 0..) |name_index, global_index| { const name = name_index.slice(wasm); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } - for (wasm.globals.keys(), wasm.global_imports.entries.len..) |resolution, global_index| { + for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { const name = resolution.name(wasm).?; try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); @@ -813,7 +820,7 @@ fn emitNameSection( const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); - const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len); + const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len); try leb.writeUleb128(binary_bytes.writer(gpa), total_globals); for (data_segments.keys(), 0..) |ds, i| { @@ -1356,7 +1363,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { // .FUNCTION_INDEX_LEB => if (symbol.flags.undefined) // @intFromEnum(symbol.pointee.function_import) // else -// @intFromEnum(symbol.pointee.function) + wasm.function_imports.items.len, +// @intFromEnum(symbol.pointee.function) + f.function_imports.items.len, // .TABLE_NUMBER_LEB => if (symbol.flags.undefined) // @intFromEnum(symbol.pointee.table_import) // else @@ -1371,7 +1378,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { // .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined) // @intFromEnum(symbol.pointee.global_import) // else -// @intFromEnum(symbol.pointee.global) + wasm.global_imports.items.len, +// @intFromEnum(symbol.pointee.global) + f.global_imports.items.len, // // .MEMORY_ADDR_I32, // .MEMORY_ADDR_I64,