authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-12 16:56:30-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
log968941b535052e2842f47dd9e7e45fa94f8f79c2
treeffc45babe98b66de6db25e90e026016931026ef0
parent264a63b000021b154fffa95e6e0565c3d402706b

wasm linker: finish the flush function

This branch is passing type checking now.

3 files changed, 127 insertions(+), 102 deletions(-)

lib/std/Target.zig+6
...@@ -1219,6 +1219,12 @@ pub const Cpu = struct {...@@ -1219,6 +1219,12 @@ pub const Cpu = struct {
1219 } else true;1219 } else true;
1220 }1220 }
12211221
1222 pub fn count(set: Set) std.math.IntFittingRange(0, needed_bit_count) {
1223 var sum: usize = 0;
1224 for (set.ints) |x| sum += @popCount(x);
1225 return @intCast(sum);
1226 }
1227
1222 pub fn isEnabled(set: Set, arch_feature_index: Index) bool {1228 pub fn isEnabled(set: Set, arch_feature_index: Index) bool {
1223 const usize_index = arch_feature_index / @bitSizeOf(usize);1229 const usize_index = arch_feature_index / @bitSizeOf(usize);
1224 const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));1230 const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
src/link/Wasm.zig+18-5
...@@ -270,8 +270,16 @@ pub const FunctionIndex = enum(u32) {...@@ -270,8 +270,16 @@ pub const FunctionIndex = enum(u32) {
270 return &wasm.functions.keys()[@intFromEnum(index)];270 return &wasm.functions.keys()[@intFromEnum(index)];
271 }271 }
272272
273 pub fn toOutputFunctionIndex(index: FunctionIndex, wasm: *const Wasm) OutputFunctionIndex {
274 return @enumFromInt(wasm.function_imports.entries.len + @intFromEnum(index));
275 }
276
273 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex {277 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex {
274 const i = wasm.functions.getIndex(.fromIpNav(wasm, nav_index)) orelse return null;278 return fromResolution(wasm, .fromIpNav(wasm, nav_index));
279 }
280
281 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex {
282 const i = wasm.functions.getIndex(resolution) orelse return null;
275 return @enumFromInt(i);283 return @enumFromInt(i);
276 }284 }
277};285};
...@@ -295,11 +303,11 @@ pub const OutputFunctionIndex = enum(u32) {...@@ -295,11 +303,11 @@ pub const OutputFunctionIndex = enum(u32) {
295 _,303 _,
296};304};
297305
298/// Index into `globals`.306/// Index into `Wasm.globals`.
299pub const GlobalIndex = enum(u32) {307pub const GlobalIndex = enum(u32) {
300 _,308 _,
301309
302 fn key(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {310 pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {
303 return &f.globals.items[@intFromEnum(index)];311 return &f.globals.items[@intFromEnum(index)];
304 }312 }
305313
...@@ -1089,7 +1097,7 @@ pub const DataSegment = extern struct {...@@ -1089,7 +1097,7 @@ pub const DataSegment = extern struct {
1089 /// The size in bytes of the data representing the segment within the section.1097 /// The size in bytes of the data representing the segment within the section.
1090 len: u32,1098 len: u32,
10911099
1092 fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 {1100 pub fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 {
1093 assert(p.off != p.len);1101 assert(p.off != p.len);
1094 return wasm.string_bytes.items[p.off..][0..p.len];1102 return wasm.string_bytes.items[p.off..][0..p.len];
1095 }1103 }
...@@ -2365,6 +2373,7 @@ pub fn flushModule(...@@ -2365,6 +2373,7 @@ pub fn flushModule(
2365 _ = tid;2373 _ = tid;
2366 const comp = wasm.base.comp;2374 const comp = wasm.base.comp;
2367 const use_lld = build_options.have_llvm and comp.config.use_lld;2375 const use_lld = build_options.have_llvm and comp.config.use_lld;
2376 const diags = &comp.link_diags;
23682377
2369 if (wasm.llvm_object) |llvm_object| {2378 if (wasm.llvm_object) |llvm_object| {
2370 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);2379 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
...@@ -2392,7 +2401,11 @@ pub fn flushModule(...@@ -2392,7 +2401,11 @@ pub fn flushModule(
2392 defer sub_prog_node.end();2401 defer sub_prog_node.end();
23932402
2394 wasm.flush_buffer.clear();2403 wasm.flush_buffer.clear();
2395 return wasm.flush_buffer.finish(wasm, arena);2404 return wasm.flush_buffer.finish(wasm, arena) catch |err| switch (err) {
2405 error.OutOfMemory => return error.OutOfMemory,
2406 error.LinkFailure => return error.LinkFailure,
2407 else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}),
2408 };
2396}2409}
23972410
2398fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {2411fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
src/link/Wasm/Flush.zig+103-97
...@@ -59,7 +59,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -59,7 +59,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
59 const gpa = comp.gpa;59 const gpa = comp.gpa;
60 const import_memory = comp.config.import_memory;60 const import_memory = comp.config.import_memory;
61 const export_memory = comp.config.export_memory;61 const export_memory = comp.config.export_memory;
62 const target = comp.root_mod.resolved_target.result;62 const target = &comp.root_mod.resolved_target.result;
63 const is_obj = comp.config.output_mode == .Obj;63 const is_obj = comp.config.output_mode == .Obj;
64 const allow_undefined = is_obj or wasm.import_symbols;64 const allow_undefined = is_obj or wasm.import_symbols;
65 const zcu = wasm.base.comp.zcu.?;65 const zcu = wasm.base.comp.zcu.?;
...@@ -523,7 +523,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -523,7 +523,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
523 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));523 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
524 try binary_bytes.appendSlice(gpa, name);524 try binary_bytes.appendSlice(gpa, name);
525 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));525 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));
526 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_imports.entries.len + @intFromEnum(exp.function_index))));526 try leb.writeUleb128(binary_writer, @intFromEnum(exp.function_index));
527 }527 }
528 exports_len += wasm.function_exports.items.len;528 exports_len += wasm.function_exports.items.len;
529529
...@@ -543,7 +543,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -543,7 +543,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
543 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));543 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
544 try binary_bytes.appendSlice(gpa, name);544 try binary_bytes.appendSlice(gpa, name);
545 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global));545 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global));
546 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.global_imports.entries.len + @intFromEnum(exp.global_index))));546 try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index));
547 }547 }
548 exports_len += wasm.global_exports.items.len;548 exports_len += wasm.global_exports.items.len;
549549
...@@ -555,38 +555,39 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -555,38 +555,39 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
555 }555 }
556 }556 }
557557
558 if (Wasm.FunctionIndex.fromResolution(wasm.entry_resolution)) |entry_index| {558 if (Wasm.FunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |entry_index| {
559 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);559 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
560 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));560 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));
561 }561 }
562562
563 // element section (function table)563 // element section (function table)
564 if (wasm.function_table.count() > 0) {564 if (f.indirect_function_table.count() > 0) {
565 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);565 @panic("TODO");
566 //const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
566567
567 const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;568 //const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
568 const table_sym = wasm.finalSymbolByLoc(table_loc);569 //const table_sym = wasm.finalSymbolByLoc(table_loc);
569570
570 const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually571 //const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
571 try leb.writeUleb128(binary_writer, flags);572 //try leb.writeUleb128(binary_writer, flags);
572 if (flags == 0x02) {573 //if (flags == 0x02) {
573 try leb.writeUleb128(binary_writer, table_sym.index);574 // try leb.writeUleb128(binary_writer, table_sym.index);
574 }575 //}
575 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid576 //try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
576 if (flags == 0x02) {577 //if (flags == 0x02) {
577 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref578 // try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
578 }579 //}
579 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));580 //try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.count())));
580 var symbol_it = wasm.function_table.keyIterator();581 //var symbol_it = f.indirect_function_table.keyIterator();
581 while (symbol_it.next()) |symbol_loc_ptr| {582 //while (symbol_it.next()) |symbol_loc_ptr| {
582 const sym = wasm.finalSymbolByLoc(symbol_loc_ptr.*);583 // const sym = wasm.finalSymbolByLoc(symbol_loc_ptr.*);
583 assert(sym.flags.alive);584 // assert(sym.flags.alive);
584 assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);585 // assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
585 try leb.writeUleb128(binary_writer, sym.index);586 // try leb.writeUleb128(binary_writer, sym.index);
586 }587 //}
587588
588 replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);589 //replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
589 section_index += 1;590 //section_index += 1;
590 }591 }
591592
592 // When the shared-memory option is enabled, we *must* emit the 'data count' section.593 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
...@@ -600,7 +601,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -600,7 +601,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
600 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);601 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
601 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count602 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
602603
603 for (wasm.functions.keys()) |resolution| switch (resolution.unpack()) {604 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {
604 .unresolved => unreachable,605 .unresolved => unreachable,
605 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),606 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),
606 .__wasm_call_ctors => @panic("TODO lower __wasm_call_ctors"),607 .__wasm_call_ctors => @panic("TODO lower __wasm_call_ctors"),
...@@ -614,10 +615,19 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -614,10 +615,19 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
614 //try leb.writeUleb128(binary_writer, atom.code.len);615 //try leb.writeUleb128(binary_writer, atom.code.len);
615 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));616 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
616 },617 },
617 .nav => |i| {618 .nav_exe => |i| {
619 assert(!is_obj);
620 _ = i;
621 _ = start_offset;
622 @panic("TODO lower nav exe code and apply relocations");
623 //try leb.writeUleb128(binary_writer, atom.code.len);
624 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
625 },
626 .nav_obj => |i| {
627 assert(is_obj);
618 _ = i;628 _ = i;
619 _ = start_offset;629 _ = start_offset;
620 @panic("TODO lower nav code and apply relocations");630 @panic("TODO lower nav obj code and apply relocations");
621 //try leb.writeUleb128(binary_writer, atom.code.len);631 //try leb.writeUleb128(binary_writer, atom.code.len);
622 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));632 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
623 },633 },
...@@ -636,7 +646,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -636,7 +646,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
636 var offset: u32 = undefined;646 var offset: u32 = undefined;
637 for (segment_indexes, segment_offsets) |segment_index, segment_offset| {647 for (segment_indexes, segment_offsets) |segment_index, segment_offset| {
638 const segment = segment_index.ptr(wasm);648 const segment = segment_index.ptr(wasm);
639 if (segment.size == 0) continue;649 const segment_payload = segment.payload.slice(wasm);
650 if (segment_payload.len == 0) continue;
640 if (!import_memory and isBss(wasm, segment.name)) {651 if (!import_memory and isBss(wasm, segment.name)) {
641 // It counted for virtual memory but it does not go into the binary.652 // It counted for virtual memory but it does not go into the binary.
642 continue;653 continue;
...@@ -657,8 +668,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -657,8 +668,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
657668
658 try binary_bytes.appendNTimes(gpa, 0, segment_offset - offset);669 try binary_bytes.appendNTimes(gpa, 0, segment_offset - offset);
659 offset = segment_offset;670 offset = segment_offset;
660 try binary_bytes.appendSlice(gpa, segment.payload.slice(wasm));671 try binary_bytes.appendSlice(gpa, segment_payload);
661 offset += segment.payload.len;672 offset += @intCast(segment_payload.len);
662 if (true) @panic("TODO apply data segment relocations");673 if (true) @panic("TODO apply data segment relocations");
663 }674 }
664 assert(group_index == f.data_segment_groups.items.len);675 assert(group_index == f.data_segment_groups.items.len);
...@@ -680,7 +691,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -680,7 +691,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
680 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);691 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);
681 //}692 //}
682 } else if (comp.config.debug_format != .strip) {693 } else if (comp.config.debug_format != .strip) {
683 try wasm.emitNameSection(binary_bytes, arena);694 try emitNameSection(wasm, binary_bytes, arena);
684 }695 }
685696
686 if (comp.config.debug_format != .strip) {697 if (comp.config.debug_format != .strip) {
...@@ -699,14 +710,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -699,14 +710,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
699 std.fmt.fmtSliceHexLower(id[8..10]),710 std.fmt.fmtSliceHexLower(id[8..10]),
700 std.fmt.fmtSliceHexLower(id[10..]),711 std.fmt.fmtSliceHexLower(id[10..]),
701 });712 });
702 try emitBuildIdSection(binary_bytes, &uuid);713 try emitBuildIdSection(gpa, binary_bytes, &uuid);
703 },714 },
704 .hexstring => |hs| {715 .hexstring => |hs| {
705 var buffer: [32 * 2]u8 = undefined;716 var buffer: [32 * 2]u8 = undefined;
706 const str = std.fmt.bufPrint(&buffer, "{s}", .{717 const str = std.fmt.bufPrint(&buffer, "{s}", .{
707 std.fmt.fmtSliceHexLower(hs.toSlice()),718 std.fmt.fmtSliceHexLower(hs.toSlice()),
708 }) catch unreachable;719 }) catch unreachable;
709 try emitBuildIdSection(binary_bytes, str);720 try emitBuildIdSection(gpa, binary_bytes, str);
710 },721 },
711 else => |mode| {722 else => |mode| {
712 var err = try diags.addErrorWithNotes(0);723 var err = try diags.addErrorWithNotes(0);
...@@ -717,9 +728,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -717,9 +728,8 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
717 var debug_bytes = std.ArrayList(u8).init(gpa);728 var debug_bytes = std.ArrayList(u8).init(gpa);
718 defer debug_bytes.deinit();729 defer debug_bytes.deinit();
719730
720 try emitProducerSection(binary_bytes);731 try emitProducerSection(gpa, binary_bytes);
721 if (!target.cpu.features.isEmpty())732 try emitFeaturesSection(gpa, binary_bytes, target);
722 try emitFeaturesSection(binary_bytes, target.cpu.features);
723 }733 }
724734
725 // Finally, write the entire binary into the file.735 // Finally, write the entire binary into the file.
...@@ -729,6 +739,10 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -729,6 +739,10 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
729}739}
730740
731fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena: Allocator) !void {741fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena: Allocator) !void {
742 if (true) {
743 std.log.warn("TODO emit name section", .{});
744 return;
745 }
732 const comp = wasm.base.comp;746 const comp = wasm.base.comp;
733 const gpa = comp.gpa;747 const gpa = comp.gpa;
734 const import_memory = comp.config.import_memory;748 const import_memory = comp.config.import_memory;
...@@ -796,31 +810,15 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena...@@ -796,31 +810,15 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena
796 // Data segments are already ordered.810 // Data segments are already ordered.
797811
798 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);812 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
799 const writer = binary_bytes.writer();813 defer writeCustomSectionHeader(binary_bytes, header_offset);
814
815 const writer = binary_bytes.writer(gpa);
800 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));816 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));
801 try writer.writeAll("name");817 try writer.writeAll("name");
802818
803 try emitNameSubsection(wasm, binary_bytes, .function, funcs.keys(), funcs.values());819 try emitNameSubsection(wasm, binary_bytes, .function, funcs.keys(), funcs.values());
804 try emitNameSubsection(wasm, binary_bytes, .global, globals.items(.index), globals.items(.name));820 try emitNameSubsection(wasm, binary_bytes, .global, globals.items(.index), globals.items(.name));
805 try emitNameSubsection(wasm, binary_bytes, .data_segment, segments.items(.index), segments.items(.name));821 try emitNameSubsection(wasm, binary_bytes, .data_segment, segments.items(.index), segments.items(.name));
806
807 try writeCustomSectionHeader(
808 binary_bytes.items,
809 header_offset,
810 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
811 );
812}
813
814fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
815 var buf: [1 + 5]u8 = undefined;
816 buf[0] = 0; // 0 = 'custom' section
817 leb.writeUnsignedFixed(5, buf[1..6], size);
818 buffer[offset..][0..buf.len].* = buf;
819}
820
821fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
822 try bytes.appendNTimes(gpa, 0, section_header_size);
823 return @intCast(bytes.items.len - section_header_size);
824}822}
825823
826fn emitNameSubsection(824fn emitNameSubsection(
...@@ -856,35 +854,40 @@ fn emitNameSubsection(...@@ -856,35 +854,40 @@ fn emitNameSubsection(
856fn emitFeaturesSection(854fn emitFeaturesSection(
857 gpa: Allocator,855 gpa: Allocator,
858 binary_bytes: *std.ArrayListUnmanaged(u8),856 binary_bytes: *std.ArrayListUnmanaged(u8),
859 features: []const Wasm.Feature,857 target: *const std.Target,
860) !void {858) Allocator.Error!void {
859 const feature_count = target.cpu.features.count();
860 if (feature_count == 0) return;
861
861 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);862 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
863 defer writeCustomSectionHeader(binary_bytes, header_offset);
862864
863 const writer = binary_bytes.writer();865 const writer = binary_bytes.writer(gpa);
864 const target_features = "target_features";866 const target_features = "target_features";
865 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));867 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
866 try writer.writeAll(target_features);868 try writer.writeAll(target_features);
867869
868 try leb.writeUleb128(writer, @as(u32, @intCast(features.len)));870 try leb.writeUleb128(writer, @as(u32, @intCast(feature_count)));
869 for (features) |feature| {871
870 assert(feature.prefix != .invalid);872 var safety_count = feature_count;
871 try leb.writeUleb128(writer, @tagName(feature.prefix)[0]);873 for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| {
872 const name = @tagName(feature.tag);874 if (!std.Target.wasm.featureSetHas(target.cpu.features, @enumFromInt(i))) continue;
873 try leb.writeUleb128(writer, @as(u32, name.len));875 safety_count -= 1;
876
877 try leb.writeUleb128(writer, @as(u32, '+'));
878 // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions.
879 const name = feature.llvm_name.?;
880 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
874 try writer.writeAll(name);881 try writer.writeAll(name);
875 }882 }
876883 assert(safety_count == 0);
877 try writeCustomSectionHeader(
878 binary_bytes.items,
879 header_offset,
880 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
881 );
882}884}
883885
884fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void {886fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void {
885 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);887 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
888 defer writeCustomSectionHeader(binary_bytes, header_offset);
886889
887 const writer = binary_bytes.writer();890 const writer = binary_bytes.writer(gpa);
888 const hdr_build_id = "build_id";891 const hdr_build_id = "build_id";
889 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));892 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));
890 try writer.writeAll(hdr_build_id);893 try writer.writeAll(hdr_build_id);
...@@ -892,18 +895,13 @@ fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8),...@@ -892,18 +895,13 @@ fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8),
892 try leb.writeUleb128(writer, @as(u32, 1));895 try leb.writeUleb128(writer, @as(u32, 1));
893 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));896 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));
894 try writer.writeAll(build_id);897 try writer.writeAll(build_id);
895
896 try writeCustomSectionHeader(
897 binary_bytes.items,
898 header_offset,
899 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
900 );
901}898}
902899
903fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void {900fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void {
904 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);901 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
902 defer writeCustomSectionHeader(binary_bytes, header_offset);
905903
906 const writer = binary_bytes.writer();904 const writer = binary_bytes.writer(gpa);
907 const producers = "producers";905 const producers = "producers";
908 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));906 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
909 try writer.writeAll(producers);907 try writer.writeAll(producers);
...@@ -947,12 +945,6 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -947,12 +945,6 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
947 try writer.writeAll(build_options.version);945 try writer.writeAll(build_options.version);
948 }946 }
949 }947 }
950
951 try writeCustomSectionHeader(
952 binary_bytes.items,
953 header_offset,
954 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
955 );
956}948}
957949
958///// For each relocatable section, emits a custom "relocation.<section_name>" section950///// For each relocatable section, emits a custom "relocation.<section_name>" section
...@@ -965,7 +957,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -965,7 +957,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
965// const comp = wasm.base.comp;957// const comp = wasm.base.comp;
966// const gpa = comp.gpa;958// const gpa = comp.gpa;
967// const code_index = wasm.code_section_index.unwrap() orelse return;959// const code_index = wasm.code_section_index.unwrap() orelse return;
968// const writer = binary_bytes.writer();960// const writer = binary_bytes.writer(gpa);
969// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);961// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
970//962//
971// // write custom section information963// // write custom section information
...@@ -1001,8 +993,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -1001,8 +993,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
1001// var buf: [5]u8 = undefined;993// var buf: [5]u8 = undefined;
1002// leb.writeUnsignedFixed(5, &buf, count);994// leb.writeUnsignedFixed(5, &buf, count);
1003// try binary_bytes.insertSlice(reloc_start, &buf);995// try binary_bytes.insertSlice(reloc_start, &buf);
1004// const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);996// writeCustomSectionHeader(binary_bytes, header_offset);
1005// try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
1006//}997//}
1007998
1008//fn emitDataRelocations(999//fn emitDataRelocations(
...@@ -1013,7 +1004,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -1013,7 +1004,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
1013//) !void {1004//) !void {
1014// const comp = wasm.base.comp;1005// const comp = wasm.base.comp;
1015// const gpa = comp.gpa;1006// const gpa = comp.gpa;
1016// const writer = binary_bytes.writer();1007// const writer = binary_bytes.writer(gpa);
1017// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1008// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1018//1009//
1019// // write custom section information1010// // write custom section information
...@@ -1052,8 +1043,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -1052,8 +1043,7 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
1052// var buf: [5]u8 = undefined;1043// var buf: [5]u8 = undefined;
1053// leb.writeUnsignedFixed(5, &buf, count);1044// leb.writeUnsignedFixed(5, &buf, count);
1054// try binary_bytes.insertSlice(reloc_start, &buf);1045// try binary_bytes.insertSlice(reloc_start, &buf);
1055// const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));1046// writeCustomSectionHeader(binary_bytes, header_offset);
1056// try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
1057//}1047//}
10581048
1059fn isBss(wasm: *Wasm, optional_name: Wasm.OptionalString) bool {1049fn isBss(wasm: *Wasm, optional_name: Wasm.OptionalString) bool {
...@@ -1104,6 +1094,21 @@ fn replaceVecSectionHeader(...@@ -1104,6 +1094,21 @@ fn replaceVecSectionHeader(
1104 bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());1094 bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());
1105}1095}
11061096
1097fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1098 try bytes.appendNTimes(gpa, 0, section_header_size);
1099 return @intCast(bytes.items.len - section_header_size);
1100}
1101
1102fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {
1103 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
1104 var buf: [section_header_size]u8 = undefined;
1105 var fbw = std.io.fixedBufferStream(&buf);
1106 const w = fbw.writer();
1107 w.writeByte(0) catch unreachable; // 0 = 'custom' section
1108 leb.writeUleb128(w, size) catch unreachable;
1109 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
1110}
1111
1107fn emitLimits(1112fn emitLimits(
1108 gpa: Allocator,1113 gpa: Allocator,
1109 binary_bytes: *std.ArrayListUnmanaged(u8),1114 binary_bytes: *std.ArrayListUnmanaged(u8),
...@@ -1171,7 +1176,7 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), ex...@@ -1171,7 +1176,7 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), ex
1171//) !void {1176//) !void {
1172// const gpa = wasm.base.comp.gpa;1177// const gpa = wasm.base.comp.gpa;
1173// const offset = try reserveCustomSectionHeader(gpa, binary_bytes);1178// const offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1174// const writer = binary_bytes.writer();1179// const writer = binary_bytes.writer(gpa);
1175// // emit "linking" custom section name1180// // emit "linking" custom section name
1176// const section_name = "linking";1181// const section_name = "linking";
1177// try leb.writeUleb128(writer, section_name.len);1182// try leb.writeUleb128(writer, section_name.len);
...@@ -1186,11 +1191,12 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), ex...@@ -1186,11 +1191,12 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), ex
1186// try wasm.emitSegmentInfo(binary_bytes);1191// try wasm.emitSegmentInfo(binary_bytes);
1187//1192//
1188// const size: u32 = @intCast(binary_bytes.items.len - offset - 6);1193// const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
1189// try writeCustomSectionHeader(binary_bytes.items, offset, size);1194// writeCustomSectionHeader(binary_bytes, offset, size);
1190//}1195//}
11911196
1192fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {1197fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1193 const writer = binary_bytes.writer();1198 const gpa = wasm.base.comp.gpa;
1199 const writer = binary_bytes.writer(gpa);
1194 try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));1200 try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));
1195 const segment_offset = binary_bytes.items.len;1201 const segment_offset = binary_bytes.items.len;
11961202
...@@ -1370,7 +1376,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {...@@ -1370,7 +1376,7 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1370// .TABLE_INDEX_I64,1376// .TABLE_INDEX_I64,
1371// .TABLE_INDEX_SLEB,1377// .TABLE_INDEX_SLEB,
1372// .TABLE_INDEX_SLEB64,1378// .TABLE_INDEX_SLEB64,
1373// => wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,1379// => wasm.indirect_function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
1374//1380//
1375// .TYPE_INDEX_LEB => unreachable, // handled above1381// .TYPE_INDEX_LEB => unreachable, // handled above
1376// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)1382// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)