authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-19 21:55:30+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-19 21:55:30+02:00
logcec1e973b626ae6bd8ce1891537926e59c814463
treec0cfb76b2b4c835645dbd405eeb4d83debcd85e7
parent0aacb6369fdf3762b7ab6069955684abb63f1459
parent142dbc7b82c741692dd17f8c0455203826342bba
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16439 from Luukdegram/wasm-linker

wasm-linker: finish shared-memory & TLS implementation

7 files changed, 217 insertions(+), 63 deletions(-)

lib/std/Build/Step/CheckObject.zig+14-3
......@@ -1012,6 +1012,10 @@ const WasmDumper = struct {
10121012 const start = try std.leb.readULEB128(u32, reader);
10131013 try writer.print("\nstart {d}\n", .{start});
10141014 },
1015 .data_count => {
1016 const count = try std.leb.readULEB128(u32, reader);
1017 try writer.print("\ncount {d}\n", .{count});
1018 },
10151019 else => {}, // skip unknown sections
10161020 }
10171021 }
......@@ -1143,9 +1147,16 @@ const WasmDumper = struct {
11431147 .data => {
11441148 var i: u32 = 0;
11451149 while (i < entries) : (i += 1) {
1146 const index = try std.leb.readULEB128(u32, reader);
1150 const flags = try std.leb.readULEB128(u32, reader);
1151 const index = if (flags & 0x02 != 0)
1152 try std.leb.readULEB128(u32, reader)
1153 else
1154 0;
11471155 try writer.print("memory index 0x{x}\n", .{index});
1148 try parseDumpInit(step, reader, writer);
1156 if (flags == 0) {
1157 try parseDumpInit(step, reader, writer);
1158 }
1159
11491160 const size = try std.leb.readULEB128(u32, reader);
11501161 try writer.print("size {d}\n", .{size});
11511162 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
......@@ -1174,7 +1185,7 @@ const WasmDumper = struct {
11741185 }
11751186
11761187 fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
1177 const byte = try std.leb.readULEB128(u8, reader);
1188 const byte = try reader.readByte();
11781189 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
11791190 return step.fail("invalid wasm opcode '{d}'", .{byte});
11801191 };
lib/std/Build/Step/Compile.zig+4
......@@ -65,6 +65,7 @@ sanitize_thread: bool,
6565rdynamic: bool,
6666dwarf_format: ?std.dwarf.Format = null,
6767import_memory: bool = false,
68export_memory: bool = false,
6869/// For WebAssembly targets, this will allow for undefined symbols to
6970/// be imported from the host environment.
7071import_symbols: bool = false,
......@@ -1662,6 +1663,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
16621663 if (self.import_memory) {
16631664 try zig_args.append("--import-memory");
16641665 }
1666 if (self.export_memory) {
1667 try zig_args.append("--export-memory");
1668 }
16651669 if (self.import_symbols) {
16661670 try zig_args.append("--import-symbols");
16671671 }
src/link/Wasm.zig+81-51
......@@ -125,6 +125,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{},
125125/// List of initialization functions. These must be called in order of priority
126126/// by the (synthetic) __wasm_call_ctors function.
127127init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},
128/// Index to a function defining the entry of the wasm file
129entry: ?u32 = null,
128130
129131/// Indirect function table, used to call function pointers
130132/// When this is non-zero, we must emit a table entry,
......@@ -409,7 +411,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
409411 },
410412 );
411413 } else {
412 symbol.index = @as(u32, @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len));
414 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
413415 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
414416 const global = try wasm_bin.wasm_globals.addOne(allocator);
415417 global.* = .{
......@@ -432,7 +434,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
432434 };
433435 if (options.output_mode == .Obj or options.import_table) {
434436 symbol.setUndefined(true);
435 symbol.index = @as(u32, @intCast(wasm_bin.imported_tables_count));
437 symbol.index = @intCast(wasm_bin.imported_tables_count);
436438 wasm_bin.imported_tables_count += 1;
437439 try wasm_bin.imports.put(allocator, loc, .{
438440 .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
......@@ -466,19 +468,34 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
466468 const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);
467469 const symbol = loc.getSymbol(wasm_bin);
468470 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
471 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
472 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
473 .global_type = .{ .valtype = .i32, .mutable = true },
474 .init = .{ .i32_const = undefined },
475 });
469476 }
470477 {
471478 const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);
472479 const symbol = loc.getSymbol(wasm_bin);
473480 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
481 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
482 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
483 .global_type = .{ .valtype = .i32, .mutable = false },
484 .init = .{ .i32_const = undefined },
485 });
474486 }
475487 {
476488 const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);
477489 const symbol = loc.getSymbol(wasm_bin);
478490 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
491 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
492 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
493 .global_type = .{ .valtype = .i32, .mutable = false },
494 .init = .{ .i32_const = undefined },
495 });
479496 }
480497 {
481 const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function);
498 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
482499 const symbol = loc.getSymbol(wasm_bin);
483500 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
484501 }
......@@ -844,6 +861,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
844861 }
845862}
846863
864/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
865fn writeI32Const(writer: anytype, val: u32) !void {
866 try writer.writeByte(std.wasm.opcode(.i32_const));
867 try leb.writeILEB128(writer, @as(i32, @bitCast(val)));
868}
869
847870fn setupInitMemoryFunction(wasm: *Wasm) !void {
848871 // Passive segments are used to avoid memory being reinitialized on each
849872 // thread's instantiation. These passive segments are initialized and
......@@ -881,12 +904,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
881904 try writer.writeByte(std.wasm.block_empty); // block type
882905
883906 // atomically check
884 try writer.writeByte(std.wasm.opcode(.i32_const));
885 try leb.writeULEB128(writer, flag_address);
886 try writer.writeByte(std.wasm.opcode(.i32_const));
887 try leb.writeULEB128(writer, @as(u32, 0));
888 try writer.writeByte(std.wasm.opcode(.i32_const));
889 try leb.writeULEB128(writer, @as(u32, 1));
907 try writeI32Const(writer, flag_address);
908 try writeI32Const(writer, 0);
909 try writeI32Const(writer, 1);
890910 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
891911 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
892912 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
......@@ -910,24 +930,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
910930 // For non-BSS segments we do a memory.init. Both these
911931 // instructions take as their first argument the destination
912932 // address.
913 try writer.writeByte(std.wasm.opcode(.i32_const));
914 try leb.writeULEB128(writer, segment.offset);
933 try writeI32Const(writer, segment.offset);
915934
916935 if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
917936 // When we initialize the TLS segment we also set the `__tls_base`
918937 // global. This allows the runtime to use this static copy of the
919938 // TLS data for the first/main thread.
920 try writer.writeByte(std.wasm.opcode(.i32_const));
921 try leb.writeULEB128(writer, segment.offset);
939 try writeI32Const(writer, segment.offset);
922940 try writer.writeByte(std.wasm.opcode(.global_set));
923941 const loc = wasm.findGlobalSymbol("__tls_base").?;
924942 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
925943 }
926944
927 try writer.writeByte(std.wasm.opcode(.i32_const));
928 try leb.writeULEB128(writer, @as(u32, 0));
929 try writer.writeByte(std.wasm.opcode(.i32_const));
930 try leb.writeULEB128(writer, segment.size);
945 try writeI32Const(writer, 0);
946 try writeI32Const(writer, segment.size);
931947 try writer.writeByte(std.wasm.opcode(.misc_prefix));
932948 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {
933949 // fill bss segment with zeroes
......@@ -943,18 +959,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
943959
944960 if (wasm.base.options.shared_memory) {
945961 // we set the init memory flag to value '2'
946 try writer.writeByte(std.wasm.opcode(.i32_const));
947 try leb.writeULEB128(writer, flag_address);
948 try writer.writeByte(std.wasm.opcode(.i32_const));
949 try leb.writeULEB128(writer, @as(u32, 2));
962 try writeI32Const(writer, flag_address);
963 try writeI32Const(writer, 2);
950964 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
951965 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
952966 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
953967 try leb.writeULEB128(writer, @as(u32, 0)); // offset
954968
955969 // notify any waiters for segment initialization completion
956 try writer.writeByte(std.wasm.opcode(.i32_const));
957 try leb.writeULEB128(writer, flag_address);
970 try writeI32Const(writer, flag_address);
958971 try writer.writeByte(std.wasm.opcode(.i32_const));
959972 try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters
960973 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
......@@ -969,12 +982,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
969982
970983 // wait for thread to initialize memory segments
971984 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
972 try writer.writeByte(std.wasm.opcode(.i32_const));
973 try leb.writeULEB128(writer, flag_address);
974 try writer.writeByte(std.wasm.opcode(.i32_const));
975 try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value
976 try writer.writeByte(std.wasm.opcode(.i32_const));
977 try leb.writeILEB128(writer, @as(i32, -1)); // timeout
985 try writeI32Const(writer, flag_address);
986 try writeI32Const(writer, 1); // expected flag value
987 try writer.writeByte(std.wasm.opcode(.i64_const));
988 try leb.writeILEB128(writer, @as(i64, -1)); // timeout
978989 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
979990 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
980991 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
......@@ -2154,7 +2165,14 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {
21542165 const segment_name = segment_info[symbol.index].outputName(merge_segment);
21552166 const segment_index = wasm.data_segments.get(segment_name).?;
21562167 const segment = wasm.segments.items[segment_index];
2157 symbol.virtual_address = atom.offset + segment.offset;
2168
2169 // TLS symbols have their virtual address set relative to their own TLS segment,
2170 // rather than the entire Data section.
2171 if (symbol.hasFlag(.WASM_SYM_TLS)) {
2172 symbol.virtual_address = atom.offset;
2173 } else {
2174 symbol.virtual_address = atom.offset + segment.offset;
2175 }
21582176 }
21592177}
21602178
......@@ -2168,7 +2186,7 @@ fn sortDataSegments(wasm: *Wasm) !void {
21682186
21692187 const SortContext = struct {
21702188 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
2171 return order(lhs) <= order(rhs);
2189 return order(lhs) < order(rhs);
21722190 }
21732191
21742192 fn order(name: []const u8) u8 {
......@@ -2399,6 +2417,13 @@ pub fn createFunction(
23992417 return loc.index;
24002418}
24012419
2420/// If required, sets the function index in the `start` section.
2421fn setupStartSection(wasm: *Wasm) !void {
2422 if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| {
2423 wasm.entry = loc.getSymbol(wasm).index;
2424 }
2425}
2426
24022427fn initializeTLSFunction(wasm: *Wasm) !void {
24032428 if (!wasm.base.options.shared_memory) return;
24042429
......@@ -2420,7 +2445,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
24202445 try leb.writeULEB128(writer, param_local);
24212446
24222447 const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;
2423 try writer.writeByte(std.wasm.opcode(.global_get));
2448 try writer.writeByte(std.wasm.opcode(.global_set));
24242449 try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);
24252450
24262451 // load stack values for the bulk-memory operation
......@@ -2748,27 +2773,18 @@ fn setupMemory(wasm: *Wasm) !void {
27482773 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
27492774 if (wasm.findGlobalSymbol("__tls_size")) |loc| {
27502775 const sym = loc.getSymbol(wasm);
2751 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2752 try wasm.wasm_globals.append(wasm.base.allocator, .{
2753 .global_type = .{ .valtype = .i32, .mutable = false },
2754 .init = .{ .i32_const = @as(i32, @intCast(segment.size)) },
2755 });
2776 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
27562777 }
27572778 if (wasm.findGlobalSymbol("__tls_align")) |loc| {
27582779 const sym = loc.getSymbol(wasm);
2759 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2760 try wasm.wasm_globals.append(wasm.base.allocator, .{
2761 .global_type = .{ .valtype = .i32, .mutable = false },
2762 .init = .{ .i32_const = @as(i32, @intCast(segment.alignment)) },
2763 });
2780 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment);
27642781 }
27652782 if (wasm.findGlobalSymbol("__tls_base")) |loc| {
27662783 const sym = loc.getSymbol(wasm);
2767 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2768 try wasm.wasm_globals.append(wasm.base.allocator, .{
2769 .global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory },
2770 .init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @as(i32, @intCast(memory_ptr)) },
2771 });
2784 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory)
2785 @as(i32, 0)
2786 else
2787 @as(i32, @intCast(memory_ptr));
27722788 }
27732789 }
27742790
......@@ -3323,6 +3339,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
33233339 try wasm.setupInitMemoryFunction();
33243340 try wasm.setupTLSRelocationsFunction();
33253341 try wasm.initializeTLSFunction();
3342 try wasm.setupStartSection();
33263343 try wasm.setupExports();
33273344 try wasm.writeToFile(enabled_features, emit_features_count, arena);
33283345
......@@ -3460,6 +3477,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
34603477 try wasm.setupInitMemoryFunction();
34613478 try wasm.setupTLSRelocationsFunction();
34623479 try wasm.initializeTLSFunction();
3480 try wasm.setupStartSection();
34633481 try wasm.setupExports();
34643482 try wasm.writeToFile(enabled_features, emit_features_count, arena);
34653483}
......@@ -3520,6 +3538,7 @@ fn writeToFile(
35203538
35213539 // Import section
35223540 const import_memory = wasm.base.options.import_memory or is_obj;
3541 const export_memory = wasm.base.options.export_memory;
35233542 if (wasm.imports.count() != 0 or import_memory) {
35243543 const header_offset = try reserveVecSectionHeader(&binary_bytes);
35253544
......@@ -3622,7 +3641,7 @@ fn writeToFile(
36223641 }
36233642
36243643 // Export section
3625 if (wasm.exports.items.len != 0 or !import_memory) {
3644 if (wasm.exports.items.len != 0 or export_memory) {
36263645 const header_offset = try reserveVecSectionHeader(&binary_bytes);
36273646
36283647 for (wasm.exports.items) |exp| {
......@@ -3633,7 +3652,7 @@ fn writeToFile(
36333652 try leb.writeULEB128(binary_writer, exp.index);
36343653 }
36353654
3636 if (!import_memory) {
3655 if (export_memory) {
36373656 try leb.writeULEB128(binary_writer, @as(u32, @intCast("memory".len)));
36383657 try binary_writer.writeAll("memory");
36393658 try binary_writer.writeByte(std.wasm.externalKind(.memory));
......@@ -3645,11 +3664,22 @@ fn writeToFile(
36453664 header_offset,
36463665 .@"export",
36473666 @as(u32, @intCast(binary_bytes.items.len - header_offset - header_size)),
3648 @as(u32, @intCast(wasm.exports.items.len)) + @intFromBool(!import_memory),
3667 @as(u32, @intCast(wasm.exports.items.len)) + @intFromBool(export_memory),
36493668 );
36503669 section_count += 1;
36513670 }
36523671
3672 if (wasm.entry) |entry_index| {
3673 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3674 try writeVecSectionHeader(
3675 binary_bytes.items,
3676 header_offset,
3677 .start,
3678 @intCast(binary_bytes.items.len - header_offset - header_size),
3679 entry_index,
3680 );
3681 }
3682
36533683 // element section (function table)
36543684 if (wasm.function_table.count() > 0) {
36553685 const header_offset = try reserveVecSectionHeader(&binary_bytes);
......@@ -3683,7 +3713,7 @@ fn writeToFile(
36833713 }
36843714
36853715 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
3686 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and import_memory);
3716 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
36873717 if (data_segments_count != 0 and wasm.base.options.shared_memory) {
36883718 const header_offset = try reserveVecSectionHeader(&binary_bytes);
36893719 try writeVecSectionHeader(
src/link/Wasm/Atom.zig+7-6
......@@ -174,14 +174,14 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
174174 return 0;
175175 }
176176 const va = @as(i64, @intCast(symbol.virtual_address));
177 return @as(u32, @intCast(va + relocation.addend));
177 return @intCast(va + relocation.addend);
178178 },
179179 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
180180 .R_WASM_SECTION_OFFSET_I32 => {
181181 const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?;
182182 const target_atom = wasm_bin.getAtom(target_atom_index);
183 const rel_value = @as(i32, @intCast(target_atom.offset)) + relocation.addend;
184 return @as(u32, @intCast(rel_value));
183 const rel_value: i32 = @intCast(target_atom.offset);
184 return @intCast(rel_value + relocation.addend);
185185 },
186186 .R_WASM_FUNCTION_OFFSET_I32 => {
187187 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
......@@ -189,13 +189,14 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
189189 };
190190 const target_atom = wasm_bin.getAtom(target_atom_index);
191191 const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)
192 const rel_value = @as(i32, @intCast(target_atom.offset + offset)) + relocation.addend;
193 return @as(u32, @intCast(rel_value));
192 const rel_value: i32 = @intCast(target_atom.offset + offset);
193 return @intCast(rel_value + relocation.addend);
194194 },
195195 .R_WASM_MEMORY_ADDR_TLS_SLEB,
196196 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
197197 => {
198 @panic("TODO: Implement TLS relocations");
198 const va: i32 = @intCast(symbol.virtual_address);
199 return @intCast(va + relocation.addend);
199200 },
200201 }
201202}
src/link/Wasm/Object.zig+8-3
......@@ -353,9 +353,14 @@ fn Parser(comptime ReaderType: type) type {
353353 var debug_names = std.ArrayList(u8).init(gpa);
354354
355355 errdefer {
356 while (relocatable_data.popOrNull()) |rel_data| {
357 gpa.free(rel_data.data[0..rel_data.size]);
358 } else relocatable_data.deinit();
356 // only free the inner contents of relocatable_data if we didn't
357 // assign it to the object yet.
358 if (parser.object.relocatable_data.len == 0) {
359 for (relocatable_data.items) |rel_data| {
360 gpa.free(rel_data.data[0..rel_data.size]);
361 }
362 relocatable_data.deinit();
363 }
359364 gpa.free(debug_names.items);
360365 debug_names.deinit();
361366 }
test/link/wasm/shared-memory/build.zig created+98
......@@ -0,0 +1,98 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
14 {
15 const lib = b.addSharedLibrary(.{
16 .name = "lib",
17 .root_source_file = .{ .path = "lib.zig" },
18 .target = .{
19 .cpu_arch = .wasm32,
20 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
21 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
22 .os_tag = .freestanding,
23 },
24 .optimize = optimize_mode,
25 });
26 lib.use_lld = false;
27 lib.strip = false;
28 lib.import_memory = true;
29 lib.export_memory = true;
30 lib.shared_memory = true;
31 lib.max_memory = 67108864;
32 lib.single_threaded = false;
33 lib.export_symbol_names = &.{"foo"};
34
35 const check_lib = lib.checkObject();
36
37 check_lib.checkStart("Section import");
38 check_lib.checkNext("entries 1");
39 check_lib.checkNext("module env");
40 check_lib.checkNext("name memory"); // ensure we are importing memory
41
42 check_lib.checkStart("Section export");
43 check_lib.checkNext("entries 2");
44 check_lib.checkNext("name memory"); // ensure we also export memory again
45
46 // This section *must* be emit as the start function is set to the index
47 // of __wasm_init_memory
48 // release modes will have the TLS segment optimized out in our test-case.
49 // This means we won't have __wasm_init_memory in such case, and therefore
50 // should also not have a section "start"
51 if (optimize_mode == .Debug) {
52 check_lib.checkStart("Section start");
53 }
54
55 // This section is only and *must* be emit when shared-memory is enabled
56 // release modes will have the TLS segment optimized out in our test-case.
57 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");
59 check_lib.checkNext("count 3");
60 }
61
62 check_lib.checkStart("Section custom");
63 check_lib.checkNext("name name");
64 check_lib.checkNext("type function");
65 if (optimize_mode == .Debug) {
66 check_lib.checkNext("name __wasm_init_memory");
67 }
68 check_lib.checkNext("name __wasm_init_tls");
69 check_lib.checkNext("type global");
70
71 // In debug mode the symbol __tls_base is resolved to an undefined symbol
72 // from the object file, hence its placement differs than in release modes
73 // where the entire tls segment is optimized away, and tls_base will have
74 // its original position.
75 if (optimize_mode == .Debug) {
76 check_lib.checkNext("name __tls_size");
77 check_lib.checkNext("name __tls_align");
78 check_lib.checkNext("name __tls_base");
79 } else {
80 check_lib.checkNext("name __tls_base");
81 check_lib.checkNext("name __tls_size");
82 check_lib.checkNext("name __tls_align");
83 }
84
85 check_lib.checkNext("type data_segment");
86 if (optimize_mode == .Debug) {
87 check_lib.checkNext("names 3");
88 check_lib.checkNext("index 0");
89 check_lib.checkNext("name .rodata");
90 check_lib.checkNext("index 1");
91 check_lib.checkNext("name .bss");
92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
94 }
95
96 test_step.dependOn(&check_lib.step);
97 }
98}
test/link/wasm/shared-memory/lib.zig created+5
......@@ -0,0 +1,5 @@
1threadlocal var some_tls_global: u32 = 1;
2
3export fn foo() void {
4 some_tls_global = 2;
5}