wasm: ensure unique function indexes

We cannot keep function indexes as maxInt(u32) due to functions being
dedupliated when they point to the same function. For this reason we now
use a regular arraylist which will have new functions appended to, and
when deleted, its index is appended to the free list, allowing us to
re-use slots in the function list.
This commit is contained in:
Luuk de Gram 2024-01-29 06:52:50 +01:00
parent fde8c2f41a
commit c153f94c89
No known key found for this signature in database
GPG key ID: A8CFE58E4DC7D664
3 changed files with 28 additions and 25 deletions

View file

@ -819,7 +819,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
} }
if (symbol.tag != existing_sym.tag) { if (symbol.tag != existing_sym.tag) {
log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) }); log.err("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
log.err(" first definition in '{s}'", .{existing_file_path}); log.err(" first definition in '{s}'", .{existing_file_path});
log.err(" next definition in '{s}'", .{obj_file.path()}); log.err(" next definition in '{s}'", .{obj_file.path()});
return error.SymbolMismatchingType; return error.SymbolMismatchingType;

View file

@ -13,7 +13,9 @@ decls_map: std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclInfo) = .{},
func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
/// List of `std.wasm.Func`. Each entry contains the function signature, /// List of `std.wasm.Func`. Each entry contains the function signature,
/// rather than the actual body. /// rather than the actual body.
functions: std.AutoHashMapUnmanaged(u32, std.wasm.Func) = .{}, functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
/// List of indexes pointing to an entry within the `functions` list which has been removed.
functions_free_list: std.ArrayListUnmanaged(u32) = .{},
/// Map of symbol locations, represented by its `types.Import`. /// Map of symbol locations, represented by its `types.Import`.
imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{}, imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{},
/// List of WebAssembly globals. /// List of WebAssembly globals.
@ -320,11 +322,7 @@ fn finishUpdateDecl(
switch (decl.ty.zigTypeTag(mod)) { switch (decl.ty.zigTypeTag(mod)) {
.Fn => { .Fn => {
try zig_object.functions.put( sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
gpa,
atom.sym_index,
.{ .type_index = zig_object.atom_types.get(atom_index).? },
);
sym.tag = .function; sym.tag = .function;
}, },
else => { else => {
@ -689,6 +687,9 @@ pub fn addOrUpdateImport(
}; };
zig_object.imported_functions_count += 1; zig_object.imported_functions_count += 1;
} }
sym.tag = .function;
} else {
sym.tag = .data;
} }
} }
@ -821,10 +822,6 @@ pub fn deleteDeclExport(
std.debug.assert(zig_object.global_syms.remove(sym.name)); std.debug.assert(zig_object.global_syms.remove(sym.name));
std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index })); std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index }));
zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {}; zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {};
if (sym.tag == .function) {
std.debug.assert(zig_object.functions.remove(sym_index));
}
sym.tag = .dead; sym.tag = .dead;
} }
} }
@ -867,17 +864,6 @@ pub fn updateExports(
else index: { else index: {
const sym_index = try zig_object.allocateSymbol(gpa); const sym_index = try zig_object.allocateSymbol(gpa);
try decl_info.appendExport(gpa, sym_index); try decl_info.appendExport(gpa, sym_index);
// For functions, we also need to put the alias in the function section.
// We simply copy the aliased function.
// The final linakge will deduplicate these functions.
if (decl.ty.zigTypeTag(mod) == .Fn) {
try zig_object.functions.putNoClobber(
gpa,
sym_index,
zig_object.functions.get(atom.sym_index).?,
);
}
break :index sym_index; break :index sym_index;
}; };
@ -969,7 +955,7 @@ pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool
} }
switch (decl.ty.zigTypeTag(mod)) { switch (decl.ty.zigTypeTag(mod)) {
.Fn => { .Fn => {
std.debug.assert(zig_object.functions.remove(atom.sym_index)); zig_object.functions_free_list.append(gpa, sym.index) catch {};
std.debug.assert(zig_object.atom_types.remove(atom_index)); std.debug.assert(zig_object.atom_types.remove(atom_index));
}, },
else => { else => {
@ -1221,7 +1207,7 @@ pub fn createFunction(
sym.tag = .function; sym.tag = .function;
sym.name = try zig_object.string_table.insert(gpa, symbol_name); sym.name = try zig_object.string_table.insert(gpa, symbol_name);
const type_index = try zig_object.putOrGetFuncType(gpa, func_ty); const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
try zig_object.functions.putNoClobber(gpa, sym_index, .{ .type_index = type_index }); sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
const atom = wasm_file.getAtomPtr(atom_index); const atom = wasm_file.getAtomPtr(atom_index);
@ -1232,6 +1218,20 @@ pub fn createFunction(
return sym_index; return sym_index;
} }
/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
idx
else idx: {
const len: u32 = @intCast(zig_object.functions.items.len);
_ = try zig_object.functions.addOne(gpa);
break :idx len;
};
zig_object.functions.items[index] = func;
return index;
}
const build_options = @import("build_options"); const build_options = @import("build_options");
const builtin = @import("builtin"); const builtin = @import("builtin");
const codegen = @import("../../codegen.zig"); const codegen = @import("../../codegen.zig");

View file

@ -91,7 +91,10 @@ pub const File = union(enum) {
pub fn function(file: File, sym_index: u32) std.wasm.Func { pub fn function(file: File, sym_index: u32) std.wasm.Func {
switch (file) { switch (file) {
.zig_object => |obj| return obj.functions.get(sym_index).?, .zig_object => |obj| {
const sym = obj.symbols.items[sym_index];
return obj.functions.items[sym.index];
},
.object => |obj| { .object => |obj| {
const sym = obj.symtable[sym_index]; const sym = obj.symtable[sym_index];
return obj.functions[sym.index - obj.imported_functions_count]; return obj.functions[sym.index - obj.imported_functions_count];