wasm linker: don't assume nav callees are fully resolved

codegen can be called which contains calls to navs which have only their
type resolved. this means the indirect function table needs to track nav
indexes not ip indexes.
This commit is contained in:
Andrew Kelley 2025-01-08 17:01:36 -08:00
parent 5186c6c4ee
commit 21a2888561
4 changed files with 8 additions and 19 deletions

View File

@ -4120,14 +4120,3 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg)
zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
return error.CodegenFail;
}
/// Check if nav is an alias to a function, in which case we want to lower the
/// actual nav, rather than the alias itself.
pub fn chaseNav(zcu: *const Zcu, nav: InternPool.Nav.Index) InternPool.Nav.Index {
return switch (zcu.intern_pool.indexToKey(zcu.navValue(nav).toIntern())) {
.func => |f| f.owner_nav,
.variable => |variable| variable.owner_nav,
.@"extern" => |@"extern"| @"extern".owner_nav,
else => nav,
};
}

View File

@ -1025,10 +1025,9 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
const comp = wasm.base.comp;
const zcu = comp.zcu.?;
const ip = &zcu.intern_pool;
const ip_index = ip.getNav(nav_ref.nav_index).status.fully_resolved.val;
if (ip.isFunctionType(ip.typeOf(ip_index))) {
if (ip.getNav(nav_ref.nav_index).isExternOrFn(ip)) {
assert(nav_ref.offset == 0);
const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, ip_index);
const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, nav_ref.nav_index);
if (!gop.found_existing) gop.value_ptr.* = {};
try cg.addInst(.{
.tag = .func_ref,
@ -1056,7 +1055,8 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
const ip = &zcu.intern_pool;
if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {
assert(uav.offset == 0);
const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, uav.ip_index);
const owner_nav = ip.toFunc(uav.ip_index).owner_nav;
const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, owner_nav);
if (!gop.found_existing) gop.value_ptr.* = {};
try cg.addInst(.{
.tag = .func_ref,
@ -3096,7 +3096,7 @@ fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerErro
const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
const offset: u64 = prev_offset + ptr.byte_offset;
return switch (ptr.base_addr) {
.nav => |nav| return .{ .nav_ref = .{ .nav_index = zcu.chaseNav(nav), .offset = @intCast(offset) } },
.nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } },
.uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset) } },
.int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
.eu_payload => return cg.fail("Wasm TODO: lower error union payload pointer", .{}),

View File

@ -260,7 +260,7 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty
/// All functions that have had their address taken and therefore might be
/// called via a `call_indirect` function.
indirect_function_table: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
indirect_function_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
error_name_table_ref_count: u32 = 0,

View File

@ -651,8 +651,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
}
try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.indirect_function_table.entries.len)));
for (wasm.indirect_function_table.keys()) |ip_index| {
const func_index: Wasm.OutputFunctionIndex = .fromIpIndex(wasm, ip_index);
for (wasm.indirect_function_table.keys()) |nav_index| {
const func_index: Wasm.OutputFunctionIndex = .fromIpNav(wasm, nav_index);
try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
}