authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-28 19:32:10+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-01 08:35:20+01:00
logf5a31cb0d693ef3c07da7f446c95999cef34488b
tree8b4ea40e8a2d8240d0559ef9220ccd4c8fe9868b
parentb1159ab7aecfe704adcb4cf44d6fedafd572a720

wasm-linker: Intern globals, exports & imports

Symbols that have globals used to have their lookup key be the symbol name. This key is now the offset into the string table. Imports have both the module name (library name) and name (of the symbol), those strings are now also being interned. This can save us up to 24bytes per import which have both their module name and name de-duplicated. Module names are almost entirely the same for all imports, providing us with a big chance of saving us 12 bytes at least. Just like imports, exports can also have a seperate name than the internal symbol name. Rather than storing the slice, we now store the offset of this string instead.

3 files changed, 122 insertions(+), 73 deletions(-)

src/link/Wasm.zig+85-49
......@@ -69,8 +69,8 @@ imported_globals_count: u32 = 0,
6969/// The count of imported tables. This number will be appended
7070/// to the table indexes when sections are merged.
7171imported_tables_count: u32 = 0,
72/// Map of symbol locations, represented by its `wasm.Import`
73imports: std.AutoHashMapUnmanaged(SymbolLoc, wasm.Import) = .{},
72/// Map of symbol locations, represented by its `types.Import`
73imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{},
7474/// Represents non-synthetic section entries.
7575/// Used for code, data and custom sections.
7676segments: std.ArrayListUnmanaged(Segment) = .{},
......@@ -94,7 +94,7 @@ memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
9494/// Output table section
9595tables: std.ArrayListUnmanaged(wasm.Table) = .{},
9696/// Output export section
97exports: std.ArrayListUnmanaged(wasm.Export) = .{},
97exports: std.ArrayListUnmanaged(types.Export) = .{},
9898
9999/// Indirect function table, used to call function pointers
100100/// When this is non-zero, we must emit a table entry,
......@@ -105,8 +105,8 @@ function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},
105105
106106/// All object files and their data which are linked into the final binary
107107objects: std.ArrayListUnmanaged(Object) = .{},
108/// A map of global names to their symbol location
109globals: std.StringHashMapUnmanaged(SymbolLoc) = .{},
108/// A map of global names (read: offset into string table) to their symbol location
109globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{},
110110/// Maps discarded symbols and their positions to the location of the symbol
111111/// it was resolved to
112112discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
......@@ -119,7 +119,8 @@ resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
119119symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},
120120/// Maps a symbol's location to its export name, which may differ from the decl's name
121121/// which does the exporting.
122export_names: std.AutoHashMapUnmanaged(SymbolLoc, []const u8) = .{},
122/// Note: The value represents the offset into the string table, rather than the actual string.
123export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
123124
124125pub const Segment = struct {
125126 alignment: u32,
......@@ -223,6 +224,15 @@ pub const StringTable = struct {
223224 return mem.sliceTo(@ptrCast([*:0]const u8, self.string_data.items.ptr + off), 0);
224225 }
225226
227 /// Returns the offset of a given string when it exists.
228 /// Will return null if the given string does not yet exist within the string table.
229 pub fn getOffset(self: *StringTable, string: []const u8) ?u32 {
230 return self.string_table.getKeyAdapted(
231 string,
232 std.hash_map.StringIndexAdapter{ .bytes = &self.string_data },
233 );
234 }
235
226236 /// Frees all resources of the string table. Any references pointing
227237 /// to the strings will be invalid.
228238 pub fn deinit(self: *StringTable, allocator: Allocator) void {
......@@ -250,16 +260,17 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
250260 try file.writeAll(&(wasm.magic ++ wasm.version));
251261
252262 // As sym_index '0' is reserved, we use it for our stack pointer symbol
263 const sym_name = try wasm_bin.string_table.put(allocator, "__stack_pointer");
253264 const symbol = try wasm_bin.symbols.addOne(allocator);
254265 symbol.* = .{
255 .name = try wasm_bin.string_table.put(allocator, "__stack_pointer"),
266 .name = sym_name,
256267 .tag = .global,
257268 .flags = 0,
258269 .index = 0,
259270 };
260271 const loc: SymbolLoc = .{ .file = null, .index = 0 };
261272 try wasm_bin.resolved_symbols.putNoClobber(allocator, loc, {});
262 try wasm_bin.globals.putNoClobber(allocator, "__stack_pointer", loc);
273 try wasm_bin.globals.putNoClobber(allocator, sym_name, loc);
263274
264275 // For object files we will import the stack pointer symbol
265276 if (options.output_mode == .Obj) {
......@@ -268,8 +279,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
268279 allocator,
269280 .{ .file = null, .index = 0 },
270281 .{
271 .module_name = wasm_bin.host_name,
272 .name = "__stack_pointer",
282 .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
283 .name = sym_name,
273284 .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
274285 },
275286 );
......@@ -344,6 +355,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
344355 .index = sym_index,
345356 };
346357 const sym_name = object.string_table.get(symbol.name);
358 const sym_name_index = try self.string_table.put(self.base.allocator, sym_name);
347359
348360 if (symbol.isLocal()) {
349361 if (symbol.isUndefined()) {
......@@ -358,7 +370,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
358370 // TODO: locals are allowed to have duplicate symbol names
359371 // TODO: Store undefined symbols so we can verify at the end if they've all been found
360372 // if not, emit an error (unless --allow-undefined is enabled).
361 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);
373 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index);
362374 if (!maybe_existing.found_existing) {
363375 maybe_existing.value_ptr.* = location;
364376 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
......@@ -383,13 +395,18 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
383395 continue; // Do not overwrite defined symbols with undefined symbols
384396 }
385397
398 // when both symbols are weak, we skip overwriting
399 if (existing_sym.isWeak() and symbol.isWeak()) {
400 continue;
401 }
402
386403 // simply overwrite with the new symbol
387404 log.debug("Overwriting symbol '{s}'", .{sym_name});
388405 log.debug(" old definition in '{s}'", .{existing_file_path});
389406 log.debug(" new definition in '{s}'", .{object.name});
390407 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
391408 maybe_existing.value_ptr.* = location;
392 try self.globals.put(self.base.allocator, sym_name, location);
409 try self.globals.put(self.base.allocator, sym_name_index, location);
393410 try self.resolved_symbols.put(self.base.allocator, location, {});
394411 assert(self.resolved_symbols.swapRemove(existing_loc));
395412 }
......@@ -696,7 +713,7 @@ pub fn deleteExport(self: *Wasm, exp: Export) void {
696713 if (self.export_names.fetchRemove(loc)) |kv| {
697714 assert(self.globals.remove(kv.value));
698715 } else {
699 assert(self.globals.remove(symbol_name));
716 assert(self.globals.remove(symbol.name));
700717 }
701718}
702719
......@@ -723,7 +740,9 @@ pub fn updateDeclExports(
723740 ));
724741 continue;
725742 }
726 if (self.globals.getPtr(exp.options.name)) |existing_loc| {
743
744 const export_name = try self.string_table.put(self.base.allocator, exp.options.name);
745 if (self.globals.getPtr(export_name)) |existing_loc| {
727746 if (existing_loc.index == decl.link.wasm.sym_index) continue;
728747 const existing_sym: Symbol = existing_loc.getSymbol(self).*;
729748
......@@ -775,13 +794,13 @@ pub fn updateDeclExports(
775794 }
776795 // Ensure the symbol will be exported using the given name
777796 if (!mem.eql(u8, exp.options.name, sym_loc.getName(self))) {
778 try self.export_names.put(self.base.allocator, sym_loc, exp.options.name);
797 try self.export_names.put(self.base.allocator, sym_loc, export_name);
779798 }
780799
781800 symbol.setGlobal(true);
782801 try self.globals.put(
783802 self.base.allocator,
784 exp.options.name,
803 export_name,
785804 sym_loc,
786805 );
787806
......@@ -832,14 +851,14 @@ fn mapFunctionTable(self: *Wasm) void {
832851
833852fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
834853 // For the import name itself, we use the decl's name, rather than the fully qualified name
835 const decl_name = mem.sliceTo(decl.name, 0);
854 const decl_name_index = try self.string_table.put(self.base.allocator, mem.sliceTo(decl.name, 0));
836855 const symbol_index = decl.link.wasm.sym_index;
837856 const symbol: *Symbol = &self.symbols.items[symbol_index];
838857 symbol.setUndefined(true);
839858 symbol.setGlobal(true);
840859 try self.globals.putNoClobber(
841860 self.base.allocator,
842 decl_name,
861 decl_name_index,
843862 .{ .file = null, .index = symbol_index },
844863 );
845864 try self.resolved_symbols.put(self.base.allocator, .{ .file = null, .index = symbol_index }, {});
......@@ -852,8 +871,8 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
852871 } else self.host_name;
853872 if (!gop.found_existing) {
854873 gop.value_ptr.* = .{
855 .module_name = module_name,
856 .name = decl_name,
874 .module_name = try self.string_table.put(self.base.allocator, module_name),
875 .name = decl_name_index,
857876 .kind = .{ .function = decl.fn_link.wasm.type_index },
858877 };
859878 }
......@@ -1001,9 +1020,18 @@ fn setupImports(self: *Wasm) !void {
10011020 }
10021021
10031022 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(self)});
1004 const import = self.objects.items[symbol_loc.file.?].findImport(symbol.tag.externalType(), symbol.index);
1005 // TODO: De-duplicate imports
1006 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
1023 const object = self.objects.items[symbol_loc.file.?];
1024 const import = object.findImport(symbol.tag.externalType(), symbol.index);
1025
1026 // We copy the import to a new import to ensure the names contain references
1027 // to the internal string table, rather than of the object file.
1028 var new_imp: types.Import = .{
1029 .module_name = try self.string_table.put(self.base.allocator, object.string_table.get(import.module_name)),
1030 .name = try self.string_table.put(self.base.allocator, object.string_table.get(import.name)),
1031 .kind = import.kind,
1032 };
1033 // TODO: De-duplicate imports when they contain the same names and type
1034 try self.imports.putNoClobber(self.base.allocator, symbol_loc, new_imp);
10071035 }
10081036
10091037 // Assign all indexes of the imports to their representing symbols
......@@ -1013,7 +1041,7 @@ fn setupImports(self: *Wasm) !void {
10131041 var it = self.imports.iterator();
10141042 while (it.next()) |entry| {
10151043 const symbol = entry.key_ptr.*.getSymbol(self);
1016 const import: wasm.Import = entry.value_ptr.*;
1044 const import: types.Import = entry.value_ptr.*;
10171045 switch (import.kind) {
10181046 .function => {
10191047 symbol.index = function_index;
......@@ -1045,7 +1073,8 @@ fn setupImports(self: *Wasm) !void {
10451073/// and merges it into a single section for each.
10461074fn mergeSections(self: *Wasm) !void {
10471075 // append the indirect function table if initialized
1048 if (self.globals.get("__indirect_function_table")) |sym_loc| {
1076 if (self.string_table.getOffset("__indirect_function_table")) |offset| {
1077 const sym_loc = self.globals.get(offset).?;
10491078 const table: wasm.Table = .{
10501079 .limits = .{ .min = @intCast(u32, self.function_table.count()), .max = null },
10511080 .reftype = .funcref,
......@@ -1114,7 +1143,7 @@ fn mergeTypes(self: *Wasm) !void {
11141143
11151144 if (symbol.isUndefined()) {
11161145 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(self)});
1117 const import: *wasm.Import = self.imports.getPtr(sym_loc).?;
1146 const import: *types.Import = self.imports.getPtr(sym_loc).?;
11181147 const original_type = object.func_types[import.kind.function];
11191148 import.kind.function = try self.putOrGetFuncType(original_type);
11201149 } else {
......@@ -1135,13 +1164,13 @@ fn setupExports(self: *Wasm) !void {
11351164 if (!symbol.isExported()) continue;
11361165
11371166 const sym_name = sym_loc.getName(self);
1138 const export_name = if (self.export_names.get(sym_loc)) |name| name else sym_name;
1139 const exp: wasm.Export = .{
1167 const export_name = if (self.export_names.get(sym_loc)) |name| name else symbol.name;
1168 const exp: types.Export = .{
11401169 .name = export_name,
11411170 .kind = symbol.tag.externalType(),
11421171 .index = symbol.index,
11431172 };
1144 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, exp.name, exp.index });
1173 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, self.string_table.get(exp.name), exp.index });
11451174 try self.exports.append(self.base.allocator, exp);
11461175 }
11471176
......@@ -1151,7 +1180,7 @@ fn setupExports(self: *Wasm) !void {
11511180fn setupStart(self: *Wasm) !void {
11521181 const entry_name = self.base.options.entry orelse "_start";
11531182
1154 const symbol_loc = self.globals.get(entry_name) orelse {
1183 const symbol_name_offset = self.string_table.getOffset(entry_name) orelse {
11551184 if (self.base.options.output_mode == .Exe) {
11561185 if (self.base.options.wasi_exec_model == .reactor) return; // Not required for reactors
11571186 } else {
......@@ -1161,6 +1190,7 @@ fn setupStart(self: *Wasm) !void {
11611190 return error.MissingSymbol;
11621191 };
11631192
1193 const symbol_loc = self.globals.get(symbol_name_offset).?;
11641194 const symbol = symbol_loc.getSymbol(self);
11651195 if (symbol.tag != .function) {
11661196 log.err("Entry symbol '{s}' is not a function", .{entry_name});
......@@ -1443,9 +1473,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14431473
14441474 // import table is always first table so emit that first
14451475 if (import_table) {
1446 const table_imp: wasm.Import = .{
1447 .module_name = self.host_name,
1448 .name = "__indirect_function_table",
1476 const table_imp: types.Import = .{
1477 .module_name = try self.string_table.put(self.base.allocator, self.host_name),
1478 .name = try self.string_table.put(self.base.allocator, "__indirect_function_table"),
14491479 .kind = .{
14501480 .table = .{
14511481 .limits = .{
......@@ -1456,23 +1486,23 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14561486 },
14571487 },
14581488 };
1459 try emitImport(writer, table_imp);
1489 try self.emitImport(writer, table_imp);
14601490 }
14611491
14621492 var it = self.imports.iterator();
14631493 while (it.next()) |entry| {
14641494 assert(entry.key_ptr.*.getSymbol(self).isUndefined());
14651495 const import = entry.value_ptr.*;
1466 try emitImport(writer, import);
1496 try self.emitImport(writer, import);
14671497 }
14681498
14691499 if (import_memory) {
1470 const mem_imp: wasm.Import = .{
1471 .module_name = self.host_name,
1472 .name = "__linear_memory",
1500 const mem_imp: types.Import = .{
1501 .module_name = try self.string_table.put(self.base.allocator, self.host_name),
1502 .name = try self.string_table.put(self.base.allocator, "__linear_memory"),
14731503 .kind = .{ .memory = self.memories.limits },
14741504 };
1475 try emitImport(writer, mem_imp);
1505 try self.emitImport(writer, mem_imp);
14761506 }
14771507
14781508 try writeVecSectionHeader(
......@@ -1567,8 +1597,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
15671597 const header_offset = try reserveVecSectionHeader(file);
15681598 const writer = file.writer();
15691599 for (self.exports.items) |exp| {
1570 try leb.writeULEB128(writer, @intCast(u32, exp.name.len));
1571 try writer.writeAll(exp.name);
1600 const name = self.string_table.get(exp.name);
1601 try leb.writeULEB128(writer, @intCast(u32, name.len));
1602 try writer.writeAll(name);
15721603 try leb.writeULEB128(writer, @enumToInt(exp.kind));
15731604 try leb.writeULEB128(writer, exp.index);
15741605 }
......@@ -1747,9 +1778,12 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
17471778
17481779 for (self.resolved_symbols.keys()) |sym_loc| {
17491780 const symbol = sym_loc.getSymbol(self).*;
1781 const name = if (symbol.isUndefined()) blk: {
1782 break :blk self.string_table.get(self.imports.get(sym_loc).?.name);
1783 } else sym_loc.getName(self);
17501784 switch (symbol.tag) {
1751 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = sym_loc.getName(self) }),
1752 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = sym_loc.getName(self) }),
1785 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = name }),
1786 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }),
17531787 else => {},
17541788 }
17551789 }
......@@ -1831,12 +1865,14 @@ fn emitInit(writer: anytype, init_expr: wasm.InitExpression) !void {
18311865 try writer.writeByte(wasm.opcode(.end));
18321866}
18331867
1834fn emitImport(writer: anytype, import: wasm.Import) !void {
1835 try leb.writeULEB128(writer, @intCast(u32, import.module_name.len));
1836 try writer.writeAll(import.module_name);
1868fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void {
1869 const module_name = self.string_table.get(import.module_name);
1870 try leb.writeULEB128(writer, @intCast(u32, module_name.len));
1871 try writer.writeAll(module_name);
18371872
1838 try leb.writeULEB128(writer, @intCast(u32, import.name.len));
1839 try writer.writeAll(import.name);
1873 const name = self.string_table.get(import.name);
1874 try leb.writeULEB128(writer, @intCast(u32, name.len));
1875 try writer.writeAll(name);
18401876
18411877 try writer.writeByte(@enumToInt(import.kind));
18421878 switch (import.kind) {
......@@ -2353,7 +2389,7 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *
23532389 try leb.writeULEB128(writer, @enumToInt(symbol.tag));
23542390 try leb.writeULEB128(writer, symbol.flags);
23552391
2356 const sym_name = if (self.export_names.get(sym_loc)) |exp_name| exp_name else sym_loc.getName(self);
2392 const sym_name = if (self.export_names.get(sym_loc)) |exp_name| self.string_table.get(exp_name) else sym_loc.getName(self);
23572393 switch (symbol.tag) {
23582394 .data => {
23592395 try leb.writeULEB128(writer, @intCast(u32, sym_name.len));
src/link/Wasm/Object.zig+17-24
......@@ -24,7 +24,7 @@ name: []const u8,
2424/// Parsed type section
2525func_types: []const std.wasm.Type = &.{},
2626/// A list of all imports for this module
27imports: []const std.wasm.Import = &.{},
27imports: []const types.Import = &.{},
2828/// Parsed function section
2929functions: []const std.wasm.Func = &.{},
3030/// Parsed table section
......@@ -34,7 +34,7 @@ memories: []const std.wasm.Memory = &.{},
3434/// Parsed global section
3535globals: []const std.wasm.Global = &.{},
3636/// Parsed export section
37exports: []const std.wasm.Export = &.{},
37exports: []const types.Export = &.{},
3838/// Parsed element section
3939elements: []const std.wasm.Element = &.{},
4040/// Represents the function ID that must be called on startup.
......@@ -127,18 +127,11 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
127127 gpa.free(func_ty.returns);
128128 }
129129 gpa.free(self.func_types);
130 for (self.imports) |imp| {
131 gpa.free(imp.name);
132 gpa.free(imp.module_name);
133 }
134130 gpa.free(self.functions);
135131 gpa.free(self.imports);
136132 gpa.free(self.tables);
137133 gpa.free(self.memories);
138134 gpa.free(self.globals);
139 for (self.exports) |exp| {
140 gpa.free(exp.name);
141 }
142135 gpa.free(self.exports);
143136 gpa.free(self.elements);
144137 gpa.free(self.features);
......@@ -163,7 +156,7 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
163156
164157/// Finds the import within the list of imports from a given kind and index of that kind.
165158/// Asserts the import exists
166pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) std.wasm.Import {
159pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import {
167160 var i: u32 = 0;
168161 return for (self.imports) |import| {
169162 if (std.meta.activeTag(import.kind) == import_kind) {
......@@ -187,7 +180,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
187180/// we initialize a new table symbol that corresponds to that import and return that symbol.
188181///
189182/// When the object file is *NOT* MVP, we return `null`.
190fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {
183fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {
191184 var table_count: usize = 0;
192185 for (self.symtable) |sym| {
193186 if (sym.tag == .table) table_count += 1;
......@@ -217,20 +210,20 @@ fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {
217210 return error.MissingTableSymbols;
218211 }
219212
220 var table_import: std.wasm.Import = for (self.imports) |imp| {
213 var table_import: types.Import = for (self.imports) |imp| {
221214 if (imp.kind == .table) {
222215 break imp;
223216 }
224217 } else unreachable;
225218
226 if (!std.mem.eql(u8, table_import.name, "__indirect_function_table")) {
227 log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{table_import.name});
219 if (!std.mem.eql(u8, self.string_table.get(table_import.name), "__indirect_function_table")) {
220 log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{self.string_table.get(table_import.name)});
228221 return error.MissingTableSymbols;
229222 }
230223
231224 var table_symbol: Symbol = .{
232225 .flags = 0,
233 .name = try self.string_table.put(gpa, table_import.name),
226 .name = table_import.name,
234227 .tag = .table,
235228 .index = 0,
236229 };
......@@ -353,12 +346,12 @@ fn Parser(comptime ReaderType: type) type {
353346 for (try readVec(&self.object.imports, reader, gpa)) |*import| {
354347 const module_len = try readLeb(u32, reader);
355348 const module_name = try gpa.alloc(u8, module_len);
356 errdefer gpa.free(module_name);
349 defer gpa.free(module_name);
357350 try reader.readNoEof(module_name);
358351
359352 const name_len = try readLeb(u32, reader);
360353 const name = try gpa.alloc(u8, name_len);
361 errdefer gpa.free(name);
354 defer gpa.free(name);
362355 try reader.readNoEof(name);
363356
364357 const kind = try readEnum(std.wasm.ExternalKind, reader);
......@@ -376,8 +369,8 @@ fn Parser(comptime ReaderType: type) type {
376369 };
377370
378371 import.* = .{
379 .module_name = module_name,
380 .name = name,
372 .module_name = try self.object.string_table.put(gpa, module_name),
373 .name = try self.object.string_table.put(gpa, name),
381374 .kind = kind_value,
382375 };
383376 }
......@@ -420,10 +413,10 @@ fn Parser(comptime ReaderType: type) type {
420413 for (try readVec(&self.object.exports, reader, gpa)) |*exp| {
421414 const name_len = try readLeb(u32, reader);
422415 const name = try gpa.alloc(u8, name_len);
423 errdefer gpa.free(name);
416 defer gpa.free(name);
424417 try reader.readNoEof(name);
425418 exp.* = .{
426 .name = name,
419 .name = try self.object.string_table.put(gpa, name),
427420 .kind = try readEnum(std.wasm.ExternalKind, reader),
428421 .index = try readLeb(u32, reader),
429422 };
......@@ -675,7 +668,7 @@ fn Parser(comptime ReaderType: type) type {
675668
676669 // we found all symbols, check for indirect function table
677670 // in case of an MVP object file
678 if (try self.object.checkLegacyIndirectFunctionTable(gpa)) |symbol| {
671 if (try self.object.checkLegacyIndirectFunctionTable()) |symbol| {
679672 try symbols.append(symbol);
680673 log.debug("Found legacy indirect function table. Created symbol", .{});
681674 }
......@@ -720,7 +713,7 @@ fn Parser(comptime ReaderType: type) type {
720713 },
721714 else => {
722715 symbol.index = try leb.readULEB128(u32, reader);
723 var maybe_import: ?std.wasm.Import = null;
716 var maybe_import: ?types.Import = null;
724717
725718 const is_undefined = symbol.isUndefined();
726719 if (is_undefined) {
......@@ -734,7 +727,7 @@ fn Parser(comptime ReaderType: type) type {
734727 try reader.readNoEof(name);
735728 symbol.name = try self.object.string_table.put(gpa, name);
736729 } else {
737 symbol.name = try self.object.string_table.put(gpa, maybe_import.?.name);
730 symbol.name = maybe_import.?.name;
738731 }
739732 },
740733 }
src/link/Wasm/types.zig+20
......@@ -78,6 +78,26 @@ pub const Relocation = struct {
7878 }
7979};
8080
81/// Unlike the `Import` object defined by the wasm spec, and existing
82/// in the std.wasm namespace, this construct saves the 'module name' and 'name'
83/// of the import using offsets into a string table, rather than the slices itself.
84/// This saves us (potentially) 24 bytes per import on 64bit machines.
85pub const Import = struct {
86 module_name: u32,
87 name: u32,
88 kind: std.wasm.Import.Kind,
89};
90
91/// Unlike the `Export` object defined by the wasm spec, and existing
92/// in the std.wasm namespace, this construct saves the 'name'
93/// of the export using offsets into a string table, rather than the slice itself.
94/// This saves us (potentially) 12 bytes per export on 64bit machines.
95pub const Export = struct {
96 name: u32,
97 index: u32,
98 kind: std.wasm.ExternalKind,
99};
100
81101pub const SubsectionType = enum(u8) {
82102 WASM_SEGMENT_INFO = 5,
83103 WASM_INIT_FUNCS = 6,