authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-15 19:55:40-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
logd6b42e585be2bfb17d959908731b02e9e97f5fd9
tree451c1047861f8526ea34ccc35ba8912aa96574a6
parente80a2037687343ba18d240c771ff86d6891b5785

wasm linker: implement name subsection

unlike the previous implementation, we can simply iterate an array.

4 files changed, 128 insertions(+), 105 deletions(-)

src/InternPool.zig+4
......@@ -11801,6 +11801,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
1180111801 return @enumFromInt(ip.indexToKey(int).int.storage.u64);
1180211802}
1180311803
11804pub fn toFunc(ip: *const InternPool, i: Index) Key.Func {
11805 return ip.indexToKey(i).func;
11806}
11807
1180411808pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
1180511809 return switch (ip.indexToKey(ty)) {
1180611810 .struct_type => ip.loadStructType(ty).field_types.len,
src/Zcu.zig+1-1
......@@ -3483,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind
34833483}
34843484
34853485pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func {
3486 return zcu.intern_pool.indexToKey(func_index).func;
3486 return zcu.intern_pool.toFunc(func_index);
34873487}
34883488
34893489pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E {
src/link/Wasm.zig+59-4
......@@ -550,7 +550,7 @@ pub const NavObj = extern struct {
550550 /// Empty if not emitting an object.
551551 relocs: OutReloc.Slice,
552552
553 /// Index into `navs`.
553 /// Index into `Wasm.navs_obj`.
554554 /// Note that swapRemove is sometimes performed on `navs`.
555555 pub const Index = enum(u32) {
556556 _,
......@@ -562,13 +562,20 @@ pub const NavObj = extern struct {
562562 pub fn value(i: @This(), wasm: *const Wasm) *NavObj {
563563 return &wasm.navs_obj.values()[@intFromEnum(i)];
564564 }
565
566 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
567 const zcu = wasm.base.comp.zcu.?;
568 const ip = &zcu.intern_pool;
569 const nav = ip.getNav(i.key(wasm).*);
570 return nav.fqn.toSlice(ip);
571 }
565572 };
566573};
567574
568575pub const NavExe = extern struct {
569576 code: DataSegment.Payload,
570577
571 /// Index into `navs`.
578 /// Index into `Wasm.navs_exe`.
572579 /// Note that swapRemove is sometimes performed on `navs`.
573580 pub const Index = enum(u32) {
574581 _,
......@@ -580,6 +587,13 @@ pub const NavExe = extern struct {
580587 pub fn value(i: @This(), wasm: *const Wasm) *NavExe {
581588 return &wasm.navs_exe.values()[@intFromEnum(i)];
582589 }
590
591 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
592 const zcu = wasm.base.comp.zcu.?;
593 const ip = &zcu.intern_pool;
594 const nav = ip.getNav(i.key(wasm).*);
595 return nav.fqn.toSlice(ip);
596 }
583597 };
584598};
585599
......@@ -598,6 +612,14 @@ pub const ZcuFunc = extern struct {
598612 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
599613 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
600614 }
615
616 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
617 const zcu = wasm.base.comp.zcu.?;
618 const ip = &zcu.intern_pool;
619 const func = ip.toFunc(i.key(wasm).*);
620 const nav = ip.getNav(func.owner_nav);
621 return nav.fqn.toSlice(ip);
622 }
601623 };
602624};
603625
......@@ -720,6 +742,19 @@ pub const FunctionImport = extern struct {
720742 .zcu_func => @panic("TODO"),
721743 };
722744 }
745
746 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
747 return switch (unpack(r, wasm)) {
748 .unresolved => unreachable,
749 .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs),
750 .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors),
751 .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory),
752 .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls),
753 .__zig_error_names => @tagName(Unpacked.__zig_error_names),
754 .object_function => |i| i.ptr(wasm).name.slice(wasm),
755 .zcu_func => |i| i.name(wasm),
756 };
757 }
723758 };
724759
725760 /// Index into `object_function_imports`.
......@@ -851,6 +886,22 @@ pub const GlobalImport = extern struct {
851886 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),
852887 });
853888 }
889
890 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
891 return switch (unpack(r, wasm)) {
892 .unresolved => unreachable,
893 .__heap_base => @tagName(Unpacked.__heap_base),
894 .__heap_end => @tagName(Unpacked.__heap_end),
895 .__stack_pointer => @tagName(Unpacked.__stack_pointer),
896 .__tls_align => @tagName(Unpacked.__tls_align),
897 .__tls_base => @tagName(Unpacked.__tls_base),
898 .__tls_size => @tagName(Unpacked.__tls_size),
899 .__zig_error_name_table => @tagName(Unpacked.__zig_error_name_table),
900 .object_global => |i| i.name(wasm).slice(wasm),
901 .nav_obj => |i| i.name(wasm),
902 .nav_exe => |i| i.name(wasm),
903 };
904 }
854905 };
855906
856907 /// Index into `Wasm.object_global_imports`.
......@@ -1038,6 +1089,10 @@ pub const ObjectGlobalIndex = enum(u32) {
10381089 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
10391090 return &wasm.object_globals.items[@intFromEnum(index)];
10401091 }
1092
1093 pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString {
1094 return index.ptr(wasm).name;
1095 }
10411096};
10421097
10431098/// Index into `Wasm.object_memories`.
......@@ -1049,7 +1104,7 @@ pub const ObjectMemoryIndex = enum(u32) {
10491104 }
10501105};
10511106
1052/// Index into `object_functions`.
1107/// Index into `Wasm.object_functions`.
10531108pub const ObjectFunctionIndex = enum(u32) {
10541109 _,
10551110
......@@ -2397,7 +2452,7 @@ pub fn flushModule(
23972452 defer sub_prog_node.end();
23982453
23992454 wasm.flush_buffer.clear();
2400 return wasm.flush_buffer.finish(wasm, arena) catch |err| switch (err) {
2455 return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {
24012456 error.OutOfMemory => return error.OutOfMemory,
24022457 error.LinkFailure => return error.LinkFailure,
24032458 else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}),
src/link/Wasm/Flush.zig+64-100
......@@ -52,7 +52,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void {
5252 f.* = undefined;
5353}
5454
55pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
55pub fn finish(f: *Flush, wasm: *Wasm) !void {
5656 const comp = wasm.base.comp;
5757 const shared_memory = comp.config.shared_memory;
5858 const diags = &comp.link_diags;
......@@ -685,7 +685,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
685685 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);
686686 //}
687687 } else if (comp.config.debug_format != .strip) {
688 try emitNameSection(wasm, binary_bytes, arena);
688 try emitNameSection(wasm, &f.data_segments, binary_bytes);
689689 }
690690
691691 if (comp.config.debug_format != .strip) {
......@@ -732,117 +732,77 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
732732 try file.setEndPos(binary_bytes.items.len);
733733}
734734
735fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena: Allocator) !void {
736 if (true) {
737 std.log.warn("TODO emit name section", .{});
738 return;
739 }
735fn emitNameSection(
736 wasm: *Wasm,
737 data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Index, u32),
738 binary_bytes: *std.ArrayListUnmanaged(u8),
739) !void {
740740 const comp = wasm.base.comp;
741741 const gpa = comp.gpa;
742 const import_memory = comp.config.import_memory;
743742
744 // Deduplicate symbols that point to the same function.
745 var funcs: std.AutoArrayHashMapUnmanaged(u32, String) = .empty;
746 try funcs.ensureUnusedCapacityPrecise(arena, wasm.functions.count() + wasm.function_imports.items.len);
743 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
744 defer writeCustomSectionHeader(binary_bytes, header_offset);
747745
748 const NamedIndex = struct {
749 index: u32,
750 name: String,
751 };
746 const name_name = "name";
747 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len));
748 try binary_bytes.appendSlice(gpa, name_name);
752749
753 var globals: std.MultiArrayList(NamedIndex) = .empty;
754 try globals.ensureTotalCapacityPrecise(arena, wasm.globals.items.len + wasm.global_imports.items.len);
755
756 var segments: std.MultiArrayList(NamedIndex) = .empty;
757 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());
758
759 for (wasm.resolved_symbols.keys()) |sym_loc| {
760 const symbol = wasm.finalSymbolByLoc(sym_loc).*;
761 if (!symbol.flags.alive) continue;
762 const name = wasm.finalSymbolByLoc(sym_loc).name;
763 switch (symbol.tag) {
764 .function => {
765 const index = if (symbol.flags.undefined)
766 @intFromEnum(symbol.pointee.function_import)
767 else
768 wasm.function_imports.items.len + @intFromEnum(symbol.pointee.function);
769 const gop = funcs.getOrPutAssumeCapacity(index);
770 if (gop.found_existing) {
771 assert(gop.value_ptr.* == name);
772 } else {
773 gop.value_ptr.* = name;
774 }
775 },
776 .global => {
777 globals.appendAssumeCapacity(.{
778 .index = if (symbol.flags.undefined)
779 @intFromEnum(symbol.pointee.global_import)
780 else
781 @intFromEnum(symbol.pointee.global),
782 .name = name,
783 });
784 },
785 else => {},
750 {
751 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
752 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));
753
754 const total_functions: u32 = @intCast(wasm.function_imports.entries.len + wasm.functions.entries.len);
755 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);
756
757 for (wasm.function_imports.keys(), 0..) |name_index, function_index| {
758 const name = name_index.slice(wasm);
759 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
760 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
761 try binary_bytes.appendSlice(gpa, name);
762 }
763 for (wasm.functions.keys(), wasm.function_imports.entries.len..) |resolution, function_index| {
764 const name = resolution.name(wasm).?;
765 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
766 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
767 try binary_bytes.appendSlice(gpa, name);
786768 }
787769 }
788770
789 for (wasm.data_segments.keys(), 0..) |key, index| {
790 // bss section is not emitted when this condition holds true, so we also
791 // do not output a name for it.
792 if (!import_memory and mem.eql(u8, key, ".bss")) continue;
793 segments.appendAssumeCapacity(.{ .index = @intCast(index), .name = key });
794 }
771 {
772 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
773 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));
795774
796 const Sort = struct {
797 indexes: []const u32,
798 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
799 return ctx.indexes[lhs] < ctx.indexes[rhs];
775 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);
776 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
777
778 for (wasm.global_imports.keys(), 0..) |name_index, global_index| {
779 const name = name_index.slice(wasm);
780 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
781 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
782 try binary_bytes.appendSlice(gpa, name);
800783 }
801 };
802 funcs.entries.sortUnstable(@as(Sort, .{ .indexes = funcs.keys() }));
803 globals.sortUnstable(@as(Sort, .{ .indexes = globals.items(.index) }));
804 // Data segments are already ordered.
784 for (wasm.globals.keys(), wasm.global_imports.entries.len..) |resolution, global_index| {
785 const name = resolution.name(wasm).?;
786 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
787 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
788 try binary_bytes.appendSlice(gpa, name);
789 }
790 }
805791
806 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
807 defer writeCustomSectionHeader(binary_bytes, header_offset);
792 {
793 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
794 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));
808795
809 const writer = binary_bytes.writer(gpa);
810 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));
811 try writer.writeAll("name");
796 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);
797 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
812798
813 try emitNameSubsection(wasm, binary_bytes, .function, funcs.keys(), funcs.values());
814 try emitNameSubsection(wasm, binary_bytes, .global, globals.items(.index), globals.items(.name));
815 try emitNameSubsection(wasm, binary_bytes, .data_segment, segments.items(.index), segments.items(.name));
816}
817
818fn emitNameSubsection(
819 wasm: *const Wasm,
820 binary_bytes: *std.ArrayListUnmanaged(u8),
821 section_id: std.wasm.NameSubsection,
822 indexes: []const u32,
823 names: []const String,
824) !void {
825 assert(indexes.len == names.len);
826 const gpa = wasm.base.comp.gpa;
827 // We must emit subsection size, so first write to a temporary list
828 var section_list: std.ArrayListUnmanaged(u8) = .empty;
829 defer section_list.deinit(gpa);
830 const sub_writer = section_list.writer(gpa);
831
832 try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len)));
833 for (indexes, names) |index, name_index| {
834 const name = name_index.slice(wasm);
835 log.debug("emit symbol '{s}' type({s})", .{ name, @tagName(section_id) });
836 try leb.writeUleb128(sub_writer, index);
837 try leb.writeUleb128(sub_writer, @as(u32, @intCast(name.len)));
838 try sub_writer.writeAll(name);
799 for (data_segments.keys(), 0..) |ds, i| {
800 const name = ds.ptr(wasm).name.slice(wasm).?;
801 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));
802 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
803 try binary_bytes.appendSlice(gpa, name);
804 }
839805 }
840
841 // From now, write to the actual writer
842 const writer = binary_bytes.writer(gpa);
843 try leb.writeUleb128(writer, @intFromEnum(section_id));
844 try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len)));
845 try binary_bytes.appendSlice(gpa, section_list.items);
846806}
847807
848808fn emitFeaturesSection(
......@@ -1094,11 +1054,15 @@ fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)
10941054}
10951055
10961056fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {
1057 return replaceHeader(bytes, offset, 0); // 0 = 'custom' section
1058}
1059
1060fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void {
10971061 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
10981062 var buf: [section_header_size]u8 = undefined;
10991063 var fbw = std.io.fixedBufferStream(&buf);
11001064 const w = fbw.writer();
1101 w.writeByte(0) catch unreachable; // 0 = 'custom' section
1065 w.writeByte(tag) catch unreachable;
11021066 leb.writeUleb128(w, size) catch unreachable;
11031067 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
11041068}