authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-08 13:55:46+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-09 03:49:24+01:00
log6069161a51c4672b37c87d8cda06999baf5fef2c
tree1e27c23352c4fdb8ebc9235b65604ec06f7ded12
parentaebd84b525d8ea91f9be5b6562e1fe7abf749220

link.Wasm: reserve sufficient capacity for `@tagName` function code

Resolves: https://codeberg.org/ziglang/zig/issues/30748

3 files changed, 86 insertions(+), 67 deletions(-)

ci/loongarch64-linux-debug.sh-1
...@@ -48,7 +48,6 @@ stage3-debug/bin/zig build test docs \...@@ -48,7 +48,6 @@ stage3-debug/bin/zig build test docs \
48 --maxrss ${ZSF_MAX_RSS:-0} \48 --maxrss ${ZSF_MAX_RSS:-0} \
49 -Dstatic-llvm \49 -Dstatic-llvm \
50 -Dskip-non-native \50 -Dskip-non-native \
51 -Dskip-test-incremental \
52 -Dtarget=native-native-musl \51 -Dtarget=native-native-musl \
53 --search-prefix "$PREFIX" \52 --search-prefix "$PREFIX" \
54 --zig-lib-dir "$PWD/../lib" \53 --zig-lib-dir "$PWD/../lib" \
ci/loongarch64-linux-release.sh-1
...@@ -48,7 +48,6 @@ stage3-release/bin/zig build test docs \...@@ -48,7 +48,6 @@ stage3-release/bin/zig build test docs \
48 --maxrss ${ZSF_MAX_RSS:-0} \48 --maxrss ${ZSF_MAX_RSS:-0} \
49 -Dstatic-llvm \49 -Dstatic-llvm \
50 -Dskip-non-native \50 -Dskip-non-native \
51 -Dskip-test-incremental \
52 -Dtarget=native-native-musl \51 -Dtarget=native-native-musl \
53 --search-prefix "$PREFIX" \52 --search-prefix "$PREFIX" \
54 --zig-lib-dir "$PWD/../lib" \53 --zig-lib-dir "$PWD/../lib" \
src/link/Wasm/Flush.zig+86-65
...@@ -1871,13 +1871,21 @@ fn emitTagNameFunction(...@@ -1871,13 +1871,21 @@ fn emitTagNameFunction(
1871 const enum_type = ip.loadEnumType(enum_type_ip);1871 const enum_type = ip.loadEnumType(enum_type_ip);
1872 const tag_values = enum_type.values.get(ip);1872 const tag_values = enum_type.values.get(ip);
18731873
1874 try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6);
1875 appendReservedUleb32(code, 0); // no locals
1876
1877 const slice_abi_size = 8;1874 const slice_abi_size = 8;
1878 const encoded_alignment = @ctz(@as(u32, 4));1875 const encoded_alignment = @ctz(@as(u32, 4));
1876
1879 if (tag_values.len == 0) {1877 if (tag_values.len == 0) {
1880 // Then it's auto-numbered and therefore a direct table lookup.1878 // Auto-numbered, therefore a direct table lookup.
1879
1880 try code.ensureUnusedCapacity(
1881 gpa,
1882 6 * @sizeOf(std.wasm.Opcode) +
1883 7 * 5 + // appendReservedUleb32
1884 1 * 6, // appendReservedI32Const
1885 );
1886
1887 appendReservedUleb32(code, 0); // no locals
1888
1881 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1889 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1882 appendReservedUleb32(code, 0);1890 appendReservedUleb32(code, 0);
18831891
...@@ -1894,78 +1902,91 @@ fn emitTagNameFunction(...@@ -1894,78 +1902,91 @@ fn emitTagNameFunction(
1894 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));1902 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));
1895 appendReservedUleb32(code, encoded_alignment);1903 appendReservedUleb32(code, encoded_alignment);
1896 appendReservedUleb32(code, 0);1904 appendReservedUleb32(code, 0);
1897 } else {
1898 const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu);
1899 const outer_block_type: std.wasm.BlockType = switch (int_info.bits) {
1900 0...32 => .i32,
1901 33...64 => .i64,
1902 else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}),
1903 };
19041905
1905 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1906 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1906 appendReservedUleb32(code, 0);
19071907
1908 // Outer block that computes table offset.1908 return;
1909 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));1909 }
1910 code.appendAssumeCapacity(@intFromEnum(outer_block_type));
1911
1912 for (tag_values, 0..) |tag_value, tag_index| {
1913 // block for this if case
1914 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));
1915 code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1916
1917 // Tag value whose name should be returned.
1918 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1919 appendReservedUleb32(code, 1);
1920
1921 const val: Zcu.Value = .fromInterned(tag_value);
1922 switch (outer_block_type) {
1923 .i32 => {
1924 const x: u32 = switch (int_info.signedness) {
1925 .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))),
1926 .unsigned => @intCast(val.toUnsignedInt(zcu)),
1927 };
1928 appendReservedI32Const(code, x);
1929 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne));
1930 },
1931 .i64 => {
1932 const x: u64 = switch (int_info.signedness) {
1933 .signed => @bitCast(val.toSignedInt(zcu)),
1934 .unsigned => val.toUnsignedInt(zcu),
1935 };
1936 appendReservedI64Const(code, x);
1937 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne));
1938 },
1939 else => unreachable,
1940 }
19411910
1942 // if they're not equal, break out of current branch1911 const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu);
1943 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if));1912 const outer_block_type: std.wasm.BlockType = switch (int_info.bits) {
1944 appendReservedUleb32(code, 0);1913 0...32 => .i32,
1914 33...64 => .i64,
1915 else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}),
1916 };
19451917
1946 // Put the table offset of the result on the stack.1918 try code.ensureUnusedCapacity(
1947 appendReservedI32Const(code, @intCast(tag_index * slice_abi_size));1919 gpa,
1920 (7 + tag_values.len * 6) * @sizeOf(std.wasm.Opcode) +
1921 (1 + tag_values.len * 1) * @sizeOf(std.wasm.BlockType) +
1922 (6 + tag_values.len * 3) * 5 + // appendReservedUleb32
1923 (tag_values.len * 2) * 11, // appendReservedI32Const / appendReservedI64Const
1924 );
19481925
1949 // break outside blocks1926 appendReservedUleb32(code, 0); // no locals
1950 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));
1951 appendReservedUleb32(code, 1);
19521927
1953 // end the block for this case1928 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1954 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1929 appendReservedUleb32(code, 0);
1955 }
1956 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable"));
1957 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
19581930
1959 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));1931 // Outer block that computes table offset.
1960 appendReservedUleb32(code, encoded_alignment);1932 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));
1961 appendReservedUleb32(code, table_base_addr + table_index * 8);1933 code.appendAssumeCapacity(@intFromEnum(outer_block_type));
19621934
1963 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));1935 for (tag_values, 0..) |tag_value, tag_index| {
1964 appendReservedUleb32(code, encoded_alignment);1936 // block for this if case
1937 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));
1938 code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));
1939
1940 // Tag value whose name should be returned.
1941 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1942 appendReservedUleb32(code, 1);
1943
1944 const val: Zcu.Value = .fromInterned(tag_value);
1945 switch (outer_block_type) {
1946 .i32 => {
1947 const x: u32 = switch (int_info.signedness) {
1948 .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))),
1949 .unsigned => @intCast(val.toUnsignedInt(zcu)),
1950 };
1951 appendReservedI32Const(code, x);
1952 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne));
1953 },
1954 .i64 => {
1955 const x: u64 = switch (int_info.signedness) {
1956 .signed => @bitCast(val.toSignedInt(zcu)),
1957 .unsigned => val.toUnsignedInt(zcu),
1958 };
1959 appendReservedI64Const(code, x);
1960 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne));
1961 },
1962 else => unreachable,
1963 }
1964
1965 // if they're not equal, break out of current branch
1966 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if));
1965 appendReservedUleb32(code, 0);1967 appendReservedUleb32(code, 0);
1968
1969 // Put the table offset of the result on the stack.
1970 appendReservedI32Const(code, @intCast(tag_index * slice_abi_size));
1971
1972 // break outside blocks
1973 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));
1974 appendReservedUleb32(code, 1);
1975
1976 // end the block for this case
1977 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1966 }1978 }
1979 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable"));
1980 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1981
1982 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));
1983 appendReservedUleb32(code, encoded_alignment);
1984 appendReservedUleb32(code, table_base_addr + table_index * 8);
1985
1986 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));
1987 appendReservedUleb32(code, encoded_alignment);
1988 appendReservedUleb32(code, 0);
19671989
1968 // End of the function body
1969 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1990 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1970}1991}
19711992