| ... | ... | @@ -297,6 +297,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 297 | 297 | // also notices threadlocal globals from Zcu code. |
| 298 | 298 | if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{}); |
| 299 | 299 | try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{}); |
| 300 | try wasm.globals.put(gpa, .__tls_base, {}); |
| 300 | 301 | } |
| 301 | 302 | |
| 302 | 303 | try wasm.tables.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -642,7 +643,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 642 | 643 | section_index += 1; |
| 643 | 644 | } |
| 644 | 645 | |
| 645 | | // Global section (used to emit stack pointer) |
| 646 | // Global section. |
| 646 | 647 | const globals_len: u32 = @intCast(wasm.globals.entries.len); |
| 647 | 648 | if (globals_len > 0) { |
| 648 | 649 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| ... | ... | @@ -653,9 +654,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 653 | 654 | .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), |
| 654 | 655 | .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), |
| 655 | 656 | .__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.?), |
| 659 | 660 | .object_global => |i| { |
| 660 | 661 | const global = i.ptr(wasm); |
| 661 | 662 | try binary_bytes.appendSlice(gpa, &.{ |
| ... | ... | @@ -664,8 +665,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 664 | 665 | }); |
| 665 | 666 | try emitExpr(wasm, binary_bytes, global.expr); |
| 666 | 667 | }, |
| 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. |
| 669 | 670 | } |
| 670 | 671 | } |
| 671 | 672 | |
| ... | ... | @@ -772,7 +773,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 772 | 773 | defer replaceSize(binary_bytes, code_start); |
| 773 | 774 | try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs); |
| 774 | 775 | }, |
| 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 | }, |
| 776 | 781 | .object_function => |i| { |
| 777 | 782 | const ptr = i.ptr(wasm); |
| 778 | 783 | const code = ptr.code.slice(wasm); |
| ... | ... | @@ -1907,6 +1912,67 @@ fn emitInitMemoryFunction( |
| 1907 | 1912 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1908 | 1913 | } |
| 1909 | 1914 | |
| 1915 | fn 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 | |
| 1910 | 1976 | fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { |
| 1911 | 1977 | const header_offset = try reserveVecSectionHeader(gpa, bytes); |
| 1912 | 1978 | replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); |