authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-11 22:12:43-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
logb5261599d70dfdb5709a19fabc0e5fe9a3d0e3aa
treec99d0fa1a6c4fd0bb755bfc9608675b816e96806
parentd0d0847cd0f9fcb190c9460fc090f3a1891ad443

wasm linker: implement `@tagName` functions when tags are autoassigned


3 files changed, 217 insertions(+), 38 deletions(-)

src/Zcu.zig+2-2
...@@ -19,8 +19,8 @@ const Ast = std.zig.Ast;...@@ -19,8 +19,8 @@ const Ast = std.zig.Ast;
19const Zcu = @This();19const Zcu = @This();
20const Compilation = @import("Compilation.zig");20const Compilation = @import("Compilation.zig");
21const Cache = std.Build.Cache;21const Cache = std.Build.Cache;
22const Value = @import("Value.zig");22pub const Value = @import("Value.zig");
23const Type = @import("Type.zig");23pub const Type = @import("Type.zig");
24const Package = @import("Package.zig");24const Package = @import("Package.zig");
25const link = @import("link.zig");25const link = @import("link.zig");
26const Air = @import("Air.zig");26const Air = @import("Air.zig");
src/link/Wasm.zig+121-26
...@@ -46,7 +46,6 @@ const lldMain = @import("../main.zig").lldMain;...@@ -46,7 +46,6 @@ const lldMain = @import("../main.zig").lldMain;
46const trace = @import("../tracy.zig").trace;46const trace = @import("../tracy.zig").trace;
47const wasi_libc = @import("../wasi_libc.zig");47const wasi_libc = @import("../wasi_libc.zig");
48const Value = @import("../Value.zig");48const Value = @import("../Value.zig");
49const ZcuType = @import("../Type.zig");
5049
51base: link.File,50base: link.File,
52/// Null-terminated strings, indexes have type String and string_table provides51/// Null-terminated strings, indexes have type String and string_table provides
...@@ -190,6 +189,7 @@ navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .emp...@@ -190,6 +189,7 @@ navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .emp
190uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty,189uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty,
191/// Tracks ref count to optimize LEB encodings for UAV references.190/// Tracks ref count to optimize LEB encodings for UAV references.
192uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty,191uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty,
192/// When the key is an enum type, this represents a `@tagName` function.
193zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,193zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
194nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,194nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
195uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,195uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
...@@ -269,6 +269,7 @@ object_indirect_function_import_set: std.AutoArrayHashMapUnmanaged(String, void)...@@ -269,6 +269,7 @@ object_indirect_function_import_set: std.AutoArrayHashMapUnmanaged(String, void)
269object_indirect_function_set: std.AutoArrayHashMapUnmanaged(ObjectFunctionIndex, void) = .empty,269object_indirect_function_set: std.AutoArrayHashMapUnmanaged(ObjectFunctionIndex, void) = .empty,
270270
271error_name_table_ref_count: u32 = 0,271error_name_table_ref_count: u32 = 0,
272tag_name_table_ref_count: u32 = 0,
272273
273/// Set to true if any `GLOBAL_INDEX` relocation is encountered with274/// Set to true if any `GLOBAL_INDEX` relocation is encountered with
274/// `SymbolFlags.tls` set to true. This is for objects only; final275/// `SymbolFlags.tls` set to true. This is for objects only; final
...@@ -294,6 +295,14 @@ error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,...@@ -294,6 +295,14 @@ error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
294/// is stored. No need to serialize; trivially reconstructed.295/// is stored. No need to serialize; trivially reconstructed.
295error_name_offs: std.ArrayListUnmanaged(u32) = .empty,296error_name_offs: std.ArrayListUnmanaged(u32) = .empty,
296297
298tag_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
299tag_name_offs: std.ArrayListUnmanaged(u32) = .empty,
300
301pub const TagNameOff = extern struct {
302 off: u32,
303 len: u32,
304};
305
297/// Index into `Wasm.zcu_indirect_function_set`.306/// Index into `Wasm.zcu_indirect_function_set`.
298pub const ZcuIndirectFunctionSetIndex = enum(u32) {307pub const ZcuIndirectFunctionSetIndex = enum(u32) {
299 _,308 _,
...@@ -857,8 +866,16 @@ const ZcuDataStarts = struct {...@@ -857,8 +866,16 @@ const ZcuDataStarts = struct {
857 }866 }
858};867};
859868
860pub const ZcuFunc = extern struct {869pub const ZcuFunc = union {
861 function: CodeGen.Function,870 function: CodeGen.Function,
871 tag_name: TagName,
872
873 pub const TagName = extern struct {
874 symbol_name: String,
875 type_index: FunctionType.Index,
876 /// Index into `Wasm.tag_name_offs`.
877 table_index: u32,
878 };
862879
863 /// Index into `Wasm.zcu_funcs`.880 /// Index into `Wasm.zcu_funcs`.
864 /// Note that swapRemove is sometimes performed on `zcu_funcs`.881 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
...@@ -876,20 +893,35 @@ pub const ZcuFunc = extern struct {...@@ -876,20 +893,35 @@ pub const ZcuFunc = extern struct {
876 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {893 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
877 const zcu = wasm.base.comp.zcu.?;894 const zcu = wasm.base.comp.zcu.?;
878 const ip = &zcu.intern_pool;895 const ip = &zcu.intern_pool;
879 const func = ip.toFunc(i.key(wasm).*);896 const ip_index = i.key(wasm).*;
880 const nav = ip.getNav(func.owner_nav);897 switch (ip.indexToKey(ip_index)) {
881 return nav.fqn.toSlice(ip);898 .func => |func| {
899 const nav = ip.getNav(func.owner_nav);
900 return nav.fqn.toSlice(ip);
901 },
902 .enum_type => {
903 return i.value(wasm).tag_name.symbol_name.slice(wasm);
904 },
905 else => unreachable,
906 }
882 }907 }
883908
884 pub fn typeIndex(i: @This(), wasm: *Wasm) ?FunctionType.Index {909 pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index {
885 const comp = wasm.base.comp;910 const comp = wasm.base.comp;
886 const zcu = comp.zcu.?;911 const zcu = comp.zcu.?;
887 const target = &comp.root_mod.resolved_target.result;912 const target = &comp.root_mod.resolved_target.result;
888 const ip = &zcu.intern_pool;913 const ip = &zcu.intern_pool;
889 const func = ip.toFunc(i.key(wasm).*);914 switch (ip.indexToKey(i.key(wasm).*)) {
890 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);915 .func => |func| {
891 const fn_info = zcu.typeToFunc(fn_ty).?;916 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
892 return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);917 const fn_info = zcu.typeToFunc(fn_ty).?;
918 return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
919 },
920 .enum_type => {
921 return i.value(wasm).tag_name.type_index;
922 },
923 else => unreachable,
924 }
893 }925 }
894 };926 };
895};927};
...@@ -988,8 +1020,12 @@ pub const FunctionImport = extern struct {...@@ -988,8 +1020,12 @@ pub const FunctionImport = extern struct {
988 return fromIpIndex(wasm, ip.getNav(nav_index).status.fully_resolved.val);1020 return fromIpIndex(wasm, ip.getNav(nav_index).status.fully_resolved.val);
989 }1021 }
9901022
1023 pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution {
1024 return pack(wasm, .{ .zcu_func = i });
1025 }
1026
991 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {1027 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {
992 return pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.getIndex(ip_index).?) });1028 return fromZcuFunc(wasm, @enumFromInt(wasm.zcu_funcs.getIndex(ip_index).?));
993 }1029 }
9941030
995 pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution {1031 pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution {
...@@ -1012,7 +1048,7 @@ pub const FunctionImport = extern struct {...@@ -1012,7 +1048,7 @@ pub const FunctionImport = extern struct {
1012 => getExistingFuncType2(wasm, &.{}, &.{}),1048 => getExistingFuncType2(wasm, &.{}, &.{}),
1013 .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}),1049 .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}),
1014 .object_function => |i| i.ptr(wasm).type_index,1050 .object_function => |i| i.ptr(wasm).type_index,
1015 .zcu_func => |i| i.typeIndex(wasm).?,1051 .zcu_func => |i| i.typeIndex(wasm),
1016 };1052 };
1017 }1053 }
10181054
...@@ -1717,6 +1753,10 @@ pub const DataPayload = extern struct {...@@ -1717,6 +1753,10 @@ pub const DataPayload = extern struct {
1717pub const DataSegmentId = enum(u32) {1753pub const DataSegmentId = enum(u32) {
1718 __zig_error_names,1754 __zig_error_names,
1719 __zig_error_name_table,1755 __zig_error_name_table,
1756 /// All name string bytes for all `@tagName` implementations, concatenated together.
1757 __zig_tag_names,
1758 /// All tag name slices for all `@tagName` implementations, concatenated together.
1759 __zig_tag_name_table,
1720 /// This and `__heap_end` are better retrieved via a global, but there is1760 /// This and `__heap_end` are better retrieved via a global, but there is
1721 /// some suboptimal code out there (wasi libc) that additionally needs them1761 /// some suboptimal code out there (wasi libc) that additionally needs them
1722 /// as data symbols.1762 /// as data symbols.
...@@ -1742,6 +1782,8 @@ pub const DataSegmentId = enum(u32) {...@@ -1742,6 +1782,8 @@ pub const DataSegmentId = enum(u32) {
1742 pub const Unpacked = union(enum) {1782 pub const Unpacked = union(enum) {
1743 __zig_error_names,1783 __zig_error_names,
1744 __zig_error_name_table,1784 __zig_error_name_table,
1785 __zig_tag_names,
1786 __zig_tag_name_table,
1745 __heap_base,1787 __heap_base,
1746 __heap_end,1788 __heap_end,
1747 object: ObjectDataSegment.Index,1789 object: ObjectDataSegment.Index,
...@@ -1755,6 +1797,8 @@ pub const DataSegmentId = enum(u32) {...@@ -1755,6 +1797,8 @@ pub const DataSegmentId = enum(u32) {
1755 return switch (unpacked) {1797 return switch (unpacked) {
1756 .__zig_error_names => .__zig_error_names,1798 .__zig_error_names => .__zig_error_names,
1757 .__zig_error_name_table => .__zig_error_name_table,1799 .__zig_error_name_table => .__zig_error_name_table,
1800 .__zig_tag_names => .__zig_tag_names,
1801 .__zig_tag_name_table => .__zig_tag_name_table,
1758 .__heap_base => .__heap_base,1802 .__heap_base => .__heap_base,
1759 .__heap_end => .__heap_end,1803 .__heap_end => .__heap_end,
1760 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),1804 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
...@@ -1768,6 +1812,8 @@ pub const DataSegmentId = enum(u32) {...@@ -1768,6 +1812,8 @@ pub const DataSegmentId = enum(u32) {
1768 return switch (id) {1812 return switch (id) {
1769 .__zig_error_names => .__zig_error_names,1813 .__zig_error_names => .__zig_error_names,
1770 .__zig_error_name_table => .__zig_error_name_table,1814 .__zig_error_name_table => .__zig_error_name_table,
1815 .__zig_tag_names => .__zig_tag_names,
1816 .__zig_tag_name_table => .__zig_tag_name_table,
1771 .__heap_base => .__heap_base,1817 .__heap_base => .__heap_base,
1772 .__heap_end => .__heap_end,1818 .__heap_end => .__heap_end,
1773 _ => {1819 _ => {
...@@ -1815,7 +1861,14 @@ pub const DataSegmentId = enum(u32) {...@@ -1815,7 +1861,14 @@ pub const DataSegmentId = enum(u32) {
18151861
1816 pub fn category(id: DataSegmentId, wasm: *const Wasm) Category {1862 pub fn category(id: DataSegmentId, wasm: *const Wasm) Category {
1817 return switch (unpack(id, wasm)) {1863 return switch (unpack(id, wasm)) {
1818 .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => .data,1864 .__zig_error_names,
1865 .__zig_error_name_table,
1866 .__zig_tag_names,
1867 .__zig_tag_name_table,
1868 .__heap_base,
1869 .__heap_end,
1870 => .data,
1871
1819 .object => |i| {1872 .object => |i| {
1820 const ptr = i.ptr(wasm);1873 const ptr = i.ptr(wasm);
1821 if (ptr.flags.tls) return .tls;1874 if (ptr.flags.tls) return .tls;
...@@ -1836,7 +1889,14 @@ pub const DataSegmentId = enum(u32) {...@@ -1836,7 +1889,14 @@ pub const DataSegmentId = enum(u32) {
18361889
1837 pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool {1890 pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool {
1838 return switch (unpack(id, wasm)) {1891 return switch (unpack(id, wasm)) {
1839 .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,1892 .__zig_error_names,
1893 .__zig_error_name_table,
1894 .__zig_tag_names,
1895 .__zig_tag_name_table,
1896 .__heap_base,
1897 .__heap_end,
1898 => false,
1899
1840 .object => |i| i.ptr(wasm).flags.tls,1900 .object => |i| i.ptr(wasm).flags.tls,
1841 .uav_exe, .uav_obj => false,1901 .uav_exe, .uav_obj => false,
1842 inline .nav_exe, .nav_obj => |i| {1902 inline .nav_exe, .nav_obj => |i| {
...@@ -1854,7 +1914,16 @@ pub const DataSegmentId = enum(u32) {...@@ -1854,7 +1914,16 @@ pub const DataSegmentId = enum(u32) {
18541914
1855 pub fn name(id: DataSegmentId, wasm: *const Wasm) []const u8 {1915 pub fn name(id: DataSegmentId, wasm: *const Wasm) []const u8 {
1856 return switch (unpack(id, wasm)) {1916 return switch (unpack(id, wasm)) {
1857 .__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj, .__heap_base, .__heap_end => ".data",1917 .__zig_error_names,
1918 .__zig_error_name_table,
1919 .__zig_tag_names,
1920 .__zig_tag_name_table,
1921 .uav_exe,
1922 .uav_obj,
1923 .__heap_base,
1924 .__heap_end,
1925 => ".data",
1926
1858 .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),1927 .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),
1859 inline .nav_exe, .nav_obj => |i| {1928 inline .nav_exe, .nav_obj => |i| {
1860 const zcu = wasm.base.comp.zcu.?;1929 const zcu = wasm.base.comp.zcu.?;
...@@ -1867,14 +1936,14 @@ pub const DataSegmentId = enum(u32) {...@@ -1867,14 +1936,14 @@ pub const DataSegmentId = enum(u32) {
18671936
1868 pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment {1937 pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment {
1869 return switch (unpack(id, wasm)) {1938 return switch (unpack(id, wasm)) {
1870 .__zig_error_names => .@"1",1939 .__zig_error_names, .__zig_tag_names => .@"1",
1871 .__zig_error_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(),1940 .__zig_error_name_table, .__zig_tag_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(),
1872 .object => |i| i.ptr(wasm).flags.alignment,1941 .object => |i| i.ptr(wasm).flags.alignment,
1873 inline .uav_exe, .uav_obj => |i| {1942 inline .uav_exe, .uav_obj => |i| {
1874 const zcu = wasm.base.comp.zcu.?;1943 const zcu = wasm.base.comp.zcu.?;
1875 const ip = &zcu.intern_pool;1944 const ip = &zcu.intern_pool;
1876 const ip_index = i.key(wasm).*;1945 const ip_index = i.key(wasm).*;
1877 const ty: ZcuType = .fromInterned(ip.typeOf(ip_index));1946 const ty: Zcu.Type = .fromInterned(ip.typeOf(ip_index));
1878 const result = ty.abiAlignment(zcu);1947 const result = ty.abiAlignment(zcu);
1879 assert(result != .none);1948 assert(result != .none);
1880 return result;1949 return result;
...@@ -1885,7 +1954,7 @@ pub const DataSegmentId = enum(u32) {...@@ -1885,7 +1954,7 @@ pub const DataSegmentId = enum(u32) {
1885 const nav = ip.getNav(i.key(wasm).*);1954 const nav = ip.getNav(i.key(wasm).*);
1886 const explicit = nav.getAlignment();1955 const explicit = nav.getAlignment();
1887 if (explicit != .none) return explicit;1956 if (explicit != .none) return explicit;
1888 const ty: ZcuType = .fromInterned(nav.typeOf(ip));1957 const ty: Zcu.Type = .fromInterned(nav.typeOf(ip));
1889 const result = ty.abiAlignment(zcu);1958 const result = ty.abiAlignment(zcu);
1890 assert(result != .none);1959 assert(result != .none);
1891 return result;1960 return result;
...@@ -1897,6 +1966,8 @@ pub const DataSegmentId = enum(u32) {...@@ -1897,6 +1966,8 @@ pub const DataSegmentId = enum(u32) {
1897 return switch (unpack(id, wasm)) {1966 return switch (unpack(id, wasm)) {
1898 .__zig_error_names => @intCast(wasm.error_name_offs.items.len),1967 .__zig_error_names => @intCast(wasm.error_name_offs.items.len),
1899 .__zig_error_name_table => wasm.error_name_table_ref_count,1968 .__zig_error_name_table => wasm.error_name_table_ref_count,
1969 .__zig_tag_names => @intCast(wasm.tag_name_offs.items.len),
1970 .__zig_tag_name_table => wasm.tag_name_table_ref_count,
1900 .object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0,1971 .object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0,
1901 inline .uav_exe, .nav_exe => |i| i.value(wasm).count,1972 inline .uav_exe, .nav_exe => |i| i.value(wasm).count,
1902 };1973 };
...@@ -1906,7 +1977,14 @@ pub const DataSegmentId = enum(u32) {...@@ -1906,7 +1977,14 @@ pub const DataSegmentId = enum(u32) {
1906 const comp = wasm.base.comp;1977 const comp = wasm.base.comp;
1907 if (comp.config.import_memory and !id.isBss(wasm)) return true;1978 if (comp.config.import_memory and !id.isBss(wasm)) return true;
1908 return switch (unpack(id, wasm)) {1979 return switch (unpack(id, wasm)) {
1909 .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,1980 .__zig_error_names,
1981 .__zig_error_name_table,
1982 .__zig_tag_names,
1983 .__zig_tag_name_table,
1984 .__heap_base,
1985 .__heap_end,
1986 => false,
1987
1910 .object => |i| i.ptr(wasm).flags.is_passive,1988 .object => |i| i.ptr(wasm).flags.is_passive,
1911 .uav_exe, .uav_obj, .nav_exe, .nav_obj => false,1989 .uav_exe, .uav_obj, .nav_exe, .nav_obj => false,
1912 };1990 };
...@@ -1914,7 +1992,14 @@ pub const DataSegmentId = enum(u32) {...@@ -1914,7 +1992,14 @@ pub const DataSegmentId = enum(u32) {
19141992
1915 pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool {1993 pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool {
1916 return switch (unpack(id, wasm)) {1994 return switch (unpack(id, wasm)) {
1917 .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,1995 .__zig_error_names,
1996 .__zig_error_name_table,
1997 .__zig_tag_names,
1998 .__zig_tag_name_table,
1999 .__heap_base,
2000 .__heap_end,
2001 => false,
2002
1918 .object => |i| i.ptr(wasm).payload.off == .none,2003 .object => |i| i.ptr(wasm).payload.off == .none,
1919 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,2004 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,
1920 };2005 };
...@@ -1927,9 +2012,17 @@ pub const DataSegmentId = enum(u32) {...@@ -1927,9 +2012,17 @@ pub const DataSegmentId = enum(u32) {
1927 const comp = wasm.base.comp;2012 const comp = wasm.base.comp;
1928 const zcu = comp.zcu.?;2013 const zcu = comp.zcu.?;
1929 const errors_len = wasm.error_name_offs.items.len;2014 const errors_len = wasm.error_name_offs.items.len;
1930 const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu);2015 const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu);
1931 return @intCast(errors_len * elem_size);2016 return @intCast(errors_len * elem_size);
1932 },2017 },
2018 .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len),
2019 .__zig_tag_name_table => {
2020 const comp = wasm.base.comp;
2021 const zcu = comp.zcu.?;
2022 const table_len = wasm.tag_name_offs.items.len;
2023 const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu);
2024 return @intCast(table_len * elem_size);
2025 },
1933 .__heap_base, .__heap_end => wasm.pointerSize(),2026 .__heap_base, .__heap_end => wasm.pointerSize(),
1934 .object => |i| i.ptr(wasm).payload.len,2027 .object => |i| i.ptr(wasm).payload.len,
1935 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len,2028 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len,
...@@ -3052,6 +3145,8 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3052,6 +3145,8 @@ pub fn deinit(wasm: *Wasm) void {
30523145
3053 wasm.error_name_bytes.deinit(gpa);3146 wasm.error_name_bytes.deinit(gpa);
3054 wasm.error_name_offs.deinit(gpa);3147 wasm.error_name_offs.deinit(gpa);
3148 wasm.tag_name_bytes.deinit(gpa);
3149 wasm.tag_name_offs.deinit(gpa);
30553150
3056 wasm.missing_exports.deinit(gpa);3151 wasm.missing_exports.deinit(gpa);
3057}3152}
...@@ -4197,7 +4292,7 @@ pub fn internFunctionType(...@@ -4197,7 +4292,7 @@ pub fn internFunctionType(
4197 wasm: *Wasm,4292 wasm: *Wasm,
4198 cc: std.builtin.CallingConvention,4293 cc: std.builtin.CallingConvention,
4199 params: []const InternPool.Index,4294 params: []const InternPool.Index,
4200 return_type: ZcuType,4295 return_type: Zcu.Type,
4201 target: *const std.Target,4296 target: *const std.Target,
4202) Allocator.Error!FunctionType.Index {4297) Allocator.Error!FunctionType.Index {
4203 try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch);4298 try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch);
...@@ -4211,7 +4306,7 @@ pub fn getExistingFunctionType(...@@ -4211,7 +4306,7 @@ pub fn getExistingFunctionType(
4211 wasm: *Wasm,4306 wasm: *Wasm,
4212 cc: std.builtin.CallingConvention,4307 cc: std.builtin.CallingConvention,
4213 params: []const InternPool.Index,4308 params: []const InternPool.Index,
4214 return_type: ZcuType,4309 return_type: Zcu.Type,
4215 target: *const std.Target,4310 target: *const std.Target,
4216) ?FunctionType.Index {4311) ?FunctionType.Index {
4217 convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {4312 convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {
...@@ -4395,7 +4490,7 @@ fn convertZcuFnType(...@@ -4395,7 +4490,7 @@ fn convertZcuFnType(
4395 comp: *Compilation,4490 comp: *Compilation,
4396 cc: std.builtin.CallingConvention,4491 cc: std.builtin.CallingConvention,
4397 params: []const InternPool.Index,4492 params: []const InternPool.Index,
4398 return_type: ZcuType,4493 return_type: Zcu.Type,
4399 target: *const std.Target,4494 target: *const std.Target,
4400 params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),4495 params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),
4401 returns_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),4496 returns_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype),
...@@ -4423,7 +4518,7 @@ fn convertZcuFnType(...@@ -4423,7 +4518,7 @@ fn convertZcuFnType(
44234518
4424 // param types4519 // param types
4425 for (params) |param_type_ip| {4520 for (params) |param_type_ip| {
4426 const param_type = ZcuType.fromInterned(param_type_ip);4521 const param_type = Zcu.Type.fromInterned(param_type_ip);
4427 if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;4522 if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue;
44284523
4429 switch (cc) {4524 switch (cc) {
src/link/Wasm/Flush.zig+94-10
...@@ -109,6 +109,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -109,6 +109,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
109 const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;109 const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;
110110
111 // Detect any intrinsics that were called; they need to have dependencies on the symbols marked.111 // Detect any intrinsics that were called; they need to have dependencies on the symbols marked.
112 // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.
112 for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {113 for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {
113 .call_intrinsic => {114 .call_intrinsic => {
114 const symbol_name = try wasm.internString(@tagName(data.intrinsic));115 const symbol_name = try wasm.internString(@tagName(data.intrinsic));
...@@ -119,6 +120,28 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -119,6 +120,28 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
119 });120 });
120 try wasm.markFunctionImport(symbol_name, i.value(wasm), i);121 try wasm.markFunctionImport(symbol_name, i.value(wasm), i);
121 },122 },
123 .call_tag_name => {
124 const zcu = comp.zcu.?;
125 const ip = &zcu.intern_pool;
126 assert(ip.indexToKey(data.ip_index) == .enum_type);
127 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
128 if (!gop.found_existing) {
129 wasm.tag_name_table_ref_count += 1;
130 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
131 gop.value_ptr.* = .{ .tag_name = .{
132 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),
133 .type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),
134 .table_index = @intCast(wasm.tag_name_offs.items.len),
135 } };
136 try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});
137 const tag_names = ip.loadEnumType(data.ip_index).names;
138 for (tag_names.get(ip)) |tag_name| {
139 const slice = tag_name.toSlice(ip);
140 try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len));
141 try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]);
142 }
143 }
144 },
122 else => continue,145 else => continue,
123 };146 };
124147
...@@ -222,7 +245,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -222,7 +245,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
222 // unused segments can be omitted.245 // unused segments can be omitted.
223 try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len +246 try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len +
224 wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +247 wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +
225 wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 2);248 wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 4);
226 if (is_obj) assert(wasm.uavs_exe.entries.len == 0);249 if (is_obj) assert(wasm.uavs_exe.entries.len == 0);
227 if (is_obj) assert(wasm.navs_exe.entries.len == 0);250 if (is_obj) assert(wasm.navs_exe.entries.len == 0);
228 if (!is_obj) assert(wasm.uavs_obj.entries.len == 0);251 if (!is_obj) assert(wasm.uavs_obj.entries.len == 0);
...@@ -243,6 +266,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -243,6 +266,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
243 f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined));266 f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined));
244 f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined));267 f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined));
245 }268 }
269 if (wasm.tag_name_table_ref_count > 0) {
270 f.data_segments.putAssumeCapacity(.__zig_tag_names, @as(u32, undefined));
271 f.data_segments.putAssumeCapacity(.__zig_tag_name_table, @as(u32, undefined));
272 }
246 for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined));273 for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined));
247274
248 try wasm.functions.ensureUnusedCapacity(gpa, 3);275 try wasm.functions.ensureUnusedCapacity(gpa, 3);
...@@ -751,7 +778,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -751,7 +778,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
751778
752 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});779 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});
753780
754 try i.value(wasm).function.lower(wasm, binary_bytes);781 const zcu = comp.zcu.?;
782 const ip = &zcu.intern_pool;
783 switch (ip.indexToKey(i.key(wasm).*)) {
784 .enum_type => {
785 try emitTagNameFunction(gpa, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index);
786 },
787 else => try i.value(wasm).function.lower(wasm, binary_bytes),
788 }
755 },789 },
756 };790 };
757791
...@@ -849,9 +883,23 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -849,9 +883,23 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
849 if (is_obj) @panic("TODO error name table reloc");883 if (is_obj) @panic("TODO error name table reloc");
850 const base = f.data_segments.get(.__zig_error_names).?;884 const base = f.data_segments.get(.__zig_error_names).?;
851 if (!is64) {885 if (!is64) {
852 try emitErrorNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);886 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);
853 } else {887 } else {
854 try emitErrorNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);888 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);
889 }
890 break :append;
891 },
892 .__zig_tag_names => {
893 try binary_bytes.appendSlice(gpa, wasm.tag_name_bytes.items);
894 break :append;
895 },
896 .__zig_tag_name_table => {
897 if (is_obj) @panic("TODO tag name table reloc");
898 const base = f.data_segments.get(.__zig_tag_names).?;
899 if (!is64) {
900 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32);
901 } else {
902 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64);
855 }903 }
856 break :append;904 break :append;
857 },905 },
...@@ -1497,18 +1545,18 @@ fn uleb128size(x: u32) u32 {...@@ -1497,18 +1545,18 @@ fn uleb128size(x: u32) u32 {
1497 return size;1545 return size;
1498}1546}
14991547
1500fn emitErrorNameTable(1548fn emitTagNameTable(
1501 gpa: Allocator,1549 gpa: Allocator,
1502 code: *std.ArrayListUnmanaged(u8),1550 code: *std.ArrayListUnmanaged(u8),
1503 error_name_offs: []const u32,1551 tag_name_offs: []const u32,
1504 error_name_bytes: []const u8,1552 tag_name_bytes: []const u8,
1505 base: u32,1553 base: u32,
1506 comptime Int: type,1554 comptime Int: type,
1507) error{OutOfMemory}!void {1555) error{OutOfMemory}!void {
1508 const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8);1556 const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8);
1509 try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * error_name_offs.len);1557 try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len);
1510 for (error_name_offs) |off| {1558 for (tag_name_offs) |off| {
1511 const name_len: u32 = @intCast(mem.indexOfScalar(u8, error_name_bytes[off..], 0).?);1559 const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?);
1512 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little);1560 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little);
1513 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);1561 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);
1514 }1562 }
...@@ -1849,6 +1897,42 @@ fn emitInitMemoryFunction(...@@ -1849,6 +1897,42 @@ fn emitInitMemoryFunction(
1849 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1897 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1850}1898}
18511899
1900fn emitTagNameFunction(
1901 gpa: Allocator,
1902 code: *std.ArrayListUnmanaged(u8),
1903 table_base_addr: u32,
1904 table_index: u32,
1905) Allocator.Error!void {
1906 try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6);
1907 appendReservedUleb32(code, 0); // no locals
1908
1909 const slice_abi_size = 8;
1910 const encoded_alignment = @ctz(@as(u32, 4));
1911 const all_tag_values_autoassigned = true;
1912 if (all_tag_values_autoassigned) {
1913 // Then it's a direct table lookup.
1914 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1915 appendReservedUleb32(code, 0);
1916
1917 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1918 appendReservedUleb32(code, 1);
1919
1920 appendReservedI32Const(code, slice_abi_size);
1921 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_mul));
1922
1923 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));
1924 appendReservedUleb32(code, encoded_alignment);
1925 appendReservedUleb32(code, table_base_addr + table_index * 8);
1926
1927 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));
1928 appendReservedUleb32(code, encoded_alignment);
1929 appendReservedUleb32(code, 0);
1930 }
1931
1932 // End of the function body
1933 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1934}
1935
1852/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.1936/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
1853fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {1937fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {
1854 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1938 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));