authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-14 19:33:36-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
logc2a918b7a38fedb71814c295b41751ac1a5ea0f4
tree2e396acd15f69ca0659dcede1d1ce661174075cd
parentf7f88787165e8417fa524f0781f6cd139864f12b

wasm linker: implement __wasm_init_tls synthetic function


1 files changed, 73 insertions(+), 7 deletions(-)

src/link/Wasm/Flush.zig+73-7
......@@ -297,6 +297,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
297297 // also notices threadlocal globals from Zcu code.
298298 if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{});
299299 try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{});
300 try wasm.globals.put(gpa, .__tls_base, {});
300301 }
301302
302303 try wasm.tables.ensureUnusedCapacity(gpa, 1);
......@@ -642,7 +643,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
642643 section_index += 1;
643644 }
644645
645 // Global section (used to emit stack pointer)
646 // Global section.
646647 const globals_len: u32 = @intCast(wasm.globals.entries.len);
647648 if (globals_len > 0) {
648649 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
......@@ -653,9 +654,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
653654 .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base),
654655 .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end),
655656 .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer),
656 .__tls_align => @panic("TODO"),
657 .__tls_base => @panic("TODO"),
658 .__tls_size => @panic("TODO"),
657 .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)),
658 .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?),
659 .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?),
659660 .object_global => |i| {
660661 const global = i.ptr(wasm);
661662 try binary_bytes.appendSlice(gpa, &.{
......@@ -664,8 +665,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
664665 });
665666 try emitExpr(wasm, binary_bytes, global.expr);
666667 },
667 .nav_exe => @panic("TODO"),
668 .nav_obj => @panic("TODO"),
668 .nav_exe => unreachable, // Zig source code currently cannot represent this.
669 .nav_obj => unreachable, // Zig source code currently cannot represent this.
669670 }
670671 }
671672
......@@ -772,7 +773,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
772773 defer replaceSize(binary_bytes, code_start);
773774 try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs);
774775 },
775 .__wasm_init_tls => @panic("TODO lower __wasm_init_tls "),
776 .__wasm_init_tls => {
777 const code_start = try reserveSize(gpa, binary_bytes);
778 defer replaceSize(binary_bytes, code_start);
779 try emitInitTlsFunction(wasm, binary_bytes);
780 },
776781 .object_function => |i| {
777782 const ptr = i.ptr(wasm);
778783 const code = ptr.code.slice(wasm);
......@@ -1907,6 +1912,67 @@ fn emitInitMemoryFunction(
19071912 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
19081913}
19091914
1915fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1916 const comp = wasm.base.comp;
1917 const gpa = comp.gpa;
1918
1919 assert(comp.config.shared_memory);
1920
1921 try bytes.ensureUnusedCapacity(gpa, 5 * 10 + 8);
1922
1923 appendReservedUleb32(bytes, 0); // no locals
1924
1925 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
1926 // TLS segment is always the first one due to how we sort the data segments.
1927 const data_segments = wasm.flush_buffer.data_segments.keys();
1928 if (data_segments.len > 0 and data_segments[0].isTls(wasm)) {
1929 const start_addr = wasm.flush_buffer.data_segments.values()[0];
1930 const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr;
1931 const group_size = end_addr - start_addr;
1932 const data_segment_index = 0;
1933
1934 const param_local: u32 = 0;
1935
1936 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1937 appendReservedUleb32(bytes, param_local);
1938
1939 const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?);
1940 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
1941 appendReservedUleb32(bytes, @intFromEnum(tls_base_global_index));
1942
1943 // load stack values for the bulk-memory operation
1944 {
1945 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));
1946 appendReservedUleb32(bytes, param_local);
1947
1948 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1949 appendReservedUleb32(bytes, 0); //segment offset
1950
1951 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1952 appendReservedUleb32(bytes, group_size); //segment offset
1953 }
1954
1955 // perform the bulk-memory operation to initialize the data segment
1956 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
1957 appendReservedUleb32(bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init));
1958 // segment immediate
1959 appendReservedUleb32(bytes, data_segment_index);
1960 // memory index immediate (always 0)
1961 appendReservedUleb32(bytes, 0);
1962 }
1963
1964 // If we have to perform any TLS relocations, call the corresponding function
1965 // which performs all runtime TLS relocations. This is a synthetic function,
1966 // generated by the linker.
1967 if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| {
1968 const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index));
1969 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
1970 appendReservedUleb32(bytes, @intFromEnum(output_function_index));
1971 }
1972
1973 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));
1974}
1975
19101976fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void {
19111977 const header_offset = try reserveVecSectionHeader(gpa, bytes);
19121978 replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i));