authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-16 16:58:47+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-19 17:22:46+02:00
log2672f7d9e8bcfc62b16a7073bddf330df4701762
tree022b50ede28e4941fc6c861a2b9f3841043c3853
parent376e1b4603e70f1407862fde0180cfe45ca56e73
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: shared-memory fixes

Implements the `start` section which will execute a given function at startup of the program. After function execution, the _start function will be called by the runtime. In the case of shared-memory we set this section to the function `__wasm_init_memory` which will initialize all memory on startup. This also fixes the above mentioned function to ensure we correctly lower the i32 values. Lastly, this fixes a typo where we would retrieve a global, instead of setting its value.

1 files changed, 46 insertions(+), 30 deletions(-)

src/link/Wasm.zig+46-30
......@@ -124,6 +124,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{},
124124/// List of initialization functions. These must be called in order of priority
125125/// by the (synthetic) __wasm_call_ctors function.
126126init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},
127/// Index to a function defining the entry of the wasm file
128entry: ?u32 = null,
127129
128130/// Indirect function table, used to call function pointers
129131/// 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
477479 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
478480 }
479481 {
480 const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function);
482 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
481483 const symbol = loc.getSymbol(wasm_bin);
482484 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
483485 }
......@@ -843,6 +845,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
843845 }
844846}
845847
848/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
849fn 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
846854fn setupInitMemoryFunction(wasm: *Wasm) !void {
847855 // Passive segments are used to avoid memory being reinitialized on each
848856 // thread's instantiation. These passive segments are initialized and
......@@ -880,12 +888,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
880888 try writer.writeByte(std.wasm.block_empty); // block type
881889
882890 // atomically check
883 try writer.writeByte(std.wasm.opcode(.i32_const));
884 try leb.writeULEB128(writer, flag_address);
885 try writer.writeByte(std.wasm.opcode(.i32_const));
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));
891 try writeI32Const(writer, flag_address);
892 try writeI32Const(writer, 0);
893 try writeI32Const(writer, 1);
889894 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
890895 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
891896 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
......@@ -909,24 +914,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
909914 // For non-BSS segments we do a memory.init. Both these
910915 // instructions take as their first argument the destination
911916 // address.
912 try writer.writeByte(std.wasm.opcode(.i32_const));
913 try leb.writeULEB128(writer, segment.offset);
917 try writeI32Const(writer, segment.offset);
914918
915919 if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
916920 // When we initialize the TLS segment we also set the `__tls_base`
917921 // global. This allows the runtime to use this static copy of the
918922 // TLS data for the first/main thread.
919 try writer.writeByte(std.wasm.opcode(.i32_const));
920 try leb.writeULEB128(writer, segment.offset);
923 try writeI32Const(writer, segment.offset);
921924 try writer.writeByte(std.wasm.opcode(.global_set));
922925 const loc = wasm.findGlobalSymbol("__tls_base").?;
923926 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
924927 }
925928
926 try writer.writeByte(std.wasm.opcode(.i32_const));
927 try leb.writeULEB128(writer, @as(u32, 0));
928 try writer.writeByte(std.wasm.opcode(.i32_const));
929 try leb.writeULEB128(writer, segment.size);
929 try writeI32Const(writer, 0);
930 try writeI32Const(writer, segment.size);
930931 try writer.writeByte(std.wasm.opcode(.misc_prefix));
931932 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {
932933 // fill bss segment with zeroes
......@@ -942,18 +943,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
942943
943944 if (wasm.base.options.shared_memory) {
944945 // we set the init memory flag to value '2'
945 try writer.writeByte(std.wasm.opcode(.i32_const));
946 try leb.writeULEB128(writer, flag_address);
947 try writer.writeByte(std.wasm.opcode(.i32_const));
948 try leb.writeULEB128(writer, @as(u32, 2));
946 try writeI32Const(writer, flag_address);
947 try writeI32Const(writer, 2);
949948 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
950949 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
951950 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
952951 try leb.writeULEB128(writer, @as(u32, 0)); // offset
953952
954953 // notify any waiters for segment initialization completion
955 try writer.writeByte(std.wasm.opcode(.i32_const));
956 try leb.writeULEB128(writer, flag_address);
954 try writeI32Const(writer, flag_address);
957955 try writer.writeByte(std.wasm.opcode(.i32_const));
958956 try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters
959957 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
......@@ -968,12 +966,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
968966
969967 // wait for thread to initialize memory segments
970968 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
971 try writer.writeByte(std.wasm.opcode(.i32_const));
972 try leb.writeULEB128(writer, flag_address);
973 try writer.writeByte(std.wasm.opcode(.i32_const));
974 try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value
975 try writer.writeByte(std.wasm.opcode(.i32_const));
976 try leb.writeILEB128(writer, @as(i32, -1)); // timeout
969 try writeI32Const(writer, flag_address);
970 try writeI32Const(writer, 1); // expected flag value
971 try writer.writeByte(std.wasm.opcode(.i64_const));
972 try leb.writeILEB128(writer, @as(i64, -1)); // timeout
977973 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
978974 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
979975 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
......@@ -2174,7 +2170,7 @@ fn sortDataSegments(wasm: *Wasm) !void {
21742170
21752171 const SortContext = struct {
21762172 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
2177 return order(lhs) <= order(rhs);
2173 return order(lhs) < order(rhs);
21782174 }
21792175
21802176 fn order(name: []const u8) u8 {
......@@ -2405,6 +2401,13 @@ pub fn createFunction(
24052401 return loc.index;
24062402}
24072403
2404/// If required, sets the function index in the `start` section.
2405fn setupStartSection(wasm: *Wasm) !void {
2406 if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| {
2407 wasm.entry = loc.getSymbol(wasm).index;
2408 }
2409}
2410
24082411fn initializeTLSFunction(wasm: *Wasm) !void {
24092412 if (!wasm.base.options.shared_memory) return;
24102413
......@@ -2426,7 +2429,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
24262429 try leb.writeULEB128(writer, param_local);
24272430
24282431 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));
24302433 try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);
24312434
24322435 // load stack values for the bulk-memory operation
......@@ -3329,6 +3332,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
33293332 try wasm.setupInitMemoryFunction();
33303333 try wasm.setupTLSRelocationsFunction();
33313334 try wasm.initializeTLSFunction();
3335 try wasm.setupStartSection();
33323336 try wasm.setupExports();
33333337 try wasm.writeToFile(enabled_features, emit_features_count, arena);
33343338
......@@ -3466,6 +3470,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
34663470 try wasm.setupInitMemoryFunction();
34673471 try wasm.setupTLSRelocationsFunction();
34683472 try wasm.initializeTLSFunction();
3473 try wasm.setupStartSection();
34693474 try wasm.setupExports();
34703475 try wasm.writeToFile(enabled_features, emit_features_count, arena);
34713476}
......@@ -3657,6 +3662,17 @@ fn writeToFile(
36573662 section_count += 1;
36583663 }
36593664
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
36603676 // element section (function table)
36613677 if (wasm.function_table.count() > 0) {
36623678 const header_offset = try reserveVecSectionHeader(&binary_bytes);
......@@ -3690,7 +3706,7 @@ fn writeToFile(
36903706 }
36913707
36923708 // 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);
36943710 if (data_segments_count != 0 and wasm.base.options.shared_memory) {
36953711 const header_offset = try reserveVecSectionHeader(&binary_bytes);
36963712 try writeVecSectionHeader(