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 {...@@ -2054,6 +2054,7 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
2054 const decl = mod.declPtr(decl_index);2054 const decl = mod.declPtr(decl_index);
2055 const atom_index = wasm.decls.get(decl_index).?;2055 const atom_index = wasm.decls.get(decl_index).?;
2056 const atom = wasm.getAtomPtr(atom_index);2056 const atom = wasm.getAtomPtr(atom_index);
2057 atom.prev = null;
2057 wasm.symbols_free_list.append(gpa, atom.sym_index) catch {};2058 wasm.symbols_free_list.append(gpa, atom.sym_index) catch {};
2058 _ = wasm.decls.remove(decl_index);2059 _ = wasm.decls.remove(decl_index);
2059 wasm.symbols.items[atom.sym_index].tag = .dead;2060 wasm.symbols.items[atom.sym_index].tag = .dead;
...@@ -2076,16 +2077,6 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {...@@ -2076,16 +2077,6 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
2076 // dwarf.freeDecl(decl_index);2077 // dwarf.freeDecl(decl_index);
2077 // }2078 // }
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 }
2089}2080}
20902081
2091/// Appends a new entry to the indirect function table2082/// 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...@@ -2327,8 +2318,6 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void
2327 const gpa = wasm.base.comp.gpa;2318 const gpa = wasm.base.comp.gpa;
2328 const atom = wasm.getAtomPtr(atom_index);2319 const atom = wasm.getAtomPtr(atom_index);
2329 if (wasm.atoms.getPtr(index)) |last_index_ptr| {2320 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
2330 const last = wasm.getAtomPtr(last_index_ptr.*);
2331 last.*.next = atom_index;
2332 atom.prev = last_index_ptr.*;2321 atom.prev = last_index_ptr.*;
2333 last_index_ptr.* = atom_index;2322 last_index_ptr.* = atom_index;
2334 } else {2323 } else {
...@@ -2375,6 +2364,11 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2375,6 +2364,11 @@ fn allocateAtoms(wasm: *Wasm) !void {
2375 while (it.next()) |entry| {2364 while (it.next()) |entry| {
2376 const segment = &wasm.segments.items[entry.key_ptr.*];2365 const segment = &wasm.segments.items[entry.key_ptr.*];
2377 var atom_index = entry.value_ptr.*;2366 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 }
2378 var offset: u32 = 0;2372 var offset: u32 = 0;
2379 while (true) {2373 while (true) {
2380 const atom = wasm.getAtomPtr(atom_index);2374 const atom = wasm.getAtomPtr(atom_index);
...@@ -2387,28 +2381,17 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2387,28 +2381,17 @@ fn allocateAtoms(wasm: *Wasm) !void {
2387 break :sym object.symtable[symbol_loc.index];2381 break :sym object.symtable[symbol_loc.index];
2388 } else wasm.symbols.items[symbol_loc.index];2382 } 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.
2390 if (sym.isDead()) {2386 if (sym.isDead()) {
2391 // Dead symbols must be unlinked from the linked-list to prevent them2387 if (entry.value_ptr.* == atom_index and atom.prev != null) {
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) {
2397 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update2388 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
2398 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that2389 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
2399 // was removed and therefore do not emit any code at all.2390 // was removed and therefore do not emit any code at all.
2400 if (atom.prev) |prev| {2391 entry.value_ptr.* = atom.prev.?;
2401 entry.value_ptr.* = prev;
2402 }
2403 }2392 }
2404 atom_index = atom.prev orelse {2393 atom_index = atom.prev orelse break;
2405 atom.next = null;
2406 break;
2407 };
2408 const prev = wasm.getAtomPtr(atom_index);
2409 prev.next = atom.next;
2410 atom.prev = null;2394 atom.prev = null;
2411 atom.next = null;
2412 continue;2395 continue;
2413 }2396 }
2414 offset = @intCast(atom.alignment.forward(offset));2397 offset = @intCast(atom.alignment.forward(offset));
...@@ -2546,16 +2529,6 @@ fn setupErrorsLen(wasm: *Wasm) !void {...@@ -2546,16 +2529,6 @@ fn setupErrorsLen(wasm: *Wasm) !void {
2546 // if not, allcoate a new atom.2529 // if not, allcoate a new atom.
2547 const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {2530 const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
2548 const atom = wasm.getAtomPtr(index);2531 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 }
2559 atom.deinit(gpa);2532 atom.deinit(gpa);
2560 break :blk index;2533 break :blk index;
2561 } else new_atom: {2534 } else new_atom: {
...@@ -2658,18 +2631,12 @@ fn createSyntheticFunction(...@@ -2658,18 +2631,12 @@ fn createSyntheticFunction(
2658 .sym_index = loc.index,2631 .sym_index = loc.index,
2659 .file = null,2632 .file = null,
2660 .alignment = .@"1",2633 .alignment = .@"1",
2661 .next = null,
2662 .prev = null,2634 .prev = null,
2663 .code = function_body.moveToUnmanaged(),2635 .code = function_body.moveToUnmanaged(),
2664 .original_offset = 0,2636 .original_offset = 0,
2665 };2637 };
2666 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);2638 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
2667 try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);2639 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;
2673}2640}
26742641
2675/// Unlike `createSyntheticFunction` this function is to be called by2642/// Unlike `createSyntheticFunction` this function is to be called by
...@@ -2695,7 +2662,6 @@ pub fn createFunction(...@@ -2695,7 +2662,6 @@ pub fn createFunction(
2695 .sym_index = loc.index,2662 .sym_index = loc.index,
2696 .file = null,2663 .file = null,
2697 .alignment = .@"1",2664 .alignment = .@"1",
2698 .next = null,
2699 .prev = null,2665 .prev = null,
2700 .code = function_body.moveToUnmanaged(),2666 .code = function_body.moveToUnmanaged(),
2701 .relocs = relocations.moveToUnmanaged(),2667 .relocs = relocations.moveToUnmanaged(),
...@@ -3452,12 +3418,10 @@ fn resetState(wasm: *Wasm) void {...@@ -3452,12 +3418,10 @@ fn resetState(wasm: *Wasm) void {
3452 var atom_it = wasm.decls.valueIterator();3418 var atom_it = wasm.decls.valueIterator();
3453 while (atom_it.next()) |atom_index| {3419 while (atom_it.next()) |atom_index| {
3454 const atom = wasm.getAtomPtr(atom_index.*);3420 const atom = wasm.getAtomPtr(atom_index.*);
3455 atom.next = null;
3456 atom.prev = null;3421 atom.prev = null;
34573422
3458 for (atom.locals.items) |local_atom_index| {3423 for (atom.locals.items) |local_atom_index| {
3459 const local_atom = wasm.getAtomPtr(local_atom_index);3424 const local_atom = wasm.getAtomPtr(local_atom_index);
3460 local_atom.next = null;
3461 local_atom.prev = null;3425 local_atom.prev = null;
3462 }3426 }
3463 }3427 }
...@@ -4085,46 +4049,29 @@ fn writeToFile(...@@ -4085,46 +4049,29 @@ fn writeToFile(
4085 }4049 }
40864050
4087 // Code section4051 // Code section
4088 var code_section_size: u32 = 0;4052 if (wasm.code_section_index != null) {
4089 if (wasm.code_section_index) |code_index| {
4090 const header_offset = try reserveVecSectionHeader(&binary_bytes);4053 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.4056 var func_it = wasm.functions.iterator();
4094 var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(gpa, wasm.functions.count());4057 while (func_it.next()) |entry| {
4095 defer sorted_atoms.deinit();4058 const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file };
40964059 const atom_index = wasm.symbol_atom.get(sym_loc).?;
4097 while (true) {
4098 const atom = wasm.getAtomPtr(atom_index);4060 const atom = wasm.getAtomPtr(atom_index);
4061
4099 if (!is_obj) {4062 if (!is_obj) {
4100 atom.resolveRelocs(wasm);4063 atom.resolveRelocs(wasm);
4101 }4064 }
4102 sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions4065 atom.offset = @intCast(binary_bytes.items.len - start_offset);
4103 atom_index = atom.prev orelse break;4066 try leb.writeULEB128(binary_writer, atom.size);
4104 }4067 try binary_writer.writeAll(atom.code.items);
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);
4120 }4068 }
41214069
4122 code_section_size = @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size));
4123 try writeVecSectionHeader(4070 try writeVecSectionHeader(
4124 binary_bytes.items,4071 binary_bytes.items,
4125 header_offset,4072 header_offset,
4126 .code,4073 .code,
4127 code_section_size,4074 @intCast(binary_bytes.items.len - header_offset - header_size),
4128 @intCast(wasm.functions.count()),4075 @intCast(wasm.functions.count()),
4129 );4076 );
4130 code_section_index = section_count;4077 code_section_index = section_count;
src/link/Wasm/Atom.zig-7
...@@ -26,18 +26,12 @@ offset: u32,...@@ -26,18 +26,12 @@ offset: u32,
26/// The original offset within the object file. This value is substracted from26/// The original offset within the object file. This value is substracted from
27/// relocation offsets to determine where in the `data` to rewrite the value27/// relocation offsets to determine where in the `data` to rewrite the value
28original_offset: u32,28original_offset: u32,
29
30/// Represents the index of the file this atom was generated from.29/// Represents the index of the file this atom was generated from.
31/// This is 'null' when the atom was generated by a Decl from Zig code.30/// This is 'null' when the atom was generated by a Decl from Zig code.
32file: ?u16,31file: ?u16,
33
34/// Next atom in relation to this atom.
35/// When null, this atom is the last atom
36next: ?Atom.Index,
37/// Previous atom in relation to this atom.32/// Previous atom in relation to this atom.
38/// is null when this atom is the first in its order33/// is null when this atom is the first in its order
39prev: ?Atom.Index,34prev: ?Atom.Index,
40
41/// Contains atoms local to a decl, all managed by this `Atom`.35/// Contains atoms local to a decl, all managed by this `Atom`.
42/// When the parent atom is being freed, it will also do so for all local atoms.36/// When the parent atom is being freed, it will also do so for all local atoms.
43locals: std.ArrayListUnmanaged(Atom.Index) = .{},37locals: std.ArrayListUnmanaged(Atom.Index) = .{},
...@@ -49,7 +43,6 @@ pub const Index = u32;...@@ -49,7 +43,6 @@ pub const Index = u32;
49pub const empty: Atom = .{43pub const empty: Atom = .{
50 .alignment = .@"1",44 .alignment = .@"1",
51 .file = null,45 .file = null,
52 .next = null,
53 .offset = 0,46 .offset = 0,
54 .prev = null,47 .prev = null,
55 .size = 0,48 .size = 0,