authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-26 18:55:06+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-28 16:40:26+01:00
log596d1cd5a8a2daad25df9d39ad384df1b67eb39e
tree32c2b130127548b829e768ea227bba30eec26fe0
parent4be3cd2754f4af4f18b969dd60d954c53a281a83
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: support `--no-gc-sections`

By default we garbage-collect sections for Wasm to reduce size, as well as finish linking quicker (as we have fewer things to do). However, when the user specifies `--no-gc-sections` we ensure all resolved symbols get marked and therefore do not get garbage collected. This is supported in both incremental-mode and traditional linking.

2 files changed, 59 insertions(+), 36 deletions(-)

src/link/Wasm.zig+58-35
...@@ -2134,10 +2134,14 @@ const Kind = union(enum) {...@@ -2134,10 +2134,14 @@ const Kind = union(enum) {
2134fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {2134fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
2135 const atom = wasm.getAtomPtr(atom_index);2135 const atom = wasm.getAtomPtr(atom_index);
2136 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);2136 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
2137 if (symbol.isDead()) {2137 const do_garbage_collect = wasm.base.options.gc_sections orelse
2138 (wasm.base.options.output_mode != .Obj);
2139
2140 if (symbol.isDead() and do_garbage_collect) {
2138 // Prevent unreferenced symbols from being parsed.2141 // Prevent unreferenced symbols from being parsed.
2139 return;2142 return;
2140 }2143 }
2144
2141 const final_index: u32 = switch (kind) {2145 const final_index: u32 = switch (kind) {
2142 .function => result: {2146 .function => result: {
2143 const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);2147 const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
...@@ -2289,16 +2293,23 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -2289,16 +2293,23 @@ fn allocateAtoms(wasm: *Wasm) !void {
2289 if (sym.isDead()) {2293 if (sym.isDead()) {
2290 // Dead symbols must be unlinked from the linked-list to prevent them2294 // Dead symbols must be unlinked from the linked-list to prevent them
2291 // from being emit into the binary.2295 // from being emit into the binary.
2292 if (atom.prev) |prev_index| {2296 if (atom.next) |next_index| {
2293 const prev = wasm.getAtomPtr(prev_index);2297 const next = wasm.getAtomPtr(next_index);
2294 prev.next = atom.next;2298 next.prev = atom.prev;
2299 } else if (entry.value_ptr.* == atom_index) {
2300 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
2301 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
2302 // was removed and therefore do not emit any code at all.
2303 if (atom.prev) |prev| {
2304 entry.value_ptr.* = prev;
2305 }
2295 }2306 }
2296 atom_index = atom.next orelse {2307 atom_index = atom.prev orelse {
2297 atom.prev = null;2308 atom.next = null;
2298 break;2309 break;
2299 };2310 };
2300 const next = wasm.getAtomPtr(atom_index);2311 const prev = wasm.getAtomPtr(atom_index);
2301 next.prev = atom.prev;2312 prev.next = atom.next;
2302 atom.prev = null;2313 atom.prev = null;
2303 atom.next = null;2314 atom.next = null;
2304 continue;2315 continue;
...@@ -2787,6 +2798,7 @@ fn mergeSections(wasm: *Wasm) !void {...@@ -2787,6 +2798,7 @@ fn mergeSections(wasm: *Wasm) !void {
2787 // We found an alias to the same function, discard this symbol in favor of2798 // We found an alias to the same function, discard this symbol in favor of
2788 // the original symbol and point the discard function to it. This ensures2799 // the original symbol and point the discard function to it. This ensures
2789 // we only emit a single function, instead of duplicates.2800 // we only emit a single function, instead of duplicates.
2801 symbol.unmark();
2790 try wasm.discarded.putNoClobber(2802 try wasm.discarded.putNoClobber(
2791 wasm.base.allocator,2803 wasm.base.allocator,
2792 sym_loc,2804 sym_loc,
...@@ -2815,7 +2827,6 @@ fn mergeSections(wasm: *Wasm) !void {...@@ -2815,7 +2827,6 @@ fn mergeSections(wasm: *Wasm) !void {
2815 // For any removed duplicates, remove them from the resolved symbols list2827 // For any removed duplicates, remove them from the resolved symbols list
2816 for (removed_duplicates.items) |sym_loc| {2828 for (removed_duplicates.items) |sym_loc| {
2817 assert(wasm.resolved_symbols.swapRemove(sym_loc));2829 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2818 sym_loc.getSymbol(wasm).unmark();
2819 }2830 }
28202831
2821 log.debug("Merged ({d}) functions", .{wasm.functions.count()});2832 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
...@@ -3741,8 +3752,8 @@ fn writeToFile(...@@ -3741,8 +3752,8 @@ fn writeToFile(
3741 binary_bytes.items,3752 binary_bytes.items,
3742 header_offset,3753 header_offset,
3743 .type,3754 .type,
3744 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3755 @intCast(binary_bytes.items.len - header_offset - header_size),
3745 @as(u32, @intCast(wasm.func_types.items.len)),3756 @intCast(wasm.func_types.items.len),
3746 );3757 );
3747 section_count += 1;3758 section_count += 1;
3748 }3759 }
...@@ -3774,8 +3785,8 @@ fn writeToFile(...@@ -3774,8 +3785,8 @@ fn writeToFile(
3774 binary_bytes.items,3785 binary_bytes.items,
3775 header_offset,3786 header_offset,
3776 .import,3787 .import,
3777 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3788 @intCast(binary_bytes.items.len - header_offset - header_size),
3778 @as(u32, @intCast(wasm.imports.count() + @intFromBool(import_memory))),3789 @intCast(wasm.imports.count() + @intFromBool(import_memory)),
3779 );3790 );
3780 section_count += 1;3791 section_count += 1;
3781 }3792 }
...@@ -3791,8 +3802,8 @@ fn writeToFile(...@@ -3791,8 +3802,8 @@ fn writeToFile(
3791 binary_bytes.items,3802 binary_bytes.items,
3792 header_offset,3803 header_offset,
3793 .function,3804 .function,
3794 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3805 @intCast(binary_bytes.items.len - header_offset - header_size),
3795 @as(u32, @intCast(wasm.functions.count())),3806 @intCast(wasm.functions.count()),
3796 );3807 );
3797 section_count += 1;3808 section_count += 1;
3798 }3809 }
...@@ -3810,8 +3821,8 @@ fn writeToFile(...@@ -3810,8 +3821,8 @@ fn writeToFile(
3810 binary_bytes.items,3821 binary_bytes.items,
3811 header_offset,3822 header_offset,
3812 .table,3823 .table,
3813 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3824 @intCast(binary_bytes.items.len - header_offset - header_size),
3814 @as(u32, @intCast(wasm.tables.items.len)),3825 @intCast(wasm.tables.items.len),
3815 );3826 );
3816 section_count += 1;3827 section_count += 1;
3817 }3828 }
...@@ -3825,8 +3836,8 @@ fn writeToFile(...@@ -3825,8 +3836,8 @@ fn writeToFile(
3825 binary_bytes.items,3836 binary_bytes.items,
3826 header_offset,3837 header_offset,
3827 .memory,3838 .memory,
3828 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3839 @intCast(binary_bytes.items.len - header_offset - header_size),
3829 @as(u32, 1), // wasm currently only supports 1 linear memory segment3840 1, // wasm currently only supports 1 linear memory segment
3830 );3841 );
3831 section_count += 1;3842 section_count += 1;
3832 }3843 }
...@@ -3845,8 +3856,8 @@ fn writeToFile(...@@ -3845,8 +3856,8 @@ fn writeToFile(
3845 binary_bytes.items,3856 binary_bytes.items,
3846 header_offset,3857 header_offset,
3847 .global,3858 .global,
3848 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3859 @intCast(binary_bytes.items.len - header_offset - header_size),
3849 @as(u32, @intCast(wasm.wasm_globals.items.len)),3860 @intCast(wasm.wasm_globals.items.len),
3850 );3861 );
3851 section_count += 1;3862 section_count += 1;
3852 }3863 }
...@@ -3874,8 +3885,8 @@ fn writeToFile(...@@ -3874,8 +3885,8 @@ fn writeToFile(
3874 binary_bytes.items,3885 binary_bytes.items,
3875 header_offset,3886 header_offset,
3876 .@"export",3887 .@"export",
3877 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3888 @intCast(binary_bytes.items.len - header_offset - header_size),
3878 @as(u32, @intCast(wasm.exports.items.len)) + @intFromBool(export_memory),3889 @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
3879 );3890 );
3880 section_count += 1;3891 section_count += 1;
3881 }3892 }
...@@ -3918,8 +3929,8 @@ fn writeToFile(...@@ -3918,8 +3929,8 @@ fn writeToFile(
3918 binary_bytes.items,3929 binary_bytes.items,
3919 header_offset,3930 header_offset,
3920 .element,3931 .element,
3921 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3932 @intCast(binary_bytes.items.len - header_offset - header_size),
3922 @as(u32, 1),3933 1,
3923 );3934 );
3924 section_count += 1;3935 section_count += 1;
3925 }3936 }
...@@ -3932,8 +3943,8 @@ fn writeToFile(...@@ -3932,8 +3943,8 @@ fn writeToFile(
3932 binary_bytes.items,3943 binary_bytes.items,
3933 header_offset,3944 header_offset,
3934 .data_count,3945 .data_count,
3935 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),3946 @intCast(binary_bytes.items.len - header_offset - header_size),
3936 @as(u32, @intCast(data_segments_count)),3947 @intCast(data_segments_count),
3937 );3948 );
3938 }3949 }
39393950
...@@ -3978,7 +3989,7 @@ fn writeToFile(...@@ -3978,7 +3989,7 @@ fn writeToFile(
3978 header_offset,3989 header_offset,
3979 .code,3990 .code,
3980 code_section_size,3991 code_section_size,
3981 @as(u32, @intCast(wasm.functions.count())),3992 @intCast(wasm.functions.count()),
3982 );3993 );
3983 code_section_index = section_count;3994 code_section_index = section_count;
3984 section_count += 1;3995 section_count += 1;
...@@ -4049,8 +4060,8 @@ fn writeToFile(...@@ -4049,8 +4060,8 @@ fn writeToFile(
4049 binary_bytes.items,4060 binary_bytes.items,
4050 header_offset,4061 header_offset,
4051 .data,4062 .data,
4052 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),4063 @intCast(binary_bytes.items.len - header_offset - header_size),
4053 @as(u32, @intCast(segment_count)),4064 @intCast(segment_count),
4054 );4065 );
4055 data_section_index = section_count;4066 data_section_index = section_count;
4056 section_count += 1;4067 section_count += 1;
...@@ -4597,6 +4608,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4597,6 +4608,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4597 try argv.append("--export-table");4608 try argv.append("--export-table");
4598 }4609 }
45994610
4611 if (wasm.base.options.gc_sections) |gc| {
4612 // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
4613 // specified it as garbage collection is enabled by default.
4614 if (!gc) {
4615 try argv.append("--no-gc-sections");
4616 }
4617 }
4618
4600 if (wasm.base.options.strip) {4619 if (wasm.base.options.strip) {
4601 try argv.append("-s");4620 try argv.append("-s");
4602 }4621 }
...@@ -4882,7 +4901,7 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:...@@ -4882,7 +4901,7 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
4882 try wasm.emitSymbolTable(binary_bytes, symbol_table);4901 try wasm.emitSymbolTable(binary_bytes, symbol_table);
4883 try wasm.emitSegmentInfo(binary_bytes);4902 try wasm.emitSegmentInfo(binary_bytes);
48844903
4885 const size = @as(u32, @intCast(binary_bytes.items.len - offset - 6));4904 const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
4886 try writeCustomSectionHeader(binary_bytes.items, offset, size);4905 try writeCustomSectionHeader(binary_bytes.items, offset, size);
4887}4906}
48884907
...@@ -4930,7 +4949,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:...@@ -4930,7 +4949,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
4930 }4949 }
49314950
4932 var buf: [10]u8 = undefined;4951 var buf: [10]u8 = undefined;
4933 leb.writeUnsignedFixed(5, buf[0..5], @as(u32, @intCast(binary_bytes.items.len - table_offset + 5)));4952 leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5));
4934 leb.writeUnsignedFixed(5, buf[5..], symbol_count);4953 leb.writeUnsignedFixed(5, buf[5..], symbol_count);
4935 try binary_bytes.insertSlice(table_offset, &buf);4954 try binary_bytes.insertSlice(table_offset, &buf);
4936}4955}
...@@ -5013,7 +5032,7 @@ fn emitCodeRelocations(...@@ -5013,7 +5032,7 @@ fn emitCodeRelocations(
5013 var buf: [5]u8 = undefined;5032 var buf: [5]u8 = undefined;
5014 leb.writeUnsignedFixed(5, &buf, count);5033 leb.writeUnsignedFixed(5, &buf, count);
5015 try binary_bytes.insertSlice(reloc_start, &buf);5034 try binary_bytes.insertSlice(reloc_start, &buf);
5016 const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));5035 const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);
5017 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);5036 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
5018}5037}
50195038
...@@ -5123,10 +5142,14 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s...@@ -5123,10 +5142,14 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s
5123fn markReferences(wasm: *Wasm) !void {5142fn markReferences(wasm: *Wasm) !void {
5124 const tracy = trace(@src());5143 const tracy = trace(@src());
5125 defer tracy.end();5144 defer tracy.end();
5145 const do_garbage_collect = wasm.base.options.gc_sections orelse
5146 (wasm.base.options.output_mode != .Obj);
5147
5126 for (wasm.resolved_symbols.keys()) |sym_loc| {5148 for (wasm.resolved_symbols.keys()) |sym_loc| {
5127 const sym = sym_loc.getSymbol(wasm);5149 const sym = sym_loc.getSymbol(wasm);
5128 if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) {5150 if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
5129 try wasm.mark(sym_loc);5151 try wasm.mark(sym_loc);
5152 continue;
5130 }5153 }
51315154
5132 // Debug sections may require to be parsed and marked when it contains5155 // Debug sections may require to be parsed and marked when it contains
...@@ -5139,7 +5162,7 @@ fn markReferences(wasm: *Wasm) !void {...@@ -5139,7 +5162,7 @@ fn markReferences(wasm: *Wasm) !void {
5139 for (atom.relocs.items) |reloc| {5162 for (atom.relocs.items) |reloc| {
5140 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = atom.file };5163 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = atom.file };
5141 const target_sym = target_loc.getSymbol(wasm);5164 const target_sym = target_loc.getSymbol(wasm);
5142 if (target_sym.isAlive()) {5165 if (target_sym.isAlive() or !do_garbage_collect) {
5143 sym.mark();5166 sym.mark();
5144 continue; // Skip all other relocations as this debug atom is already marked now5167 continue; // Skip all other relocations as this debug atom is already marked now
5145 }5168 }
src/link/Wasm/Object.zig+1-1
...@@ -920,7 +920,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32...@@ -920,7 +920,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
920 const start = searchRelocStart(relocations, relocatable_data.offset);920 const start = searchRelocStart(relocations, relocatable_data.offset);
921 const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);921 const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
922 atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]);922 atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]);
923 for (atom.relocs.items) |*reloc| {923 for (atom.relocs.items) |reloc| {
924 switch (reloc.relocation_type) {924 switch (reloc.relocation_type) {
925 .R_WASM_TABLE_INDEX_I32,925 .R_WASM_TABLE_INDEX_I32,
926 .R_WASM_TABLE_INDEX_I64,926 .R_WASM_TABLE_INDEX_I64,