| ... | @@ -297,6 +297,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -297,6 +297,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 297 | // also notices threadlocal globals from Zcu code. | 297 | // also notices threadlocal globals from Zcu code. |
| 298 | if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{}); | 298 | if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{}); |
| 299 | try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{}); | 299 | try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{}); |
| | 300 | try wasm.globals.put(gpa, .__tls_base, {}); |
| 300 | } | 301 | } |
| 301 | | 302 | |
| 302 | try wasm.tables.ensureUnusedCapacity(gpa, 1); | 303 | try wasm.tables.ensureUnusedCapacity(gpa, 1); |
| ... | @@ -642,7 +643,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -642,7 +643,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 642 | section_index += 1; | 643 | section_index += 1; |
| 643 | } | 644 | } |
| 644 | | 645 | |
| 645 | // Global section (used to emit stack pointer) | 646 | // Global section. |
| 646 | const globals_len: u32 = @intCast(wasm.globals.entries.len); | 647 | const globals_len: u32 = @intCast(wasm.globals.entries.len); |
| 647 | if (globals_len > 0) { | 648 | if (globals_len > 0) { |
| 648 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | 649 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| ... | @@ -653,9 +654,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -653,9 +654,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 653 | .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), | 654 | .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), |
| 654 | .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), | 655 | .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), |
| 655 | .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer), | 656 | .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer), |
| 656 | .__tls_align => @panic("TODO"), | 657 | .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)), |
| 657 | .__tls_base => @panic("TODO"), | 658 | .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?), |
| 658 | .__tls_size => @panic("TODO"), | 659 | .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?), |
| 659 | .object_global => |i| { | 660 | .object_global => |i| { |
| 660 | const global = i.ptr(wasm); | 661 | const global = i.ptr(wasm); |
| 661 | try binary_bytes.appendSlice(gpa, &.{ | 662 | try binary_bytes.appendSlice(gpa, &.{ |
| ... | @@ -664,8 +665,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -664,8 +665,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 664 | }); | 665 | }); |
| 665 | try emitExpr(wasm, binary_bytes, global.expr); | 666 | try emitExpr(wasm, binary_bytes, global.expr); |
| 666 | }, | 667 | }, |
| 667 | .nav_exe => @panic("TODO"), | 668 | .nav_exe => unreachable, // Zig source code currently cannot represent this. |
| 668 | .nav_obj => @panic("TODO"), | 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,7 +773,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 772 | defer replaceSize(binary_bytes, code_start); | 773 | defer replaceSize(binary_bytes, code_start); |
| 773 | try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs); | 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 | .object_function => |i| { | 781 | .object_function => |i| { |
| 777 | const ptr = i.ptr(wasm); | 782 | const ptr = i.ptr(wasm); |
| 778 | const code = ptr.code.slice(wasm); | 783 | const code = ptr.code.slice(wasm); |
| ... | @@ -1907,6 +1912,67 @@ fn emitInitMemoryFunction( | ... | @@ -1907,6 +1912,67 @@ fn emitInitMemoryFunction( |
| 1907 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | 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 | fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { | 1976 | fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { |
| 1911 | const header_offset = try reserveVecSectionHeader(gpa, bytes); | 1977 | const header_offset = try reserveVecSectionHeader(gpa, bytes); |
| 1912 | replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); | 1978 | replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); |