authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-01-10 06:35:50+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-01-12 14:57:36+01:00
log7fe629a8124f27e8db01e090031a5243a452e831
treeb9bb349520685f9b972a789a49f72ddd719464ad
parent2b3e6f680c5843877f6252bd3d85a20abe367da6
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: delay code atom allocation till write

We delay atom allocation for the code section until we write the actual atoms. We do this to ensure the offset of the atom also includes the 'size' field which is leb128-encoded and therefore variable. We need this correct offset to ensure debug info works correctly. The ordering of the code section is now automatic due to iterating the function section and then finding the corresponding atom to each function. This also ensures each function corresponds to the right atom, and they do not go out-of-sync. Lastly, we removed the `next` field as it is no longer required and also removed manually setting the offset in synthetic functions. This means atoms use less memory and synthetic functions are less prone. They will also be placed in order of function order correctly.

2 files changed, 22 insertions(+), 82 deletions(-)

src/link/Wasm.zig+22-75
......@@ -2054,6 +2054,7 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
20542054 const decl = mod.declPtr(decl_index);
20552055 const atom_index = wasm.decls.get(decl_index).?;
20562056 const atom = wasm.getAtomPtr(atom_index);
2057 atom.prev = null;
20572058 wasm.symbols_free_list.append(gpa, atom.sym_index) catch {};
20582059 _ = wasm.decls.remove(decl_index);
20592060 wasm.symbols.items[atom.sym_index].tag = .dead;
......@@ -2076,16 +2077,6 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
20762077 // dwarf.freeDecl(decl_index);
20772078 // }
20782079
2079 if (atom.next) |next_atom_index| {
2080 const next_atom = wasm.getAtomPtr(next_atom_index);
2081 next_atom.prev = atom.prev;
2082 atom.next = null;
2083 }
2084 if (atom.prev) |prev_index| {
2085 const prev_atom = wasm.getAtomPtr(prev_index);
2086 prev_atom.next = atom.next;
2087 atom.prev = null;
2088 }
20892080}
20902081
20912082/// Appends a new entry to the indirect function table
......@@ -2327,8 +2318,6 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void
23272318 const gpa = wasm.base.comp.gpa;
23282319 const atom = wasm.getAtomPtr(atom_index);
23292320 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
2330 const last = wasm.getAtomPtr(last_index_ptr.*);
2331 last.*.next = atom_index;
23322321 atom.prev = last_index_ptr.*;
23332322 last_index_ptr.* = atom_index;
23342323 } else {
......@@ -2375,6 +2364,11 @@ fn allocateAtoms(wasm: *Wasm) !void {
23752364 while (it.next()) |entry| {
23762365 const segment = &wasm.segments.items[entry.key_ptr.*];
23772366 var atom_index = entry.value_ptr.*;
2367 if (entry.key_ptr.* == wasm.code_section_index) {
2368 // Code section is allocated upon writing as they are required to be ordered
2369 // to synchronise with the function section.
2370 continue;
2371 }
23782372 var offset: u32 = 0;
23792373 while (true) {
23802374 const atom = wasm.getAtomPtr(atom_index);
......@@ -2387,28 +2381,17 @@ fn allocateAtoms(wasm: *Wasm) !void {
23872381 break :sym object.symtable[symbol_loc.index];
23882382 } else wasm.symbols.items[symbol_loc.index];
23892383
2384 // Dead symbols must be unlinked from the linked-list to prevent them
2385 // from being emit into the binary.
23902386 if (sym.isDead()) {
2391 // Dead symbols must be unlinked from the linked-list to prevent them
2392 // from being emit into the binary.
2393 if (atom.next) |next_index| {
2394 const next = wasm.getAtomPtr(next_index);
2395 next.prev = atom.prev;
2396 } else if (entry.value_ptr.* == atom_index) {
2387 if (entry.value_ptr.* == atom_index and atom.prev != null) {
23972388 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
23982389 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
23992390 // was removed and therefore do not emit any code at all.
2400 if (atom.prev) |prev| {
2401 entry.value_ptr.* = prev;
2402 }
2391 entry.value_ptr.* = atom.prev.?;
24032392 }
2404 atom_index = atom.prev orelse {
2405 atom.next = null;
2406 break;
2407 };
2408 const prev = wasm.getAtomPtr(atom_index);
2409 prev.next = atom.next;
2393 atom_index = atom.prev orelse break;
24102394 atom.prev = null;
2411 atom.next = null;
24122395 continue;
24132396 }
24142397 offset = @intCast(atom.alignment.forward(offset));
......@@ -2546,16 +2529,6 @@ fn setupErrorsLen(wasm: *Wasm) !void {
25462529 // if not, allcoate a new atom.
25472530 const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
25482531 const atom = wasm.getAtomPtr(index);
2549 if (atom.next) |next_atom_index| {
2550 const next_atom = wasm.getAtomPtr(next_atom_index);
2551 next_atom.prev = atom.prev;
2552 atom.next = null;
2553 }
2554 if (atom.prev) |prev_index| {
2555 const prev_atom = wasm.getAtomPtr(prev_index);
2556 prev_atom.next = atom.next;
2557 atom.prev = null;
2558 }
25592532 atom.deinit(gpa);
25602533 break :blk index;
25612534 } else new_atom: {
......@@ -2658,18 +2631,12 @@ fn createSyntheticFunction(
26582631 .sym_index = loc.index,
26592632 .file = null,
26602633 .alignment = .@"1",
2661 .next = null,
26622634 .prev = null,
26632635 .code = function_body.moveToUnmanaged(),
26642636 .original_offset = 0,
26652637 };
26662638 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
26672639 try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
2668
2669 // `allocateAtoms` has already been called, set the atom's offset manually.
2670 // This is fine to do manually as we insert the atom at the very end.
2671 const prev_atom = wasm.getAtom(atom.prev.?);
2672 atom.offset = prev_atom.offset + prev_atom.size;
26732640}
26742641
26752642/// Unlike `createSyntheticFunction` this function is to be called by
......@@ -2695,7 +2662,6 @@ pub fn createFunction(
26952662 .sym_index = loc.index,
26962663 .file = null,
26972664 .alignment = .@"1",
2698 .next = null,
26992665 .prev = null,
27002666 .code = function_body.moveToUnmanaged(),
27012667 .relocs = relocations.moveToUnmanaged(),
......@@ -3452,12 +3418,10 @@ fn resetState(wasm: *Wasm) void {
34523418 var atom_it = wasm.decls.valueIterator();
34533419 while (atom_it.next()) |atom_index| {
34543420 const atom = wasm.getAtomPtr(atom_index.*);
3455 atom.next = null;
34563421 atom.prev = null;
34573422
34583423 for (atom.locals.items) |local_atom_index| {
34593424 const local_atom = wasm.getAtomPtr(local_atom_index);
3460 local_atom.next = null;
34613425 local_atom.prev = null;
34623426 }
34633427 }
......@@ -4085,46 +4049,29 @@ fn writeToFile(
40854049 }
40864050
40874051 // Code section
4088 var code_section_size: u32 = 0;
4089 if (wasm.code_section_index) |code_index| {
4052 if (wasm.code_section_index != null) {
40904053 const header_offset = try reserveVecSectionHeader(&binary_bytes);
4091 var atom_index = wasm.atoms.get(code_index).?;
4054 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
40924055
4093 // The code section must be sorted in line with the function order.
4094 var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(gpa, wasm.functions.count());
4095 defer sorted_atoms.deinit();
4096
4097 while (true) {
4056 var func_it = wasm.functions.iterator();
4057 while (func_it.next()) |entry| {
4058 const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file };
4059 const atom_index = wasm.symbol_atom.get(sym_loc).?;
40984060 const atom = wasm.getAtomPtr(atom_index);
4061
40994062 if (!is_obj) {
41004063 atom.resolveRelocs(wasm);
41014064 }
4102 sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions
4103 atom_index = atom.prev orelse break;
4104 }
4105 assert(wasm.functions.count() == sorted_atoms.items.len);
4106
4107 const atom_sort_fn = struct {
4108 fn sort(ctx: *const Wasm, lhs: *const Atom, rhs: *const Atom) bool {
4109 const lhs_sym = lhs.symbolLoc().getSymbol(ctx);
4110 const rhs_sym = rhs.symbolLoc().getSymbol(ctx);
4111 return lhs_sym.index < rhs_sym.index;
4112 }
4113 }.sort;
4114
4115 mem.sort(*const Atom, sorted_atoms.items, wasm, atom_sort_fn);
4116
4117 for (sorted_atoms.items) |sorted_atom| {
4118 try leb.writeULEB128(binary_writer, sorted_atom.size);
4119 try binary_writer.writeAll(sorted_atom.code.items);
4065 atom.offset = @intCast(binary_bytes.items.len - start_offset);
4066 try leb.writeULEB128(binary_writer, atom.size);
4067 try binary_writer.writeAll(atom.code.items);
41204068 }
41214069
4122 code_section_size = @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size));
41234070 try writeVecSectionHeader(
41244071 binary_bytes.items,
41254072 header_offset,
41264073 .code,
4127 code_section_size,
4074 @intCast(binary_bytes.items.len - header_offset - header_size),
41284075 @intCast(wasm.functions.count()),
41294076 );
41304077 code_section_index = section_count;
src/link/Wasm/Atom.zig-7
......@@ -26,18 +26,12 @@ offset: u32,
2626/// The original offset within the object file. This value is substracted from
2727/// relocation offsets to determine where in the `data` to rewrite the value
2828original_offset: u32,
29
3029/// Represents the index of the file this atom was generated from.
3130/// This is 'null' when the atom was generated by a Decl from Zig code.
3231file: ?u16,
33
34/// Next atom in relation to this atom.
35/// When null, this atom is the last atom
36next: ?Atom.Index,
3732/// Previous atom in relation to this atom.
3833/// is null when this atom is the first in its order
3934prev: ?Atom.Index,
40
4135/// Contains atoms local to a decl, all managed by this `Atom`.
4236/// When the parent atom is being freed, it will also do so for all local atoms.
4337locals: std.ArrayListUnmanaged(Atom.Index) = .{},
......@@ -49,7 +43,6 @@ pub const Index = u32;
4943pub const empty: Atom = .{
5044 .alignment = .@"1",
5145 .file = null,
52 .next = null,
5346 .offset = 0,
5447 .prev = null,
5548 .size = 0,