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 {...@@ -11801,6 +11801,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
11801 return @enumFromInt(ip.indexToKey(int).int.storage.u64);11801 return @enumFromInt(ip.indexToKey(int).int.storage.u64);
11802}11802}
1180311803
11804pub fn toFunc(ip: *const InternPool, i: Index) Key.Func {
11805 return ip.indexToKey(i).func;
11806}
11807
11804pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {11808pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
11805 return switch (ip.indexToKey(ty)) {11809 return switch (ip.indexToKey(ty)) {
11806 .struct_type => ip.loadStructType(ty).field_types.len,11810 .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...@@ -3483,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind
3483}3483}
34843484
3485pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func {3485pub 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);
3487}3487}
34883488
3489pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E {3489pub 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 {...@@ -550,7 +550,7 @@ pub const NavObj = extern struct {
550 /// Empty if not emitting an object.550 /// Empty if not emitting an object.
551 relocs: OutReloc.Slice,551 relocs: OutReloc.Slice,
552552
553 /// Index into `navs`.553 /// Index into `Wasm.navs_obj`.
554 /// Note that swapRemove is sometimes performed on `navs`.554 /// Note that swapRemove is sometimes performed on `navs`.
555 pub const Index = enum(u32) {555 pub const Index = enum(u32) {
556 _,556 _,
...@@ -562,13 +562,20 @@ pub const NavObj = extern struct {...@@ -562,13 +562,20 @@ pub const NavObj = extern struct {
562 pub fn value(i: @This(), wasm: *const Wasm) *NavObj {562 pub fn value(i: @This(), wasm: *const Wasm) *NavObj {
563 return &wasm.navs_obj.values()[@intFromEnum(i)];563 return &wasm.navs_obj.values()[@intFromEnum(i)];
564 }564 }
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 }
565 };572 };
566};573};
567574
568pub const NavExe = extern struct {575pub const NavExe = extern struct {
569 code: DataSegment.Payload,576 code: DataSegment.Payload,
570577
571 /// Index into `navs`.578 /// Index into `Wasm.navs_exe`.
572 /// Note that swapRemove is sometimes performed on `navs`.579 /// Note that swapRemove is sometimes performed on `navs`.
573 pub const Index = enum(u32) {580 pub const Index = enum(u32) {
574 _,581 _,
...@@ -580,6 +587,13 @@ pub const NavExe = extern struct {...@@ -580,6 +587,13 @@ pub const NavExe = extern struct {
580 pub fn value(i: @This(), wasm: *const Wasm) *NavExe {587 pub fn value(i: @This(), wasm: *const Wasm) *NavExe {
581 return &wasm.navs_exe.values()[@intFromEnum(i)];588 return &wasm.navs_exe.values()[@intFromEnum(i)];
582 }589 }
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 }
583 };597 };
584};598};
585599
...@@ -598,6 +612,14 @@ pub const ZcuFunc = extern struct {...@@ -598,6 +612,14 @@ pub const ZcuFunc = extern struct {
598 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {612 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
599 return &wasm.zcu_funcs.values()[@intFromEnum(i)];613 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
600 }614 }
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 }
601 };623 };
602};624};
603625
...@@ -720,6 +742,19 @@ pub const FunctionImport = extern struct {...@@ -720,6 +742,19 @@ pub const FunctionImport = extern struct {
720 .zcu_func => @panic("TODO"),742 .zcu_func => @panic("TODO"),
721 };743 };
722 }744 }
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 }
723 };758 };
724759
725 /// Index into `object_function_imports`.760 /// Index into `object_function_imports`.
...@@ -851,6 +886,22 @@ pub const GlobalImport = extern struct {...@@ -851,6 +886,22 @@ pub const GlobalImport = extern struct {
851 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),886 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),
852 });887 });
853 }888 }
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 }
854 };905 };
855906
856 /// Index into `Wasm.object_global_imports`.907 /// Index into `Wasm.object_global_imports`.
...@@ -1038,6 +1089,10 @@ pub const ObjectGlobalIndex = enum(u32) {...@@ -1038,6 +1089,10 @@ pub const ObjectGlobalIndex = enum(u32) {
1038 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {1089 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
1039 return &wasm.object_globals.items[@intFromEnum(index)];1090 return &wasm.object_globals.items[@intFromEnum(index)];
1040 }1091 }
1092
1093 pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString {
1094 return index.ptr(wasm).name;
1095 }
1041};1096};
10421097
1043/// Index into `Wasm.object_memories`.1098/// Index into `Wasm.object_memories`.
...@@ -1049,7 +1104,7 @@ pub const ObjectMemoryIndex = enum(u32) {...@@ -1049,7 +1104,7 @@ pub const ObjectMemoryIndex = enum(u32) {
1049 }1104 }
1050};1105};
10511106
1052/// Index into `object_functions`.1107/// Index into `Wasm.object_functions`.
1053pub const ObjectFunctionIndex = enum(u32) {1108pub const ObjectFunctionIndex = enum(u32) {
1054 _,1109 _,
10551110
...@@ -2397,7 +2452,7 @@ pub fn flushModule(...@@ -2397,7 +2452,7 @@ pub fn flushModule(
2397 defer sub_prog_node.end();2452 defer sub_prog_node.end();
23982453
2399 wasm.flush_buffer.clear();2454 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) {
2401 error.OutOfMemory => return error.OutOfMemory,2456 error.OutOfMemory => return error.OutOfMemory,
2402 error.LinkFailure => return error.LinkFailure,2457 error.LinkFailure => return error.LinkFailure,
2403 else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}),2458 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 {...@@ -52,7 +52,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void {
52 f.* = undefined;52 f.* = undefined;
53}53}
5454
55pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {55pub fn finish(f: *Flush, wasm: *Wasm) !void {
56 const comp = wasm.base.comp;56 const comp = wasm.base.comp;
57 const shared_memory = comp.config.shared_memory;57 const shared_memory = comp.config.shared_memory;
58 const diags = &comp.link_diags;58 const diags = &comp.link_diags;
...@@ -685,7 +685,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -685,7 +685,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
685 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);685 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);
686 //}686 //}
687 } else if (comp.config.debug_format != .strip) {687 } else if (comp.config.debug_format != .strip) {
688 try emitNameSection(wasm, binary_bytes, arena);688 try emitNameSection(wasm, &f.data_segments, binary_bytes);
689 }689 }
690690
691 if (comp.config.debug_format != .strip) {691 if (comp.config.debug_format != .strip) {
...@@ -732,117 +732,77 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -732,117 +732,77 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
732 try file.setEndPos(binary_bytes.items.len);732 try file.setEndPos(binary_bytes.items.len);
733}733}
734734
735fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena: Allocator) !void {735fn emitNameSection(
736 if (true) {736 wasm: *Wasm,
737 std.log.warn("TODO emit name section", .{});737 data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Index, u32),
738 return;738 binary_bytes: *std.ArrayListUnmanaged(u8),
739 }739) !void {
740 const comp = wasm.base.comp;740 const comp = wasm.base.comp;
741 const gpa = comp.gpa;741 const gpa = comp.gpa;
742 const import_memory = comp.config.import_memory;
743742
744 // Deduplicate symbols that point to the same function.743 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
745 var funcs: std.AutoArrayHashMapUnmanaged(u32, String) = .empty;744 defer writeCustomSectionHeader(binary_bytes, header_offset);
746 try funcs.ensureUnusedCapacityPrecise(arena, wasm.functions.count() + wasm.function_imports.items.len);
747745
748 const NamedIndex = struct {746 const name_name = "name";
749 index: u32,747 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len));
750 name: String,748 try binary_bytes.appendSlice(gpa, name_name);
751 };
752749
753 var globals: std.MultiArrayList(NamedIndex) = .empty;750 {
754 try globals.ensureTotalCapacityPrecise(arena, wasm.globals.items.len + wasm.global_imports.items.len);751 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
755752 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));
756 var segments: std.MultiArrayList(NamedIndex) = .empty;753
757 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());754 const total_functions: u32 = @intCast(wasm.function_imports.entries.len + wasm.functions.entries.len);
758755 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);
759 for (wasm.resolved_symbols.keys()) |sym_loc| {756
760 const symbol = wasm.finalSymbolByLoc(sym_loc).*;757 for (wasm.function_imports.keys(), 0..) |name_index, function_index| {
761 if (!symbol.flags.alive) continue;758 const name = name_index.slice(wasm);
762 const name = wasm.finalSymbolByLoc(sym_loc).name;759 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
763 switch (symbol.tag) {760 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
764 .function => {761 try binary_bytes.appendSlice(gpa, name);
765 const index = if (symbol.flags.undefined)762 }
766 @intFromEnum(symbol.pointee.function_import)763 for (wasm.functions.keys(), wasm.function_imports.entries.len..) |resolution, function_index| {
767 else764 const name = resolution.name(wasm).?;
768 wasm.function_imports.items.len + @intFromEnum(symbol.pointee.function);765 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));
769 const gop = funcs.getOrPutAssumeCapacity(index);766 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
770 if (gop.found_existing) {767 try binary_bytes.appendSlice(gpa, name);
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 => {},
786 }768 }
787 }769 }
788770
789 for (wasm.data_segments.keys(), 0..) |key, index| {771 {
790 // bss section is not emitted when this condition holds true, so we also772 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
791 // do not output a name for it.773 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));
792 if (!import_memory and mem.eql(u8, key, ".bss")) continue;
793 segments.appendAssumeCapacity(.{ .index = @intCast(index), .name = key });
794 }
795774
796 const Sort = struct {775 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);
797 indexes: []const u32,776 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
798 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {777
799 return ctx.indexes[lhs] < ctx.indexes[rhs];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);
800 }783 }
801 };784 for (wasm.globals.keys(), wasm.global_imports.entries.len..) |resolution, global_index| {
802 funcs.entries.sortUnstable(@as(Sort, .{ .indexes = funcs.keys() }));785 const name = resolution.name(wasm).?;
803 globals.sortUnstable(@as(Sort, .{ .indexes = globals.items(.index) }));786 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));
804 // Data segments are already ordered.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);792 {
807 defer writeCustomSectionHeader(binary_bytes, header_offset);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);796 const total_globals: u32 = @intCast(wasm.global_imports.entries.len + wasm.globals.entries.len);
810 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));797 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
811 try writer.writeAll("name");
812798
813 try emitNameSubsection(wasm, binary_bytes, .function, funcs.keys(), funcs.values());799 for (data_segments.keys(), 0..) |ds, i| {
814 try emitNameSubsection(wasm, binary_bytes, .global, globals.items(.index), globals.items(.name));800 const name = ds.ptr(wasm).name.slice(wasm).?;
815 try emitNameSubsection(wasm, binary_bytes, .data_segment, segments.items(.index), segments.items(.name));801 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));
816}802 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
817803 try binary_bytes.appendSlice(gpa, name);
818fn emitNameSubsection(804 }
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);
839 }805 }
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);
846}806}
847807
848fn emitFeaturesSection(808fn emitFeaturesSection(
...@@ -1094,11 +1054,15 @@ fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)...@@ -1094,11 +1054,15 @@ fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)
1094}1054}
10951055
1096fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {1056fn 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 {
1097 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);1061 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
1098 var buf: [section_header_size]u8 = undefined;1062 var buf: [section_header_size]u8 = undefined;
1099 var fbw = std.io.fixedBufferStream(&buf);1063 var fbw = std.io.fixedBufferStream(&buf);
1100 const w = fbw.writer();1064 const w = fbw.writer();
1101 w.writeByte(0) catch unreachable; // 0 = 'custom' section1065 w.writeByte(tag) catch unreachable;
1102 leb.writeUleb128(w, size) catch unreachable;1066 leb.writeUleb128(w, size) catch unreachable;
1103 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());1067 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
1104}1068}