authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-15 18:37:02-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
loge80a2037687343ba18d240c771ff86d6891b5785
tree46e65c22994e3b34d4a9529881a986f8d6790d63
parent4ecc4addc432792514a86f6a8fde0285de4468b2

wasm linker fixes

* function resolution now links to zcu_funcs, not navs_exe/navs_obj * updateFunc now adds things to output functions * updateNav now handles function aliases correctly * only report start symbol missing when it is unresolved

2 files changed, 54 insertions(+), 69 deletions(-)

src/link/Wasm.zig+46-55
...@@ -586,7 +586,7 @@ pub const NavExe = extern struct {...@@ -586,7 +586,7 @@ pub const NavExe = extern struct {
586pub const ZcuFunc = extern struct {586pub const ZcuFunc = extern struct {
587 function: CodeGen.Function,587 function: CodeGen.Function,
588588
589 /// Index into `zcu_funcs`.589 /// Index into `Wasm.zcu_funcs`.
590 /// Note that swapRemove is sometimes performed on `zcu_funcs`.590 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
591 pub const Index = enum(u32) {591 pub const Index = enum(u32) {
592 _,592 _,
...@@ -641,7 +641,7 @@ pub const FunctionImport = extern struct {...@@ -641,7 +641,7 @@ pub const FunctionImport = extern struct {
641 __wasm_init_tls,641 __wasm_init_tls,
642 __zig_error_names,642 __zig_error_names,
643 // Next, index into `object_functions`.643 // Next, index into `object_functions`.
644 // Next, index into `navs_exe` or `navs_obj` depending on whether emitting an object.644 // Next, index into `zcu_funcs`.
645 _,645 _,
646646
647 const first_object_function = @intFromEnum(Resolution.__zig_error_names) + 1;647 const first_object_function = @intFromEnum(Resolution.__zig_error_names) + 1;
...@@ -654,8 +654,7 @@ pub const FunctionImport = extern struct {...@@ -654,8 +654,7 @@ pub const FunctionImport = extern struct {
654 __wasm_init_tls,654 __wasm_init_tls,
655 __zig_error_names,655 __zig_error_names,
656 object_function: ObjectFunctionIndex,656 object_function: ObjectFunctionIndex,
657 nav_exe: NavExe.Index,657 zcu_func: ZcuFunc.Index,
658 nav_obj: NavObj.Index,
659 };658 };
660659
661 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {660 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
...@@ -669,16 +668,13 @@ pub const FunctionImport = extern struct {...@@ -669,16 +668,13 @@ pub const FunctionImport = extern struct {
669 _ => {668 _ => {
670 const i: u32 = @intFromEnum(r);669 const i: u32 = @intFromEnum(r);
671 const object_function_index = i - first_object_function;670 const object_function_index = i - first_object_function;
672 if (object_function_index < wasm.object_functions.items.len)671 if (object_function_index < wasm.object_functions.items.len) {
673 return .{ .object_function = @enumFromInt(object_function_index) };672 return .{ .object_function = @enumFromInt(object_function_index) };
674 const comp = wasm.base.comp;673 } else {
675 const is_obj = comp.config.output_mode == .Obj;674 return .{
676 const nav_index = object_function_index - wasm.object_functions.items.len;675 .zcu_func = @enumFromInt(object_function_index - wasm.object_functions.items.len),
677 return if (is_obj) .{676 };
678 .nav_obj = @enumFromInt(nav_index),677 }
679 } else .{
680 .nav_exe = @enumFromInt(nav_index),
681 };
682 },678 },
683 };679 };
684 }680 }
...@@ -692,24 +688,22 @@ pub const FunctionImport = extern struct {...@@ -692,24 +688,22 @@ pub const FunctionImport = extern struct {
692 .__wasm_init_tls => .__wasm_init_tls,688 .__wasm_init_tls => .__wasm_init_tls,
693 .__zig_error_names => .__zig_error_names,689 .__zig_error_names => .__zig_error_names,
694 .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)),690 .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)),
695 .nav_obj => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),691 .zcu_func => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),
696 .nav_exe => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),
697 };692 };
698 }693 }
699694
700 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {695 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution {
701 const comp = wasm.base.comp;696 const zcu = wasm.base.comp.zcu.?;
702 const is_obj = comp.config.output_mode == .Obj;697 const ip = &zcu.intern_pool;
703 return pack(wasm, if (is_obj) .{698 const nav = ip.getNav(nav_index);
704 .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(ip_nav).?),699 return pack(wasm, .{
705 } else .{700 .zcu_func = @enumFromInt(wasm.zcu_funcs.getIndex(nav.status.resolved.val).?),
706 .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?),
707 });701 });
708 }702 }
709703
710 pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool {704 pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool {
711 return switch (r.unpack(wasm)) {705 return switch (r.unpack(wasm)) {
712 .unresolved, .nav_obj, .nav_exe => true,706 .unresolved, .zcu_func => true,
713 else => false,707 else => false,
714 };708 };
715 }709 }
...@@ -723,8 +717,7 @@ pub const FunctionImport = extern struct {...@@ -723,8 +717,7 @@ pub const FunctionImport = extern struct {
723 .__wasm_init_tls => @panic("TODO"),717 .__wasm_init_tls => @panic("TODO"),
724 .__zig_error_names => @panic("TODO"),718 .__zig_error_names => @panic("TODO"),
725 .object_function => |i| i.ptr(wasm).type_index,719 .object_function => |i| i.ptr(wasm).type_index,
726 .nav_exe => @panic("TODO"),720 .zcu_func => @panic("TODO"),
727 .nav_obj => @panic("TODO"),
728 };721 };
729 }722 }
730 };723 };
...@@ -1940,13 +1933,18 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -1940,13 +1933,18 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
19401933
1941 dev.check(.wasm_backend);1934 dev.check(.wasm_backend);
19421935
1936 const gpa = pt.zcu.gpa;
1937 try wasm.functions.ensureUnusedCapacity(gpa, 1);
1938 try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1);
1939
1943 // This converts AIR to MIR but does not yet lower to wasm code.1940 // This converts AIR to MIR but does not yet lower to wasm code.
1944 // That lowering happens during `flush`, after garbage collection, which1941 // That lowering happens during `flush`, after garbage collection, which
1945 // can affect function and global indexes, which affects the LEB integer1942 // can affect function and global indexes, which affects the LEB integer
1946 // encoding, which affects the output binary size.1943 // encoding, which affects the output binary size.
1947 try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{1944 wasm.zcu_funcs.putAssumeCapacity(func_index, .{
1948 .function = try CodeGen.function(wasm, pt, func_index, air, liveness),1945 .function = try CodeGen.function(wasm, pt, func_index, air, liveness),
1949 });1946 });
1947 wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {});
1950}1948}
19511949
1952// Generate code for the "Nav", storing it in memory to be later written to1950// Generate code for the "Nav", storing it in memory to be later written to
...@@ -1963,19 +1961,14 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -1963,19 +1961,14 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
1963 const gpa = comp.gpa;1961 const gpa = comp.gpa;
1964 const is_obj = comp.config.output_mode == .Obj;1962 const is_obj = comp.config.output_mode == .Obj;
19651963
1966 const nav_val = zcu.navValue(nav_index);1964 const is_extern, const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
1967 const is_extern, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {1965 .func => return,
1968 .variable => |variable| .{ false, Value.fromInterned(variable.init) },1966 .@"extern" => .{ true, .none },
1969 .func => unreachable,1967 .variable => |variable| .{ false, variable.init },
1970 .@"extern" => b: {1968 else => .{ false, nav.status.resolved.val },
1971 assert(!ip.isFunctionType(nav.typeOf(ip)));
1972 break :b .{ true, nav_val };
1973 },
1974 else => .{ false, nav_val },
1975 };1969 };
19761970 if (is_extern) {
1977 if (!nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {1971 try wasm.imports.put(gpa, nav_index, {});
1978 _ = wasm.imports.swapRemove(nav_index);
1979 if (is_obj) {1972 if (is_obj) {
1980 if (wasm.navs_obj.swapRemove(nav_index)) @panic("TODO reclaim resources");1973 if (wasm.navs_obj.swapRemove(nav_index)) @panic("TODO reclaim resources");
1981 } else {1974 } else {
...@@ -1983,9 +1976,9 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -1983,9 +1976,9 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
1983 }1976 }
1984 return;1977 return;
1985 }1978 }
1979 _ = wasm.imports.swapRemove(nav_index);
19861980
1987 if (is_extern) {1981 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
1988 try wasm.imports.put(gpa, nav_index, {});
1989 if (is_obj) {1982 if (is_obj) {
1990 if (wasm.navs_obj.swapRemove(nav_index)) @panic("TODO reclaim resources");1983 if (wasm.navs_obj.swapRemove(nav_index)) @panic("TODO reclaim resources");
1991 } else {1984 } else {
...@@ -2002,7 +1995,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2002,7 +1995,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2002 &wasm.base,1995 &wasm.base,
2003 pt,1996 pt,
2004 zcu.navSrcLoc(nav_index),1997 zcu.navSrcLoc(nav_index),
2005 nav_init,1998 Value.fromInterned(nav_init),
2006 &wasm.string_bytes,1999 &wasm.string_bytes,
2007 .none,2000 .none,
2008 );2001 );
...@@ -2030,11 +2023,6 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2030,11 +2023,6 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
20302023
2031 if (is_obj) {2024 if (is_obj) {
2032 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);2025 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
2033 if (gop.found_existing) {
2034 @panic("TODO reuse these resources");
2035 } else {
2036 _ = wasm.imports.swapRemove(nav_index);
2037 }
2038 gop.value_ptr.* = .{2026 gop.value_ptr.* = .{
2039 .code = code,2027 .code = code,
2040 .relocs = .{2028 .relocs = .{
...@@ -2047,11 +2035,6 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2047,11 +2035,6 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2047 assert(relocs_len == 0);2035 assert(relocs_len == 0);
20482036
2049 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);2037 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
2050 if (gop.found_existing) {
2051 @panic("TODO reuse these resources");
2052 } else {
2053 _ = wasm.imports.swapRemove(nav_index);
2054 }
2055 gop.value_ptr.* = .{2038 gop.value_ptr.* = .{
2056 .code = code,2039 .code = code,
2057 };2040 };
...@@ -2072,9 +2055,13 @@ pub fn deleteExport(...@@ -2072,9 +2055,13 @@ pub fn deleteExport(
20722055
2073 const zcu = wasm.base.comp.zcu.?;2056 const zcu = wasm.base.comp.zcu.?;
2074 const ip = &zcu.intern_pool;2057 const ip = &zcu.intern_pool;
2075 const export_name = wasm.getExistingString(name.toSlice(ip)).?;2058 const name_slice = name.toSlice(ip);
2059 const export_name = wasm.getExistingString(name_slice).?;
2076 switch (exported) {2060 switch (exported) {
2077 .nav => |nav_index| assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name })),2061 .nav => |nav_index| {
2062 log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) });
2063 assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name }));
2064 },
2078 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),2065 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),
2079 }2066 }
2080 wasm.any_exports_updated = true;2067 wasm.any_exports_updated = true;
...@@ -2096,9 +2083,13 @@ pub fn updateExports(...@@ -2096,9 +2083,13 @@ pub fn updateExports(
2096 const ip = &zcu.intern_pool;2083 const ip = &zcu.intern_pool;
2097 for (export_indices) |export_idx| {2084 for (export_indices) |export_idx| {
2098 const exp = export_idx.ptr(zcu);2085 const exp = export_idx.ptr(zcu);
2099 const name = try wasm.internString(exp.opts.name.toSlice(ip));2086 const name_slice = exp.opts.name.toSlice(ip);
2087 const name = try wasm.internString(name_slice);
2100 switch (exported) {2088 switch (exported) {
2101 .nav => |nav_index| try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),2089 .nav => |nav_index| {
2090 log.debug("updateExports '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) });
2091 try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx);
2092 },
2102 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),2093 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
2103 }2094 }
2104 }2095 }
src/link/Wasm/Flush.zig+8-14
...@@ -79,6 +79,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -79,6 +79,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
7979
80 for (wasm.nav_exports.keys()) |*nav_export| {80 for (wasm.nav_exports.keys()) |*nav_export| {
81 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {81 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {
82 log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index });
82 try wasm.function_exports.append(gpa, .{83 try wasm.function_exports.append(gpa, .{
83 .name = nav_export.name,84 .name = nav_export.name,
84 .function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?,85 .function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?,
...@@ -103,9 +104,11 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -103,9 +104,11 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
103 }104 }
104105
105 if (entry_name.unwrap()) |name| {106 if (entry_name.unwrap()) |name| {
106 var err = try diags.addErrorWithNotes(1);107 if (wasm.entry_resolution == .unresolved) {
107 try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)});108 var err = try diags.addErrorWithNotes(1);
108 try err.addNote("'-fno-entry' suppresses this error", .{});109 try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)});
110 try err.addNote("'-fno-entry' suppresses this error", .{});
111 }
109 }112 }
110 }113 }
111114
...@@ -615,19 +618,10 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -615,19 +618,10 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
615 //try leb.writeUleb128(binary_writer, atom.code.len);618 //try leb.writeUleb128(binary_writer, atom.code.len);
616 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));619 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
617 },620 },
618 .nav_exe => |i| {621 .zcu_func => |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);
628 _ = i;622 _ = i;
629 _ = start_offset;623 _ = start_offset;
630 @panic("TODO lower nav obj code and apply relocations");624 @panic("TODO lower zcu_func code and apply relocations");
631 //try leb.writeUleb128(binary_writer, atom.code.len);625 //try leb.writeUleb128(binary_writer, atom.code.len);
632 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));626 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
633 },627 },