Commit 1a3304ed23

Luuk de Gram <luuk@degram.dev>
2023-07-17 20:22:25
test/link: add shared-memory test for WebAssembly
1 parent 3fd6e93
Changed files (4)
lib
test
link
wasm
shared-memory
lib/std/Build/Step/CheckObject.zig
@@ -1012,6 +1012,10 @@ const WasmDumper = struct {
                 const start = try std.leb.readULEB128(u32, reader);
                 try writer.print("\nstart {d}\n", .{start});
             },
+            .data_count => {
+                const count = try std.leb.readULEB128(u32, reader);
+                try writer.print("\ncount {d}\n", .{count});
+            },
             else => {}, // skip unknown sections
         }
     }
@@ -1143,9 +1147,16 @@ const WasmDumper = struct {
             .data => {
                 var i: u32 = 0;
                 while (i < entries) : (i += 1) {
-                    const index = try std.leb.readULEB128(u32, reader);
+                    const flags = try std.leb.readULEB128(u32, reader);
+                    const index = if (flags & 0x02 != 0)
+                        try std.leb.readULEB128(u32, reader)
+                    else
+                        0;
                     try writer.print("memory index 0x{x}\n", .{index});
-                    try parseDumpInit(step, reader, writer);
+                    if (flags == 0) {
+                        try parseDumpInit(step, reader, writer);
+                    }
+
                     const size = try std.leb.readULEB128(u32, reader);
                     try writer.print("size {d}\n", .{size});
                     try reader.skipBytes(size, .{}); // we do not care about the content of the segments
@@ -1174,7 +1185,7 @@ const WasmDumper = struct {
     }
 
     fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
-        const byte = try std.leb.readULEB128(u8, reader);
+        const byte = try reader.readByte();
         const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
             return step.fail("invalid wasm opcode '{d}'", .{byte});
         };
lib/std/Build/Step/Compile.zig
@@ -65,6 +65,7 @@ sanitize_thread: bool,
 rdynamic: bool,
 dwarf_format: ?std.dwarf.Format = null,
 import_memory: bool = false,
+export_memory: bool = false,
 /// For WebAssembly targets, this will allow for undefined symbols to
 /// be imported from the host environment.
 import_symbols: bool = false,
@@ -1662,6 +1663,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     if (self.import_memory) {
         try zig_args.append("--import-memory");
     }
+    if (self.export_memory) {
+        try zig_args.append("--export-memory");
+    }
     if (self.import_symbols) {
         try zig_args.append("--import-symbols");
     }
test/link/wasm/shared-memory/build.zig
@@ -0,0 +1,77 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const test_step = b.step("test", "Test");
+    b.default_step = test_step;
+
+    add(b, test_step, .Debug);
+
+    // Enable the following build modes once garbage-collection is implemented properly.
+    // add(b, test_step, .ReleaseFast);
+    // add(b, test_step, .ReleaseSmall);
+    // add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
+    {
+        const lib = b.addSharedLibrary(.{
+            .name = "lib",
+            .root_source_file = .{ .path = "lib.zig" },
+            .target = .{
+                .cpu_arch = .wasm32,
+                .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
+                .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
+                .os_tag = .freestanding,
+            },
+            .optimize = optimize_mode,
+        });
+        lib.use_lld = false;
+        lib.strip = false;
+        lib.import_memory = true;
+        lib.export_memory = true;
+        lib.shared_memory = true;
+        lib.max_memory = 67108864;
+        lib.single_threaded = false;
+        lib.export_symbol_names = &.{"foo"};
+
+        const check_lib = lib.checkObject();
+
+        check_lib.checkStart("Section import");
+        check_lib.checkNext("entries 1");
+        check_lib.checkNext("module env");
+        check_lib.checkNext("name memory"); // ensure we are importing memory
+
+        check_lib.checkStart("Section export");
+        check_lib.checkNext("entries 2");
+        check_lib.checkNext("name memory"); // ensure we also export memory again
+
+        // This section *must* be emit as the start function is set to the index
+        // of __wasm_init_memory
+        check_lib.checkStart("Section start");
+
+        // This section is only and *must* be emit when shared-memory is enabled
+        check_lib.checkStart("Section data_count");
+        check_lib.checkNext("count 3");
+
+        check_lib.checkStart("Section custom");
+        check_lib.checkNext("name name");
+        check_lib.checkNext("type function");
+        check_lib.checkNext("name __wasm_init_memory");
+        check_lib.checkNext("name __wasm_init_tls");
+        check_lib.checkNext("type global");
+        check_lib.checkNext("name __tls_size");
+        check_lib.checkNext("name __tls_align");
+        check_lib.checkNext("name __tls_base");
+
+        check_lib.checkNext("type data_segment");
+        check_lib.checkNext("names 3");
+        check_lib.checkNext("index 0");
+        check_lib.checkNext("name .rodata");
+        check_lib.checkNext("index 1");
+        check_lib.checkNext("name .bss");
+        check_lib.checkNext("index 2");
+        check_lib.checkNext("name .tdata");
+
+        test_step.dependOn(&check_lib.step);
+    }
+}
test/link/wasm/shared-memory/lib.zig
@@ -0,0 +1,5 @@
+threadlocal var some_tls_global: u32 = 1;
+
+export fn foo() void {
+    some_tls_global = 2;
+}