authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-18 19:07:14-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log23d0882b54f1d7b8907eac47c445cfe4e093249d
tree2407b527c9c690de0b87a54f0016d6ccf331dd01
parent766284fec8184e7765b9a7900aea1a7494214153

wasm linker: handle extern functions in updateNav


2 files changed, 55 insertions(+), 25 deletions(-)

src/link/Wasm.zig+31-8
...@@ -209,7 +209,17 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp...@@ -209,7 +209,17 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp
209/// Tracks the value at the end of prelink, at which point `functions`209/// Tracks the value at the end of prelink, at which point `functions`
210/// contains only object file functions, and nothing from the Zcu yet.210/// contains only object file functions, and nothing from the Zcu yet.
211functions_end_prelink: u32 = 0,211functions_end_prelink: u32 = 0,
212/// Entries are deleted as they are satisfied by the Zcu.212/// At the end of prelink, this is populated with needed functions from
213/// objects.
214///
215/// During the Zcu phase, entries are not deleted from this table
216/// because doing so would be irreversible when a `deleteExport` call is
217/// handled. However, entries are added during the Zcu phase when extern
218/// functions are passed to `updateNav`.
219///
220/// `flush` gets a copy of this table, and then Zcu exports are applied to
221/// remove elements from the table, and the remainder are either undefined
222/// symbol errors, or import section entries depending on the output mode.
213function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty,223function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty,
214224
215/// Ordered list of non-import globals that will appear in the final binary.225/// Ordered list of non-import globals that will appear in the final binary.
...@@ -1156,11 +1166,6 @@ pub const ObjectTableIndex = enum(u32) {...@@ -1156,11 +1166,6 @@ pub const ObjectTableIndex = enum(u32) {
1156 }1166 }
1157};1167};
11581168
1159/// Index into `global_imports`.
1160pub const GlobalImportIndex = enum(u32) {
1161 _,
1162};
1163
1164/// Index into `Wasm.object_globals`.1169/// Index into `Wasm.object_globals`.
1165pub const ObjectGlobalIndex = enum(u32) {1170pub const ObjectGlobalIndex = enum(u32) {
1166 _,1171 _,
...@@ -1662,6 +1667,10 @@ pub const FunctionImportId = enum(u32) {...@@ -1662,6 +1667,10 @@ pub const FunctionImportId = enum(u32) {
1662 return pack(.{ .object_function_import = function_import_index }, wasm);1667 return pack(.{ .object_function_import = function_import_index }, wasm);
1663 }1668 }
16641669
1670 pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId {
1671 return pack(.{ .zcu_import = zcu_import }, wasm);
1672 }
1673
1665 /// This function is allowed O(N) lookup because it is only called during1674 /// This function is allowed O(N) lookup because it is only called during
1666 /// diagnostic generation.1675 /// diagnostic generation.
1667 pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation {1676 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...@@ -2297,13 +2306,21 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
22972306
2298 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {2307 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
2299 .func => return, // global const which is a function alias2308 .func => return, // global const which is a function alias
2300 .@"extern" => {2309 .@"extern" => |ext| {
2301 if (is_obj) {2310 if (is_obj) {
2302 assert(!wasm.navs_obj.contains(nav_index));2311 assert(!wasm.navs_obj.contains(nav_index));
2303 } else {2312 } else {
2304 assert(!wasm.navs_exe.contains(nav_index));2313 assert(!wasm.navs_exe.contains(nav_index));
2305 }2314 }
2306 try wasm.imports.put(gpa, nav_index, {});2315 const name = try wasm.internString(ext.name.toSlice(ip));
2316 try wasm.imports.ensureUnusedCapacity(gpa, 1);
2317 if (ip.isFunctionType(nav.typeOf(ip))) {
2318 try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
2319 const zcu_import = wasm.addZcuImportReserved(ext.owner_nav);
2320 wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
2321 } else {
2322 @panic("TODO extern data");
2323 }
2307 return;2324 return;
2308 },2325 },
2309 .variable => |variable| variable.init,2326 .variable => |variable| variable.init,
...@@ -3464,3 +3481,9 @@ fn pointerAlignment(wasm: *const Wasm) Alignment {...@@ -3464,3 +3481,9 @@ fn pointerAlignment(wasm: *const Wasm) Alignment {
3464 else => unreachable,3481 else => unreachable,
3465 };3482 };
3466}3483}
3484
3485fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex {
3486 const gop = wasm.imports.getOrPutAssumeCapacity(nav_index);
3487 gop.value_ptr.* = {};
3488 return @enumFromInt(gop.index);
3489}
src/link/Wasm/Flush.zig+24-17
...@@ -76,7 +76,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -76,7 +76,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
76 .function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?,76 .function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?,
77 });77 });
78 _ = f.missing_exports.swapRemove(nav_export.name);78 _ = f.missing_exports.swapRemove(nav_export.name);
79 _ = wasm.function_imports.swapRemove(nav_export.name);79 _ = f.function_imports.swapRemove(nav_export.name);
8080
81 if (nav_export.name.toOptional() == entry_name)81 if (nav_export.name.toOptional() == entry_name)
82 wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index);82 wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index);
...@@ -86,7 +86,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -86,7 +86,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
86 .global_index = Wasm.GlobalIndex.fromIpNav(wasm, nav_export.nav_index).?,86 .global_index = Wasm.GlobalIndex.fromIpNav(wasm, nav_export.nav_index).?,
87 });87 });
88 _ = f.missing_exports.swapRemove(nav_export.name);88 _ = f.missing_exports.swapRemove(nav_export.name);
89 _ = wasm.global_imports.swapRemove(nav_export.name);89 _ = f.global_imports.swapRemove(nav_export.name);
90 }90 }
91 }91 }
9292
...@@ -104,11 +104,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -104,11 +104,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
104 }104 }
105105
106 if (!allow_undefined) {106 if (!allow_undefined) {
107 for (wasm.function_imports.keys(), wasm.function_imports.values()) |name, function_import_id| {107 for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {
108 const src_loc = function_import_id.sourceLocation(wasm);108 const src_loc = function_import_id.sourceLocation(wasm);
109 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});109 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});
110 }110 }
111 for (wasm.global_imports.keys(), wasm.global_imports.values()) |name, global_import_id| {111 for (f.global_imports.keys(), f.global_imports.values()) |name, global_import_id| {
112 const src_loc = global_import_id.sourceLocation(wasm);112 const src_loc = global_import_id.sourceLocation(wasm);
113 src_loc.addError(wasm, "undefined global: {s}", .{name.slice(wasm)});113 src_loc.addError(wasm, "undefined global: {s}", .{name.slice(wasm)});
114 }114 }
...@@ -391,12 +391,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -391,12 +391,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
391 section_index += 1;391 section_index += 1;
392 }392 }
393393
394 if (!is_obj) {
395 // TODO: sort function_imports by ref count descending for optimal LEB encodings
396 // TODO: sort global_imports by ref count descending for optimal LEB encodings
397 // TODO: sort output functions by ref count descending for optimal LEB encodings
398 }
399
394 // Import section400 // Import section
395 {401 {
396 var total_imports: usize = 0;402 var total_imports: usize = 0;
397 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);403 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
398404
399 for (wasm.function_imports.values()) |id| {405 for (f.function_imports.values()) |id| {
400 const module_name = id.moduleName(wasm).slice(wasm);406 const module_name = id.moduleName(wasm).slice(wasm);
401 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));407 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
402 try binary_writer.writeAll(module_name);408 try binary_writer.writeAll(module_name);
...@@ -408,7 +414,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -408,7 +414,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
408 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));414 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
409 try leb.writeUleb128(binary_writer, @intFromEnum(id.functionType(wasm)));415 try leb.writeUleb128(binary_writer, @intFromEnum(id.functionType(wasm)));
410 }416 }
411 total_imports += wasm.function_imports.entries.len;417 total_imports += f.function_imports.entries.len;
412418
413 for (wasm.table_imports.values()) |id| {419 for (wasm.table_imports.values()) |id| {
414 const table_import = id.value(wasm);420 const table_import = id.value(wasm);
...@@ -441,7 +447,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -441,7 +447,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
441 total_imports += 1;447 total_imports += 1;
442 }448 }
443449
444 for (wasm.global_imports.values()) |id| {450 for (f.global_imports.values()) |id| {
445 const module_name = id.moduleName(wasm).slice(wasm);451 const module_name = id.moduleName(wasm).slice(wasm);
446 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));452 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
447 try binary_writer.writeAll(module_name);453 try binary_writer.writeAll(module_name);
...@@ -455,7 +461,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -455,7 +461,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
455 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));461 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
456 try binary_writer.writeByte(@intFromBool(global_type.mutable));462 try binary_writer.writeByte(@intFromBool(global_type.mutable));
457 }463 }
458 total_imports += wasm.global_imports.entries.len;464 total_imports += f.global_imports.entries.len;
459465
460 if (total_imports > 0) {466 if (total_imports > 0) {
461 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));467 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));
...@@ -757,6 +763,7 @@ fn emitNameSection(...@@ -757,6 +763,7 @@ fn emitNameSection(
757 data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32),763 data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32),
758 binary_bytes: *std.ArrayListUnmanaged(u8),764 binary_bytes: *std.ArrayListUnmanaged(u8),
759) !void {765) !void {
766 const f = &wasm.flush_buffer;
760 const comp = wasm.base.comp;767 const comp = wasm.base.comp;
761 const gpa = comp.gpa;768 const gpa = comp.gpa;
762769
...@@ -771,16 +778,16 @@ fn emitNameSection(...@@ -771,16 +778,16 @@ fn emitNameSection(
771 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);778 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
772 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));779 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));
773780
774 const total_functions: u32 = @intCast(wasm.function_imports.entries.len + wasm.functions.entries.len);781 const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len);
775 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);782 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);
776783
777 for (wasm.function_imports.keys(), 0..) |name_index, function_index| {784 for (f.function_imports.keys(), 0..) |name_index, function_index| {
778 const name = name_index.slice(wasm);785 const name = name_index.slice(wasm);
779 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));786 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
780 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));787 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
781 try binary_bytes.appendSlice(gpa, name);788 try binary_bytes.appendSlice(gpa, name);
782 }789 }
783 for (wasm.functions.keys(), wasm.function_imports.entries.len..) |resolution, function_index| {790 for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| {
784 const name = resolution.name(wasm).?;791 const name = resolution.name(wasm).?;
785 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));792 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
786 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));793 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
...@@ -792,16 +799,16 @@ fn emitNameSection(...@@ -792,16 +799,16 @@ fn emitNameSection(
792 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);799 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
793 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));800 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));
794801
795 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);802 const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len);
796 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);803 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
797804
798 for (wasm.global_imports.keys(), 0..) |name_index, global_index| {805 for (f.global_imports.keys(), 0..) |name_index, global_index| {
799 const name = name_index.slice(wasm);806 const name = name_index.slice(wasm);
800 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));807 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
801 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));808 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
802 try binary_bytes.appendSlice(gpa, name);809 try binary_bytes.appendSlice(gpa, name);
803 }810 }
804 for (wasm.globals.keys(), wasm.global_imports.entries.len..) |resolution, global_index| {811 for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| {
805 const name = resolution.name(wasm).?;812 const name = resolution.name(wasm).?;
806 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));813 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
807 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));814 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
...@@ -813,7 +820,7 @@ fn emitNameSection(...@@ -813,7 +820,7 @@ fn emitNameSection(
813 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);820 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
814 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));821 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));
815822
816 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);823 const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len);
817 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);824 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
818825
819 for (data_segments.keys(), 0..) |ds, i| {826 for (data_segments.keys(), 0..) |ds, i| {
...@@ -1356,7 +1363,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {...@@ -1356,7 +1363,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1356// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined)1363// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined)
1357// @intFromEnum(symbol.pointee.function_import)1364// @intFromEnum(symbol.pointee.function_import)
1358// else1365// else
1359// @intFromEnum(symbol.pointee.function) + wasm.function_imports.items.len,1366// @intFromEnum(symbol.pointee.function) + f.function_imports.items.len,
1360// .TABLE_NUMBER_LEB => if (symbol.flags.undefined)1367// .TABLE_NUMBER_LEB => if (symbol.flags.undefined)
1361// @intFromEnum(symbol.pointee.table_import)1368// @intFromEnum(symbol.pointee.table_import)
1362// else1369// else
...@@ -1371,7 +1378,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {...@@ -1371,7 +1378,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1371// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)1378// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)
1372// @intFromEnum(symbol.pointee.global_import)1379// @intFromEnum(symbol.pointee.global_import)
1373// else1380// else
1374// @intFromEnum(symbol.pointee.global) + wasm.global_imports.items.len,1381// @intFromEnum(symbol.pointee.global) + f.global_imports.items.len,
1375//1382//
1376// .MEMORY_ADDR_I32,1383// .MEMORY_ADDR_I32,
1377// .MEMORY_ADDR_I64,1384// .MEMORY_ADDR_I64,