| ... | @@ -124,6 +124,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{}, | ... | @@ -124,6 +124,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{}, |
| 124 | /// List of initialization functions. These must be called in order of priority | 124 | /// List of initialization functions. These must be called in order of priority |
| 125 | /// by the (synthetic) __wasm_call_ctors function. | 125 | /// by the (synthetic) __wasm_call_ctors function. |
| 126 | init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{}, | 126 | init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{}, |
| | 127 | /// Index to a function defining the entry of the wasm file |
| | 128 | entry: ?u32 = null, |
| 127 | | 129 | |
| 128 | /// Indirect function table, used to call function pointers | 130 | /// Indirect function table, used to call function pointers |
| 129 | /// When this is non-zero, we must emit a table entry, | 131 | /// When this is non-zero, we must emit a table entry, |
| ... | @@ -477,7 +479,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -477,7 +479,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 477 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 479 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 478 | } | 480 | } |
| 479 | { | 481 | { |
| 480 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function); | 482 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function); |
| 481 | const symbol = loc.getSymbol(wasm_bin); | 483 | const symbol = loc.getSymbol(wasm_bin); |
| 482 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 484 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 483 | } | 485 | } |
| ... | @@ -843,6 +845,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { | ... | @@ -843,6 +845,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 843 | } | 845 | } |
| 844 | } | 846 | } |
| 845 | | 847 | |
| | 848 | /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. |
| | 849 | fn writeI32Const(writer: anytype, val: u32) !void { |
| | 850 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 851 | try leb.writeILEB128(writer, @as(i32, @bitCast(val))); |
| | 852 | } |
| | 853 | |
| 846 | fn setupInitMemoryFunction(wasm: *Wasm) !void { | 854 | fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 847 | // Passive segments are used to avoid memory being reinitialized on each | 855 | // Passive segments are used to avoid memory being reinitialized on each |
| 848 | // thread's instantiation. These passive segments are initialized and | 856 | // thread's instantiation. These passive segments are initialized and |
| ... | @@ -880,12 +888,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { | ... | @@ -880,12 +888,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 880 | try writer.writeByte(std.wasm.block_empty); // block type | 888 | try writer.writeByte(std.wasm.block_empty); // block type |
| 881 | | 889 | |
| 882 | // atomically check | 890 | // atomically check |
| 883 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 891 | try writeI32Const(writer, flag_address); |
| 884 | try leb.writeULEB128(writer, flag_address); | 892 | try writeI32Const(writer, 0); |
| 885 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 893 | try writeI32Const(writer, 1); |
| 886 | try leb.writeULEB128(writer, @as(u32, 0)); | | |
| 887 | try writer.writeByte(std.wasm.opcode(.i32_const)); | | |
| 888 | try leb.writeULEB128(writer, @as(u32, 1)); | | |
| 889 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | 894 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| 890 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg)); | 895 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg)); |
| 891 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment | 896 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| ... | @@ -909,24 +914,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { | ... | @@ -909,24 +914,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 909 | // For non-BSS segments we do a memory.init. Both these | 914 | // For non-BSS segments we do a memory.init. Both these |
| 910 | // instructions take as their first argument the destination | 915 | // instructions take as their first argument the destination |
| 911 | // address. | 916 | // address. |
| 912 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 917 | try writeI32Const(writer, segment.offset); |
| 913 | try leb.writeULEB128(writer, segment.offset); | | |
| 914 | | 918 | |
| 915 | if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { | 919 | if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { |
| 916 | // When we initialize the TLS segment we also set the `__tls_base` | 920 | // When we initialize the TLS segment we also set the `__tls_base` |
| 917 | // global. This allows the runtime to use this static copy of the | 921 | // global. This allows the runtime to use this static copy of the |
| 918 | // TLS data for the first/main thread. | 922 | // TLS data for the first/main thread. |
| 919 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 923 | try writeI32Const(writer, segment.offset); |
| 920 | try leb.writeULEB128(writer, segment.offset); | | |
| 921 | try writer.writeByte(std.wasm.opcode(.global_set)); | 924 | try writer.writeByte(std.wasm.opcode(.global_set)); |
| 922 | const loc = wasm.findGlobalSymbol("__tls_base").?; | 925 | const loc = wasm.findGlobalSymbol("__tls_base").?; |
| 923 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); | 926 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); |
| 924 | } | 927 | } |
| 925 | | 928 | |
| 926 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 929 | try writeI32Const(writer, 0); |
| 927 | try leb.writeULEB128(writer, @as(u32, 0)); | 930 | try writeI32Const(writer, segment.size); |
| 928 | try writer.writeByte(std.wasm.opcode(.i32_const)); | | |
| 929 | try leb.writeULEB128(writer, segment.size); | | |
| 930 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | 931 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); |
| 931 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) { | 932 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) { |
| 932 | // fill bss segment with zeroes | 933 | // fill bss segment with zeroes |
| ... | @@ -942,18 +943,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { | ... | @@ -942,18 +943,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 942 | | 943 | |
| 943 | if (wasm.base.options.shared_memory) { | 944 | if (wasm.base.options.shared_memory) { |
| 944 | // we set the init memory flag to value '2' | 945 | // we set the init memory flag to value '2' |
| 945 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 946 | try writeI32Const(writer, flag_address); |
| 946 | try leb.writeULEB128(writer, flag_address); | 947 | try writeI32Const(writer, 2); |
| 947 | try writer.writeByte(std.wasm.opcode(.i32_const)); | | |
| 948 | try leb.writeULEB128(writer, @as(u32, 2)); | | |
| 949 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | 948 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| 950 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store)); | 949 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store)); |
| 951 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment | 950 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| 952 | try leb.writeULEB128(writer, @as(u32, 0)); // offset | 951 | try leb.writeULEB128(writer, @as(u32, 0)); // offset |
| 953 | | 952 | |
| 954 | // notify any waiters for segment initialization completion | 953 | // notify any waiters for segment initialization completion |
| 955 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 954 | try writeI32Const(writer, flag_address); |
| 956 | try leb.writeULEB128(writer, flag_address); | | |
| 957 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 955 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| 958 | try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters | 956 | try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters |
| 959 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | 957 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| ... | @@ -968,12 +966,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { | ... | @@ -968,12 +966,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| 968 | | 966 | |
| 969 | // wait for thread to initialize memory segments | 967 | // wait for thread to initialize memory segments |
| 970 | try writer.writeByte(std.wasm.opcode(.end)); // end $wait | 968 | try writer.writeByte(std.wasm.opcode(.end)); // end $wait |
| 971 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 969 | try writeI32Const(writer, flag_address); |
| 972 | try leb.writeULEB128(writer, flag_address); | 970 | try writeI32Const(writer, 1); // expected flag value |
| 973 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 971 | try writer.writeByte(std.wasm.opcode(.i64_const)); |
| 974 | try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value | 972 | try leb.writeILEB128(writer, @as(i64, -1)); // timeout |
| 975 | try writer.writeByte(std.wasm.opcode(.i32_const)); | | |
| 976 | try leb.writeILEB128(writer, @as(i32, -1)); // timeout | | |
| 977 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | 973 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| 978 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32)); | 974 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32)); |
| 979 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment | 975 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| ... | @@ -2174,7 +2170,7 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -2174,7 +2170,7 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 2174 | | 2170 | |
| 2175 | const SortContext = struct { | 2171 | const SortContext = struct { |
| 2176 | fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { | 2172 | fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { |
| 2177 | return order(lhs) <= order(rhs); | 2173 | return order(lhs) < order(rhs); |
| 2178 | } | 2174 | } |
| 2179 | | 2175 | |
| 2180 | fn order(name: []const u8) u8 { | 2176 | fn order(name: []const u8) u8 { |
| ... | @@ -2405,6 +2401,13 @@ pub fn createFunction( | ... | @@ -2405,6 +2401,13 @@ pub fn createFunction( |
| 2405 | return loc.index; | 2401 | return loc.index; |
| 2406 | } | 2402 | } |
| 2407 | | 2403 | |
| | 2404 | /// If required, sets the function index in the `start` section. |
| | 2405 | fn setupStartSection(wasm: *Wasm) !void { |
| | 2406 | if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| { |
| | 2407 | wasm.entry = loc.getSymbol(wasm).index; |
| | 2408 | } |
| | 2409 | } |
| | 2410 | |
| 2408 | fn initializeTLSFunction(wasm: *Wasm) !void { | 2411 | fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2409 | if (!wasm.base.options.shared_memory) return; | 2412 | if (!wasm.base.options.shared_memory) return; |
| 2410 | | 2413 | |
| ... | @@ -2426,7 +2429,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { | ... | @@ -2426,7 +2429,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { |
| 2426 | try leb.writeULEB128(writer, param_local); | 2429 | try leb.writeULEB128(writer, param_local); |
| 2427 | | 2430 | |
| 2428 | const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?; | 2431 | const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?; |
| 2429 | try writer.writeByte(std.wasm.opcode(.global_get)); | 2432 | try writer.writeByte(std.wasm.opcode(.global_set)); |
| 2430 | try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index); | 2433 | try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index); |
| 2431 | | 2434 | |
| 2432 | // load stack values for the bulk-memory operation | 2435 | // load stack values for the bulk-memory operation |
| ... | @@ -3329,6 +3332,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -3329,6 +3332,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 3329 | try wasm.setupInitMemoryFunction(); | 3332 | try wasm.setupInitMemoryFunction(); |
| 3330 | try wasm.setupTLSRelocationsFunction(); | 3333 | try wasm.setupTLSRelocationsFunction(); |
| 3331 | try wasm.initializeTLSFunction(); | 3334 | try wasm.initializeTLSFunction(); |
| | 3335 | try wasm.setupStartSection(); |
| 3332 | try wasm.setupExports(); | 3336 | try wasm.setupExports(); |
| 3333 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 3337 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 3334 | | 3338 | |
| ... | @@ -3466,6 +3470,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -3466,6 +3470,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 3466 | try wasm.setupInitMemoryFunction(); | 3470 | try wasm.setupInitMemoryFunction(); |
| 3467 | try wasm.setupTLSRelocationsFunction(); | 3471 | try wasm.setupTLSRelocationsFunction(); |
| 3468 | try wasm.initializeTLSFunction(); | 3472 | try wasm.initializeTLSFunction(); |
| | 3473 | try wasm.setupStartSection(); |
| 3469 | try wasm.setupExports(); | 3474 | try wasm.setupExports(); |
| 3470 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 3475 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 3471 | } | 3476 | } |
| ... | @@ -3657,6 +3662,17 @@ fn writeToFile( | ... | @@ -3657,6 +3662,17 @@ fn writeToFile( |
| 3657 | section_count += 1; | 3662 | section_count += 1; |
| 3658 | } | 3663 | } |
| 3659 | | 3664 | |
| | 3665 | if (wasm.entry) |entry_index| { |
| | 3666 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| | 3667 | try writeVecSectionHeader( |
| | 3668 | binary_bytes.items, |
| | 3669 | header_offset, |
| | 3670 | .start, |
| | 3671 | @intCast(binary_bytes.items.len - header_offset - header_size), |
| | 3672 | entry_index, |
| | 3673 | ); |
| | 3674 | } |
| | 3675 | |
| 3660 | // element section (function table) | 3676 | // element section (function table) |
| 3661 | if (wasm.function_table.count() > 0) { | 3677 | if (wasm.function_table.count() > 0) { |
| 3662 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3678 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| ... | @@ -3690,7 +3706,7 @@ fn writeToFile( | ... | @@ -3690,7 +3706,7 @@ fn writeToFile( |
| 3690 | } | 3706 | } |
| 3691 | | 3707 | |
| 3692 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. | 3708 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. |
| 3693 | const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and import_memory); | 3709 | const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory); |
| 3694 | if (data_segments_count != 0 and wasm.base.options.shared_memory) { | 3710 | if (data_segments_count != 0 and wasm.base.options.shared_memory) { |
| 3695 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3711 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 3696 | try writeVecSectionHeader( | 3712 | try writeVecSectionHeader( |